export.js 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525
  1. /*
  2. Plugin Name: amCharts Export
  3. Description: Adds export capabilities to amCharts products
  4. Author: Benjamin Maertz, amCharts
  5. Version: 1.1.3
  6. Author URI: http://www.amcharts.com/
  7. Copyright 2015 amCharts
  8. Licensed under the Apache License, Version 2.0 (the "License");
  9. you may not use this file except in compliance with the License.
  10. You may obtain a copy of the License at
  11. http://www.apache.org/licenses/LICENSE-2.0
  12. Unless required by applicable law or agreed to in writing, software
  13. distributed under the License is distributed on an "AS IS" BASIS,
  14. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. See the License for the specific language governing permissions and
  16. limitations under the License.
  17. Please note that the above license covers only this plugin. It by all means does
  18. not apply to any other amCharts products that are covered by different licenses.
  19. */
  20. AmCharts.addInitHandler( function( chart ) {
  21. var _this = {
  22. name: "export",
  23. version: "1.1.3",
  24. libs: {
  25. async: true,
  26. autoLoad: true,
  27. reload: false,
  28. path: ( ( chart.path || "" ) + "plugins/export/libs/" ),
  29. resources: [ {
  30. "pdfmake/pdfmake.js": [ "pdfmake/vfs_fonts.js" ],
  31. "jszip/jszip.js": [ "xlsx/xlsx.js" ]
  32. }, "fabric.js/fabric.js", "FileSaver.js/FileSaver.js" ]
  33. },
  34. config: {},
  35. setup: {
  36. hasBlob: false
  37. },
  38. drawing: {
  39. enabled: false,
  40. actions: [ "undo", "redo", "done", "cancel" ],
  41. undos: [],
  42. undo: function() {
  43. var last = _this.drawing.undos.pop();
  44. if ( last ) {
  45. _this.drawing.redos.push( last );
  46. last.path.remove();
  47. }
  48. },
  49. redos: [],
  50. redo: function() {
  51. var last = _this.drawing.redos.pop();
  52. if ( last ) {
  53. _this.setup.fabric.add( last.path );
  54. _this.drawing.undos.push( last );
  55. }
  56. },
  57. done: function() {
  58. _this.drawing.enabled = false;
  59. _this.drawing.undos = [];
  60. _this.drawing.redos = [];
  61. _this.createMenu( _this.config.menu );
  62. _this.setup.wrapper.setAttribute( "class", _this.setup.chart.classNamePrefix + "-export-canvas" );
  63. }
  64. },
  65. defaults: {
  66. position: "top-right",
  67. fileName: "amCharts",
  68. action: "download",
  69. formats: {
  70. JPG: {
  71. mimeType: "image/jpg",
  72. extension: "jpg",
  73. capture: true
  74. },
  75. PNG: {
  76. mimeType: "image/png",
  77. extension: "png",
  78. capture: true
  79. },
  80. SVG: {
  81. mimeType: "text/xml",
  82. extension: "svg",
  83. capture: true
  84. },
  85. PDF: {
  86. mimeType: "application/pdf",
  87. extension: "pdf",
  88. capture: true
  89. },
  90. CSV: {
  91. mimeType: "text/plain",
  92. extension: "csv"
  93. },
  94. JSON: {
  95. mimeType: "text/plain",
  96. extension: "json"
  97. },
  98. XLSX: {
  99. mimeType: "application/octet-stream",
  100. extension: "xlsx"
  101. }
  102. },
  103. fabric: {
  104. backgroundColor: "#FFFFFF",
  105. isDrawingMode: false,
  106. selection: false,
  107. removeImages: true
  108. },
  109. pdfMake: {
  110. pageSize: "A4",
  111. pageOrientation: "portrait",
  112. images: {},
  113. content: [ {
  114. image: "reference",
  115. fit: [ 523.28, 769.89 ]
  116. } ]
  117. },
  118. divId: null,
  119. menuReviver: null,
  120. menuWalker: null,
  121. menu: [ {
  122. "class": "export-main",
  123. label: "Export",
  124. menu: [ {
  125. label: "Download as ...",
  126. menu: [ "PNG", "JPG", "SVG", {
  127. format: "PDF",
  128. content: [ "Saved from:", window.location.href, {
  129. image: "reference",
  130. fit: [ 523.28, 769.89 ] // fit image to A4
  131. } ]
  132. } ]
  133. }, {
  134. label: "Save data ...",
  135. menu: [ "CSV", "XLSX", "JSON" ]
  136. }, {
  137. label: "Annotate",
  138. action: "draw",
  139. menu: [ {
  140. "class": "export-drawing",
  141. menu: [ {
  142. label: "Color ...",
  143. menu: [ {
  144. "class": "export-drawing-color export-drawing-color-black",
  145. label: "Black",
  146. click: function() {
  147. this.setup.fabric.freeDrawingBrush.color = "#000";
  148. }
  149. }, {
  150. "class": "export-drawing-color export-drawing-color-white",
  151. label: "White",
  152. click: function() {
  153. this.setup.fabric.freeDrawingBrush.color = "#fff";
  154. }
  155. }, {
  156. "class": "export-drawing-color export-drawing-color-red",
  157. label: "Red",
  158. click: function() {
  159. this.setup.fabric.freeDrawingBrush.color = "#f00";
  160. }
  161. }, {
  162. "class": "export-drawing-color export-drawing-color-green",
  163. label: "Green",
  164. click: function() {
  165. this.setup.fabric.freeDrawingBrush.color = "#0f0";
  166. }
  167. }, {
  168. "class": "export-drawing-color export-drawing-color-blue",
  169. label: "Blue",
  170. click: function() {
  171. this.setup.fabric.freeDrawingBrush.color = "#00f";
  172. }
  173. } ]
  174. }, "UNDO", "REDO", {
  175. label: "Save as ...",
  176. menu: [ "PNG", "JPG", "SVG", {
  177. format: "PDF",
  178. content: [ "Saved from:", window.location.href, {
  179. image: "reference",
  180. fit: [ 523.28, 769.89 ] // fit image to A4
  181. } ]
  182. } ]
  183. }, {
  184. format: "PRINT",
  185. label: "Print"
  186. }, "CANCEL" ]
  187. } ]
  188. }, {
  189. format: "PRINT",
  190. label: "Print"
  191. } ]
  192. } ],
  193. timer: 0,
  194. fallback: {
  195. text: "CTRL + C to copy the data into the clipboard.",
  196. image: "Rightclick -> Save picture as... to save the image."
  197. }
  198. },
  199. download: function( data, type, filename ) {
  200. // SAVE
  201. if ( window.saveAs && _this.setup.hasBlob ) {
  202. var blob = _this.toBlob( {
  203. data: data,
  204. type: type
  205. }, function( data ) {
  206. saveAs( data, filename );
  207. } );
  208. // FALLBACK TEXTAREA
  209. } else if ( _this.config.fallback && type == "text/plain" ) {
  210. var div = document.createElement( "div" );
  211. var msg = document.createElement( "div" );
  212. var textarea = document.createElement( "textarea" );
  213. msg.innerHTML = _this.config.fallback.text;
  214. div.appendChild( msg );
  215. div.appendChild( textarea );
  216. msg.setAttribute( "class", "amcharts-export-fallback-message" );
  217. div.setAttribute( "class", "amcharts-export-fallback" );
  218. _this.setup.chart.containerDiv.appendChild( div );
  219. // Fulfill textarea and preselect
  220. textarea.setAttribute( "readonly", "" );
  221. textarea.value = data;
  222. textarea.focus();
  223. textarea.select();
  224. // Update menu
  225. _this.createMenu( [ {
  226. "class": "export-main export-close",
  227. label: "Done",
  228. click: function() {
  229. _this.createMenu( _this.config.menu );
  230. _this.setup.chart.containerDiv.removeChild( div );
  231. }
  232. } ] );
  233. // FALLBACK IMAGE
  234. } else if ( _this.config.fallback && type.split( "/" )[ 0 ] == "image" ) {
  235. var div = document.createElement( "div" );
  236. var msg = document.createElement( "div" );
  237. var img = _this.toImage( {
  238. data: data
  239. } );
  240. msg.innerHTML = _this.config.fallback.image;
  241. // Fulfill textarea and preselect
  242. div.appendChild( msg );
  243. div.appendChild( img );
  244. msg.setAttribute( "class", "amcharts-export-fallback-message" );
  245. div.setAttribute( "class", "amcharts-export-fallback" );
  246. _this.setup.chart.containerDiv.appendChild( div );
  247. // Update menu
  248. _this.createMenu( [ {
  249. "class": "export-main export-close",
  250. label: "Done",
  251. click: function() {
  252. _this.createMenu( _this.config.menu );
  253. _this.setup.chart.containerDiv.removeChild( div );
  254. }
  255. } ] );
  256. // ERROR
  257. } else {
  258. throw new Error( "Unable to create file. Ensure saveAs (FileSaver.js) is supported." );
  259. }
  260. return data;
  261. },
  262. loadResource: function( src, addons ) {
  263. var i1, exist, node, item, check, type;
  264. var url = src.indexOf( "//" ) != -1 ? src : [ _this.libs.path, src ].join( "" );
  265. function callback() {
  266. if ( addons ) {
  267. for ( i1 = 0; i1 < addons.length; i1++ ) {
  268. _this.loadResource( addons[ i1 ] );
  269. }
  270. }
  271. }
  272. if ( src.indexOf( ".js" ) != -1 ) {
  273. node = document.createElement( "script" );
  274. node.setAttribute( "type", "text/javascript" );
  275. node.setAttribute( "src", url );
  276. if ( _this.libs.async ) {
  277. node.setAttribute( "async", "" );
  278. }
  279. } else if ( src.indexOf( ".css" ) != -1 ) {
  280. node = document.createElement( "link" );
  281. node.setAttribute( "type", "text/css" );
  282. node.setAttribute( "rel", "stylesheet" );
  283. node.setAttribute( "href", url );
  284. }
  285. for ( i1 = 0; i1 < document.head.childNodes.length; i1++ ) {
  286. item = document.head.childNodes[ i1 ];
  287. check = item ? ( item.src || item.href ) : false;
  288. type = item ? item.tagName : false;
  289. if ( item && check && check.indexOf( src ) != -1 ) {
  290. if ( _this.libs.reload ) {
  291. document.head.removeChild( item );
  292. }
  293. exist = true;
  294. break;
  295. }
  296. }
  297. if ( !exist || _this.libs.reload ) {
  298. node.addEventListener( "load", callback );
  299. document.head.appendChild( node );
  300. }
  301. },
  302. loadDependencies: function() {
  303. var i1, i2;
  304. if ( _this.libs.autoLoad ) {
  305. for ( i1 = 0; i1 < _this.libs.resources.length; i1++ ) {
  306. if ( _this.libs.resources[ i1 ] instanceof Object ) {
  307. for ( i2 in _this.libs.resources[ i1 ] ) {
  308. _this.loadResource( i2, _this.libs.resources[ i1 ][ i2 ] );
  309. }
  310. } else {
  311. _this.loadResource( _this.libs.resources[ i1 ] );
  312. }
  313. }
  314. }
  315. },
  316. pxToNumber: function( attr ) {
  317. return Number( String( attr ).replace( "px", "" ) ) || 0;
  318. },
  319. deepMerge: function( a, b, overwrite ) {
  320. var i1, v, type = b instanceof Array ? "array" : "object";
  321. for ( i1 in b ) {
  322. // PREVENT METHODS
  323. if ( type == "array" && isNaN( i1 ) ) {
  324. continue;
  325. }
  326. v = b[ i1 ];
  327. // NEW
  328. if ( a[ i1 ] == undefined || overwrite ) {
  329. if ( v instanceof Array ) {
  330. a[ i1 ] = new Array();
  331. } else if ( v instanceof Function ) {
  332. a[ i1 ] = new Function();
  333. } else if ( v instanceof Date ) {
  334. a[ i1 ] = new Date();
  335. } else if ( v instanceof Object ) {
  336. a[ i1 ] = new Object();
  337. } else if ( v instanceof Number ) {
  338. a[ i1 ] = new Number();
  339. } else if ( v instanceof String ) {
  340. a[ i1 ] = new String();
  341. }
  342. }
  343. if ( !( v instanceof Function || v instanceof Date || _this.isElement( v ) ) && ( v instanceof Object || v instanceof Array ) ) {
  344. _this.deepMerge( a[ i1 ], v, overwrite );
  345. } else {
  346. if ( a instanceof Array && !overwrite ) {
  347. a.push( v );
  348. } else {
  349. a[ i1 ] = v;
  350. }
  351. }
  352. }
  353. return a;
  354. },
  355. // CHECK IF IT'S AN ELEMENT
  356. isElement: function( thingy ) {
  357. return thingy instanceof Object && thingy && thingy.nodeType === 1;
  358. },
  359. // CHECK IF TAINTED
  360. isTainted: function( source ) {
  361. if (
  362. source &&
  363. source.indexOf( "//" ) != -1 &&
  364. source.indexOf( location.origin ) == -1
  365. ) {
  366. return true;
  367. }
  368. return false;
  369. },
  370. // CAPTURE EMOTIONAL MOMENT
  371. capture: function( options, callback ) {
  372. var i1, i2, i3;
  373. var cfg = _this.deepMerge( _this.deepMerge( {}, _this.config.fabric ), options || {} );
  374. var groups = [];
  375. var offset = {
  376. x: 0,
  377. y: 0,
  378. width: _this.setup.chart.divRealWidth,
  379. height: _this.setup.chart.divRealHeight
  380. };
  381. // GATHER SVGs
  382. var svgs = _this.setup.chart.containerDiv.getElementsByTagName( "svg" );
  383. for ( i1 = 0; i1 < svgs.length; i1++ ) {
  384. var group = {
  385. svg: svgs[ i1 ],
  386. parent: svgs[ i1 ].parentNode,
  387. children: svgs[ i1 ].getElementsByTagName( "*" ),
  388. offset: {
  389. x: 0,
  390. y: 0
  391. },
  392. patterns: {},
  393. clippings: {}
  394. }
  395. for ( i2 = 0; i2 < group.children.length; i2++ ) {
  396. var childNode = group.children[ i2 ];
  397. // CLIPPATH
  398. if ( childNode.tagName == "clipPath" ) {
  399. for ( i3 = 0; i3 < childNode.childNodes.length; i3++ ) {
  400. childNode.childNodes[ i3 ].setAttribute( "fill", "transparent" );
  401. }
  402. group.clippings[ childNode.id ] = childNode;
  403. // PATTERN
  404. } else if ( childNode.tagName == "pattern" ) {
  405. for ( i3 = 0; i3 < childNode.childNodes.length; i3++ ) {
  406. var props = {
  407. node: childNode,
  408. source: childNode.getAttribute( "xlink:href" ),
  409. width: Number( childNode.getAttribute( "width" ) ),
  410. height: Number( childNode.getAttribute( "height" ) ),
  411. repeat: "repeat"
  412. }
  413. // TAINTED
  414. if ( cfg.removeImages && _this.isTainted( props.source ) ) {
  415. group.patterns[ childNode.id ] = "transparent";
  416. // REPLACE SOURCE
  417. } else {
  418. var rect = childNode.getElementsByTagName( "rect" );
  419. if ( rect.length > 0 ) {
  420. var IMG = new Image();
  421. IMG.src = props.source;
  422. var PSC = new fabric.StaticCanvas( undefined, {
  423. width: props.width,
  424. height: props.height,
  425. backgroundColor: rect[ 0 ].getAttribute( "fill" )
  426. } );
  427. var RECT = new fabric.Rect( {
  428. width: props.width,
  429. height: props.height,
  430. fill: new fabric.Pattern( {
  431. source: IMG,
  432. repeat: "repeat"
  433. } )
  434. } );
  435. PSC.add( RECT );
  436. props.source = PSC.toDataURL();
  437. }
  438. // BUFFER PATTERN
  439. group.patterns[ childNode.id ] = new fabric.Pattern( props );
  440. }
  441. }
  442. }
  443. }
  444. // APPEND GROUP
  445. groups.push( group );
  446. }
  447. // GATHER EXTERNAL LEGEND
  448. if ( _this.config.legend && _this.setup.chart.legend && _this.setup.chart.legend.position == "outside" ) {
  449. var group = {
  450. svg: _this.setup.chart.legend.container.container,
  451. parent: _this.setup.chart.legend.container.div,
  452. offset: {
  453. x: 0,
  454. y: 0
  455. },
  456. legend: {
  457. type: [ "top", "left" ].indexOf( _this.config.legend.position ) != -1 ? "unshift" : "push",
  458. position: _this.config.legend.position,
  459. width: _this.config.legend.width ? _this.config.legend.width : _this.setup.chart.legend.container.width,
  460. height: _this.config.legend.height ? _this.config.legend.height : _this.setup.chart.legend.container.height
  461. }
  462. }
  463. // Adapt canvas dimensions
  464. if ( [ "left", "right" ].indexOf( group.legend.position ) != -1 ) {
  465. offset.width += group.legend.width;
  466. offset.height = group.legend.height > offset.height ? group.legend.height : offset.height;
  467. } else if ( [ "top", "bottom" ].indexOf( group.legend.position ) != -1 ) {
  468. offset.height += group.legend.height;
  469. }
  470. // PRE/APPEND SVG
  471. groups[ group.legend.type ]( group );
  472. }
  473. // STOCK CHART
  474. if ( _this.setup.chart.type == "stock" ) {
  475. if ( _this.setup.chart.leftContainer ) {
  476. offset.width -= _this.pxToNumber( _this.setup.chart.leftContainer.style.width );
  477. _this.setup.wrapper.style.paddingLeft = _this.pxToNumber( _this.setup.chart.leftContainer.style.width ) + _this.setup.chart.panelsSettings.panelSpacing * 2;
  478. }
  479. if ( _this.setup.chart.rightContainer ) {
  480. offset.width -= _this.pxToNumber( _this.setup.chart.rightContainer.style.width );
  481. _this.setup.wrapper.style.paddingRight = _this.pxToNumber( _this.setup.chart.rightContainer.style.width ) + _this.setup.chart.panelsSettings.panelSpacing * 2;
  482. }
  483. if ( _this.setup.chart.periodSelector && [ "top", "bottom" ].indexOf( _this.setup.chart.periodSelector.position ) != -1 ) {
  484. offset.height -= _this.setup.chart.periodSelector.offsetHeight + _this.setup.chart.panelsSettings.panelSpacing;
  485. }
  486. if ( _this.setup.chart.dataSetSelector && [ "top", "bottom" ].indexOf( _this.setup.chart.dataSetSelector.position ) != -1 ) {
  487. offset.height -= _this.setup.chart.dataSetSelector.offsetHeight;
  488. }
  489. }
  490. // CLEAR IF EXIST
  491. _this.drawing.enabled = cfg.isDrawingMode = ( cfg.drawing && cfg.drawing.enabled ) ? true : cfg.action == "draw";
  492. if ( !_this.setup.wrapper ) {
  493. _this.setup.wrapper = document.createElement( "div" );
  494. _this.setup.wrapper.setAttribute( "class", _this.setup.chart.classNamePrefix + "-export-canvas" );
  495. _this.setup.wrapper.appendChild( _this.setup.canvas );
  496. } else {
  497. _this.setup.wrapper.innerHTML = "";
  498. }
  499. _this.setup.canvas = document.createElement( "canvas" );
  500. _this.setup.wrapper.appendChild( _this.setup.canvas );
  501. _this.setup.fabric = new fabric.Canvas( _this.setup.canvas, _this.deepMerge( {
  502. width: offset.width,
  503. height: offset.height
  504. }, cfg ) );
  505. _this.deepMerge( _this.setup.fabric, cfg );
  506. // OBSERVE MOUSE
  507. _this.setup.fabric.on( "path:created", function( path ) {
  508. _this.drawing.undos.push( path );
  509. } );
  510. // DRAWING
  511. if ( _this.drawing.enabled ) {
  512. _this.setup.wrapper.setAttribute( "class", _this.setup.chart.classNamePrefix + "-export-canvas active" );
  513. _this.setup.wrapper.style.backgroundColor = cfg.backgroundColor;
  514. } else {
  515. _this.setup.wrapper.setAttribute( "class", _this.setup.chart.classNamePrefix + "-export-canvas" );
  516. }
  517. for ( i1 = 0; i1 < groups.length; i1++ ) {
  518. var group = groups[ i1 ];
  519. // GATHER POSITION
  520. if ( group.parent.style.top || group.parent.style.left ) {
  521. group.offset.y = _this.pxToNumber( group.parent.style.top );
  522. group.offset.x = _this.pxToNumber( group.parent.style.left );
  523. } else {
  524. // EXTERNAL LEGEND
  525. if ( group.legend ) {
  526. if ( group.legend.position == "left" ) {
  527. offset.x += chart.legend.container.width;
  528. } else if ( group.legend.position == "right" ) {
  529. group.offset.x += offset.width - group.legend.width;
  530. } else if ( group.legend.position == "top" ) {
  531. offset.y += group.legend.height;
  532. } else if ( group.legend.position == "bottom" ) {
  533. group.offset.y += offset.height - group.legend.height; // offset.y
  534. }
  535. // NORMAL
  536. } else {
  537. group.offset.x = offset.x;
  538. group.offset.y = offset.y;
  539. offset.y += _this.pxToNumber( group.parent.style.height );
  540. }
  541. // PANEL
  542. if ( group.parent && ( group.parent.getAttribute( "class" ) || "" ).split( " " ).indexOf( "amChartsLegend" ) != -1 ) {
  543. offset.y += _this.pxToNumber( group.parent.parentNode.parentNode.style.marginTop );
  544. group.offset.y += _this.pxToNumber( group.parent.parentNode.parentNode.style.marginTop );
  545. }
  546. }
  547. // ADD TO CANVAS
  548. fabric.parseSVGDocument( group.svg, ( function( group ) {
  549. return function( objects, options ) {
  550. var i1;
  551. var g = fabric.util.groupSVGElements( objects, options );
  552. var tmp = {
  553. top: group.offset.y,
  554. left: group.offset.x
  555. };
  556. for ( i1 = 0; i1 < g.paths.length; i1++ ) {
  557. // OPACITY; TODO: Distinguish opacity types
  558. if ( g.paths[ i1 ] ) {
  559. // CHECK ORIGIN; REMOVE TAINTED
  560. if ( cfg.removeImages && _this.isTainted( g.paths[ i1 ][ "xlink:href" ] ) ) {
  561. g.paths.splice( i1, 1 );
  562. continue;
  563. }
  564. // SET OPACITY
  565. if ( g.paths[ i1 ].fill instanceof Object ) {
  566. // MISINTERPRETATION OF FABRIC
  567. if ( g.paths[ i1 ].fill.type == "radial" ) {
  568. g.paths[ i1 ].fill.coords.r2 = g.paths[ i1 ].fill.coords.r1 * -1;
  569. g.paths[ i1 ].fill.coords.r1 = 0;
  570. }
  571. g.paths[ i1 ].set( {
  572. opacity: g.paths[ i1 ].fillOpacity
  573. } );
  574. // PATTERN; TODO: Distinguish opacity types
  575. } else if ( String( g.paths[ i1 ].fill ).slice( 0, 3 ) == "url" ) {
  576. var PID = g.paths[ i1 ].fill.slice( 5, -1 );
  577. if ( group.patterns[ PID ] ) {
  578. g.paths[ i1 ].set( {
  579. fill: group.patterns[ PID ],
  580. opacity: g.paths[ i1 ].fillOpacity
  581. } );
  582. }
  583. }
  584. if ( String( g.paths[ i1 ].clipPath ).slice( 0, 3 ) == "url" ) {
  585. var PID = g.paths[ i1 ].clipPath.slice( 5, -1 );
  586. if ( group.clippings[ PID ] ) {
  587. var mask = group.clippings[ PID ].childNodes[ 0 ];
  588. var transform = g.paths[ i1 ].svg.getAttribute( "transform" ) || "translate(0,0)";
  589. transform = transform.slice( 10, -1 ).split( "," );
  590. g.paths[ i1 ].set( {
  591. clipTo: ( function( mask, transform ) {
  592. return function( ctx ) {
  593. var width = Number( mask.getAttribute( "width" ) || "0" );
  594. var height = Number( mask.getAttribute( "height" ) || "0" );
  595. var x = Number( mask.getAttribute( "x" ) || "0" );
  596. var y = Number( mask.getAttribute( "y" ) || "0" );
  597. ctx.rect( Number( transform[ 0 ] ) * -1 + x, Number( transform[ 1 ] ) * -1 + y, width, height );
  598. }
  599. } )( mask, transform )
  600. } );
  601. }
  602. }
  603. }
  604. }
  605. g.set( tmp );
  606. _this.setup.fabric.add( g );
  607. // ADD BALLOONS
  608. if ( group.svg.parentNode && group.svg.parentNode.getElementsByTagName ) {
  609. var balloons = group.svg.parentNode.getElementsByClassName( _this.setup.chart.classNamePrefix + "-balloon-div" );
  610. for ( i1 = 0; i1 < balloons.length; i1++ ) {
  611. if ( cfg.balloonFunction instanceof Function ) {
  612. cfg.balloonFunction.apply( _this, [ balloons[ i1 ], group ] );
  613. } else {
  614. var parent = balloons[ i1 ];
  615. var text = parent.childNodes[ 0 ];
  616. var label = new fabric.Text( text.innerText || text.innerHTML, {
  617. fontSize: _this.pxToNumber( text.style.fontSize ),
  618. fontFamily: text.style.fontFamily,
  619. fill: text.style.color,
  620. top: _this.pxToNumber( parent.style.top ) + group.offset.y,
  621. left: _this.pxToNumber( parent.style.left ) + group.offset.x
  622. } );
  623. _this.setup.fabric.add( label );
  624. }
  625. }
  626. }
  627. if ( group.svg.nextSibling && group.svg.nextSibling.tagName == "A" ) {
  628. var label = new fabric.Text( group.svg.nextSibling.innerText || group.svg.nextSibling.innerHTML, {
  629. fontSize: _this.pxToNumber( group.svg.nextSibling.style.fontSize ),
  630. fontFamily: group.svg.nextSibling.style.fontFamily,
  631. fill: group.svg.nextSibling.style.color,
  632. top: _this.pxToNumber( group.svg.nextSibling.style.top ) + group.offset.y,
  633. left: _this.pxToNumber( group.svg.nextSibling.style.left ) + group.offset.x
  634. } );
  635. _this.setup.fabric.add( label );
  636. }
  637. groups.pop();
  638. // Trigger callback with safety delay
  639. if ( !groups.length ) {
  640. setTimeout( function() {
  641. _this.setup.fabric.renderAll();
  642. _this.handleCallback( callback );
  643. }, 1 );
  644. }
  645. }
  646. // Identify elements through classnames
  647. } )( group ), function( svg, obj ) {
  648. var i1;
  649. var className = svg.getAttribute( "class" ) || svg.parentNode.getAttribute( "class" ) || "";
  650. var visibility = svg.getAttribute( "visibility" ) || svg.parentNode.getAttribute( "visibility" ) || svg.parentNode.parentNode.getAttribute( "visibility" ) || "";
  651. var clipPath = svg.getAttribute( "clip-path" ) || svg.parentNode.getAttribute( "clip-path" ) || "";
  652. obj.className = className;
  653. obj.clipPath = clipPath;
  654. obj.svg = svg;
  655. // HIDE HIDDEN ELEMENTS; TODO: Find a better way to handle that
  656. if ( visibility == "hidden" ) {
  657. obj.opacity = 0;
  658. // WALKTHROUGH ELEMENTS
  659. } else {
  660. // TRANSPORT FILL/STROKE OPACITY
  661. var attrs = [ "fill", "stroke" ];
  662. for ( i1 = 0; i1 < attrs.length; i1++ ) {
  663. var attr = attrs[ i1 ]
  664. var attrVal = String( svg.getAttribute( attr ) || "" );
  665. var attrOpacity = Number( svg.getAttribute( attr + "-opacity" ) || "1" );
  666. var attrRGBA = fabric.Color.fromHex( attrVal ).getSource();
  667. // EXCEPTION
  668. if ( obj.className == _this.setup.chart.classNamePrefix + "-guide-fill" && !attrVal ) {
  669. attrOpacity = 0;
  670. attrRGBA = fabric.Color.fromHex( "#000000" ).getSource();
  671. }
  672. if ( attrRGBA ) {
  673. attrRGBA.pop();
  674. attrRGBA.push( attrOpacity )
  675. obj[ attr ] = "rgba(" + attrRGBA.join() + ")";
  676. obj[ _this.capitalize( attr + "-opacity" ) ] = attrOpacity;
  677. }
  678. }
  679. }
  680. // REVIVER
  681. if ( cfg.reviver && cfg.reviver instanceof Function ) {
  682. cfg.reviver.apply( _this, [ obj ] );
  683. }
  684. } );
  685. }
  686. },
  687. toCanvas: function( options, callback ) {
  688. var cfg = _this.deepMerge( {
  689. // Nuffin
  690. }, options || {} );
  691. var data = _this.setup.canvas;
  692. _this.handleCallback( callback, data );
  693. return data;
  694. },
  695. toImage: function( options, callback ) {
  696. var cfg = _this.deepMerge( {
  697. format: "png",
  698. quality: 1,
  699. multiplier: 1
  700. }, options || {} );
  701. var data = cfg.data;
  702. var img = document.createElement( "img" );
  703. if ( !cfg.data ) {
  704. if ( cfg.lossless || cfg.format == "svg" ) {
  705. data = _this.toSVG( _this.deepMerge( cfg, {
  706. getBase64: true
  707. } ) );
  708. } else {
  709. data = _this.setup.fabric.toDataURL( cfg );
  710. }
  711. }
  712. img.setAttribute( "src", data );
  713. _this.handleCallback( callback, img );
  714. return img;
  715. },
  716. toBlob: function( options, callback ) {
  717. var cfg = _this.deepMerge( {
  718. data: "empty",
  719. type: "text/plain"
  720. }, options || {} );
  721. var isBase64 = /^data:.+;base64,(.*)$/.exec( cfg.data );
  722. // GATHER BODY
  723. if ( isBase64 ) {
  724. cfg.data = isBase64[ 0 ];
  725. cfg.type = cfg.data.slice( 5, cfg.data.indexOf( "," ) - 7 );
  726. cfg.data = _this.toByteArray( {
  727. data: cfg.data.slice( cfg.data.indexOf( "," ) + 1, cfg.data.length )
  728. } );
  729. }
  730. if ( cfg.getByteArray ) {
  731. data = cfg.data;
  732. } else {
  733. data = new Blob( [ cfg.data ], {
  734. type: cfg.type
  735. } );
  736. }
  737. _this.handleCallback( callback, data );
  738. return data;
  739. },
  740. toJPG: function( options, callback ) {
  741. var cfg = _this.deepMerge( {
  742. format: "jpeg",
  743. quality: 1,
  744. multiplier: 1
  745. }, options || {} );
  746. var data = _this.setup.fabric.toDataURL( cfg );
  747. _this.handleCallback( callback, data );
  748. return data;
  749. },
  750. toPNG: function( options, callback ) {
  751. var cfg = _this.deepMerge( {
  752. format: "png",
  753. quality: 1,
  754. multiplier: 1
  755. }, options || {} );
  756. var data = _this.setup.fabric.toDataURL( cfg );
  757. _this.handleCallback( callback, data );
  758. return data;
  759. },
  760. toSVG: function( options, callback ) {
  761. var cfg = _this.deepMerge( {
  762. // nothing in here
  763. }, options || {} );
  764. var data = _this.setup.fabric.toSVG( cfg );
  765. if ( cfg.getBase64 ) {
  766. data = "data:image/svg+xml;base64," + btoa( data );
  767. }
  768. _this.handleCallback( callback, data );
  769. return data;
  770. },
  771. toPDF: function( options, callback ) {
  772. var cfg = _this.deepMerge( _this.deepMerge( {
  773. multiplier: 2
  774. }, _this.config.pdfMake ), options || {}, true );
  775. cfg.images.reference = _this.toPNG( cfg );
  776. var data = new pdfMake.createPdf( cfg );
  777. if ( callback ) {
  778. data.getDataUrl( ( function( callback ) {
  779. return function() {
  780. callback.apply( _this, arguments );
  781. }
  782. } )( callback ) );
  783. }
  784. return data;
  785. },
  786. toPRINT: function( options, callback ) {
  787. var i1;
  788. var cfg = _this.deepMerge( {
  789. delay: 1,
  790. lossless: false
  791. }, options || {} );
  792. var data = _this.toImage( cfg );
  793. var states = [];
  794. var items = document.body.childNodes;
  795. data.setAttribute( "style", "width: 100%; max-height: 100%;" );
  796. for ( i1 = 0; i1 < items.length; i1++ ) {
  797. if ( _this.isElement( items[ i1 ] ) ) {
  798. states[ i1 ] = items[ i1 ].style.display;
  799. items[ i1 ].style.display = "none";
  800. }
  801. }
  802. document.body.appendChild( data );
  803. window.print();
  804. setTimeout( function() {
  805. for ( i1 = 0; i1 < items.length; i1++ ) {
  806. if ( _this.isElement( items[ i1 ] ) ) {
  807. items[ i1 ].style.display = states[ i1 ];
  808. }
  809. }
  810. document.body.removeChild( data );
  811. _this.handleCallback( callback, data );
  812. }, cfg.delay );
  813. return data;
  814. },
  815. toJSON: function( options, callback ) {
  816. var cfg = _this.deepMerge( {
  817. data: _this.getChartData()
  818. }, options || {}, true );
  819. var data = JSON.stringify( cfg.data, undefined, "\t" );
  820. _this.handleCallback( callback, data );
  821. return data;
  822. },
  823. toCSV: function( options, callback ) {
  824. var row, col;
  825. var cfg = _this.deepMerge( {
  826. data: _this.getChartData(),
  827. delimiter: ",",
  828. quotes: true,
  829. escape: true,
  830. dateFields: [],
  831. dateFormat: _this.setup.chart.dataDateFormat || "YYYY-MM-DD"
  832. }, options || {}, true );
  833. var data = "";
  834. var cols = [];
  835. var buffer = [];
  836. if ( _this.setup.chart.categoryAxis && _this.setup.chart.categoryAxis.parseDates && _this.setup.chart.categoryField ) {
  837. cfg.dateFields.push( _this.setup.chart.categoryField );
  838. }
  839. function enchant( value, column ) {
  840. // STRING
  841. if ( typeof value === "string" ) {
  842. value = value;
  843. // DATE FORMAT
  844. } else if ( column && cfg.dateFormat && value instanceof Date && cfg.dateFields.indexOf( column ) != -1 ) {
  845. value = AmCharts.formatDate( value, cfg.dateFormat );
  846. }
  847. // WRAP IN QUOTES
  848. if ( typeof value === "string" ) {
  849. if ( cfg.escape ) {
  850. value = value.replace( '"', '""' );
  851. }
  852. if ( cfg.quotes ) {
  853. value = [ '"', value, '"' ].join( "" );
  854. }
  855. }
  856. return value;
  857. }
  858. // HEADER
  859. for ( value in cfg.data[ 0 ] ) {
  860. buffer.push( enchant( value ) );
  861. cols.push( value );
  862. }
  863. data += buffer.join( cfg.delimiter ) + "\n";
  864. // BODY
  865. for ( row in cfg.data ) {
  866. buffer = [];
  867. if ( !isNaN( row ) ) {
  868. for ( col in cols ) {
  869. if ( !isNaN( col ) ) {
  870. var column = cols[ col ];
  871. var value = cfg.data[ row ][ column ];
  872. buffer.push( enchant( value, column ) );
  873. }
  874. }
  875. data += buffer.join( cfg.delimiter ) + "\n";
  876. }
  877. }
  878. _this.handleCallback( callback, data );
  879. return data;
  880. },
  881. toXLSX: function( options, callback ) {
  882. var cfg = _this.deepMerge( {
  883. data: _this.getChartData(),
  884. name: "amCharts",
  885. withHeader: true
  886. }, options || {}, true );
  887. var data = "";
  888. var wb = {
  889. SheetNames: [],
  890. Sheets: {}
  891. }
  892. function datenum( v, date1904 ) {
  893. if ( date1904 ) v += 1462;
  894. var epoch = Date.parse( v );
  895. return ( epoch - new Date( Date.UTC( 1899, 11, 30 ) ) ) / ( 24 * 60 * 60 * 1000 );
  896. }
  897. function sheet_from_array_of_arrays( data, opts ) {
  898. var ws = {};
  899. var range = {
  900. s: {
  901. c: 10000000,
  902. r: 10000000
  903. },
  904. e: {
  905. c: 0,
  906. r: 0
  907. }
  908. };
  909. for ( var R = 0; R != data.length; ++R ) {
  910. for ( var C = 0; C != data[ R ].length; ++C ) {
  911. if ( range.s.r > R ) range.s.r = R;
  912. if ( range.s.c > C ) range.s.c = C;
  913. if ( range.e.r < R ) range.e.r = R;
  914. if ( range.e.c < C ) range.e.c = C;
  915. var cell = {
  916. v: data[ R ][ C ]
  917. };
  918. if ( cell.v == null ) continue;
  919. var cell_ref = XLSX.utils.encode_cell( {
  920. c: C,
  921. r: R
  922. } );
  923. if ( typeof cell.v === "number" ) cell.t = "n";
  924. else if ( typeof cell.v === "boolean" ) cell.t = "b";
  925. else if ( cell.v instanceof Date ) {
  926. cell.t = "n";
  927. cell.z = XLSX.SSF._table[ 14 ];
  928. cell.v = datenum( cell.v );
  929. } else cell.t = "s";
  930. ws[ cell_ref ] = cell;
  931. }
  932. }
  933. if ( range.s.c < 10000000 ) ws[ "!ref" ] = XLSX.utils.encode_range( range );
  934. return ws;
  935. }
  936. wb.SheetNames.push( cfg.name );
  937. wb.Sheets[ cfg.name ] = sheet_from_array_of_arrays( _this.toArray( cfg ) );
  938. data = XLSX.write( wb, {
  939. bookType: "xlsx",
  940. bookSST: true,
  941. type: "base64"
  942. } );
  943. data = "data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64," + data;
  944. _this.handleCallback( callback, data );
  945. return data;
  946. },
  947. toArray: function( options, callback ) {
  948. var row, col;
  949. var cfg = _this.deepMerge( {
  950. data: _this.getChartData(),
  951. dateFields: [],
  952. dateFormat: false,
  953. withHeader: false
  954. }, options || {}, true );
  955. var data = [];
  956. var cols = [];
  957. // HEADER
  958. if ( cfg.withHeader ) {
  959. for ( col in cfg.data[ 0 ] ) {
  960. cols.push( col );
  961. }
  962. data.push( cols );
  963. }
  964. // BODY
  965. for ( row in cfg.data ) {
  966. var buffer = [];
  967. if ( !isNaN( row ) ) {
  968. for ( col in cols ) {
  969. var col = cols[ col ];
  970. var value = cfg.data[ row ][ col ] || "";
  971. if ( cfg.dateFormat && value instanceof Date && cfg.dateFields.indexOf( col ) != -1 ) {
  972. value = AmCharts.formatDate( value, cfg.dateFormat );
  973. } else {
  974. value = String( value )
  975. }
  976. buffer.push( value );
  977. }
  978. data.push( buffer );
  979. }
  980. }
  981. _this.handleCallback( callback, data );
  982. return data;
  983. },
  984. toByteArray: function( options, callback ) {
  985. var cfg = _this.deepMerge( {
  986. // Nuffin
  987. }, options || {} );
  988. var Arr = ( typeof Uint8Array !== 'undefined' ) ? Uint8Array : Array
  989. var PLUS = '+'.charCodeAt( 0 )
  990. var SLASH = '/'.charCodeAt( 0 )
  991. var NUMBER = '0'.charCodeAt( 0 )
  992. var LOWER = 'a'.charCodeAt( 0 )
  993. var UPPER = 'A'.charCodeAt( 0 )
  994. var data = b64ToByteArray( cfg.data );
  995. function decode( elt ) {
  996. var code = elt.charCodeAt( 0 )
  997. if ( code === PLUS )
  998. return 62 // '+'
  999. if ( code === SLASH )
  1000. return 63 // '/'
  1001. if ( code < NUMBER )
  1002. return -1 //no match
  1003. if ( code < NUMBER + 10 )
  1004. return code - NUMBER + 26 + 26
  1005. if ( code < UPPER + 26 )
  1006. return code - UPPER
  1007. if ( code < LOWER + 26 )
  1008. return code - LOWER + 26
  1009. }
  1010. function b64ToByteArray( b64 ) {
  1011. var i, j, l, tmp, placeHolders, arr
  1012. if ( b64.length % 4 > 0 ) {
  1013. throw new Error( 'Invalid string. Length must be a multiple of 4' )
  1014. }
  1015. // the number of equal signs (place holders)
  1016. // if there are two placeholders, than the two characters before it
  1017. // represent one byte
  1018. // if there is only one, then the three characters before it represent 2 bytes
  1019. // this is just a cheap hack to not do indexOf twice
  1020. var len = b64.length
  1021. placeHolders = '=' === b64.charAt( len - 2 ) ? 2 : '=' === b64.charAt( len - 1 ) ? 1 : 0
  1022. // base64 is 4/3 + up to two characters of the original data
  1023. arr = new Arr( b64.length * 3 / 4 - placeHolders )
  1024. // if there are placeholders, only get up to the last complete 4 chars
  1025. l = placeHolders > 0 ? b64.length - 4 : b64.length
  1026. var L = 0
  1027. function push( v ) {
  1028. arr[ L++ ] = v
  1029. }
  1030. for ( i = 0, j = 0; i < l; i += 4, j += 3 ) {
  1031. tmp = ( decode( b64.charAt( i ) ) << 18 ) | ( decode( b64.charAt( i + 1 ) ) << 12 ) | ( decode( b64.charAt( i + 2 ) ) << 6 ) | decode( b64.charAt( i + 3 ) )
  1032. push( ( tmp & 0xFF0000 ) >> 16 )
  1033. push( ( tmp & 0xFF00 ) >> 8 )
  1034. push( tmp & 0xFF )
  1035. }
  1036. if ( placeHolders === 2 ) {
  1037. tmp = ( decode( b64.charAt( i ) ) << 2 ) | ( decode( b64.charAt( i + 1 ) ) >> 4 )
  1038. push( tmp & 0xFF )
  1039. } else if ( placeHolders === 1 ) {
  1040. tmp = ( decode( b64.charAt( i ) ) << 10 ) | ( decode( b64.charAt( i + 1 ) ) << 4 ) | ( decode( b64.charAt( i + 2 ) ) >> 2 )
  1041. push( ( tmp >> 8 ) & 0xFF )
  1042. push( tmp & 0xFF )
  1043. }
  1044. return arr
  1045. }
  1046. _this.handleCallback( callback, data );
  1047. return data;
  1048. },
  1049. // CALLBACK HANDLER
  1050. handleCallback: function( callback, data ) {
  1051. if ( callback ) {
  1052. callback.apply( _this, [ data ] );
  1053. }
  1054. },
  1055. getChartData: function() {
  1056. var data = [];
  1057. if ( _this.setup.chart.type == "stock" ) {
  1058. data = _this.setup.chart.mainDataSet.dataProvider;
  1059. } else if ( _this.setup.chart.type == "gantt" ) {
  1060. var segmentsField = _this.setup.chart.segmentsField;
  1061. for ( var i1 = 0; i1 < _this.setup.chart.dataProvider.length; i1++ ) {
  1062. if ( _this.setup.chart.dataProvider[ i1 ][ segmentsField ] ) {
  1063. for ( var i2 = 0; i2 < _this.setup.chart.dataProvider[ i1 ][ segmentsField ].length; i2++ ) {
  1064. data.push( _this.setup.chart.dataProvider[ i1 ][ segmentsField ][ i2 ] )
  1065. }
  1066. }
  1067. }
  1068. } else {
  1069. data = _this.setup.chart.dataProvider;
  1070. }
  1071. return data;
  1072. },
  1073. capitalize: function( string ) {
  1074. return string.charAt( 0 ).toUpperCase() + string.slice( 1 ).toLowerCase();
  1075. },
  1076. // MENU BUILDER
  1077. createMenu: function( list, container ) {
  1078. var div;
  1079. function buildList( list, container ) {
  1080. var i1, ul = document.createElement( "ul" );
  1081. for ( i1 = 0; i1 < list.length; i1++ ) {
  1082. var item = typeof list[ i1 ] === "string" ? {
  1083. format: list[ i1 ]
  1084. } : list[ i1 ];
  1085. var li = document.createElement( "li" );
  1086. var a = document.createElement( "a" );
  1087. var img = document.createElement( "img" );
  1088. var span = document.createElement( "span" );
  1089. var action = String( item.action ? item.action : item.format ).toLowerCase();
  1090. item.format = String( item.format ).toUpperCase();
  1091. // MERGE WITH GIVEN FORMAT
  1092. if ( _this.config.formats[ item.format ] ) {
  1093. item = _this.deepMerge( {
  1094. label: item.icon ? "" : item.format,
  1095. format: item.format,
  1096. mimeType: _this.config.formats[ item.format ].mimeType,
  1097. extension: _this.config.formats[ item.format ].extension,
  1098. capture: _this.config.formats[ item.format ].capture,
  1099. action: _this.config.action,
  1100. fileName: _this.config.fileName
  1101. }, item );
  1102. } else if ( !item.menu && !item.items ) {
  1103. item.label = item.label ? item.label : _this.capitalize( action );
  1104. }
  1105. // FILTER; TOGGLE FLAG
  1106. if ( [ "CSV", "JSON", "XLSX" ].indexOf( item.format ) != -1 && [ "map", "gauge" ].indexOf( _this.setup.chart.type ) != -1 ) {
  1107. continue;
  1108. // BLOB EXCEPTION
  1109. } else if ( !_this.setup.hasBlob && item.format != "UNDEFINED" ) {
  1110. if ( item.mimeType && item.mimeType.split( "/" )[ 0 ] != "image" && item.mimeType != "text/plain" ) {
  1111. continue;
  1112. }
  1113. }
  1114. // ADD CLICK HANDLER
  1115. if ( !item.click && !item.menu && !item.items ) {
  1116. // DRAWING METHODS
  1117. if ( _this.drawing.actions.indexOf( action ) != -1 ) {
  1118. item.action = action;
  1119. item.click = ( function( item ) {
  1120. return function() {
  1121. this.drawing[ item.action ]();
  1122. }
  1123. } )( item );
  1124. // DRAWING
  1125. } else if ( _this.drawing.enabled ) {
  1126. item.click = ( function( item ) {
  1127. return function() {
  1128. this[ "to" + item.format ]( item, function( data ) {
  1129. this.drawing.done();
  1130. if ( item.action != "print" && item.format != "PRINT" ) {
  1131. this.download( data, item.mimeType, [ item.fileName, item.extension ].join( "." ) );
  1132. }
  1133. } );
  1134. }
  1135. } )( item );
  1136. // REGULAR
  1137. } else if ( item.format != "UNDEFINED" ) {
  1138. item.click = ( function( item ) {
  1139. return function() {
  1140. if ( item.capture || ( item.action == "print" || item.format == "PRINT" ) ) {
  1141. this.capture( item, function() {
  1142. this[ "to" + item.format ]( item, function( data ) {
  1143. if ( item.action == "download" ) {
  1144. this.download( data, item.mimeType, [ item.fileName, item.extension ].join( "." ) );
  1145. }
  1146. } );
  1147. } )
  1148. } else if ( this[ "to" + item.format ] ) {
  1149. this[ "to" + item.format ]( item, function( data ) {
  1150. this.download( data, item.mimeType, [ item.fileName, item.extension ].join( "." ) );
  1151. } );
  1152. } else {
  1153. throw new Error( 'Invalid format. Could not determine output type.' );
  1154. }
  1155. }
  1156. } )( item );
  1157. }
  1158. // DRAWING
  1159. } else if ( item.action == "draw" ) {
  1160. item.click = ( function( item ) {
  1161. return function() {
  1162. this.capture( item, function() {
  1163. this.createMenu( item.menu );
  1164. } );
  1165. }
  1166. } )( item );
  1167. }
  1168. // ADD LINK ATTR
  1169. a.setAttribute( "href", "#" );
  1170. a.addEventListener( "click", ( function( callback ) {
  1171. return function( e ) {
  1172. callback.apply( _this, arguments );
  1173. e.preventDefault();
  1174. }
  1175. } )( item.click || function( e ) {
  1176. e.preventDefault();
  1177. } ) );
  1178. li.appendChild( a );
  1179. // ADD LABEL
  1180. span.innerHTML = item.label;
  1181. // APPEND ITEMS
  1182. if ( item[ "class" ] ) {
  1183. li.className = item[ "class" ];
  1184. }
  1185. if ( item.icon ) {
  1186. img.setAttribute( "src", ( item.icon.slice( 0, 10 ).indexOf( "//" ) == -1 ? chart.pathToImages : "" ) + item.icon );
  1187. a.appendChild( img );
  1188. }
  1189. if ( item.label ) {
  1190. a.appendChild( span );
  1191. }
  1192. if ( item.title ) {
  1193. a.setAttribute( "title", item.title );
  1194. }
  1195. // CALLBACK; REVIVER FOR MENU ITEMS
  1196. if ( _this.config.menuReviver ) {
  1197. li = _this.config.menuReviver.apply( _this, [ item, li ] );
  1198. }
  1199. // ADD SUBLIST; JUST WITH ENTRIES
  1200. if ( ( item.menu || item.items ) && item.action != "draw" ) {
  1201. if ( buildList( item.menu || item.items, li ).childNodes.length ) {
  1202. ul.appendChild( li );
  1203. }
  1204. } else {
  1205. ul.appendChild( li );
  1206. }
  1207. }
  1208. // JUST ADD THOSE WITH ENTRIES
  1209. return container.appendChild( ul );
  1210. }
  1211. // DETERMINE CONTAINER
  1212. if ( !container ) {
  1213. if ( typeof _this.config.divId == "string" ) {
  1214. _this.config.divId = container = document.getElementById( _this.config.divId );
  1215. } else if ( _this.isElement( _this.config.divId ) ) {
  1216. container = _this.config.divId;
  1217. } else {
  1218. container = _this.setup.chart.containerDiv;
  1219. }
  1220. }
  1221. // CREATE / RESET MENU CONTAINER
  1222. if ( _this.isElement( _this.setup.menu ) ) {
  1223. _this.setup.menu.innerHTML = "";
  1224. } else {
  1225. _this.setup.menu = document.createElement( "div" );
  1226. }
  1227. _this.setup.menu.setAttribute( "class", _this.setup.chart.classNamePrefix + "-export-menu " + _this.setup.chart.classNamePrefix + "-export-menu-" + _this.config.position + " amExportButton" );
  1228. // CALLBACK; REPLACES THE MENU WALKER
  1229. if ( _this.config.menuWalker ) {
  1230. buildList = _this.config.menuWalker;
  1231. }
  1232. buildList.apply( this, [ list, _this.setup.menu ] );
  1233. container.appendChild( _this.setup.menu );
  1234. return _this.setup.menu;
  1235. },
  1236. migrateSetup: function( chart ) {
  1237. if ( chart.amExport || chart.exportConfig ) {
  1238. var config = _this.deepMerge( {
  1239. enabled: true,
  1240. migrated: true,
  1241. libs: {
  1242. autoLoad: false
  1243. }
  1244. }, _this.deepMerge( _this.defaults, {
  1245. menu: []
  1246. }, true ) );
  1247. function crawler( object ) {
  1248. var key;
  1249. for ( key in object ) {
  1250. var value = object[ key ];
  1251. if ( key.slice( 0, 6 ) == "export" && value ) {
  1252. config.menu.push( key.slice( 6 ) );
  1253. } else if ( key == "userCFG" ) {
  1254. crawler( value );
  1255. } else if ( key == "menuItems" ) {
  1256. config.menu = value;
  1257. } else if ( key == "libs" ) {
  1258. config.libs = value;
  1259. } else if ( typeof key == "string" ) {
  1260. config[ key ] = value;
  1261. }
  1262. }
  1263. }
  1264. crawler( chart.amExport || chart.exportConfig );
  1265. chart[ "export" ] = config;
  1266. }
  1267. return chart;
  1268. },
  1269. // INITIATE; DELAYED UNTIL CHART CONTAINER IS READY
  1270. init: function() {
  1271. clearTimeout( _this.timer );
  1272. _this.timer = setInterval( function() {
  1273. if ( _this.setup.chart.containerDiv ) {
  1274. clearTimeout( _this.timer );
  1275. _this.setup.canvas = document.createElement( "canvas" );
  1276. _this.setup.wrapper = document.createElement( "div" );
  1277. _this.setup.wrapper.setAttribute( "class", _this.setup.chart.classNamePrefix + "-export-canvas" );
  1278. _this.setup.wrapper.appendChild( _this.setup.canvas );
  1279. _this.setup.chart.containerDiv.appendChild( _this.setup.wrapper );
  1280. _this.setup.chart.AmExport = _this;
  1281. _this.createMenu( _this.config.menu );
  1282. }
  1283. }, AmCharts.updateRate );
  1284. }
  1285. }
  1286. // EXTEND DRAWING TO SUPPORT "CANCEL" MENU ACTION
  1287. _this.drawing.cancel = _this.drawing.done;
  1288. // MIRGRATE
  1289. _this.setup.chart = _this.migrateSetup( chart );
  1290. // ENABLED-I-O?
  1291. if ( undefined === _this.setup.chart[ "export" ] || !_this.setup.chart[ "export" ].enabled ) {
  1292. return;
  1293. }
  1294. try {
  1295. _this.setup.hasBlob = !!new Blob;
  1296. } catch ( e ) {}
  1297. // MERGE SETTINGS
  1298. _this.deepMerge( _this.libs, _this.setup.chart[ "export" ].libs || {}, true );
  1299. _this.deepMerge( _this.defaults.pdfMake, _this.setup.chart[ "export" ] );
  1300. _this.deepMerge( _this.defaults.fabric, _this.setup.chart[ "export" ] );
  1301. _this.config = _this.deepMerge( _this.defaults, _this.setup.chart[ "export" ], true );
  1302. // SUPPORT IE ONLY IF WE'VE ACCESS TO THE HEAD
  1303. if ( AmCharts.isIE && AmCharts.IEversion <= 9 ) {
  1304. if ( !document.head || _this.config.fallback === false ) {
  1305. return;
  1306. }
  1307. }
  1308. _this.setup.chart[ "export" ] = _this;
  1309. _this.setup.chart.addClassNames = true;
  1310. // LOAD DEPENDENCIES
  1311. _this.loadDependencies( _this.libs.resources, _this.libs.reload );
  1312. // INIT
  1313. _this.init();
  1314. }, [ "pie", "serial", "xy", "funnel", "radar", "gauge", "stock", "map", "gantt" ] );