PageSourceSearch

https://www.easyhits4u.com/js/amcharts/exporting/amexport.js

js easyhits4u.com collected 2026-10-01 11:46:22 UTC 19,440 bytes, 662 lines download raw bytes

1AmCharts.AmExport = AmCharts.Class({
2	construct: function(chart, cfg) {
3		var _this		= this;
4		_this.DEBUG		= false;
5		_this.chart		= chart;
6		_this.canvas	= null;
7		_this.svgs		= [];
8		_this.cfg		= {
9			menuTop					: 'auto',
10			menuLeft				: 'auto',
11			menuRight				: '0px',
12			menuBottom				: '0px',
13			menuItems				: [{
14				textAlign				: 'center',
15				icon					: _this.chart.pathToImages + 'export.png',
16				iconTitle				: 'Save chart as an image',
17				format					: 'png'
18			}],
19			menuItemStyle			: {
20			backgroundColor			: 'transparent',
21			rollOverBackgroundColor	: '#EFEFEF',
22			color					: '#000000',
23			rollOverColor			: '#CC0000',
24			paddingTop				: '6px',
25			paddingRight			: '6px',
26			paddingBottom			: '6px',
27			paddingLeft				: '6px',
28			marginTop				: '0px',
29			marginRight				: '0px',
30			marginBottom			: '0px',
31			marginLeft				: '0px',
32			textAlign				: 'left',
33			textDecoration			: 'none',
34			fontFamily				: _this.chart.fontFamily,
35			fontSize				: _this.chart.fontSize + 'px'
36			},
37			menuItemOutput			: {
38				backgroundColor			: '#FFFFFF',
39				fileName				: 'amChart',
40				format					: 'png',
41				output					: 'dataurlnewwindow',
42				render					: 'browser',
43				dpi						: 90,
44				onclick					: function(instance, config, event) {
45					event.preventDefault();
46					// Polify SVG; needs to wait
47					instance.polifySVG();
48					instance.output(config);
49				}
50			},
51			removeImagery: false
52		};
53		_this.processing = {
54			buffer: [],
55			drawn: 0,
56			timer: 0
57		};
58
59		// Config dependency adaption
60		if (typeof(window.canvg) != 'undefined' && typeof(window.RGBColor) != 'undefined') {
61			_this.cfg.menuItemOutput.render = 'canvg';
62		}
63		if (typeof(window.saveAs) != 'undefined') {
64			_this.cfg.menuItemOutput.output = 'save';
65		}
66		if (AmCharts.isIE && AmCharts.IEversion < 10) {
67			_this.cfg.menuItemOutput.output = 'dataurlnewwindow';
68		}
69
70		// Merge given configs
71		if (cfg) {
72			cfg.menuItemOutput = AmCharts.extend(_this.cfg.menuItemOutput, cfg.menuItemOutput || {});
73			cfg.menuItemStyle = AmCharts.extend(_this.cfg.menuItemStyle, cfg.menuItemStyle || {});
74			_this.cfg = AmCharts.extend(_this.cfg, cfg);
75		}
76
77		// Add reference to chart
78		_this.chart.AmExport = _this;
79
80		// Listen to the drawer
81		_this.chart.addListener('rendered', function() {
82			_this.setup();
83		});
84
85		// DEBUG; Public reference
86		if (_this.DEBUG) {
87			window.AmExport = _this;
88		}
89	},
90
91	/*
92	Simple log function for internal purpose
93	@param **args
94	*/
95	log: function() {
96		console.log('AmExport: ', arguments);
97	},
98
99	/* PUBLIC
100	Prepares everything to get exported
101	@param none
102	*/
103	setup: function() {
104		var _this = this;
105
106		if (_this.DEBUG == 10) {
107			_this.log('SETUP START');
108		} // DEBUG
109
110
111		if (!AmCharts.isIE || (AmCharts.isIE && AmCharts.IEversion > 9)) {
112			// Build Buttons
113			_this.generateButtons();
114			if (_this.DEBUG == 10) {
115				_this.log('SETUP END');
116			} // DEBUG
117		} else {
118			if (_this.DEBUG == 10) {
119				_this.log('< IE10 NOT SUPPORTED');
120			} // DEBUG
121		}
122	},
123
124	/* PUBLIC
125	Decodes base64 string to binary array
126	@param base64_string
127	@copyright Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
128	*/
129	generateBinaryArray: function(base64_string) {
130		var
131		len = base64_string.length,
132			buffer = new Uint8Array(len / 4 * 3 | 0),
133			i = 0,
134			outptr = 0,
135			last = [0, 0],
136			state = 0,
137			save = 0,
138			rank, code, undef, base64_ranks = new Uint8Array([
139				62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
140			]);
141		while (len--) {
142			code = base64_string.charCodeAt(i++);
143			rank = base64_ranks[code - 43];
144			if (rank !== 255 && rank !== undef) {
145				last[1] = last[0];
146				last[0] = code;
147				save = (save << 6) | rank;
148				state++;
149				if (state === 4) {
150					buffer[outptr++] = save >>> 16;
151					if (last[1] !== 61 /* padding character */ ) {
152						buffer[outptr++] = save >>> 8;
153					}
154					if (last[0] !== 61 /* padding character */ ) {
155						buffer[outptr++] = save;
156					}
157					state = 0;
158				}
159			}
160		}
161		// 2/3 chance there's going to be some null bytes at the end, but that
162		// doesn't really matter with most image formats.
163		// If it somehow matters for you, truncate the buffer up outptr.
164		return buffer;
165	},
166
167	/*
168	Creates blob object
169	@param base64_datastring string
170	@param type string
171	*/
172	generateBlob: function(datastring, type) {
173		var _this = this,
174			header_end = datastring.indexOf(',') + 1,
175			header = datastring.substring(0, header_end),
176			data = datastring,
177			blob = new Blob();
178
179		if (header.indexOf('base64') != -1) {
180			data = _this.generateBinaryArray(datastring.substring(header_end));
181		}
182
183		// Fake blob for IE
184		if (AmCharts.isIE && AmCharts.IEversion < 10) {
185			blob.data = data;
186			blob.size = data.length;
187			blob.type = type;
188			blob.encoding = 'base64';
189		} else {
190			blob = new Blob([data], {
191				type: type
192			});
193		}
194		return blob;
195	},
196
197	/*
198	Creates PDF object
199	@param config object
200	*/
201	generatePDF: function(cfg) {
202		var _this = this,
203			pdf = {
204				output: function() {
205					return '';
206				}
207			},
208			data = _this.canvas.toDataURL('image/jpeg'), // JSPDF ONLY SUPPORTS JPG
209			width = (_this.canvas.width * 25.4) / cfg.dpi,
210			height = (_this.canvas.height * 25.4) / cfg.dpi;
211
212		// Check
213		if (window.jsPDF) {
214			pdf = new jsPDF();
215			if (pdf.addImage) {
216				pdf.addImage(data, 'JPEG', 0, 0, width, height);
217			} else {
218				alert("Missing jsPDF plugin; Please add the 'addImage' plugin.");
219			}
220		} else {
221			alert("Missing jsPDF lib; Don't forget to add the addImage plugin.");
222		}
223
224		return pdf;
225	},
226
227	/*
228	Creates the CANVAS to receive the image data
229	@param format void()
230	@param callback; given callback function which returns the blob or datastring of the configured ouput type
231	*/
232	output: function(cfg, externalCallback) {
233		var _this = this;
234		cfg = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), cfg || {});
235
236		/* PRIVATE
237		Callback function which gets called after the drawing process is done
238		@param none
239		*/
240		function internalCallback() {
241			var data = null;
242			var blob;
243			if (_this.DEBUG == 10) {
244				_this.log('OUTPUT', format);
245			} // DEBUG
246
247			// SVG
248			if (cfg.format == 'image/svg+xml' || cfg.format == 'svg') {
249				for (var i = 0; i < _this.processing.buffer.length; i++) {
250					data = new XMLSerializer().serializeToString(_this.processing.buffer[i][0]);
251					blob = _this.generateBlob(data, 'image/svg+xml');
252
253					if (cfg.output == 'save') {
254						saveAs(blob, cfg.fileName + '.svg');
255					} else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
256						blob = 'data:image/svg+xml;base64,' + btoa(data);
257					} else if (cfg.output == 'dataurlnewwindow') {
258						window.open('data:image/svg+xml;base64,' + btoa(data));
259					} else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
260						location.href = 'data:image/svg+xml;base64,' + btoa(data);
261					} else if (cfg.output == 'datastream') {
262						location.href = 'data:image/octet-stream;base64,' + btoa(data);
263					}
264
265					if (externalCallback)
266						externalCallback.apply(_this, [blob]);
267				}
268				// PDF
269			} else if (cfg.format == 'application/pdf' || cfg.format == 'pdf') {
270				data = _this.generatePDF(cfg).output('dataurlstring');
271				blob = _this.generateBlob(data, 'application/pdf');
272
273				if (cfg.output == 'save') {
274					saveAs(blob, cfg.fileName + '.pdf');
275				} else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
276					blob = data;
277				} else if (cfg.output == 'dataurlnewwindow') {
278					window.open(data);
279				} else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
280					location.href = data;
281				} else if (cfg.output == 'datastream') {
282					location.href = data.replace('application/pdf', 'application/octet-stream');
283				}
284
285				if (externalCallback)
286					externalCallback.apply(_this, [blob]);
287
288				// PNG
289			} else if (cfg.format == 'image/png' || cfg.format == 'png') {
290				data = _this.canvas.toDataURL('image/png');
291				blob = _this.generateBlob(data, 'image/png');
292
293				if (cfg.output == 'save') {
294					saveAs(blob, cfg.fileName + '.png');
295				} else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
296					blob = data;
297				} else if (cfg.output == 'dataurlnewwindow') {
298					window.open(data);
299				} else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
300					location.href = data;
301				} else if (cfg.output == 'datastream') {
302					location.href = data.replace('image/png', 'image/octet-stream');
303				}
304
305				if (externalCallback)
306					externalCallback.apply(_this, [blob]);
307
308				// JPG
309			} else if (cfg.format == 'image/jpeg' || cfg.format == 'jpeg' || cfg.format == 'jpg') {
310				data = _this.canvas.toDataURL('image/jpeg');
311				blob = _this.generateBlob(data, 'image/jpeg');
312
313				if (cfg.output == 'save') {
314					saveAs(blob, cfg.fileName + '.jpg');
315				} else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
316					blob = data;
317				} else if (cfg.output == 'dataurlnewwindow') {
318					window.open(data);
319				} else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
320					location.href = data;
321				} else if (cfg.output == 'datastream') {
322					location.href = data.replace('image/jpeg', 'image/octet-stream');
323				}
324
325				if (externalCallback)
326					externalCallback.apply(_this, [blob]);
327			}
328
329		}
330
331		return _this.generateOutput(cfg, internalCallback);
332	},
333
334	/* PUBLIC
335	Polifies missing attributes to the SVG and replaces images to embedded base64 images
336	@param none
337	*/
338	polifySVG: function() {
339		var _this = this;
340		var svgs = _this.chart.div.getElementsByTagName('svg');
341
342		// Recursive function to force the attributes
343		function recursiveChange(svg, tag) {
344			var items = svg.getElementsByTagName(tag);
345
346			for (var i = 0; i < items.length; i++) {
347
348				if ( _this.cfg.removeImagery ) {
349					items[i].parentNode.removeChild(items[i]);
350
351				} else {
352					var image		= document.createElement('img');
353					var canvas		= document.createElement('canvas');
354					var ctx			= canvas.getContext('2d');
355
356					canvas.width	= items[i].getAttribute('width');
357					canvas.height	= items[i].getAttribute('height');
358					image.src		= items[i].getAttribute('xlink:href');
359					image.width		= items[i].getAttribute('width');
360					image.height	= items[i].getAttribute('height');
361
362					try {
363						ctx.drawImage(image, 0, 0, image.width, image.height);
364						datastring = canvas.toDataURL(); // image.src; // canvas.toDataURL(); //
365					} catch (err) {
366						datastring = image.src; // image.src; // canvas.toDataURL(); //
367
368						_this.log('Tainted canvas, reached browser CORS security; origin from imagery must be equal to the server!');
369						throw new Error(err);
370					}
371
372					items[i].setAttribute('xlink:href', datastring);
373				}
374
375				if (_this.DEBUG == 10) {
376					_this.log('POLIFIED', items[i]);
377				} // DEBUG
378			}
379		}
380
381		// Loop through svgs to add some standardization
382		for (var i = 0; i < svgs.length; i++) {
383			var parent = svgs[i].parentNode;
384
385			// Put some attrs to it
386			/*
387			if ( !AmCharts.isIE ) {
388				svgs[i].setAttribute('xmlns','http://www.w3.org/2000/svg');
389				svgs[i].setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
390			}
391			svgs[i].setAttribute('width',parent.style.width);
392			svgs[i].setAttribute('height',parent.style.height);
393			*/
394
395			if (_this.DEBUG == 10) {
396				_this.log('POLIFIED', svgs[i]);
397			} // DEBUG
398
399			// Force link adaption
400			recursiveChange(svgs[i], 'pattern');
401			recursiveChange(svgs[i], 'image');
402
403			_this.svgs.push(svgs[i]);
404		}
405
406		return svgs;
407	},
408
409	/* PUBLIC
410	Generates the canvas with the given SVGs and configured renderer
411	@param callback; function(); gets called after drawing process on the canvas has been finished
412	*/
413	generateOutput: function(cfg, callback) {
414		var _this = this,
415			svgs = _this.chart.div.getElementsByTagName('svg'),
416			canvas = document.createElement('canvas'),
417			context = canvas.getContext('2d'),
418			offset = {
419				y: 0,
420				x: 0
421			},
422			tmp = {};
423
424		// Reset
425		_this.processing.buffer = [];
426		_this.processing.drawn = 0;
427		_this.canvas = canvas;
428
429		// Walkthroug SVGs
430		if (_this.DEBUG == 10) {
431			_this.log('START EXPORT');
432		} // DEBUG
433		if (_this.DEBUG == 10) {
434			_this.log('START BUFFERING');
435		} // DEBUG
436		for (var i = 0; i < svgs.length; i++) {
437			var parent = svgs[i].parentNode,
438				svgX = Number(parent.style.left.slice(0, -2)),
439				svgY = Number(parent.style.top.slice(0, -2));
440			tmp = AmCharts.extend({}, offset);
441
442			// Overtake parent position if givwn
443			offset.x = svgX ? svgX : offset.x;
444			offset.y = svgY ? svgY : offset.y;
445
446			_this.processing.buffer.push([svgs[i], AmCharts.extend({}, offset)]);
447
448			// Put back from "cache"
449			if (svgY && svgX) {
450				offset = tmp;
451
452				// New offset for next one
453			} else {
454				offset.y += svgY ? 0 : parent.offsetHeight;
455			}
456
457			if (_this.DEBUG == 10) {
458				_this.log('BUFFERED', svgs[i], offset);
459			} // DEBUG
460		}
461		if (_this.DEBUG == 10) {
462			_this.log('END BUFFERING');
463		} // DEBUG
464
465		// Apply background
466		if (_this.DEBUG == 10) {
467			_this.log('START DRAWING', cfg.render);
468		} // DEBUG
469		if (_this.DEBUG == 10) {
470			_this.log('FILL BACKGROUND');
471		} // DEBUG
472		canvas.id = AmCharts.getUniqueId();
473		canvas.width = _this.chart.divRealWidth;
474		canvas.height = _this.chart.divRealHeight;
475
476		// Set given background; jpeg default
477		if (cfg.backgroundColor || format == 'image/jpeg') {
478			context.fillStyle = cfg.backgroundColor || '#FFFFFF';
479			context.fillRect(0, 0, canvas.width, canvas.height);
480		}
481
482		/* PRIVATE
483		Recursive function to draw the images to the canvas;
484		@param none;
485		*/
486		function drawItWhenItsLoaded() {
487			var img, buffer, offset, source;
488
489			// DRAWING PROCESS DONE
490			if (_this.processing.buffer.length == _this.processing.drawn) {
491				if (_this.DEBUG == 10) {
492					_this.log('END DRAWING');
493				} // DEBUG
494				return callback();
495
496				// LOOPING LUI
497			} else {
498				if (_this.DEBUG == 10) {
499					_this.log('DRAW', _this.processing.drawn + 1, 'OF', _this.processing.buffer.length);
500				} // DEBUG
501
502				buffer = _this.processing.buffer[_this.processing.drawn];
503				source = new XMLSerializer().serializeToString(buffer[0]); //source = 'data:image/svg+xml;base64,' + btoa();
504				offset = buffer[1];
505
506				if (_this.DEBUG == 10) {
507					_this.log('SOURCE', source);
508				} // DEBUG
509
510				// NATIVE
511				if (cfg.render == 'browser') {
512					img = new Image();
513					img.id = AmCharts.getUniqueId();
514					source = 'data:image/svg+xml;base64,' + btoa(source);
515
516					//img.crossOrigin	= "Anonymous";
517					img.onload = function() {
518						context.drawImage(this, buffer[1].x, buffer[1].y);
519						_this.processing.drawn++;
520
521						if (_this.DEBUG == 10) {
522							_this.log('ONLOAD', this);
523						} // DEBUG
524						drawItWhenItsLoaded();
525					};
526					img.onerror = function() {
527						if (_this.DEBUG == 10) {
528							_this.log('ONERROR', this);
529						} // DEBUG
530						context.drawImage(this, buffer[1].x, buffer[1].y);
531						_this.processing.drawn++;
532						drawItWhenItsLoaded();
533					};
534					img.src = source;
535
536					if (_this.DEBUG == 10) {
537						_this.log('ADD', img);
538					} // DEBUG
539					if (img.complete || typeof(img.complete) == 'undefined' || img.complete === undefined) {
540						if (_this.DEBUG == 10) {
541							_this.log('FORCE ONLOAD', img);
542						} // DEBUG
543						img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
544						img.src = source;
545					}
546
547					// CANVG
548				} else if (cfg.render == 'canvg') {
549					canvg(canvas, source, {
550						offsetX: offset.x,
551						offsetY: offset.y,
552						ignoreMouse: true,
553						ignoreAnimation: true,
554						ignoreDimensions: true,
555						ignoreClear: true,
556						renderCallback: function() {
557							_this.processing.drawn++;
558							drawItWhenItsLoaded();
559						}
560					});
561				}
562			}
563		}
564		return drawItWhenItsLoaded();
565	},
566
567	/*
568	Generates the export menu to trigger the exportation
569	@param none;
570	*/
571	generateButtons: function() {
572		var _this = this,
573			div = document.createElement('div'),
574			lvl = 0;
575
576		// Push sublings
577		function createList(items) {
578			var ul = document.createElement('ul');
579
580			ul.setAttribute('style', 'list-style: none; margin: 0; padding: 0;');
581
582			// Walkthrough items
583			for (var i = 0; i < items.length; i++) {
584				var li = document.createElement('li'),
585					img = document.createElement('img'),
586					a = document.createElement('a'),
587					item = items[i],
588					children = null,
589					itemStyle = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemStyle), items[i]);
590
591				// MERGE CFG
592				item = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), item);
593
594				// ICON
595				if (item['icon']) {
596					img.alt = '';
597					img.src = item['icon'];
598					img.setAttribute('style', 'margin: 0 auto;border: none;outline: none');
599					if (item['iconTitle']) {
600						img.title = item['iconTitle'];
601					}
602					a.appendChild(img);
603				}
604
605				// TITLE; STYLING
606				a.href = '#';
607				if (item['title']) {
608					img.setAttribute('style', 'margin-right: 5px;');
609					a.innerHTML += item.title;
610				}
611				a.setAttribute('style', 'display: block;');
612				AmCharts.extend(a.style, itemStyle);
613
614				// ONCLICK
615				a.onclick = item.onclick.bind(a, _this, item);
616				li.appendChild(a);
617
618				// APPEND SIBLINGS
619				if (item.items) {
620					children = createList(item.items);
621					li.appendChild(children);
622
623					li.onmouseover = function() {
624						children.style.display = 'block';
625					};
626					li.onmouseout = function() {
627						children.style.display = 'none';
628					};
629					children.style.display = 'none';
630				}
631
632				// Append to parent
633				ul.appendChild(li);
634
635				// Apply hover
636				a.onmouseover = function() {
637					this.style.backgroundColor = itemStyle.rollOverBackgroundColor;
638					this.style.color = itemStyle.rollOverColor;
639					this.style.borderColor = itemStyle.rollOverBorderColor;
640				};
641				a.onmouseout = function() {
642					this.style.backgroundColor = itemStyle.backgroundColor;
643					this.style.color = itemStyle.color;
644					this.style.borderColor = itemStyle.borderColor;
645				};
646			}
647			lvl++;
648
649			if (_this.DEBUG == 10) {
650				_this.log('MENU', ul);
651			} // DEBUG
652
653			return ul;
654		}
655
656		// Style wrapper; Push into chart div
657		div.setAttribute('style', 'width:39px; height:28px; position: absolute;top:' + _this.cfg.menuTop + ';right:' + _this.cfg.menuRight + ';bottom:' + _this.cfg.menuBottom + ';left:' + _this.cfg.menuLeft + ';box-shadow:0px 0px 1px 0px rgba(0,0,0,0);');
658		div.appendChild(createList(_this.cfg.menuItems));
659		//_this.chart.div.style.position = 'relative';
660		_this.chart.containerDiv.appendChild(div);
661	}
662});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.