export const b64toBlob = function (b64Data:string, contentType:string, sliceSize?:number): Blob { // function taken from http://stackoverflow.com/a/16245768/2591950 // author Jeremy Banks http://stackoverflow.com/users/1114/jeremy-banks contentType = contentType || ''; sliceSize = sliceSize || 512; const byteCharacters = atob(b64Data); const byteArrays = []; for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { const slice = byteCharacters.slice(offset, offset + sliceSize); const byteNumbers = new Array(slice.length); for (let i = 0; i < slice.length; i = i + 1) { byteNumbers[i] = slice.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); byteArrays.push(byteArray); } return new Blob(byteArrays, { type: contentType }); }; export const templates = {excel: ' {table}
'}; /** * Convert a string to Base64. */ export const base64 = function(s:string) : string { return btoa(unescape(encodeURIComponent(s))); }; export const format = function(s:string, context:any) : string { return s.replace(new RegExp("{(\\w+)}", "g"), function(m, p) { return context[p] || "{" + p + "}"; }); }; /** * Get element by ID. * @param {*} element */ export const getTable = function(element :(HTMLTableElement|string)) : HTMLTableElement { if (typeof element === 'string') { return document.getElementById(element) as HTMLTableElement; } return element; }; /** * Get element by ID. * @param {*} element */ export const getAnchor = function(element :(HTMLAnchorElement|string)) : HTMLAnchorElement { if (typeof element === 'string') { return document.getElementById(element) as HTMLAnchorElement; } return element; }; /** * Encode a value for CSV. * @param {*} value */ export const fixCSVField = function(value:string, csvDelimiter:string) : string { let fixedValue = value; const addQuotes = (value.indexOf(csvDelimiter) !== -1) || (value.indexOf('\r') !== -1) || (value.indexOf('\n') !== -1); const replaceDoubleQuotes = (value.indexOf('"') !== -1); if (replaceDoubleQuotes) { fixedValue = fixedValue.replace(/"/g, '""'); } if (addQuotes || replaceDoubleQuotes) { fixedValue = '"' + fixedValue + '"'; } return fixedValue; }; /** * Parse an HTML table into a data array and a list of merge ranges, * honouring colspan and rowspan attributes. */ export const parseTable = function(table: HTMLTableElement): { data: any[][], merges: {s: {r: number, c: number}, e: {r: number, c: number}}[] } { const rows = Array.prototype.slice.call(table.querySelectorAll('tr')) as HTMLTableRowElement[]; const numRows = rows.length; // grid[r][c] holds the cell value; undefined means "not yet placed" const grid: any[][] = []; // occupied[r][c] = true when the slot has been claimed by a span const occupied: boolean[][] = []; const merges: {s: {r: number, c: number}, e: {r: number, c: number}}[] = []; for (let r = 0; r < numRows; r++) { if (!grid[r]) grid[r] = []; if (!occupied[r]) occupied[r] = []; const cells = Array.prototype.slice.call(rows[r].querySelectorAll('th,td')) as HTMLTableCellElement[]; let c = 0; cells.forEach(function(cell) { // Advance past columns already occupied by a previous rowspan while (occupied[r] && occupied[r][c]) { c++; } const colspan = cell.colSpan || 1; const rowspan = cell.rowSpan || 1; const value = cell.innerHTML; grid[r][c] = value; // Mark all spanned cells as occupied for (let dr = 0; dr < rowspan; dr++) { for (let dc = 0; dc < colspan; dc++) { if (!occupied[r + dr]) occupied[r + dr] = []; occupied[r + dr][c + dc] = true; } } // Record the merge range when spanning more than one cell if (colspan > 1 || rowspan > 1) { merges.push({ s: { r: r, c: c }, e: { r: r + rowspan - 1, c: c + colspan - 1 } }); } c += colspan; }); } // Normalise grid to a rectangular array let numCols = 0; grid.forEach(function(row) { if (row.length > numCols) numCols = row.length; }); const data = grid.map(function(row) { return Array.from({ length: numCols }, function(_, i) { return row[i] !== undefined ? row[i] : ''; }); }); return { data, merges }; }; export const tableToArray = function(table:HTMLTableElement) : any[][] { return parseTable(table).data; }; export const tableToMerges = function(table:HTMLTableElement) : {s: {r: number, c: number}, e: {r: number, c: number}}[] { return parseTable(table).merges; }; export const tableToCSV = function(table:HTMLTableElement, csvDelimiter:string = ',', csvNewLine:string = '\n') : string { let data = ""; for (let i = 0; i < table.rows.length; i=i+1) { const row = table.rows[i]; for (let j = 0; j < row.cells.length; j=j+1) { const col = row.cells[j]; data = data + (j ? csvDelimiter : '') + fixCSVField(col.textContent.trim(), csvDelimiter); } data = data + csvNewLine; } return data; }; export const createDownloadLink = function(anchor:HTMLAnchorElement, base64data:string, exporttype:string, filename:string) : boolean { if (window.navigator.msSaveBlob) { const blob = b64toBlob(base64data, exporttype); window.navigator.msSaveBlob(blob, filename); return false; } else if (window.URL.createObjectURL) { const blob = b64toBlob(base64data, exporttype); anchor.href = window.URL.createObjectURL(blob); } else { anchor.download = filename; anchor.href = "data:" + exporttype + ";base64," + base64data; } // Return true to allow the link to work return true; }; // String to ArrayBuffer export const string2ArrayBuffer = function (s:string): ArrayBuffer { let buf = new ArrayBuffer(s.length); let view = new Uint8Array(buf); for (let i=0; i !== s.length; ++i) { view[i] = s.charCodeAt(i) & 0xFF; } return buf; }; export const removeColumns = function(dataArray:any[][], columnIndexes:number[]) { const uniqueIndexes = [...new Set(columnIndexes)].sort().reverse(); uniqueIndexes.forEach(function(columnIndex) { dataArray.forEach(function(row) { row.splice(columnIndex, 1); }); }); }; export const hasContent = function(value:any) : boolean { return value !== undefined && value !== null && value !== ""; }