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: '
'};
/**
* 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 !== "";
}