www/jqwidgets/jqxexport.js

changeset 733
67bf19c50fcc
equal deleted inserted replaced
732:db4de4f37b65 733:67bf19c50fcc
1 /* tslint:disable */
2 /* eslint-disable */
3 (function ($) {
4 window.jqxToDash = function(value) {
5 return value.split(/(?=[A-Z])/).join('-').toLowerCase();
6 }
7
8 class DataExporter {
9 constructor(exportDetails, groupBy, filterBy, conditionalFormatting) {
10 const that = this;
11
12 if (!exportDetails) {
13 exportDetails = {};
14 }
15
16 /*
17 * "style" object definition (all properties are optional):
18 *
19 * «any valid CSS property» - applied to whole table
20 * header (Object)
21 * «any valid CSS property» - applied to header cells
22 * «any column name» (Object)
23 * «any valid CSS property» - applied to particular column header cell
24 * columns (Object)
25 * «any valid CSS property» - applied to column cells
26 * «any column name» (Object)
27 * «any valid CSS property» - applied to the cells of particular column
28 * format - applicable to numeric and date columns
29 * «n» (Object), where «n» is a row index (related to use of "ConditionalFormatting" object)
30 * background
31 * border
32 * color
33 * rows (Object)
34 * «any valid CSS property» - applied to rows
35 * alternationCount
36 * alternationStart
37 * alternationEnd
38 * alternationIndex«n»Color, where «n» is an integer
39 * alternationIndex«n»BorderColor, where «n» is an integer
40 * alternationIndex«n»BackgroundColor, where «n» is an integer
41 * «n» (Object), where «n» is a row index
42 * «any valid CSS property» - applied to particular row
43 */
44 that.style = exportDetails.style;
45
46 that.header = exportDetails.header;
47 that.exportHeader = exportDetails.exportHeader || true;
48 that.hierarchical = exportDetails.hierarchical;
49 that.expandChar = exportDetails.expandChar || '+';
50 that.collapseChar = exportDetails.collapseChar || '-';
51 that.pageOrientation = exportDetails.pageOrientation;
52
53 if (!that.hierarchical && groupBy && groupBy.length > 0) {
54 that.groupBy = groupBy;
55 }
56 else {
57 that.mergedCells = exportDetails.mergedCells;
58 }
59
60 if (!that.groupBy && filterBy && Object.keys(filterBy).length > 0) {
61 that.filterBy = filterBy;
62 }
63
64 if (conditionalFormatting) {
65 that.conditionalFormatting = conditionalFormatting;
66 }
67
68 that.timeBetween1900And1970 = new Date(1970, 0, 1).getTime() - new Date(1900, 0, 1).getTime();
69 }
70
71 /**
72 * Generates and downloads a file.
73 */
74 downloadFile(data, type, fileName) {
75 let file;
76
77 if (!fileName) {
78 return data;
79 }
80
81 if (data instanceof Blob) {
82 file = data;
83 }
84 else {
85 file = new Blob([data], { type: type });
86 }
87
88 if (window.navigator.msSaveOrOpenBlob) { // Edge
89 window.navigator.msSaveOrOpenBlob(file, fileName);
90 }
91 else { // Chrome, Firefox, Safari
92 const a = document.createElement('a'),
93 url = URL.createObjectURL(file);
94
95 a.href = url;
96 a.download = fileName;
97 a.style.position = 'absolute';
98 a.style.visibility = 'hidden';
99
100 document.body.appendChild(a);
101
102 a.click();
103
104 setTimeout(function () {
105 document.body.removeChild(a);
106 window.URL.revokeObjectURL(url);
107 }, 0);
108 }
109 }
110
111 /**
112 * Exports data.
113 */
114 exportData(data, format, fileName, callback) {
115 const that = this;
116
117 that.actualHierarchy = that.hierarchical;
118 format = format.toLowerCase();
119
120 if (that.exportHeader) {
121 if (that.header) {
122 data = data.slice(0);
123
124 if (data.length === 0) {
125 that.actualHierarchy = false;
126 }
127
128 that.processComplexHeader(that.header, data, format);
129 }
130 else if (data.length === 1) {
131 that.actualHierarchy = false;
132 }
133 }
134
135 if (data.length === 0) {
136 // eslint-disable-next-line
137 console.warn('No data to export.');
138 return;
139 }
140
141 if (format === 'xlsx') {
142 that.xlsxStartIndex = that.complexHeader ? that.complexHeader.length : +that.exportHeader;
143 }
144
145 if (that.actualHierarchy) {
146 data = that.processHierarchicalData(data, format);
147 }
148
149 that.getDatafields(data);
150
151 if (fileName && fileName.slice(fileName.length - format.length - 1, fileName.length) !== '.' + format) {
152 fileName += '.' + format;
153 }
154
155 let output = null;
156 switch (format) {
157 case 'csv':
158 output = that.exportToCSVAndTSV(data, { delimiter: ', ', MIME: 'text/csv', toRemove: 2 }, fileName);
159 break;
160 case 'html':
161 output = that.exportToHTML(data, fileName);
162 break;
163 case 'jpeg':
164 case 'png':
165 that.exportToImage(data, fileName, format, callback);
166 break;
167 case 'json':
168 output = that.exportToJSON(data, fileName);
169 break;
170 case 'pdf':
171 output = that.exportToPDF(data, fileName);
172 break;
173 case 'tsv':
174 output = that.exportToCSVAndTSV(data, { delimiter: '\t', MIME: 'text/tab-separated-values', toRemove: 1 }, fileName);
175 break;
176 case 'xlsx':
177 output = that.exportToXLSX(data, fileName);
178 break;
179 case 'xml':
180 output = that.exportToXML(data, fileName);
181 break;
182 }
183
184 if (callback && output) {
185 callback(output);
186 }
187
188 delete that.complexHeader;
189
190 return output;
191 }
192
193 /**
194 * Exports to CSV and TSV.
195 */
196 exportToCSVAndTSV(data, formatOptions, fileName) {
197 const that = this,
198 datafields = that.datafields;
199 let stringResult = '';
200
201 for (let i = 0; i < data.length; i++) {
202 const currentRecord = data[i];
203 let stringifiedCurrentRecord = '';
204
205 for (let j = 0; j < datafields.length; j++) {
206 if (that.actualHierarchy && j === 0) {
207 stringifiedCurrentRecord += ('""' + formatOptions.delimiter).repeat(currentRecord._level - 1) +
208 '"' + currentRecord[datafields[j]] + '"' + formatOptions.delimiter +
209 ('""' + formatOptions.delimiter).repeat(that.maxLevel - currentRecord._level);
210 continue;
211 }
212
213 stringifiedCurrentRecord += '"' + currentRecord[datafields[j]] + '"' + formatOptions.delimiter;
214 }
215
216 stringifiedCurrentRecord = stringifiedCurrentRecord.slice(0, stringifiedCurrentRecord.length - formatOptions.toRemove) + '\n';
217 stringResult += stringifiedCurrentRecord;
218 }
219
220 return this.downloadFile(stringResult, formatOptions.MIME, fileName);
221 }
222
223 /**
224 * Exports to HTML.
225 */
226 exportToHTML(data, fileName) {
227 const that = this,
228 datafields = that.datafields,
229 style = that.style;
230 let header = '',
231 startIndex = 0,
232 html2canvas = '';
233
234 data = that.processGroupingInformation(data);
235 that.data = data;
236
237 if (that.exportHeader) {
238 header = that.getHTMLHeader(datafields, data);
239 startIndex = 1;
240 }
241
242 if (arguments[2]) {
243 const scripts = Array.from(document.getElementsByTagName('script')),
244 html2canvasScript = scripts.find(script => script.src.indexOf('html2canvas') !== -1);
245 html2canvas = `<script type="text/javascript" src="${html2canvasScript.src}"></script>`;
246 }
247
248 let htmlContent = `<!DOCTYPE html>
249 <html>
250 <head>
251 <meta charset="UTF-8">
252 <style type="text/css">
253 ${that.getRowStyle()}${that.getColumnStyle()}
254 </style>${html2canvas}${that.toggleableFunctionality()}
255 </head>
256 <body>
257 <table${that.getTableStyle()}>${header}
258 <tbody>\n`;
259
260 const mergedMainCells = {},
261 mergedSecondaryCells = {},
262 groupsHandled = [];
263
264 that.getMergedCellsInfo(mergedMainCells, mergedSecondaryCells);
265
266 mainLoop:
267 for (let i = startIndex; i < data.length; i++) {
268 const currentRecord = data[i],
269 row = i - startIndex;
270 let n = that.getAlternationIndex(row, ' rowN'),
271 toCollapse = '',
272 level = '',
273 groupId = '',
274 outlineLevel = 0;
275
276 if (that.actualHierarchy) {
277 if (currentRecord._collapsed) {
278 toCollapse = ' collapsed';
279 }
280
281 level = ` level="${currentRecord._level}"`;
282 }
283 else if (that.groupBy) {
284 for (let k = 0; k < that.groupBy.length; k++) {
285 const datafield = that.groupBy[k],
286 currentGroup = currentRecord[datafield],
287 currentGroupLabel = that.groups[datafield][currentGroup];
288
289 groupId += currentGroup;
290
291 if (groupsHandled.indexOf(groupId) === -1) {
292 htmlContent += ` <tr class="row">
293 <td class="column group" style="padding-left: ${outlineLevel * 25}px;" colspan="${that.datafields.length}">${currentGroupLabel}</td>
294 </tr>`;
295 groupsHandled.push(groupId);
296 i--;
297 continue mainLoop;
298 }
299
300 outlineLevel++;
301 }
302 }
303
304 let currentContent = ` <tr class="row row${row}${n}${toCollapse}"${level}`;
305
306 if (!fileName) {
307 currentContent += ' style="page-break-inside: avoid;"'
308 }
309
310 currentContent += '>\n';
311
312 for (let j = 0; j < datafields.length; j++) {
313 const cellCode = j + ',' + (row);
314 let colspan = 1, rowspan = 1;
315
316 if (mergedMainCells[cellCode]) {
317 colspan = mergedMainCells[cellCode].colspan;
318 rowspan = mergedMainCells[cellCode].rowspan;
319 }
320 else if (mergedSecondaryCells[cellCode]) {
321 continue;
322 }
323
324 const datafield = datafields[j];
325 let value = currentRecord[datafield],
326 indent = '';
327
328 if (that.actualHierarchy && j === 0) {
329 let sign = '';
330
331 if (currentRecord._expanded) {
332 sign = that.collapseChar;
333 }
334 else if (currentRecord._expanded === false) {
335 sign = that.expandChar;
336 }
337
338 indent = `<div class="toggle-element" style="margin-left: ${25 * (currentRecord._level - 1) + 5}px;" expanded>${sign}</div>`;
339 }
340
341 value = that.getFormattedValue(value, datafield);
342
343 let css = '';
344
345 if (style && style.columns && style.columns[datafield] && style.columns[datafield][row]) {
346 const uniqueStyle = style.columns[datafield][row];
347
348 css += `border-color: ${uniqueStyle.border}; background-color: ${uniqueStyle.background}; color: ${uniqueStyle.color};"`;
349 }
350
351 if (j === 0 && outlineLevel > 1) {
352 css += `padding-left: ${(outlineLevel - 1) * 25}px;"`;
353 }
354
355 if (css) {
356 css = ` style="${css}"`;
357 }
358
359 currentContent += ` <td class="column column${datafield}"${css} colspan="${colspan}" rowspan="${rowspan}">${indent + value}</td>\n`;
360 }
361
362 htmlContent += currentContent + ' </tr>\n';
363 }
364
365 htmlContent += ` </tbody>
366 </table>
367 </body>
368 </html>`;
369
370 if (arguments[2]) {
371 return htmlContent;
372 }
373
374 return this.downloadFile(htmlContent, 'text/html', fileName);
375 }
376
377 /**
378 * Exports to an image (PNG/JPEG).
379 */
380 exportToImage(data, fileName, fileExtension, callback) {
381 const that = this;
382
383 try {
384 html2canvas;
385 }
386 catch (error) {
387 throw new Error('jqx-grid: Missing reference to \'html2canvas.min.js\'.');
388 }
389
390 let imageData = null;
391
392 const htmlContent = that.exportToHTML(data, fileName, true),
393 iframe = document.createElement('iframe');
394
395 iframe.style.position = 'absolute';
396 iframe.style.top = 0;
397 iframe.style.left = 0;
398 iframe.style.border = 'none';
399 iframe.style.width = '100%';
400 iframe.style.height = '100%';
401 iframe.style.opacity = 0;
402
403 document.body.appendChild(iframe);
404
405 iframe.contentDocument.write(htmlContent);
406
407 function checkIframePopulated() {
408 if (!iframe.contentDocument.body || !iframe.contentDocument.body.firstElementChild) {
409 requestAnimationFrame(checkIframePopulated);
410 }
411 else {
412 iframe.contentWindow.html2canvas(iframe.contentDocument.body.firstElementChild).then(canvas => {
413 const draw = $.jqxDraw(document.createElement('div'));
414
415 imageData = canvas.toDataURL('image/png');
416
417 if (callback) {
418 callback(imageData);
419 }
420 else {
421 document.body.appendChild(canvas);
422 draw.exportImage(undefined, canvas, fileExtension, fileName);
423 }
424
425 iframe.remove();
426 canvas.remove();
427 });
428 }
429 }
430
431 checkIframePopulated();
432
433 return imageData;
434 }
435
436 /**
437 * Gets merged cells information (for use in HTML and PDF export).
438 */
439 getMergedCellsInfo(mergedMainCells, mergedSecondaryCells, mapping) {
440 const that = this;
441
442 if (!that.mergedCells) {
443 return;
444 }
445
446 const multipleTables = mapping && mapping[that.datafields.length - 1] !== 0;
447
448 that.mergedCellsPDF = that.mergedCells.slice(0);
449
450 for (let i = 0; i < that.mergedCellsPDF.length; i++) {
451 const cellDefinition = that.mergedCellsPDF[i];
452 let colspan = cellDefinition.colspan,
453 rowspan = cellDefinition.rowspan;
454
455 if (rowspan < 2 && colspan < 2) {
456 continue;
457 }
458
459 const row = cellDefinition.cell[1];
460 let col = cellDefinition.cell[0];
461
462 if (multipleTables && colspan > 1) {
463 const startTable = mapping[col],
464 endTable = mapping[col + colspan - 1],
465 splitCells = [];
466
467 if (endTable > startTable) {
468 let currentTable = startTable,
469 currentColumn = col,
470 overal = 0;
471
472 mainLoop:
473 for (let i = startTable; i <= endTable; i++) {
474 let start = currentColumn,
475 span = 0;
476
477 while (mapping[currentColumn] === currentTable) {
478 currentColumn++;
479 overal++;
480 span++;
481
482 if (overal === colspan) {
483 splitCells.push({ start: start, span: span });
484 break mainLoop;
485 }
486 }
487
488 splitCells.push({ start: start, span: span });
489 currentTable = mapping[currentColumn];
490 }
491
492 colspan = splitCells[0].span;
493
494 for (let i = 1; i < splitCells.length; i++) {
495 that.mergedCellsPDF.push({ cell: [splitCells[i].start, row], colspan: splitCells[i].span, rowspan: rowspan, originalCell: col });
496 }
497 }
498 }
499
500 for (let j = col; j < col + colspan; j++) {
501 for (let k = row; k < row + rowspan; k++) {
502 const code = j + ',' + k;
503
504 if (j === col && k === row) {
505 mergedMainCells[code] = { colspan: colspan, rowspan: rowspan, originalCell: cellDefinition.originalCell };
506 continue;
507 }
508
509 mergedSecondaryCells[code] = true;
510 }
511 }
512 }
513 }
514
515 /**
516 * Gets alternation index.
517 */
518 getAlternationIndex(row, prefix) {
519 const that = this;
520
521 if (!that.style) {
522 return '';
523 }
524
525 const rowsDefinition = that.style.rows,
526 alternationCount = rowsDefinition && rowsDefinition.alternationCount;
527
528 if (alternationCount &&
529 (((rowsDefinition.alternationStart === undefined || row >= rowsDefinition.alternationStart) &&
530 (rowsDefinition.alternationEnd === undefined || row <= rowsDefinition.alternationEnd)) ||
531 rowsDefinition.alternationStart === rowsDefinition.alternationEnd)) {
532 return prefix + (row % rowsDefinition.alternationCount);
533 }
534
535 return '';
536 }
537
538 /**
539 * Gets formatted numeric or date value (for use in HTML and PDF export).
540 */
541 getFormattedValue(value, datafield) {
542 const that = this,
543 style = that.style;
544
545 if (datafield && style && style.columns &&
546 style.columns[datafield] && style.columns[datafield].format) {
547 if (typeof value === 'number') {
548 return that.formatNumber(value, style.columns[datafield].format);
549 }
550 else if (value instanceof Date) {
551 return that.formatDate(value, style.columns[datafield].format);
552 }
553 }
554 else if (value instanceof Date) {
555 return that.formatDate(value, 'd');
556 }
557
558 return value;
559 }
560
561 /**
562 * Exports to JSON.
563 */
564 exportToJSON(data, fileName) {
565 return this.downloadFile(JSON.stringify(data, this.datafields.concat('rows')), 'application/json', fileName);
566 }
567
568 /**
569 * Exports to PDF.
570 */
571 exportToPDF(data, fileName) {
572 const that = this,
573 datafields = that.datafields,
574 startIndex = +that.exportHeader,
575 groupsHandled = [],
576 mergedMainCells = {},
577 mergedSecondaryCells = {},
578 mapping = {},
579 headerRows = startIndex ? that.complexHeader ? that.complexHeader.length : 1 : 0,
580 docDefinition = {
581 pageOrientation: that.pageOrientation || 'portrait'
582 };
583 let header = [], content = [], tables;
584
585 function createTableRow() {
586 let tableRow = [];
587
588 for (let i = 0; i < tables.length; i++) {
589 tableRow.push([]);
590 }
591
592 return tableRow;
593 }
594
595 data = that.processGroupingInformation(data);
596 that.data = data;
597 that.headerRows = headerRows;
598 that.getPDFStyle();
599
600 const styleInfo = that.styleInfo;
601
602 tables = styleInfo ? that.wrapPDFColumns(docDefinition, mapping) : [{ body: header, datafields: datafields }];
603
604 if (startIndex) {
605 header = that.getPDFHeader(datafields, tables, mapping);
606 }
607
608 that.getMergedCellsInfo(mergedMainCells, mergedSecondaryCells, mapping);
609
610 mainLoop:
611 for (let i = startIndex; i < data.length; i++) {
612 const currentRecord = data[i];
613 let groupId = '',
614 outlineLevel = 0;
615
616 if (that.groupBy) {
617 for (let k = 0; k < that.groupBy.length; k++) {
618 const datafield = that.groupBy[k],
619 currentGroup = currentRecord[datafield],
620 currentGroupLabel = that.groups[datafield][currentGroup];
621
622 groupId += currentGroup;
623
624 if (groupsHandled.indexOf(groupId) === -1) {
625 that.createGroupHeaderRow(tables, { text: currentGroupLabel, style: ['row', 'cell', 'group'], marginLeft: outlineLevel * 7.5 });
626 groupsHandled.push(groupId);
627 i--;
628 continue mainLoop;
629 }
630
631 outlineLevel++;
632 }
633 }
634
635 const tableRow = createTableRow(),
636 row = i - startIndex;
637 let n = that.getAlternationIndex(row, '');
638
639 for (let j = 0; j < datafields.length; j++) {
640 const datafield = datafields[j],
641 entry = { style: ['row', 'row' + row, 'cell', 'cell' + datafield] },
642 tableIndex = mapping[j] || 0;
643
644 if (n !== undefined) {
645 entry.style.splice(1, 0, 'rowN' + n);
646 }
647
648 if (that.mergedCellsPDF) {
649 const cellCode = j + ',' + row,
650 mergeInfo = mergedMainCells[cellCode];
651
652 if (mergeInfo) {
653 entry.colSpan = mergeInfo.colspan;
654 entry.rowSpan = mergeInfo.rowspan;
655
656 if (mergeInfo.originalCell !== undefined) {
657 entry.text = '';
658 entry.style[entry.style.length - 1] = 'cell' + datafields[mergeInfo.originalCell];
659 tableRow[tableIndex].push(entry);
660 continue;
661 }
662 }
663 else if (mergedSecondaryCells[cellCode]) {
664 tableRow[tableIndex].push({});
665 continue;
666 }
667 }
668
669 const value = that.getFormattedValue(currentRecord[datafield], datafield);
670
671 entry.text = value.toString();
672 that.getUniqueStylePDF(entry, datafield, row);
673 that.setIndentation(entry, { j: j, currentRecord: currentRecord, value: value, outlineLevel: outlineLevel });
674 tableRow[tableIndex].push(entry);
675 }
676
677 for (let k = 0; k < tables.length; k++) {
678 tables[k].body.push(tableRow[k]);
679 }
680 }
681
682 if (styleInfo) {
683 for (let i = 0; i < tables.length; i++) {
684 const body = tables[i].body;
685
686 for (let j = headerRows - 1; j >= 0; j--) {
687 body.unshift(header[i][j]);
688 }
689
690 content.push({
691 table: {
692 headerRows: headerRows,
693 widths: tables[i].widths,
694 heights: function (row) {
695 if (styleInfo.heights[row]) {
696 return styleInfo.heights[row];
697 }
698
699 if (styleInfo.defaultHeight) {
700 return styleInfo.defaultHeight;
701 }
702 },
703 body: body
704 },
705 pageBreak: 'after'
706 });
707 }
708
709 delete content[tables.length - 1].pageBreak;
710 docDefinition.styles = styleInfo.styles;
711 }
712 else {
713 const body = tables[0].body;
714
715 for (let j = headerRows - 1; j >= 0; j--) {
716 body.unshift(header[0][j]);
717 }
718
719 content = [{ table: { headerRows: headerRows, body: body } }];
720 docDefinition.styles = { header: { bold: true }, group: { bold: true } };
721 }
722
723 docDefinition.content = content;
724 pdfMake.createPdf(docDefinition).download(fileName);
725
726 delete that.mergedCellsPDF;
727 delete that.styleInfo;
728 }
729
730 /**
731 * Gets the header content when exporting to PDF.
732 */
733 getPDFStyle() {
734 const that = this,
735 style = that.style;
736
737 if (!style) {
738 return '';
739 }
740
741 const sampleRecord = that.data[0],
742 headerDefinition = style.header,
743 columnsDefinition = style.columns,
744 rowsDefinition = style.rows,
745 styleInfo = {
746 heights: [],
747 widths: Array(that.datafields.length).fill('*'),
748 styles: {
749 header: {},
750 row: {},
751 cell: {},
752 group: { fillColor: '#FFFFFF', color: '#000000', bold: true }
753 }
754 };
755
756 that.styleInfo = styleInfo;
757
758 function processStyleDefinition(definition, type) {
759 if (!definition) {
760 return;
761 }
762
763 for (let prop in definition) {
764 if (!definition.hasOwnProperty(prop)) {
765 continue;
766 }
767
768 if (sampleRecord[prop] === undefined) {
769 if (prop === 'height' && type === 'header') {
770 for (let i = 0; i < that.headerRows; i++) {
771 styleInfo.heights[i] = (parseInt(definition[prop], 10) / that.headerRows) / 1.57;
772 }
773 }
774 else {
775 that.storePDFStyle({ prop: prop, value: definition[prop], toUpdate: type });
776 }
777 }
778 else {
779 for (let columnProp in definition[prop]) {
780 if (!isNaN(columnProp) || !definition[prop].hasOwnProperty(columnProp)) {
781 continue;
782 }
783
784 const value = definition[prop][columnProp],
785 index = that.datafields.indexOf(prop);
786
787 if (columnProp === 'width' && styleInfo.widths[index] === '*') {
788 styleInfo.widths[index] = parseFloat(value);
789 }
790 else {
791 that.storePDFStyle({ prop: columnProp, value: value, toUpdate: type + prop });
792 }
793 }
794 }
795 }
796 }
797
798 processStyleDefinition(headerDefinition, 'header');
799 processStyleDefinition(columnsDefinition, 'cell');
800
801 if (!rowsDefinition) {
802 return;
803 }
804
805 for (let prop in rowsDefinition) {
806 if (!rowsDefinition.hasOwnProperty(prop) || prop.indexOf('alt') !== -1) {
807 continue;
808 }
809
810 const value = rowsDefinition[prop];
811
812 if (!isNaN(prop)) {
813 for (let rowProp in value) {
814 if (value.hasOwnProperty(rowProp)) {
815 if (rowProp === 'height') {
816 styleInfo.heights[parseFloat(prop) + that.headerRows] = parseFloat(value[rowProp]) / 1.57;
817 }
818 else {
819 that.storePDFStyle({ prop: rowProp, value: value[rowProp], toUpdate: 'row' + prop });
820 }
821 }
822 }
823
824 continue;
825 }
826
827 if (prop === 'height') {
828 styleInfo.defaultHeight = parseFloat(value) / 1.57;
829 }
830 else {
831 that.storePDFStyle({ prop: prop, value: value, toUpdate: 'row' });
832 }
833 }
834
835 if (!rowsDefinition.alternationCount) {
836 return;
837 }
838
839 for (let i = 0; i < rowsDefinition.alternationCount; i++) {
840 const styleN = {};
841
842 if (rowsDefinition[`alternationIndex${i}Color`]) {
843 styleN.color = rowsDefinition[`alternationIndex${i}Color`];
844 }
845
846 if (rowsDefinition[`alternationIndex${i}BackgroundColor`]) {
847 styleN.fillColor = rowsDefinition[`alternationIndex${i}BackgroundColor`];
848 }
849
850 styleInfo.styles['rowN' + i] = styleN;
851 }
852 }
853
854 /**
855 * Stores style in object to be applied to generated PDF.
856 */
857 storePDFStyle(details) {
858 const that = this;
859 let objectToUpdate = that.styleInfo.styles[details.toUpdate];
860
861 if (!objectToUpdate) {
862 objectToUpdate = {};
863 that.styleInfo.styles[details.toUpdate] = objectToUpdate;
864 }
865
866 let value = details.value;
867
868 switch (details.prop) {
869 case 'backgroundColor':
870 objectToUpdate.fillColor = value;
871 break;
872 case 'color':
873 objectToUpdate.color = value;
874 break;
875 case 'fontSize':
876 objectToUpdate.fontSize = parseFloat(value);
877 break;
878 case 'fontStyle':
879 if (value === 'italic') {
880 objectToUpdate.italics = true;
881 }
882
883 break;
884 case 'fontWeight':
885 if (value === 'bold') {
886 objectToUpdate.bold = true;
887 }
888
889 break;
890 case 'textAlign':
891 objectToUpdate.alignment = value;
892 break;
893 }
894 }
895
896 /**
897 * Enables column wrapping when exporting to PDF.
898 */
899 wrapPDFColumns(docDefinition, mapping) {
900 const that = this,
901 styleInfo = this.styleInfo,
902 maxPerPage = docDefinition.pageOrientation === 'portrait' ? 775 : 1155, // maximum of 775px (portrait) or 1155px (landscape) per A4 page
903 tables = [];
904 let currentPage = 0;
905
906 for (let i = 0; i < styleInfo.widths.length; i++) {
907 let currentWidth = styleInfo.widths[i],
908 numericWidth = currentWidth;
909
910 if (currentWidth === '*') {
911 numericWidth = 150;
912 }
913 else if (currentWidth >= maxPerPage) {
914 numericWidth = maxPerPage
915 currentWidth = '*';
916 }
917 else {
918 currentWidth /= 1.57;
919 }
920
921 if (tables[currentPage] === undefined) {
922 const body = [];
923
924 tables[currentPage] = {
925 body: body,
926 width: numericWidth,
927 widths: [currentWidth],
928 datafields: [that.datafields[i]]
929 };
930 mapping[i] = currentPage;
931 continue;
932 }
933
934 const table = tables[currentPage];
935
936 if (table.width + numericWidth > maxPerPage) {
937 currentPage++;
938 i--;
939 continue;
940 }
941
942 mapping[i] = currentPage;
943 table.width += numericWidth;
944 table.widths.push(currentWidth);
945 table.datafields.push(that.datafields[i]);
946 }
947
948 return tables;
949 }
950
951 /**
952 * Gets the header content when exporting to PDF.
953 */
954 getPDFHeader(datafields, tables, mapping) {
955 const that = this,
956 headerArray = [],
957 headerRows = that.headerRows,
958 headerStructure = that.complexHeader ? that.complexHeader : [Object.values(that.data[0])],
959 headers = [];
960 let result = [];
961
962 for (let i = 0; i < headerRows; i++) {
963 const row = headerStructure[i];
964
965 for (let k = 0; k < row.length; k++) {
966 let tableIndex = mapping[k] || 0;
967
968 if (!headers[tableIndex]) {
969 headers[tableIndex] = [];
970 }
971
972 if (!headers[tableIndex][i]) {
973 headers[tableIndex][i] = [];
974 }
975
976 headers[tableIndex][i].push(row[k]);
977 }
978 }
979
980 function processHeader(header, result, table) {
981 for (let j = 0; j < headerRows; j++) {
982 const row = header[j];
983 const tableRow = [];
984
985 for (let k = 0; k < row.length; k++) {
986 const currentLabel = row[k];
987 let colspan = 1, rowspan = 1;
988
989 if ((row[k - 1] && row[k - 1] === currentLabel) ||
990 (header[j - 1] && (header[j - 1][k] === currentLabel))) {
991 tableRow.push({});
992 continue;
993 }
994
995 let iterator = k + 1;
996
997 while (row[iterator] && row[iterator] === row[iterator - 1]) {
998 colspan++;
999 iterator++;
1000 }
1001
1002 iterator = j + 1;
1003
1004 while (header[iterator] && header[iterator][k] === currentLabel) {
1005 rowspan++;
1006 iterator++;
1007 }
1008
1009 const datafield = j === headerRows - 1 || rowspan + j === headerRows ?
1010 table.datafields[k] : null,
1011 entry = {
1012 text: currentLabel, colSpan: colspan, rowSpan: rowspan
1013 };
1014
1015 if (!datafield) {
1016 entry.alignment = 'center';
1017 entry.style = 'header';
1018 }
1019 else {
1020 entry.style = ['header', 'header' + datafield];
1021 }
1022
1023 tableRow.push(entry);
1024 }
1025
1026 result.push(tableRow);
1027 }
1028 }
1029
1030 for (let i = 0; i < tables.length; i++) {
1031 result = [];
1032 processHeader(headers[i], result, tables[i]);
1033 headerArray.push(result);
1034 }
1035
1036 return headerArray;
1037 }
1038
1039 /**
1040 * Creates group header rows when exporting to PDF.
1041 */
1042 createGroupHeaderRow(tables, entryTemplate) {
1043 for (let i = 0; i < tables.length; i++) {
1044 const entry = Object.assign({}, entryTemplate),
1045 colspan = tables[i].datafields.length,
1046 tableRow = [entry];
1047
1048 entry.colSpan = colspan;
1049 tableRow.length = colspan;
1050 tableRow.fill({}, 1, colspan - 1);
1051
1052 tables[i].body.push(tableRow);
1053 }
1054 }
1055
1056 /**
1057 * Gets unique cell style when exporting to PDF.
1058 */
1059 getUniqueStylePDF(entry, datafield, row) {
1060 const style = this.style;
1061
1062 function toHex(background) {
1063 const parts = /rgba\((\d+),(\d+),(\d+)\,(\d*.\d+|\d+)\)/gi.exec(background.replace(/\s/g, '')),
1064 r = parseFloat(parts[1]).toString(16).toUpperCase(),
1065 g = parseFloat(parts[2]).toString(16).toUpperCase(),
1066 b = parseFloat(parts[3]).toString(16).toUpperCase();
1067
1068 return '#' + ('0').repeat(2 - r.length) + r +
1069 ('0').repeat(2 - g.length) + g +
1070 ('0').repeat(2 - b.length) + b;
1071 }
1072
1073 if (!style || !style.columns || !style.columns[datafield]) {
1074 return;
1075 }
1076
1077 const uniqueStyle = style.columns[datafield][row];
1078
1079 if (!uniqueStyle) {
1080 return;
1081 }
1082
1083 entry.fillColor = toHex(uniqueStyle.background);
1084 entry.color = uniqueStyle.color.toLowerCase();
1085 }
1086
1087 /**
1088 * Sets the indentation of a PDF cell.
1089 */
1090 setIndentation(entry, details) {
1091 if (details.j !== 0) {
1092 return;
1093 }
1094
1095 const that = this;
1096
1097 if (that.actualHierarchy) {
1098 const currentRecord = details.currentRecord;
1099
1100 if (currentRecord._expanded !== undefined) {
1101 entry.marginLeft = 25 * (currentRecord._level - 1);
1102 entry.text = that.collapseChar + ' ' + details.value;
1103 }
1104 else {
1105 entry.marginLeft = 25 * (currentRecord._level - 1) + 6;
1106 }
1107 }
1108 else if (details.outlineLevel > 1) {
1109 entry.marginLeft = (details.outlineLevel - 1) * 7.5;
1110 }
1111 }
1112
1113 /**
1114 * Exports to XLSX.
1115 */
1116 exportToXLSX(data, fileName) {
1117 const that = this;
1118 let style = that.style;
1119
1120 data = that.processGroupingInformation(data, true);
1121 that.data = data;
1122 that.getColumnsArray();
1123
1124 that.complexHeaderMergedCells = [];
1125
1126 if (that.complexHeaderMergeInfo) {
1127 for (let cell in that.complexHeaderMergeInfo) {
1128 if (that.complexHeaderMergeInfo.hasOwnProperty(cell)) {
1129 const currentEntry = that.complexHeaderMergeInfo[cell];
1130
1131 if (currentEntry.from[0] === currentEntry.to[0] &&
1132 currentEntry.from[1] === currentEntry.to[1]) {
1133 continue;
1134 }
1135
1136 that.complexHeaderMergedCells.push({
1137 from: that.columnsArray[currentEntry.from[1]] + (currentEntry.from[0] + 1),
1138 to: that.columnsArray[currentEntry.to[1]] + (currentEntry.to[0] + 1)
1139 });
1140 }
1141 }
1142 }
1143
1144 that.getConditionalFormatting();
1145
1146 if (!style) {
1147 style = that.generateDefaultStyle(data);
1148 }
1149
1150 const sharedStrings = that.generateSharedStrings(data),
1151 sharedStringsCollection = sharedStrings.collection,
1152 sharedStringsXML = sharedStrings.xml,
1153 stylesXML = that.generateStyles(style),
1154 sheet1XML = that.groupBy ? that.generateSheet1WithGrouping(data, sharedStringsCollection) :
1155 that.generateSheet1(data, sharedStringsCollection),
1156 auxiliaryFiles = that.generateAuxiliaryFiles(),
1157
1158 // eslint-disable-next-line
1159 zip = new JSZip(),
1160 _rels = zip.folder('_rels'),
1161 docProps = zip.folder('docProps'),
1162 xl = zip.folder('xl'),
1163 xl_rels = xl.folder('_rels'),
1164 theme = xl.folder('theme'),
1165 worksheets = xl.folder('worksheets');
1166
1167 _rels.file('.rels', auxiliaryFiles._relsRels);
1168 docProps.file('app.xml', auxiliaryFiles.docPropsAppXml);
1169 docProps.file('core.xml', auxiliaryFiles.docPropsCoreXml);
1170 xl_rels.file('workbook.xml.rels', auxiliaryFiles.xl_relsWorkbookXmlRels);
1171 theme.file('theme1.xml', auxiliaryFiles.xlThemeTheme1Xml);
1172 worksheets.file('sheet1.xml', sheet1XML);
1173 xl.file('sharedStrings.xml', sharedStringsXML);
1174 xl.file('styles.xml', stylesXML);
1175 xl.file('workbook.xml', auxiliaryFiles.xlWorkbookXml);
1176 zip.file('[Content_Types].xml', auxiliaryFiles.Content_TypesXml);
1177
1178 zip.generateAsync({
1179 type: 'blob',
1180 mimeType:
1181 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
1182 })
1183 .then(function (content) {
1184 return that.downloadFile(content, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', fileName);
1185 });
1186
1187 delete that.conditionalFormattingXLSX;
1188 delete that.complexHeaderMergeInfo;
1189 delete that.defaultRowHeight;
1190 delete that.rowHeight;
1191 }
1192
1193 /**
1194 * Processes grouping information.
1195 */
1196 processGroupingInformation(data, xlsx) {
1197 const that = this;
1198
1199 if (!that.groupBy) {
1200 return data;
1201 }
1202
1203 let header;
1204
1205 data = data.slice(0);
1206
1207 if (that.exportHeader) {
1208 if (xlsx && that.complexHeader) {
1209 header = data.slice(0, that.complexHeader.length);
1210 data.splice(0, that.complexHeader.length);
1211 }
1212 else {
1213 header = [data[0]];
1214 data.splice(0, 1);
1215 }
1216 }
1217
1218 if (data.length > 1) {
1219 const getCompareFunction = function (a, knownDataType) {
1220 // gets data type of column (not necessary if the Grid provides this information)
1221 const dataType = knownDataType || typeof a;
1222 let compareFunction;
1223
1224 switch (dataType) {
1225 case 'string':
1226 compareFunction = new Intl.Collator().compare;
1227 break;
1228 case 'number':
1229 compareFunction = function (a, b) {
1230 return a - b;
1231 };
1232 break;
1233 case 'boolean':
1234 case 'bool':
1235 compareFunction = function (a, b) {
1236 if (a === b) {
1237 return 0;
1238 }
1239 else if (a === false) {
1240 return -1;
1241 }
1242 else {
1243 return 1;
1244 }
1245 };
1246 break;
1247 case 'date':
1248 case 'time':
1249 case 'dateTime':
1250 if (a instanceof Date) {
1251 compareFunction = function (a, b) {
1252 return a.compare(b);
1253 };
1254 }
1255 break;
1256 case 'object':
1257 if (a instanceof Date) {
1258 compareFunction = function (a, b) {
1259 return a.getTime() - b.getTime();
1260 };
1261 }
1262
1263
1264 break;
1265 }
1266
1267 return compareFunction;
1268 }
1269
1270 const sortByMultipleColumns = function (dataSource, sortColumns, directions, customSortingCallback) {
1271 if (!dataSource || !(Array.isArray(dataSource)) || dataSource.length === 0 ||
1272 !sortColumns || Array.isArray(sortColumns) && sortColumns.length === 0) {
1273 return;
1274 }
1275
1276 if (typeof sortColumns === 'string') {
1277 sortColumns = [sortColumns];
1278 }
1279
1280 const directionCoefficients = [],
1281 compareFunctions = [];
1282
1283 if (directions === undefined) {
1284 directions = [];
1285 }
1286
1287 for (let i = 0; i < sortColumns.length; i++) {
1288 if (directions[i] === undefined || directions[i] === 'asc' || directions[i] === 'ascending') {
1289 directionCoefficients[i] = 1;
1290 }
1291 else {
1292 directionCoefficients[i] = -1;
1293 }
1294
1295 compareFunctions[i] = getCompareFunction(dataSource[0][sortColumns[i]]);
1296 }
1297
1298 if (customSortingCallback) {
1299 customSortingCallback(dataSource, sortColumns, directions, compareFunctions);
1300 return;
1301 }
1302
1303 dataSource.sort(function (a, b) {
1304 for (let i = 0; i < sortColumns.length; i++) {
1305 const result = compareFunctions[i](a[sortColumns[i]], b[sortColumns[i]]);
1306
1307 if (result === 0) {
1308 if (sortColumns[i + 1]) {
1309 continue;
1310 }
1311 else if (a._index !== undefined) {
1312 // makes sorting stable
1313 return (a._index - b._index) * directionCoefficients[i];
1314 }
1315
1316 return 0;
1317 }
1318
1319 return result * directionCoefficients[i];
1320 }
1321 });
1322 }
1323
1324 sortByMultipleColumns(data, that.groupBy);
1325 }
1326
1327 if (header) {
1328 data = header.concat(data);
1329 }
1330
1331 that.getGroupLabels(data);
1332
1333 return data;
1334 }
1335
1336 /**
1337 * Exports to XML.
1338 */
1339 exportToXML(data, fileName) {
1340 const datafields = this.datafields.slice(0);
1341 let xmlContent = '<?xml version="1.0" encoding="UTF-8" ?>\n<table>\n';
1342
1343 if (datafields.indexOf('rows') === -1) {
1344 datafields.push('rows');
1345 }
1346
1347 function recursion(records, indent) {
1348 let content = '';
1349
1350 for (let i = 0; i < records.length; i++) {
1351 const currentRecord = records[i];
1352
1353 content += indent + '<row>\n';
1354
1355 for (let j = 0; j < datafields.length; j++) {
1356 const datafield = datafields[j];
1357
1358 if (datafield === 'rows') {
1359 if (!currentRecord.rows) {
1360 continue;
1361 }
1362
1363 content += `${indent} <rows>\n${recursion(currentRecord.rows, indent + ' ')}${indent} </rows>\n`;
1364 continue;
1365 }
1366
1367 content += indent + ` <${datafield}>${currentRecord[datafield]}</${datafield}>\n`;
1368 }
1369
1370 content += indent + '</row>\n';
1371 }
1372
1373 return content;
1374 }
1375
1376 xmlContent += recursion(data, ' ') + '</table>';
1377
1378 return this.downloadFile(xmlContent, 'application/xml', fileName);
1379 }
1380
1381 /**
1382 * Formats a date.
1383 */
1384 formatDate(value, format) {
1385 var date = $.jqx.formatDate(value, format);
1386
1387 return date;
1388 }
1389
1390 /**
1391 * Formats a number.
1392 */
1393 formatNumber(value, format) {
1394 var number = $.jqx.formatNumber(value, format);
1395
1396 return number;
1397 }
1398
1399 /**
1400 * Generates auxiliary files necessary for XLSX.
1401 */
1402 generateAuxiliaryFiles() {
1403 // _rels\.rels
1404 const _relsRels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1405 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
1406
1407 // docProps\app.xml
1408 const docPropsAppXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1409 <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>Microsoft Excel</Application><DocSecurity>0</DocSecurity><ScaleCrop>false</ScaleCrop><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>Worksheets</vt:lpstr></vt:variant><vt:variant><vt:i4>1</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="1" baseType="lpstr"><vt:lpstr>Sheet1</vt:lpstr></vt:vector></TitlesOfParts><Company></Company><LinksUpToDate>false</LinksUpToDate><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0300</AppVersion></Properties>`;
1410
1411 // docProps\core.xml
1412 const now = new Date().toISOString(),
1413 docPropsCoreXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1414 <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:creator>Smart HTML Elements</dc:creator><cp:lastModifiedBy>Smart HTML Elements</cp:lastModifiedBy><dcterms:created xsi:type="dcterms:W3CDTF">${now}</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified></cp:coreProperties>`;
1415
1416 // xl\_rels\workbook.xml.rels
1417 const xl_relsWorkbookXmlRels = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1418 <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/></Relationships>`;
1419
1420 // xl\theme\theme1.xml
1421 const xlThemeTheme1Xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1422 <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme"><a:themeElements><a:clrScheme name="Office"><a:dk1><a:sysClr val="windowText" lastClr="000000"/></a:dk1><a:lt1><a:sysClr val="window" lastClr="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="44546A"/></a:dk2><a:lt2><a:srgbClr val="E7E6E6"/></a:lt2><a:accent1><a:srgbClr val="4472C4"/></a:accent1><a:accent2><a:srgbClr val="ED7D31"/></a:accent2><a:accent3><a:srgbClr val="A5A5A5"/></a:accent3><a:accent4><a:srgbClr val="FFC000"/></a:accent4><a:accent5><a:srgbClr val="5B9BD5"/></a:accent5><a:accent6><a:srgbClr val="70AD47"/></a:accent6><a:hlink><a:srgbClr val="0563C1"/></a:hlink><a:folHlink><a:srgbClr val="954F72"/></a:folHlink></a:clrScheme><a:fontScheme name="Office"><a:majorFont><a:latin typeface="Calibri Light" panose="020F0302020204030204"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック Light"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线 Light"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Times New Roman"/><a:font script="Hebr" typeface="Times New Roman"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="MoolBoran"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Times New Roman"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/><a:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:majorFont><a:minorFont><a:latin typeface="Calibri" panose="020F0502020204030204"/><a:ea typeface=""/><a:cs typeface=""/><a:font script="Jpan" typeface="游ゴシック"/><a:font script="Hang" typeface="맑은 고딕"/><a:font script="Hans" typeface="等线"/><a:font script="Hant" typeface="新細明體"/><a:font script="Arab" typeface="Arial"/><a:font script="Hebr" typeface="Arial"/><a:font script="Thai" typeface="Tahoma"/><a:font script="Ethi" typeface="Nyala"/><a:font script="Beng" typeface="Vrinda"/><a:font script="Gujr" typeface="Shruti"/><a:font script="Khmr" typeface="DaunPenh"/><a:font script="Knda" typeface="Tunga"/><a:font script="Guru" typeface="Raavi"/><a:font script="Cans" typeface="Euphemia"/><a:font script="Cher" typeface="Plantagenet Cherokee"/><a:font script="Yiii" typeface="Microsoft Yi Baiti"/><a:font script="Tibt" typeface="Microsoft Himalaya"/><a:font script="Thaa" typeface="MV Boli"/><a:font script="Deva" typeface="Mangal"/><a:font script="Telu" typeface="Gautami"/><a:font script="Taml" typeface="Latha"/><a:font script="Syrc" typeface="Estrangelo Edessa"/><a:font script="Orya" typeface="Kalinga"/><a:font script="Mlym" typeface="Kartika"/><a:font script="Laoo" typeface="DokChampa"/><a:font script="Sinh" typeface="Iskoola Pota"/><a:font script="Mong" typeface="Mongolian Baiti"/><a:font script="Viet" typeface="Arial"/><a:font script="Uigh" typeface="Microsoft Uighur"/><a:font script="Geor" typeface="Sylfaen"/><a:font script="Armn" typeface="Arial"/><a:font script="Bugi" typeface="Leelawadee UI"/><a:font script="Bopo" typeface="Microsoft JhengHei"/><a:font script="Java" typeface="Javanese Text"/><a:font script="Lisu" typeface="Segoe UI"/><a:font script="Mymr" typeface="Myanmar Text"/><a:font script="Nkoo" typeface="Ebrima"/><a:font script="Olck" typeface="Nirmala UI"/><a:font script="Osma" typeface="Ebrima"/><a:font script="Phag" typeface="Phagspa"/><a:font script="Syrn" typeface="Estrangelo Edessa"/><a:font script="Syrj" typeface="Estrangelo Edessa"/><a:font script="Syre" typeface="Estrangelo Edessa"/><a:font script="Sora" typeface="Nirmala UI"/><a:font script="Tale" typeface="Microsoft Tai Le"/><a:font script="Talu" typeface="Microsoft New Tai Lue"/><a:font script="Tfng" typeface="Ebrima"/></a:minorFont></a:fontScheme><a:fmtScheme name="Office"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:lumMod val="110000"/><a:satMod val="105000"/><a:tint val="67000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="103000"/><a:tint val="73000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="105000"/><a:satMod val="109000"/><a:tint val="81000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:satMod val="103000"/><a:lumMod val="102000"/><a:tint val="94000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:satMod val="110000"/><a:lumMod val="100000"/><a:shade val="100000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:lumMod val="99000"/><a:satMod val="120000"/><a:shade val="78000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:fillStyleLst><a:lnStyleLst><a:ln w="6350" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln><a:ln w="12700" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln><a:ln w="19050" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/><a:miter lim="800000"/></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst><a:outerShdw blurRad="57150" dist="19050" dir="5400000" algn="ctr" rotWithShape="0"><a:srgbClr val="000000"><a:alpha val="63000"/></a:srgbClr></a:outerShdw></a:effectLst></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:solidFill><a:schemeClr val="phClr"><a:tint val="95000"/><a:satMod val="170000"/></a:schemeClr></a:solidFill><a:gradFill rotWithShape="1"><a:gsLst><a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="93000"/><a:satMod val="150000"/><a:shade val="98000"/><a:lumMod val="102000"/></a:schemeClr></a:gs><a:gs pos="50000"><a:schemeClr val="phClr"><a:tint val="98000"/><a:satMod val="130000"/><a:shade val="90000"/><a:lumMod val="103000"/></a:schemeClr></a:gs><a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="63000"/><a:satMod val="120000"/></a:schemeClr></a:gs></a:gsLst><a:lin ang="5400000" scaled="0"/></a:gradFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements><a:objectDefaults/><a:extraClrSchemeLst/><a:extLst><a:ext uri="{05A4C25C-085E-4340-85A3-A5531E510DB2}"><thm15:themeFamily xmlns:thm15="http://schemas.microsoft.com/office/thememl/2012/main" name="Office Theme" id="{62F939B6-93AF-4DB8-9C6B-D6C7DFDC589F}" vid="{4A3C46E8-61CC-4603-A589-7422A47A8E4A}"/></a:ext></a:extLst></a:theme>`;
1423
1424 // xl\workbook.xml
1425 const xlWorkbookXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1426 <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x15 xr xr6 xr10 xr2" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:xr6="http://schemas.microsoft.com/office/spreadsheetml/2016/revision6" xmlns:xr10="http://schemas.microsoft.com/office/spreadsheetml/2016/revision10" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2"><fileVersion appName="xl" lastEdited="7" lowestEdited="7" rupBuild="20325"/><workbookPr defaultThemeVersion="166925"/><mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"><mc:Choice Requires="x15"><x15ac:absPath url="C:\Users\jqwidgets\Desktop\" xmlns:x15ac="http://schemas.microsoft.com/office/spreadsheetml/2010/11/ac"/></mc:Choice></mc:AlternateContent><xr:revisionPtr revIDLastSave="0" documentId="13_ncr:1_{0DEDCB6D-5403-4CD8-AAA5-59B6D238A8B6}" xr6:coauthVersionLast="34" xr6:coauthVersionMax="34" xr10:uidLastSave="{00000000-0000-0000-0000-000000000000}"/><bookViews><workbookView xWindow="0" yWindow="0" windowWidth="19200" windowHeight="6950" xr2:uid="{0CB664E6-3800-4A88-B158-B46A682E7484}"/></bookViews><sheets><sheet name="Sheet1" sheetId="1" r:id="rId1"/></sheets><calcPr calcId="179021"/><extLst><ext uri="{140A7094-0E35-4892-8432-C4D2E57EDEB5}" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"><x15:workbookPr chartTrackingRefBase="1"/></ext></extLst></workbook>`;
1427
1428 // [Content_Types].xml
1429 const Content_TypesXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1430 <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="bin" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>`;
1431
1432 return {
1433 _relsRels: _relsRels,
1434 docPropsAppXml: docPropsAppXml,
1435 docPropsCoreXml: docPropsCoreXml,
1436 xl_relsWorkbookXmlRels: xl_relsWorkbookXmlRels,
1437 xlThemeTheme1Xml: xlThemeTheme1Xml,
1438 xlWorkbookXml: xlWorkbookXml,
1439 Content_TypesXml: Content_TypesXml
1440 };
1441 }
1442
1443 /**
1444 * Generates default style object (for use in XLSX export).
1445 */
1446 generateDefaultStyle(data) {
1447 const that = this,
1448 defaultStyle = {},
1449 datafields = that.datafields,
1450 firstRecord = that.complexHeader ? data[that.complexHeader.length] : data[+that.exportHeader];
1451
1452 if (!firstRecord) {
1453 return defaultStyle;
1454 }
1455
1456 for (let i = 0; i < datafields.length; i++) {
1457 const sampleValue = firstRecord[datafields[i]];
1458
1459 if (sampleValue instanceof Date) {
1460 if (!defaultStyle.columns) {
1461 defaultStyle.columns = [];
1462 }
1463
1464 defaultStyle.columns[datafields[i]] = { format: 'd' };
1465 }
1466 }
1467
1468 return defaultStyle;
1469 }
1470
1471 /**
1472 * Generates group row.
1473 */
1474 generateGroupRow(details) {
1475 const rowNumber = details.rowNumber,
1476 from = 'A' + rowNumber,
1477 recordXML = ` <row r="${rowNumber}" outlineLevel="${details.outlineLevel}" spans="1:${details.numberOfColumns}"${this.getCustomRowHeight(rowNumber - 1)} x14ac:dyDescent="0.45">
1478 <c r="${from}" t="s" s="0">
1479 <v>${details.sharedStringIndex}</v>
1480 </c>
1481 </row>\n`;
1482
1483 details.mergedCells.push({ from: from, to: this.columnsArray[details.numberOfColumns - 1] + rowNumber });
1484
1485 return recordXML;
1486 }
1487
1488 /**
1489 * Generates sharedStrings.xml.
1490 */
1491 generateSharedStrings(data) {
1492 const that = this,
1493 datafields = that.datafields,
1494 collection = [];
1495 let xml = '',
1496 count = 0,
1497 uniqueCount = 0;
1498
1499 function addSharedString(currentValue) {
1500 count++;
1501
1502 if (collection.indexOf(currentValue) === -1) {
1503 uniqueCount++;
1504 collection.push(currentValue);
1505
1506 currentValue = currentValue.replace(/&(?!amp;)/g, '&amp;');
1507 currentValue = currentValue.replace(/'/g, '&apos;');
1508 currentValue = currentValue.replace(/"/g, '&quot;');
1509 currentValue = currentValue.replace(/>/g, '&gt;');
1510 currentValue = currentValue.replace(/</g, '&lt;');
1511
1512 xml += `<si><t>${currentValue}</t></si>`;
1513 }
1514 }
1515
1516 for (let i = 0; i < data.length; i++) {
1517 const currentRecord = data[i];
1518
1519 for (let j = 0; j < datafields.length; j++) {
1520 let currentValue = currentRecord[datafields[j]];
1521
1522 if (typeof currentValue !== 'string') {
1523 continue;
1524 }
1525
1526 addSharedString(currentValue);
1527 }
1528 }
1529
1530 if (that.groupLabels) {
1531 for (let i = 0; i < that.groupLabels.length; i++) {
1532 addSharedString(that.groupLabels[i]);
1533 }
1534 }
1535
1536 xml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1537 <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="${count}" uniqueCount="${uniqueCount}">${xml}</sst>`;
1538
1539 return { collection: collection, xml: xml };
1540 }
1541
1542 /**
1543 * Generates sheet1.xml.
1544 */
1545 generateSheet1(data, sharedStrings) {
1546 const that = this,
1547 numberOfColumns = that.columnsArray.length,
1548 numberOfRows = data.length,
1549 dimensionEnd = that.columnsArray[numberOfColumns - 1] + numberOfRows,
1550 datafields = that.datafields,
1551 autoFilter = that.getFilters(),
1552 mergedCells = [].concat(that.complexHeaderMergedCells);
1553
1554 let xmlContent = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1555 <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac xr xr2 xr3" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xr:uid="{7F25248B-C640-4C64-AD47-C0EA0E5D90D0}">
1556 <sheetPr filterMode="${autoFilter !== ''}" />
1557 <dimension ref="A1:${dimensionEnd}" />
1558 <sheetViews>
1559 <sheetView tabSelected="1" workbookViewId="0" />
1560 </sheetViews>
1561 <sheetFormatPr defaultRowHeight="14.5" x14ac:dyDescent="0.35" />${that.getCustomColumnWidths()}
1562 <sheetData>\n`;
1563
1564 function r(col, row) {
1565 return that.columnsArray[col] + row;
1566 }
1567
1568 for (let i = 0; i <= data.length; i++) {
1569 const currentRecord = data[i],
1570 rowNumber = i + 1;
1571 let collapsed = '';
1572
1573 if (that.actualHierarchy) {
1574 const previousRecord = data[i - 1];
1575
1576 if (previousRecord && previousRecord._collapsed &&
1577 (!currentRecord || previousRecord._level > currentRecord._level)) {
1578 collapsed = ' collapsed="true"';
1579 }
1580 }
1581
1582 if (i === data.length) {
1583 if (collapsed) {
1584 xmlContent += ` <row r="${rowNumber}" outlineLevel="${Math.max(data[i - 1]._level - 2, 0)}" hidden="false" collapsed="true" />\n`;
1585 }
1586
1587 break;
1588 }
1589
1590 let recordXML = ` <row r="${rowNumber}"${that.getOutlineLevel(currentRecord)} hidden="${currentRecord._hidden || currentRecord._collapsed || false}"${collapsed} spans="1:${numberOfColumns}"${that.getCustomRowHeight(rowNumber - 1)} x14ac:dyDescent="0.45">\n`;
1591
1592 for (let j = 0; j < datafields.length; j++) {
1593 const s = that.getXLSXCellStyle(r(j, rowNumber));
1594
1595 recordXML += that.getActualCellData(currentRecord[datafields[j]], { r: r(j, rowNumber), s: s }, sharedStrings);
1596 }
1597
1598 recordXML += ' </row>\n';
1599 xmlContent += recordXML;
1600 }
1601
1602 xmlContent += ` </sheetData>${that.conditionalFormattingXLSX.conditions}${autoFilter}${that.getMergedCells(mergedCells)}
1603 <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3" />
1604 <pageSetup paperSize="9" orientation="portrait" r:id="rId1" />
1605 </worksheet>`;
1606
1607 return xmlContent;
1608 }
1609
1610 /**
1611 * Generates sheet1.xml with grouping.
1612 */
1613 generateSheet1WithGrouping(data, sharedStrings) {
1614 const that = this,
1615 numberOfColumns = that.columnsArray.length,
1616 numberOfRows = data.length,
1617 dimensionEnd = that.columnsArray[numberOfColumns - 1] + numberOfRows,
1618 datafields = that.datafields,
1619 mergedCells = [].concat(that.complexHeaderMergedCells);
1620
1621 let xmlContent = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
1622 <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac xr xr2 xr3" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xr:uid="{7F25248B-C640-4C64-AD47-C0EA0E5D90D0}">
1623 <dimension ref="A1:${dimensionEnd}" />
1624 <sheetViews>
1625 <sheetView tabSelected="1" workbookViewId="0" />
1626 </sheetViews>
1627 <sheetFormatPr defaultRowHeight="14.5" x14ac:dyDescent="0.35" />${that.getCustomColumnWidths()}
1628 <sheetData>\n`,
1629 rowNumberCorrection = 0,
1630 groupsHandled = [];
1631
1632 function r(col, row) {
1633 return that.columnsArray[col] + row;
1634 }
1635
1636 mainLoop:
1637 for (let i = 0; i < data.length; i++) {
1638 const currentRecord = data[i],
1639 rowNumber = i + 1 + rowNumberCorrection;
1640 let outlineLevel = 0,
1641 outlineXML = '';
1642
1643 if (!that.exportHeader ||
1644 (!that.complexHeader && i !== 0) ||
1645 (that.complexHeader && i >= that.complexHeader.length)) {
1646 let groupId = '';
1647
1648 for (let k = 0; k < that.groupBy.length; k++) {
1649 const datafield = that.groupBy[k],
1650 currentGroup = currentRecord[datafield],
1651 currentGroupLabel = that.groups[datafield][currentGroup];
1652
1653 groupId += currentGroup;
1654
1655 if (groupsHandled.indexOf(groupId) === -1) {
1656 let sharedStringIndex = sharedStrings.indexOf(currentGroupLabel);
1657
1658 xmlContent += that.generateGroupRow({
1659 rowNumber: rowNumber,
1660 outlineLevel: outlineLevel,
1661 numberOfColumns: numberOfColumns,
1662 sharedStringIndex: sharedStringIndex,
1663 mergedCells: mergedCells
1664 });
1665 groupsHandled.push(groupId);
1666 i--;
1667 rowNumberCorrection++;
1668 continue mainLoop;
1669 }
1670
1671 outlineLevel++;
1672 }
1673
1674 outlineXML = ` outlineLevel="${outlineLevel}"`;
1675 }
1676
1677 let recordXML = ` <row r="${rowNumber}"${outlineXML} spans="1:${numberOfColumns}"${that.getCustomRowHeight(rowNumber - 1)} x14ac:dyDescent="0.45">\n`;
1678
1679 for (let j = 0; j < datafields.length; j++) {
1680 const s = that.getXLSXCellStyle(r(j, i + 1));
1681
1682 recordXML += that.getActualCellData(currentRecord[datafields[j]], { r: r(j, rowNumber), s: s }, sharedStrings);
1683 }
1684
1685 recordXML += ' </row>\n';
1686 xmlContent += recordXML;
1687 }
1688
1689 xmlContent += ` </sheetData>${that.getMergedCells(mergedCells)}
1690 <pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3" />
1691 <pageSetup paperSize="9" orientation="portrait" r:id="rId1" />
1692 </worksheet>`;
1693
1694 return xmlContent;
1695 }
1696
1697 /**
1698 * Gets actual spreadsheet cell data.
1699 */
1700 getActualCellData(currentValue, details, sharedStrings) {
1701 const r = details.r,
1702 s = details.s || ' s="0"';
1703
1704 if (typeof currentValue === 'string') {
1705 return ` <c r="${r}" t="s"${s}>
1706 <v>${sharedStrings.indexOf(currentValue)}</v>
1707 </c>\n`;
1708 }
1709
1710 if (typeof currentValue === 'boolean') {
1711 return ` <c r="${r}" t="b"${s}>
1712 <v>${+currentValue}</v>
1713 </c>\n`;
1714 }
1715
1716 if (currentValue instanceof Date) {
1717 const excelDate = (currentValue.getTime() + this.timeBetween1900And1970) / 86400000 + 2;
1718
1719 return ` <c r="${r}"${s}>
1720 <v>${excelDate}</v>
1721 </c>\n`;
1722 }
1723
1724 // numeric cells
1725 return ` <c r="${r}"${s}>
1726 <v>${currentValue}</v>
1727 </c>\n`;
1728 }
1729
1730 /**
1731 * Gets column labels.
1732 */
1733 getColumnsArray() {
1734 const that = this,
1735 numberOfColumns = that.datafields.length,
1736 columnsCollection = [];
1737
1738 function getIterator(i) {
1739 if (i < 26) {
1740 return '';
1741 }
1742
1743 return String.fromCharCode(64 + Math.floor(i / 26));
1744 }
1745
1746 for (let i = 0; i < numberOfColumns; i++) {
1747 columnsCollection.push(getIterator(i) + String.fromCharCode(65 + (i < 26 ? i : i % 26)));
1748 }
1749
1750 that.columnsArray = columnsCollection;
1751 }
1752
1753 /**
1754 * Gets column style.
1755 */
1756 getColumnStyle() {
1757 const that = this,
1758 style = that.style;
1759
1760 if (!style) {
1761 return ` .header { border: 1px solid black; padding: 5px; }
1762 .column { border: 1px solid black; padding: 5px; }
1763 .group { background-color: #FFFFFF; color: #000000; font-weight: bold; }`;
1764 }
1765
1766 const styles = {
1767 header: 'border: 1px solid black; padding: 5px; ',
1768 column: 'white-space: nowrap; overflow: hidden; border: 1px solid black; padding: 5px; ',
1769 group: 'background-color: #FFFFFF; color: #000000; font-weight: bold; '
1770 },
1771 sampleRecord = that.data[0];
1772 let generatedStyle = '';
1773
1774 const headerDefinition = style.header || {};
1775
1776 for (let prop in headerDefinition) {
1777 if (!headerDefinition.hasOwnProperty(prop)) {
1778 continue;
1779 }
1780
1781 const value = headerDefinition[prop];
1782
1783 if (sampleRecord[prop]) {
1784 if (!styles['header' + prop]) {
1785 styles['header' + prop] = '';
1786 }
1787
1788 for (let columnProp in value) {
1789 if (value.hasOwnProperty(columnProp)) {
1790 const css = window.jqxToDash(columnProp) + ': ' + value[columnProp] + '; ';
1791
1792 styles['header' + prop] += css;
1793
1794 if (columnProp === 'width') {
1795 if (!styles['column' + prop]) {
1796 styles['column' + prop] = '';
1797 }
1798
1799 styles['column' + prop] += css;
1800 }
1801 }
1802 }
1803
1804 continue;
1805 }
1806
1807 if (prop === 'height' && that.complexHeader) {
1808 styles.header += 'height: ' + parseInt(headerDefinition[prop], 10) / that.complexHeader.length + 'px; ';
1809 }
1810 else {
1811 styles.header += window.jqxToDash(prop) + ': ' + headerDefinition[prop] + '; ';
1812 }
1813 }
1814
1815 const columnsDefinition = style.columns || {};
1816
1817 for (let prop in columnsDefinition) {
1818 if (!columnsDefinition.hasOwnProperty(prop)) {
1819 continue;
1820 }
1821
1822 const value = columnsDefinition[prop];
1823
1824 if (sampleRecord[prop]) {
1825 if (!styles['column' + prop]) {
1826 styles['column' + prop] = '';
1827 }
1828
1829 for (let columnProp in value) {
1830 if (isNaN(columnProp) && value.hasOwnProperty(columnProp) && columnProp !== 'format') {
1831 styles['column' + prop] += window.jqxToDash(columnProp) + ': ' + value[columnProp] + '; ';
1832 }
1833 }
1834
1835 continue;
1836 }
1837
1838 styles.column += window.jqxToDash(prop) + ': ' + value + '; ';
1839 }
1840
1841 for (let prop in styles) {
1842 if (styles.hasOwnProperty(prop)) {
1843 generatedStyle += ` .${prop} { ${styles[prop]}}\n`;
1844 }
1845 }
1846
1847 return generatedStyle;
1848 }
1849
1850 /**
1851 * Gets custom column widths.
1852 */
1853 getCustomColumnWidths() {
1854 const that = this;
1855
1856 if (!that.style || !that.columnWidth || that.columnWidth.length === 0) {
1857 return '';
1858 }
1859
1860 let xml = '\n <cols>\n';
1861
1862 for (let i = 0; i < that.columnWidth.length; i++) {
1863 let width = that.columnWidth[i];
1864
1865 if (width !== undefined) {
1866 width = Math.round(parseFloat(width)) / 11;
1867 xml += ` <col min="${i + 1}" max="${i + 1}" width="${width}" customWidth="1" />\n`;
1868 }
1869 }
1870
1871 xml += ' </cols>';
1872
1873 return xml;
1874 }
1875
1876 /**
1877 * Returns customFilter tag.
1878 */
1879 getCustomFilter(value, condition) {
1880 let operator = 'equal',
1881 val;
1882
1883 if (value instanceof Date) {
1884 value = (value.getTime() + this.timeBetween1900And1970) / 86400000 + 2;
1885 }
1886
1887 condition = condition.toUpperCase();
1888
1889 switch (condition) {
1890 case 'EMPTY':
1891 val = '';
1892 break;
1893 case 'NOT_EMPTY':
1894 val = '';
1895 operator = 'notEqual';
1896 break;
1897 case 'CONTAINS':
1898 case 'CONTAINS_CASE_SENSITIVE':
1899 val = `*${value}*`;
1900 break;
1901 case 'DOES_NOT_CONTAIN':
1902 case 'DOES_NOT_CONTAIN_CASE_SENSITIVE':
1903 val = `*${value}*`;
1904 operator = 'notEqual';
1905 break;
1906 case 'STARTS_WITH':
1907 case 'STARTS_WITH_CASE_SENSITIVE':
1908 val = `${value}*`;
1909 break;
1910 case 'ENDS_WITH':
1911 case 'ENDS_WITH_CASE_SENSITIVE':
1912 val = `*${value}`;
1913 break;
1914 case 'EQUAL':
1915 case 'EQUAL_CASE_SENSITIVE':
1916 val = value;
1917 break;
1918 case 'NULL':
1919 val = null;
1920 break;
1921 case 'NOT_NULL':
1922 val = null;
1923 operator = 'notEqual';
1924 break;
1925 case 'NOT_EQUAL':
1926 val = value;
1927 operator = 'notEqual';
1928 break;
1929 case 'LESS_THAN':
1930 val = value;
1931 operator = 'lessThan';
1932 break;
1933 case 'LESS_THAN_OR_EQUAL':
1934 val = value;
1935 operator = 'lessThanOrEqual';
1936 break;
1937 case 'GREATER_THAN':
1938 val = value;
1939 operator = 'greaterThan';
1940 break;
1941 case 'GREATER_THAN_OR_EQUAL':
1942 val = value;
1943 operator = 'greaterThanOrEqual';
1944 break;
1945 }
1946
1947 return ` <customFilter val="${val}" operator="${operator}"/>\n`;
1948 }
1949
1950 /**
1951 * Gets custom row height.
1952 */
1953 getCustomRowHeight(row) {
1954 const that = this;
1955
1956 if (that.style) {
1957 return that.rowHeight[row] || that.defaultRowHeight || '';
1958 }
1959
1960 return '';
1961 }
1962
1963 /**
1964 * Gets datafields.
1965 */
1966 getDatafields(data) {
1967 const that = this,
1968 sampleRecord = data[0],
1969 datafields = [];
1970
1971 for (let prop in sampleRecord) {
1972 if (sampleRecord.hasOwnProperty(prop) && prop.charAt(0) !== '_') {
1973 datafields.push(prop);
1974 }
1975 }
1976
1977 that.datafields = datafields;
1978 }
1979
1980 /**
1981 * Returns autoFilter XML.
1982 */
1983 getFilters() {
1984 const that = this,
1985 filterBy = that.filterBy;
1986
1987 if (!filterBy) {
1988 return '';
1989 }
1990
1991 let xml = '';
1992
1993 for (let datafield in filterBy) {
1994 if (filterBy.hasOwnProperty(datafield)) {
1995 const colId = that.datafields.indexOf(datafield);
1996
1997 if (colId === -1) {
1998 continue;
1999 }
2000
2001 const filterDetails = filterBy[datafield],
2002 filters = filterDetails.filters;
2003
2004 xml += ` <filterColumn colId="${colId}">
2005 <customFilters and="${!filterDetails.operator}">\n`;
2006
2007 for (let i = 0; i < filters.length; i++) {
2008 xml += that.getCustomFilter(filters[i].value, filters[i].condition);
2009 }
2010
2011 xml += ` </customFilters>
2012 </filterColumn>`;
2013 }
2014 }
2015
2016 if (!xml) {
2017 return '';
2018 }
2019
2020 xml = `\n <autoFilter ref="A1:${that.columnsArray[that.columnsArray.length - 1] + that.data.length}">\n${xml}\n </autoFilter>`;
2021 return xml;
2022 }
2023
2024 /**
2025 * Gets group labels based on data.
2026 */
2027 getGroupLabels(data) {
2028 const that = this,
2029 startIndex = that.xlsxStartIndex !== undefined ? that.xlsxStartIndex : +that.exportHeader,
2030 groups = {},
2031 groupLabels = [];
2032
2033 for (let i = startIndex; i < data.length; i++) {
2034 const currentRecord = data[i];
2035
2036 for (let j = 0; j < that.groupBy.length; j++) {
2037 const datafield = that.groupBy[j],
2038 currentValue = currentRecord[datafield];
2039 let group = groups[datafield];
2040
2041 if (group === undefined) {
2042 groups[datafield] = {};
2043 group = groups[datafield];
2044 }
2045
2046 if (group[currentValue] === undefined) {
2047 group[currentValue] = (that.exportHeader ? data[startIndex - 1][datafield] : datafield) + ': ' + currentValue;
2048 groupLabels.push(group[currentValue]);
2049 }
2050 }
2051 }
2052
2053 that.groups = groups;
2054 that.groupLabels = groupLabels;
2055 }
2056
2057 /**
2058 * Gets the header content when exporting to HTML.
2059 */
2060 getHTMLHeader(datafields, data) {
2061 const that = this;
2062 let header = '\n <thead>\n';
2063
2064 if (!that.complexHeader) {
2065 header += ' <tr>\n';
2066
2067 for (let j = 0; j < datafields.length; j++) {
2068 const datafield = datafields[j];
2069
2070 header += ` <th class="header header${datafield}">${data[0][datafield]}</th>\n`;
2071 }
2072
2073 header += ' </tr>\n </thead>';
2074 return header;
2075 }
2076
2077 for (let j = 0; j < that.complexHeader.length; j++) {
2078 const row = that.complexHeader[j];
2079
2080 header += ' <tr>\n';
2081
2082 for (let k = 0; k < row.length; k++) {
2083 const currentLabel = row[k];
2084 let colspan = 1, rowspan = 1;
2085
2086 if ((row[k - 1] && row[k - 1] === currentLabel) ||
2087 (that.complexHeader[j - 1] && (that.complexHeader[j - 1][k] === currentLabel))) {
2088 continue;
2089 }
2090
2091 let iterator = k + 1;
2092
2093 while (row[iterator] && row[iterator] === row[iterator - 1]) {
2094 colspan++;
2095 iterator++;
2096 }
2097
2098 iterator = j + 1;
2099
2100 while (that.complexHeader[iterator] && that.complexHeader[iterator][k] === currentLabel) {
2101 rowspan++;
2102 iterator++;
2103 }
2104
2105 const datafield = j === that.complexHeader.length - 1 || rowspan + j === that.complexHeader.length ?
2106 ' header' + datafields[k] : '';
2107
2108 header += ` <th class="header${datafield}" colspan="${colspan}" rowspan="${rowspan}">${currentLabel}</th>\n`;
2109 }
2110
2111 header += ' </tr>\n';
2112 }
2113
2114 header += ' </thead>';
2115 return header;
2116 }
2117
2118 /**
2119 * Gets conditional formatting XML.
2120 */
2121 getConditionalFormatting() {
2122 const that = this,
2123 conditionalFormatting = that.conditionalFormatting;
2124
2125 if (!conditionalFormatting) {
2126 that.conditionalFormattingXLSX = { conditions: '', styles: '' };
2127 return;
2128 }
2129
2130 const dxfCodes = [];
2131 let conditionsXml = '',
2132 stylesXml = '';
2133
2134 for (let i = conditionalFormatting.length - 1; i >= 0; i--) {
2135 const columnFormat = conditionalFormatting[i],
2136 columnLetter = that.columnsArray[that.datafields.indexOf(columnFormat.column)],
2137 startCell = columnLetter + (that.xlsxStartIndex + 1),
2138 sqref = startCell + ':' + columnLetter + (that.data.length),
2139 dxfCode = columnFormat.background + columnFormat.color,
2140 attr = that.getConditionalAttributes(columnFormat, startCell);
2141 let dxfId = dxfCodes.indexOf(dxfCode);
2142
2143 if (dxfId === -1) {
2144 const newDxf = ` <dxf>
2145 <font>
2146 <b val="0"/>
2147 <i val="0"/>
2148 <color rgb="${columnFormat.color === 'White' ? 'FFFFFFFF' : 'FF000000'}"/>
2149 <sz val="10"/>
2150 </font>
2151 <fill>
2152 <patternFill>
2153 <bgColor rgb="${that.toARGB(columnFormat.background)}"/>
2154 </patternFill>
2155 </fill>
2156 </dxf>\n`;
2157
2158 stylesXml += newDxf;
2159 dxfId = dxfCodes.length;
2160 dxfCodes.push(dxfCode);
2161 }
2162
2163 conditionsXml += ` <conditionalFormatting sqref="${sqref}">
2164 <cfRule dxfId="${dxfId}" text="${attr.text}" rank="${attr.rank}" percent="${attr.percent}" bottom="${attr.bottom}" equalAverage="${attr.equalAverage}" aboveAverage="${attr.aboveAverage}"${attr.operator}${attr.timePeriod} priority="${i + 2}" type="${attr.type}">
2165 ${attr.formula} </cfRule>
2166 </conditionalFormatting>\n`;
2167 }
2168
2169 stylesXml = ` <dxfs count="${dxfCodes.length}">\n${stylesXml} </dxfs>`;
2170
2171 that.conditionalFormattingXLSX = { conditions: conditionsXml, styles: stylesXml };
2172 }
2173
2174 /**
2175 * Gets conditional formatting XML attributes.
2176 */
2177 getConditionalAttributes(columnFormat, startCell) {
2178 let condition = columnFormat.condition,
2179 comparator = columnFormat.comparator,
2180 text = '',
2181 rank = 0,
2182 percent = 0,
2183 bottom = 0,
2184 equalAverage = 0,
2185 aboveAverage = 0,
2186 operator = '',
2187 timePeriod = '',
2188 type = '',
2189 formula = '';
2190
2191 switch (condition) {
2192 case 'equal':
2193 operator = 'equal';
2194 type = 'cellIs';
2195 formula = ` <formula>${comparator}</formula>\n`;
2196 break;
2197 case 'lessThan':
2198 operator = 'lessThan';
2199 type = 'cellIs';
2200 formula = ` <formula>${comparator}</formula>\n`;
2201 break;
2202 case 'greaterThan':
2203 operator = 'greaterThan';
2204 type = 'cellIs';
2205 formula = ` <formula>${comparator}</formula>\n`;
2206 break;
2207 case 'notEqual':
2208 operator = 'notEqual';
2209 type = 'cellIs';
2210 formula = ` <formula>${comparator}</formula>\n`;
2211 break;
2212 case 'between':
2213 operator = 'between';
2214 type = 'cellIs';
2215 formula = ` <formula>${columnFormat.min}</formula>
2216 <formula>${columnFormat.max}</formula>\n`;
2217 break;
2218 case 'duplicate':
2219 type = 'duplicateValues';
2220 formula = ' <formula>0</formula>\n';
2221 break;
2222 case 'topNItems':
2223 rank = comparator;
2224 type = 'top10';
2225 break;
2226 case 'bottomNItems':
2227 rank = comparator;
2228 bottom = 1;
2229 type = 'top10';
2230 break;
2231 case 'topNPercent':
2232 rank = comparator;
2233 percent = 1;
2234 type = 'top10';
2235 break;
2236 case 'bottomNPercent':
2237 rank = comparator;
2238 percent = 1;
2239 bottom = 1;
2240 type = 'top10';
2241 break;
2242 case 'aboveAverage':
2243 aboveAverage = 1;
2244 type = 'aboveAverage';
2245 formula = ' <formula>0</formula>\n';
2246 break;
2247 case 'belowAverage':
2248 type = 'aboveAverage';
2249 formula = ' <formula>0</formula>\n';
2250 break;
2251 case 'contains':
2252 text = comparator;
2253 operator = 'containsText';
2254 type = 'containsText';
2255 formula = ` <formula>NOT(ISERROR(SEARCH("${comparator}",${startCell})))</formula>\n`;
2256 break;
2257 case 'doesNotContain':
2258 text = comparator;
2259 operator = 'notContains';
2260 type = 'notContainsText';
2261 formula = ` <formula>ISERROR(SEARCH("${comparator}",${startCell}))</formula>\n`;
2262 break;
2263 case 'dateOccur':
2264 timePeriod = ` timePeriod="${comparator}"`;
2265 type = 'timePeriod';
2266 break;
2267 }
2268
2269 if (operator) {
2270 operator = ` operator="${operator}" `;
2271 }
2272
2273 return {
2274 text: text,
2275 rank: rank,
2276 percent: percent,
2277 bottom: bottom,
2278 equalAverage: equalAverage,
2279 aboveAverage: aboveAverage,
2280 operator: operator,
2281 timePeriod: timePeriod,
2282 type: type,
2283 formula: formula
2284 }
2285 }
2286
2287 /**
2288 * Gets merged cells XML.
2289 */
2290 getMergedCells(mergedCells) {
2291 const that = this;
2292
2293 let mergeCellsXml = '';
2294
2295 for (let i = 0; i < mergedCells.length; i++) {
2296 if (mergedCells[i].from === mergedCells[i].to) {
2297 continue;
2298 }
2299
2300 mergeCellsXml += `\n <mergeCell ref="${mergedCells[i].from}:${mergedCells[i].to}" />\n`;
2301 }
2302
2303 if (that.mergedCells) {
2304 for (let i = 0; i < that.mergedCells.length; i++) {
2305 const cellDefinition = that.mergedCells[i];
2306
2307 if (cellDefinition.rowspan < 2 && cellDefinition.colspan < 2) {
2308 continue;
2309 }
2310
2311 const from = that.columnsArray[cellDefinition.cell[0]] + (cellDefinition.cell[1] + that.xlsxStartIndex + 1),
2312 to = that.columnsArray[cellDefinition.cell[0] + cellDefinition.colspan - 1] + (cellDefinition.cell[1] + that.xlsxStartIndex + cellDefinition.rowspan);
2313
2314 mergeCellsXml += `\n <mergeCell ref="${from}:${to}" />\n`;
2315 }
2316 }
2317
2318 if (mergeCellsXml) {
2319 mergeCellsXml = `\n <mergeCells count="${mergedCells.length}">${mergeCellsXml} </mergeCells>`;
2320 }
2321
2322 return mergeCellsXml;
2323 }
2324
2325 /**
2326 * Gets numFmt index.
2327 */
2328 getNumFmtIndex(format, numFmts) {
2329 let index = numFmts.collection.indexOf(format);
2330
2331 if (index === -1) {
2332 index = numFmts.collection.length + 100;
2333 numFmts.collection.push(format);
2334 numFmts.xml += `<numFmt numFmtId="${index}" formatCode="${format}"/>`;
2335 }
2336 else {
2337 index += 100;
2338 }
2339
2340 return index;
2341 }
2342
2343 /**
2344 * Returns outlineLevel.
2345 */
2346 getOutlineLevel(record) {
2347 if (!this.actualHierarchy || record._level === 1) {
2348 return '';
2349 }
2350
2351 return ` outlineLevel="${record._level - 1}"`;
2352 }
2353
2354 /**
2355 * Gets row style.
2356 */
2357 getRowStyle() {
2358 const that = this,
2359 style = that.style;
2360
2361 if (!style) {
2362 return '';
2363 }
2364
2365 const rowsDefinition = style.rows;
2366
2367 if (!rowsDefinition) {
2368 return '';
2369 }
2370
2371 const styles = {
2372 row: ''
2373 };
2374 let generatedStyle = '';
2375
2376 for (let prop in rowsDefinition) {
2377 if (!rowsDefinition.hasOwnProperty(prop) ||
2378 prop === 'alternationCount' ||
2379 prop === 'alternationStart' ||
2380 prop === 'alternationEnd') {
2381 continue;
2382 }
2383
2384 const value = rowsDefinition[prop];
2385
2386 if (prop.indexOf('alt') !== -1) {
2387 const i = prop.slice(16, 17),
2388 property = prop.slice(17);
2389
2390 if (!styles['rowN' + i]) {
2391 styles['rowN' + i] = '';
2392 }
2393
2394 if (property === 'Color') {
2395 styles['rowN' + i] += 'color : ' + value + '; ';
2396 }
2397 else if (property === 'BorderColor') {
2398 styles['rowN' + i] += 'border-color : ' + value + '; ';
2399 }
2400 else {
2401 styles['rowN' + i] += 'background-color : ' + value + '; ';
2402 }
2403
2404 continue;
2405 }
2406
2407 if (!isNaN(prop)) {
2408 if (!styles['row' + prop]) {
2409 styles['row' + prop] = '';
2410 }
2411
2412 for (let rowProp in value) {
2413 if (value.hasOwnProperty(rowProp)) {
2414 styles['row' + prop] += window.jqxToDash(rowProp) + ': ' + value[rowProp] + '; ';
2415 }
2416 }
2417
2418 continue;
2419 }
2420
2421 styles.row += window.jqxToDash(prop) + ': ' + rowsDefinition[prop] + '; ';
2422 }
2423
2424 let keys = Object.keys(styles);
2425
2426 keys.sort(function (a, b) {
2427 if (a === 'row') {
2428 return -1;
2429 }
2430
2431 if (b === 'row') {
2432 return 1;
2433 }
2434
2435 const aIsNum = !isNaN(a.slice(3)),
2436 bIsNum = !isNaN(b.slice(3));
2437
2438 if (aIsNum && !bIsNum) {
2439 return 1;
2440 }
2441
2442 if (!aIsNum && bIsNum) {
2443 return -1;
2444 }
2445
2446 return +(a < b);
2447 });
2448
2449 for (let i = 0; i < keys.length; i++) {
2450 generatedStyle += ` .${keys[i]} { ${styles[keys[i]]}}\n`;
2451 }
2452
2453 return generatedStyle;
2454 }
2455
2456 /**
2457 * Gets table style.
2458 */
2459 getTableStyle() {
2460 const that = this,
2461 style = that.style;
2462
2463 if (!style) {
2464 return ' style="table-layout: fixed; border: 1px solid black; border-collapse: collapse;"';
2465 }
2466
2467 let generatedStyle = 'table-layout: fixed; ';
2468
2469 for (let prop in style) {
2470 if (style.hasOwnProperty(prop) && ['header', 'columns', 'rows'].indexOf(prop) === -1) {
2471 generatedStyle += window.jqxToDash(prop) + ': ' + style[prop] + '; ';
2472 }
2473 }
2474
2475 if (generatedStyle) {
2476 generatedStyle = ' style="' + generatedStyle + '"';
2477 }
2478
2479 return generatedStyle;
2480 }
2481
2482 /**
2483 * Gets the "s" (style) attribute of an XLSX cell.
2484 */
2485 getXLSXCellStyle(r) {
2486 const that = this;
2487
2488 if (that.cellStyleMapping[r] !== undefined) {
2489 return ` s="${that.cellStyleMapping[r]}"`;
2490 }
2491
2492 return '';
2493 }
2494
2495 /**
2496 * Gets the "s" (style) attribute of an XLSX cell.
2497 */
2498 getXLSXFormat(format, cellValue) {
2499 if (typeof cellValue === 'number') {
2500 let precision = parseFloat(format.slice(1)) || 0,
2501 precisionCode = precision > 0 ? '.' + ('0').repeat(precision) : '';
2502
2503 format = format.slice(0, 1);
2504
2505 switch (format) {
2506 case 'C':
2507 case 'c':
2508 return '\$#,0' + precisionCode;
2509 case 'D':
2510 case 'd':
2511 if (precision) {
2512 return ('0').repeat(precision);
2513 }
2514
2515 return '0';
2516 case 'E':
2517 case 'e':
2518 return '0' + precisionCode + format + '000';
2519 case 'F':
2520 case 'f':
2521 return '0' + precisionCode;
2522 case 'N':
2523 case 'n':
2524 return '#,0' + precisionCode;
2525 case 'P':
2526 case 'p':
2527 return '#,0' + precisionCode + ' %';
2528 default:
2529 return;
2530 }
2531 }
2532 else if (cellValue instanceof Date) {
2533 switch (format) {
2534 case 'd':
2535 return 'm/d/yyyy';
2536 case 'D':
2537 return 'nnnnmmmm dd, yyyy';
2538 case 't':
2539 return 'h:m AM/PM';
2540 case 'T':
2541 return 'h:mm:ss AM/PM';
2542 case 'f':
2543 return 'nnnnmmmm dd, yyyy h:m AM/PM';
2544 case 'F':
2545 return 'nnnnmmmm dd, yyyy h:mm:ss AM/PM';
2546 case 'M':
2547 return 'mmmm d';
2548 case 'Y':
2549 return 'yyyy mmmm';
2550 case 'FP':
2551 case 'PP':
2552 return 'yyyy-mm-dd hh:mm:ss';
2553 case 'FT':
2554 case 'PT':
2555 return 'hh:mm:ss';
2556 }
2557
2558 format = format.replace(/f|u|n|p|e|a|x|o/gi, '');
2559 format = format.replace(/tt/gi, 'AM/PM');
2560 format = format.replace(/:{2,}|:\s|:$|\.$/g, '');
2561 format = format.trim();
2562 return format;
2563 }
2564 }
2565
2566 /**
2567 * Processes column styles.
2568 */
2569 processColumnStyle(style) {
2570 const that = this,
2571 headerDefinition = style.header,
2572 columnsDefinition = style.columns,
2573 sampleRecord = that.data[0],
2574 startIndex = that.xlsxStartIndex;
2575
2576 that.columnWidth = [];
2577
2578 if (startIndex && headerDefinition) {
2579 for (let i = 0; i < that.columnsArray.length; i++) {
2580 const columnLetter = that.columnsArray[i],
2581 cell = columnLetter + startIndex,
2582 columnSpecific = headerDefinition[that.datafields[i]];
2583
2584 for (let prop in headerDefinition) {
2585 if (headerDefinition.hasOwnProperty(prop) && sampleRecord[prop] === undefined) {
2586 if (that.complexHeader) {
2587 for (let j = 0; j < that.complexHeader.length; j++) {
2588 if (prop === 'height') {
2589 that.rowHeight[j] = ` ht="${(parseFloat(headerDefinition.height) / that.complexHeader.length) / 2}"`;
2590 continue;
2591 }
2592 else {
2593 that.storeCellStyle(columnLetter + (j + 1), prop, headerDefinition[prop]);
2594 }
2595 }
2596 }
2597 else {
2598 if (prop === 'height') {
2599 that.rowHeight[0] = ` ht="${parseFloat(headerDefinition.height) / 2}"`;
2600 continue;
2601 }
2602
2603 that.storeCellStyle(cell, prop, headerDefinition[prop]);
2604 }
2605 }
2606 }
2607
2608 if (!columnSpecific) {
2609 continue;
2610 }
2611
2612 for (let prop in columnSpecific) {
2613 if (columnSpecific.hasOwnProperty(prop)) {
2614 if (prop === 'width') {
2615 that.columnWidth[i] = columnSpecific.width;
2616 continue;
2617 }
2618
2619 that.storeCellStyle(cell, prop, columnSpecific[prop]);
2620 }
2621 }
2622 }
2623 }
2624 else if (headerDefinition) {
2625 for (let i = 0; i < that.columnsArray.length; i++) {
2626 const columnSpecific = headerDefinition[that.datafields[i]];
2627
2628 if (columnSpecific && columnSpecific.width !== undefined) {
2629 that.columnWidth[i] = columnSpecific.width;
2630 }
2631 }
2632 }
2633
2634 if (!columnsDefinition) {
2635 return '';
2636 }
2637
2638 for (let i = startIndex; i < that.data.length; i++) {
2639 for (let j = 0; j < that.columnsArray.length; j++) {
2640 const columnLetter = that.columnsArray[j],
2641 cell = columnLetter + (i + 1),
2642 datafield = that.datafields[j],
2643 columnSpecific = columnsDefinition[datafield];
2644
2645 for (let prop in columnsDefinition) {
2646 if (columnsDefinition.hasOwnProperty(prop) && sampleRecord[prop] === undefined) {
2647 that.storeCellStyle(cell, prop, columnsDefinition[prop]);
2648 }
2649 }
2650
2651 if (!columnSpecific) {
2652 continue;
2653 }
2654
2655 for (let prop in columnSpecific) {
2656 if (!isNaN(prop) || !columnSpecific.hasOwnProperty(prop)) {
2657 continue;
2658 }
2659
2660 that.storeCellStyle(cell, prop, columnSpecific[prop], that.data[i][datafield]);
2661 }
2662 }
2663 }
2664 }
2665
2666 /**
2667 * Processes complex header object.
2668 */
2669 processComplexHeader(header, data, format) {
2670 const that = this,
2671 flatHeader = {},
2672 processGrouping = ['html', 'jpeg', 'pdf', 'png', 'xlsx'].indexOf(format) !== -1 && header.columngroups,
2673 datafieldMapping = [],
2674 columnGroupHierarchy = {},
2675 complexHeader = [];
2676 let headerDepth = 0;
2677
2678 function getColumnGroup(columnGroup) {
2679 for (let i = 0; i < header.columngroups.length; i++) {
2680 const currentGroupDefinition = header.columngroups[i];
2681
2682 if (currentGroupDefinition.name === columnGroup) {
2683 return currentGroupDefinition;
2684 }
2685 }
2686 }
2687
2688 function getColumnGroupHierarchy(groupDefinition) {
2689 const columnGroups = [];
2690
2691 while (groupDefinition) {
2692 columnGroups.unshift(groupDefinition.label);
2693
2694 if (groupDefinition.parentGroup) {
2695 groupDefinition = getColumnGroup(groupDefinition.parentGroup);
2696 }
2697 else {
2698 return columnGroups;
2699 }
2700 }
2701 }
2702
2703 if (processGrouping) {
2704 for (let i = 0; i < header.columngroups.length; i++) {
2705 const currentGroupDefinition = header.columngroups[i],
2706 groupHierarchy = getColumnGroupHierarchy(currentGroupDefinition);
2707
2708 columnGroupHierarchy[currentGroupDefinition.name] = groupHierarchy;
2709 headerDepth = Math.max(headerDepth, groupHierarchy.length);
2710 }
2711
2712 headerDepth++;
2713
2714 for (let i = 0; i < headerDepth; i++) {
2715 complexHeader[i] = [];
2716 }
2717 }
2718
2719 for (let i = 0; i < header.columns.length; i++) {
2720 const currentColumn = header.columns[i];
2721
2722 flatHeader[currentColumn.dataField] = currentColumn.label;
2723
2724 if (!processGrouping) {
2725 continue;
2726 }
2727
2728 datafieldMapping[i] = currentColumn.dataField;
2729 complexHeader[headerDepth - 1][i] = currentColumn.label;
2730
2731 if (!currentColumn.columnGroup) {
2732 continue;
2733 }
2734
2735 const columnGroups = columnGroupHierarchy[currentColumn.columnGroup];
2736
2737 for (let j = 0; j < columnGroups.length; j++) {
2738 complexHeader[j][i] = columnGroups[j];
2739 }
2740 }
2741
2742 if (complexHeader.length > 1) {
2743 const numberOfDatafields = Object.keys(flatHeader).length;
2744
2745 for (let i = 0; i < headerDepth - 1; i++) {
2746 const entry = {};
2747
2748 for (let j = 0; j < numberOfDatafields; j++) {
2749 if (complexHeader[i][j] === undefined) {
2750 let iterator = i + 1;
2751
2752 while (complexHeader[iterator][j] === undefined) {
2753 iterator++;
2754 }
2755
2756 complexHeader[i][j] = complexHeader[iterator][j];
2757 }
2758
2759 entry[datafieldMapping[j]] = complexHeader[i][j];
2760 }
2761
2762 if (format === 'xlsx') {
2763 data.splice(i, 0, entry);
2764 }
2765 }
2766
2767 that.complexHeader = complexHeader;
2768
2769 if (format !== 'xlsx') {
2770 data.unshift(flatHeader);
2771 }
2772 else {
2773 data.splice(headerDepth - 1, 0, flatHeader);
2774
2775 const toMerge = {};
2776
2777 for (let i = 0; i < headerDepth; i++) {
2778 for (let j = 0; j < numberOfDatafields; j++) {
2779 const label = complexHeader[i][j];
2780
2781 if (!toMerge[label]) {
2782 toMerge[label] = { from: [i, j] };
2783 toMerge[label].to = toMerge[label].from;
2784 }
2785 else {
2786 toMerge[label].to = [i, j];
2787 }
2788 }
2789 }
2790
2791 that.complexHeaderMergeInfo = toMerge;
2792 }
2793 }
2794 else {
2795 data.unshift(flatHeader);
2796 }
2797 }
2798
2799 /**
2800 * Processes hierarchical data.
2801 */
2802 processHierarchicalData(data, format) {
2803 const that = this,
2804 startIndex = format !== 'xlsx' ? +that.exportHeader : that.xlsxStartIndex,
2805 siblingGroups = {},
2806 processedData = [];
2807 let maxLevel = 0,
2808 actualHierarchy = false;
2809
2810 function process(parentKey, level, collapsed) {
2811 const group = siblingGroups[parentKey];
2812
2813 maxLevel = Math.max(maxLevel, level);
2814
2815 if (group === undefined) {
2816 return;
2817 }
2818
2819 for (let i = 0; i < group.length; i++) {
2820 const currentRecord = group[i],
2821 keyDataField = currentRecord._keyDataField;
2822
2823 currentRecord._collapsed = collapsed;
2824 currentRecord._level = level;
2825 processedData.push(currentRecord);
2826
2827 if (siblingGroups[keyDataField]) {
2828 actualHierarchy = true;
2829 currentRecord._expanded = currentRecord._expanded !== undefined ? currentRecord._expanded : true;
2830 process(keyDataField, level + 1, collapsed || !currentRecord._expanded);
2831 }
2832 }
2833 }
2834
2835 function processJSONXML(parentKey, level, parent) {
2836 const group = siblingGroups[parentKey];
2837
2838 maxLevel = Math.max(maxLevel, level);
2839
2840 if (group === undefined) {
2841 return;
2842 }
2843
2844 for (let i = 0; i < group.length; i++) {
2845 const currentRecord = group[i],
2846 keyDataField = currentRecord._keyDataField;
2847 let cleanedRecord;
2848
2849 if (format === 'json') {
2850 cleanedRecord = {};
2851
2852 for (let prop in currentRecord) {
2853 if (currentRecord.hasOwnProperty(prop) && prop.charAt(0) !== '_') {
2854 cleanedRecord[prop] = currentRecord[prop];
2855 }
2856 }
2857 }
2858 else {
2859 cleanedRecord = Object.assign({}, currentRecord);
2860 }
2861
2862 parent.push(cleanedRecord);
2863
2864 if (siblingGroups[keyDataField]) {
2865 actualHierarchy = true;
2866 cleanedRecord.rows = [];
2867 processJSONXML(keyDataField, level + 1, cleanedRecord.rows);
2868 }
2869 }
2870 }
2871
2872 if (data[startIndex]._keyDataField === undefined) {
2873 return that.processNestedData(data, format, startIndex);
2874 }
2875
2876 for (let i = startIndex; i < data.length; i++) {
2877 const currentRecord = Object.assign({}, data[i]),
2878 parentKey = currentRecord._parentDataField;
2879
2880 if (siblingGroups[parentKey] === undefined) {
2881 siblingGroups[parentKey] = [currentRecord];
2882 }
2883 else {
2884 siblingGroups[parentKey].push(currentRecord);
2885 }
2886 }
2887
2888 if (startIndex) {
2889 for (let i = 0; i < startIndex; i++) {
2890 processedData.push(Object.assign({}, data[i]));
2891
2892 if (['json', 'pdf', 'xml'].indexOf(format) === -1) {
2893 processedData[i]._level = 1;
2894 }
2895 }
2896 }
2897
2898 if (format !== 'json' && format !== 'xml') {
2899 process(null, 1, false);
2900 }
2901 else {
2902 processJSONXML(null, 1, processedData);
2903 }
2904
2905 if (!actualHierarchy) {
2906 that.actualHierarchy = false;
2907 }
2908
2909 that.maxLevel = maxLevel;
2910 return processedData;
2911 }
2912
2913 /**
2914 * Processes nested hierarchical data.
2915 */
2916 processNestedData(data, format, startIndex) {
2917 const that = this,
2918 processedData = [];
2919 let maxLevel = 0,
2920 actualHierarchy = false;
2921
2922 function process(start, children, level, collapsed) {
2923 maxLevel = Math.max(maxLevel, level);
2924
2925 for (let i = start; i < children.length; i++) {
2926 const currentRecord = Object.assign({}, children[i]);
2927
2928 currentRecord._collapsed = collapsed;
2929 currentRecord._level = level;
2930 processedData.push(currentRecord);
2931
2932 if (currentRecord.children && currentRecord.children.length > 0) {
2933 actualHierarchy = true;
2934 currentRecord._expanded = currentRecord._expanded !== undefined ? currentRecord._expanded : true;
2935 process(0, currentRecord.children, level + 1, collapsed || !currentRecord._expanded);
2936 }
2937
2938 delete currentRecord.children;
2939 }
2940 }
2941
2942 function processJSONXML(start, children, rows, level) {
2943 maxLevel = Math.max(maxLevel, level);
2944
2945 for (let i = start; i < children.length; i++) {
2946 const currentRecord = Object.assign({}, children[i]);
2947
2948 if (level === 1) {
2949 processedData[i] = currentRecord;
2950 }
2951 else {
2952 rows[i] = currentRecord;
2953 }
2954
2955 if (currentRecord.children && currentRecord.children.length > 0) {
2956 actualHierarchy = true;
2957 currentRecord.rows = [];
2958 processJSONXML(0, currentRecord.children, currentRecord.rows, level + 1);
2959 }
2960
2961 delete currentRecord.children;
2962 }
2963 }
2964
2965 if (startIndex) {
2966 for (let i = 0; i < startIndex; i++) {
2967 processedData.push(Object.assign({}, data[i]));
2968
2969 if (['json', 'pdf', 'xml'].indexOf(format) === -1) {
2970 processedData[i]._level = 1;
2971 }
2972 }
2973 }
2974
2975 if (format !== 'json' && format !== 'xml') {
2976 process(startIndex, data, 1, false);
2977 }
2978 else {
2979 processJSONXML(startIndex, data, undefined, 1);
2980 }
2981
2982 if (!actualHierarchy) {
2983 that.actualHierarchy = false;
2984 }
2985
2986 that.maxLevel = maxLevel;
2987 return processedData;
2988 }
2989
2990 /**
2991 * Processes row styles.
2992 */
2993 processRowStyle(style) {
2994 const that = this,
2995 rowsDefinition = style.rows;
2996
2997 that.rowHeight = [];
2998
2999 if (!rowsDefinition) {
3000 return;
3001 }
3002
3003 const startIndex = that.xlsxStartIndex;
3004
3005 function applyToRowCells(row, prop, value) {
3006 for (let j = 0; j < that.columnsArray.length; j++) {
3007 const currentCell = that.columnsArray[j] + (row + 1 + startIndex);
3008
3009 that.storeCellStyle(currentCell, prop, value);
3010 }
3011 }
3012
3013 if (rowsDefinition.height) {
3014 that.defaultRowHeight = ` ht="${parseFloat(rowsDefinition.height) / 2}"`;
3015 }
3016
3017 for (let i = startIndex; i < that.data.length; i++) {
3018 const row = i - startIndex;
3019
3020 for (let prop in rowsDefinition) {
3021 if (rowsDefinition.hasOwnProperty(prop) &&
3022 prop.indexOf('alt') === -1 &&
3023 isNaN(prop) &&
3024 prop !== 'height') {
3025 applyToRowCells(row, prop, rowsDefinition[prop]);
3026 }
3027 }
3028
3029 if (rowsDefinition.alternationCount &&
3030 (((rowsDefinition.alternationStart === undefined || row >= rowsDefinition.alternationStart) &&
3031 (rowsDefinition.alternationEnd === undefined || row <= rowsDefinition.alternationEnd)) ||
3032 rowsDefinition.alternationStart === rowsDefinition.alternationEnd)) {
3033 const start = rowsDefinition.alternationStart || 0,
3034 i = (row - start) % rowsDefinition.alternationCount;
3035
3036 if (rowsDefinition[`alternationIndex${i}Color`]) {
3037 applyToRowCells(row, 'color', rowsDefinition[`alternationIndex${i}Color`]);
3038 }
3039
3040 if (rowsDefinition[`alternationIndex${i}BorderColor`]) {
3041 applyToRowCells(row, 'borderColor', rowsDefinition[`alternationIndex${i}BorderColor`]);
3042 }
3043
3044 if (rowsDefinition[`alternationIndex${i}BackgroundColor`]) {
3045 applyToRowCells(row, 'backgroundColor', rowsDefinition[`alternationIndex${i}BackgroundColor`]);
3046 }
3047 }
3048
3049 if (rowsDefinition[row]) {
3050 for (let prop in rowsDefinition[row]) {
3051 if (rowsDefinition[row].hasOwnProperty(prop)) {
3052 if (prop === 'height') {
3053 that.rowHeight[i] = ` ht="${parseFloat(rowsDefinition[row].height) / 2}"`;
3054 continue;
3055 }
3056
3057 applyToRowCells(row, prop, rowsDefinition[row][prop]);
3058 }
3059 }
3060 }
3061 }
3062 }
3063
3064 /**
3065 * Stores cell style in "styleMap" object.
3066 */
3067 storeCellStyle(cell, prop, value) {
3068 const that = this,
3069 cellMap = that.styleMap[cell];
3070
3071 switch (prop) {
3072 case 'backgroundColor':
3073 cellMap.fills.fgColor = value;
3074 break;
3075 case 'color':
3076 cellMap.fonts.color = value;
3077 break;
3078 case 'fontFamily':
3079 cellMap.fonts.name = value.replace(/"/g, '\'');
3080 break;
3081 case 'fontSize':
3082 cellMap.fonts.sz = parseFloat(value);
3083 break;
3084 case 'fontStyle':
3085 if (value === 'italic') {
3086 cellMap.fonts.i = true;
3087 }
3088 else {
3089 delete cellMap.fonts.i;
3090 }
3091
3092 break;
3093 case 'fontWeight':
3094 if (value === 'bold') {
3095 cellMap.fonts.b = true;
3096 }
3097 else {
3098 delete cellMap.fonts.b;
3099 }
3100
3101 break;
3102 case 'numFmt': {
3103 cellMap.numFmt = value;
3104 break;
3105 }
3106 case 'textAlign':
3107 cellMap.alignment.horizontal = value;
3108 break;
3109 case 'textDecoration':
3110 if (value === 'underline') {
3111 cellMap.fonts.u = true;
3112 }
3113 else {
3114 delete cellMap.fonts.u;
3115 }
3116
3117 break;
3118 case 'verticalAlign':
3119 if (value === 'middle') {
3120 value = 'center';
3121 }
3122
3123 cellMap.alignment.vertical = value;
3124 break;
3125 }
3126 }
3127
3128 /**
3129 * Returns an Alpha Red Green Blue color value.
3130 */
3131 toARGB(color) {
3132 color = color.replace(/\s/g, '');
3133
3134 const rgbResult = /rgb\((\d+),(\d+),(\d+)\)/gi.exec(color);
3135
3136 if (rgbResult !== null) {
3137 const r = parseFloat(rgbResult[1]).toString(16).toUpperCase(),
3138 g = parseFloat(rgbResult[2]).toString(16).toUpperCase(),
3139 b = parseFloat(rgbResult[3]).toString(16).toUpperCase();
3140
3141 return 'FF' + ('0').repeat(2 - r.length) + r +
3142 ('0').repeat(2 - g.length) + g +
3143 ('0').repeat(2 - b.length) + b;
3144 }
3145
3146 const rgbaResult = /rgba\((\d+),(\d+),(\d+)\,(\d*.\d+|\d+)\)/gi.exec(color);
3147
3148 if (rgbaResult !== null) {
3149 const a = Math.round(parseFloat(rgbaResult[4]) * 255).toString(16).toUpperCase(),
3150 r = parseFloat(rgbaResult[1]).toString(16).toUpperCase(),
3151 g = parseFloat(rgbaResult[2]).toString(16).toUpperCase(),
3152 b = parseFloat(rgbaResult[3]).toString(16).toUpperCase();
3153
3154 return ('0').repeat(2 - a.length) + a +
3155 ('0').repeat(2 - r.length) + r +
3156 ('0').repeat(2 - g.length) + g +
3157 ('0').repeat(2 - b.length) + b;
3158 }
3159
3160 const shortHexResult = /^#(.)(.)(.)$/gi.exec(color);
3161
3162 if (shortHexResult !== null) {
3163 const r = shortHexResult[1].toUpperCase(),
3164 g = shortHexResult[2].toUpperCase(),
3165 b = shortHexResult[3].toUpperCase();
3166
3167 return 'FF' + r + r + g + g + b + b;
3168 }
3169
3170 return 'FF' + color.toUpperCase().slice(1);
3171 }
3172
3173 /**
3174 * Adds toggleable functionality.
3175 */
3176 toggleableFunctionality() {
3177 const that = this;
3178
3179 if (!that.actualHierarchy) {
3180 return '';
3181 }
3182
3183 return `\n <style type="text/css">
3184 .toggle-element {
3185 width: 5px;
3186 height: 1px;
3187 padding-right: 5px;
3188 float: left;
3189 text-align: right;
3190 cursor: pointer;
3191 user-select: none;
3192 }
3193
3194 .collapsed {
3195 display: none;
3196 }
3197 </style>
3198 <script type="text/javascript">
3199 window.onload = function () {
3200 var expandChar = '${that.expandChar}',
3201 collapseChar = '${that.collapseChar}',
3202 toggleElements = document.getElementsByClassName('toggle-element');
3203
3204 function getParent(child) {
3205 var prevSibling = child.previousElementSibling;
3206
3207 while (prevSibling) {
3208 if (child.getAttribute('level') > prevSibling.getAttribute('level')) {
3209 return prevSibling;
3210 }
3211
3212 prevSibling = prevSibling.previousElementSibling;
3213 }
3214
3215 }
3216
3217 function getFirstCollapsedAncestor(child) {
3218 var parent = getParent(child);
3219
3220 while (parent) {
3221 if (parent.firstElementChild.firstElementChild.innerHTML === expandChar) {
3222 return parent;
3223 }
3224
3225 parent = getParent(parent);
3226 }
3227 }
3228
3229 for (var i = 0; i < toggleElements.length; i++) {
3230 toggleElements[i].addEventListener('click', function (event) {
3231 var expanded = this.innerHTML === collapseChar,
3232 row = this.parentElement.parentElement,
3233 sibling = row.nextElementSibling;
3234
3235 if (expanded) {
3236 this.innerHTML = expandChar;
3237 }
3238 else {
3239 this.innerHTML = collapseChar;
3240 }
3241
3242 while (sibling && row.getAttribute('level') < sibling.getAttribute('level')) {
3243 if (expanded) {
3244 sibling.style.display = 'none';
3245 }
3246 else {
3247 var firstCollapsedAncestor = getFirstCollapsedAncestor(sibling);
3248
3249 if (!firstCollapsedAncestor || firstCollapsedAncestor === row) {
3250 sibling.classList.remove('collapsed');
3251 sibling.style.display = null;
3252 }
3253
3254 }
3255
3256 sibling = sibling.nextElementSibling;
3257 }
3258 });
3259 }
3260 }
3261 </script>`;
3262 }
3263
3264 /**
3265 * Generates styles.xml.
3266 */
3267 generateStyles(style) {
3268 const that = this;
3269
3270 that.cellStyleMapping = {};
3271
3272 if (Object.keys(style).length === 0 && !that.complexHeader) {
3273 // default style
3274 return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3275 <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac x16r2 xr" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:x16r2="http://schemas.microsoft.com/office/spreadsheetml/2015/02/main" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision"><fonts count="1" x14ac:knownFonts="1"><font><sz val="11"/><color theme="1"/><name val="Calibri"/><family val="2"/><charset val="204"/><scheme val="minor"/></font></fonts><fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>${that.conditionalFormattingXLSX.styles || '<dxfs count="0"/>'}<tableStyles count="0" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleLight16"/><extLst><ext uri="{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:slicerStyles defaultSlicerStyle="SlicerStyleLight1"/></ext><ext uri="{9260A510-F301-46a8-8635-F512D64BE5F5}" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"><x15:timelineStyles defaultTimelineStyle="TimeSlicerStyleLight1"/></ext></extLst></styleSheet>`;
3276 }
3277
3278 that.styleMap = {};
3279
3280 for (let i = 0; i < that.data.length; i++) {
3281 for (let j = 0; j < that.columnsArray.length; j++) {
3282 that.styleMap[that.columnsArray[j] + (i + 1)] = {
3283 numFmts: {}, fonts: {}, fills: {}, borders: {}, alignment: {}
3284 }
3285 }
3286 }
3287
3288 if (style && style.columns) {
3289 for (let i = 0; i < that.columnsArray.length; i++) {
3290 const datafield = that.datafields[i];
3291
3292 if (!style.columns[datafield] || !style.columns[datafield].format) {
3293 continue;
3294 }
3295
3296 const XLSXFormat = that.getXLSXFormat(style.columns[datafield].format, that.data[that.data.length - 1][datafield]);
3297
3298 if (XLSXFormat) {
3299 style.columns[datafield].numFmt = XLSXFormat;
3300 }
3301 }
3302 }
3303
3304 that.processRowStyle(style);
3305 that.processColumnStyle(style);
3306
3307 const cellAliases = {};
3308
3309 for (let i = 0; i < that.complexHeaderMergedCells.length; i++) {
3310 const currentCell = that.complexHeaderMergedCells[i];
3311
3312 if (parseFloat(currentCell.to[1]) === that.complexHeader.length) {
3313 cellAliases[currentCell.to] = currentCell.from;
3314 continue;
3315 }
3316
3317 that.styleMap[currentCell.from].alignment.horizontal = 'center';
3318 that.styleMap[currentCell.from].alignment.vertical = 'center';
3319 }
3320
3321 const fonts = {
3322 xml: '<font><sz val="11" /><color theme="1" /><name val="Calibri" /><family val="2" /><charset val="204" /><scheme val="minor" /></font>',
3323 collection: ['default']
3324 },
3325 fills = {
3326 xml: '<fill><patternFill patternType="none" /></fill><fill><patternFill patternType="gray125" /></fill>',
3327 collection: ['default', 'gray125']
3328 },
3329 numFmts = {
3330 xml: '',
3331 collection: []
3332 },
3333 cellXfs = {
3334 xml: '<xf fontId="0" fillId="0" borderId="1"/>',
3335 collection: ['default']
3336 };
3337
3338 for (let i = 0; i < that.data.length; i++) { // iterate rows
3339 for (let j = 0; j < that.columnsArray.length; j++) { // iterate columns
3340 const currentCell = that.columnsArray[j] + (i + 1),
3341 currentCellStyle = that.styleMap[currentCell];
3342 let currentFont = '', currentFill = '', currentAlignment = '',
3343 currentFontCode = [], currentFillCode = [], currentAlignmentCode = [], xf = [];
3344
3345 for (let prop in currentCellStyle.fonts) {
3346 if (currentCellStyle.fonts.hasOwnProperty(prop)) {
3347 const value = currentCellStyle.fonts[prop];
3348
3349 switch (prop) {
3350 case 'color':
3351 currentFontCode[0] = value;
3352 currentFont += `<color rgb="${that.toARGB(value)}" />`;
3353 break;
3354 case 'name':
3355 currentFontCode[1] = value;
3356 currentFont += `<name val="${value}" />`;
3357 break;
3358 case 'sz':
3359 currentFontCode[2] = value;
3360 currentFont += `<sz val="${value}" />`;
3361 break;
3362 case 'i':
3363 currentFontCode[3] = value;
3364 currentFont += '<i />';
3365 break;
3366 case 'b':
3367 currentFontCode[4] = value;
3368 currentFont += '<b />';
3369 break;
3370 case 'u':
3371 currentFontCode[5] = value;
3372 currentFont += '<u />';
3373 break;
3374 }
3375 }
3376 }
3377
3378 for (let prop in currentCellStyle.fills) {
3379 if (currentCellStyle.fills.hasOwnProperty(prop)) {
3380 const value = currentCellStyle.fills[prop];
3381
3382 switch (prop) {
3383 case 'fgColor':
3384 currentFillCode[0] = value;
3385 currentFill += `<fgColor rgb="${that.toARGB(value)}" />`;
3386 break;
3387 }
3388 }
3389 }
3390
3391 for (let prop in currentCellStyle.alignment) {
3392 if (currentCellStyle.alignment.hasOwnProperty(prop)) {
3393 const value = currentCellStyle.alignment[prop];
3394
3395 switch (prop) {
3396 case 'horizontal':
3397 currentAlignmentCode[0] = value;
3398 currentAlignment += `horizontal="${value}" `;
3399 break;
3400 case 'vertical':
3401 currentAlignmentCode[1] = value;
3402 currentAlignment += `vertical="${value}" `;
3403 break;
3404 }
3405 }
3406 }
3407
3408 currentFontCode = currentFontCode.toString();
3409 currentFillCode = currentFillCode.toString();
3410
3411 if (currentFont !== '') {
3412 let fontIndex = fonts.collection.indexOf(currentFontCode);
3413
3414 if (fontIndex === -1) {
3415 fontIndex = fonts.collection.length;
3416
3417 fonts.xml += '<font>' + currentFont + '</font>';
3418 fonts.collection.push(currentFontCode);
3419 }
3420
3421 xf[0] = fontIndex;
3422 }
3423
3424 if (currentFill !== '') {
3425 let fillIndex = fills.collection.indexOf(currentFillCode);
3426
3427 if (fillIndex === -1) {
3428 fillIndex = fills.collection.length;
3429
3430 fills.xml += '<fill><patternFill patternType="solid">' + currentFill + '</patternFill></fill>';
3431 fills.collection.push(currentFillCode);
3432 }
3433
3434 xf[1] = fillIndex;
3435 }
3436
3437 if (currentAlignmentCode.length > 0) {
3438 xf[2] = currentAlignment;
3439 }
3440
3441 if (currentCellStyle.numFmt !== undefined) {
3442 xf[3] = that.getNumFmtIndex(currentCellStyle.numFmt, numFmts);
3443 }
3444
3445 const xfCode = xf.toString();
3446
3447 if (xfCode !== '') {
3448 let xfIndex = cellXfs.collection.indexOf(xfCode);
3449
3450 if (xfIndex === -1) {
3451 let newXfXML = '<xf ';
3452
3453 xfIndex = cellXfs.collection.length;
3454
3455 if (xf[0] !== undefined) {
3456 newXfXML += `fontId="${xf[0]}" `;
3457 }
3458
3459 if (xf[1] !== undefined) {
3460 newXfXML += `fillId="${xf[1]}" `;
3461 }
3462
3463 if (xf[3] !== undefined) {
3464 newXfXML += `numFmtId="${xf[3]}" `;
3465 }
3466
3467 if (xf[2] !== undefined) {
3468 newXfXML += `applyAlignment="1" borderId="1"><alignment ${currentAlignment}/></xf>`;
3469 }
3470 else {
3471 newXfXML += ' borderId="1"/>';
3472 }
3473
3474 cellXfs.xml += newXfXML;
3475 cellXfs.collection.push(xfCode);
3476 }
3477
3478 that.cellStyleMapping[cellAliases[currentCell] || currentCell] = xfIndex;
3479 }
3480 }
3481 }
3482
3483 if (numFmts.collection.length) {
3484 numFmts.xml = `<numFmts count="${numFmts.collection.length}">${numFmts.xml}</numFmts>`;
3485 }
3486
3487 return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
3488 <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac x16r2 xr" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:x16r2="http://schemas.microsoft.com/office/spreadsheetml/2015/02/main" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision">${numFmts.xml}<fonts count="${fonts.collection.length}" x14ac:knownFonts="1">${fonts.xml}</fonts><fills count="${fills.collection.length}">${fills.xml}</fills><borders count="2"><border><left/><right/><top/><bottom/></border><border><left style="hair"/><right style="hair"/><top style="hair"/><bottom style="hair"/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="${cellXfs.collection.length}">${cellXfs.xml}</cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>${that.conditionalFormattingXLSX.styles}<dxfs count="0"/><tableStyles count="0" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleLight16"/><extLst><ext uri="{EB79DEF2-80B8-43e5-95BD-54CBDDF9020C}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:slicerStyles defaultSlicerStyle="SlicerStyleLight1"/></ext><ext uri="{9260A510-F301-46a8-8635-F512D64BE5F5}" xmlns:x15="http://schemas.microsoft.com/office/spreadsheetml/2010/11/main"><x15:timelineStyles defaultTimelineStyle="TimeSlicerStyleLight1"/></ext></extLst></styleSheet>`;
3489 }
3490 }
3491
3492 if ($.jqx && $.jqx.dataAdapter) {
3493 $.jqx.dataAdapter.DataExporter = DataExporter;
3494 }
3495 })(jqxBaseFramework);

mercurial