496 lines
21 KiB
JavaScript
496 lines
21 KiB
JavaScript
/** @copyright 2018 Planning Village Corporation Co., Ltd. */
|
|
(function () {
|
|
'use strict';
|
|
|
|
// [IE11] Polyfill of Object.values
|
|
if (!Object.values) {
|
|
const ownKeys = (
|
|
(typeof Reflect === 'object' && typeof Reflect.ownKeys === 'function') ? Reflect.ownKeys :
|
|
// @ts-ignore
|
|
(typeof Object.getOwnPropertySymbols === 'function') ? (function (O) {
|
|
// @ts-ignore
|
|
return Object.getOwnPropertyNames(O).concat(Object.getOwnPropertySymbols(O));
|
|
}) :
|
|
Object.getOwnPropertyNames);
|
|
// @ts-ignore
|
|
const reduce = Function.bind.call(Function.call, Array.prototype.reduce);
|
|
// @ts-ignore
|
|
const isEnumerable = Function.bind.call(Function.call, Object.prototype.propertyIsEnumerable);
|
|
// @ts-ignore
|
|
const concat = Function.bind.call(Function.call, Array.prototype.concat);
|
|
//@ts-ignore
|
|
Object.values = function values(O) {
|
|
//@ts-ignore
|
|
return reduce(ownKeys(O), function (v, k) {
|
|
return concat(v, typeof k === 'string' && isEnumerable(O, k) ? [O[k]] : [])
|
|
}, [])
|
|
};
|
|
}
|
|
|
|
// [IE11] Polyfill of Number.isNaN
|
|
if (!Number.isNaN) {
|
|
Number.isNaN = function (value) {
|
|
return value !== null && (value != value || +value != value);
|
|
};
|
|
}
|
|
|
|
// [IE11] Polyfill of String.prototype.startsWith
|
|
if (!String.prototype.startsWith) {
|
|
Object.defineProperty(String.prototype, 'startsWith', {
|
|
//@ts-ignore
|
|
value: function (search, rawPos) {
|
|
var pos = rawPos > 0 ? rawPos | 0 : 0;
|
|
return this.substring(pos, pos + search.length) === search;
|
|
}
|
|
});
|
|
}
|
|
|
|
// [IE11] Polyfill of String.prototype.includes
|
|
if (!String.prototype.includes) {
|
|
String.prototype.includes = function (search, start) {
|
|
'use strict';
|
|
//@ts-ignore
|
|
if (search instanceof RegExp) {
|
|
throw TypeError('first argument must not be a RegExp');
|
|
}
|
|
if (start === undefined) {
|
|
start = 0;
|
|
}
|
|
return this.indexOf(search, start) !== -1;
|
|
};
|
|
}
|
|
|
|
// [IE11] Polyfill of Array.prototype.findIndex
|
|
if (!Array.prototype.findIndex) {
|
|
// @ts-ignore
|
|
Array.prototype.findIndex = function (predicate) {
|
|
if (this === null) {
|
|
throw new TypeError('Array.prototype.findIndex called on null or undefined');
|
|
}
|
|
if (typeof predicate !== 'function') {
|
|
throw new TypeError('predicate must be a function');
|
|
}
|
|
var list = Object(this);
|
|
var length = list.length >>> 0;
|
|
var thisArg = arguments[1];
|
|
var value;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
value = list[i];
|
|
if (predicate.call(thisArg, value, i, list)) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
};
|
|
}
|
|
|
|
//@ts-ignore
|
|
window.pvc = window.pvc || {};
|
|
//@ts-ignore
|
|
window.pvc.lib = window.pvc.lib || {};
|
|
//@ts-ignore
|
|
window.pvc.lib.exceljsUtil = (function () {
|
|
const exceljs = {
|
|
/**
|
|
* ワークブックの blob を読み取ります。
|
|
* @param {Blob} blob Blob
|
|
* @return {Promise<ExcelJS.Workbook>} ワークブック
|
|
*/
|
|
loadWorkbookBlob: function (blob) {
|
|
return new Promise(function (resolve, reject) {
|
|
var reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
// @ts-ignore
|
|
resolve(e.target.result);
|
|
};
|
|
reader.onerror = function (e) {
|
|
reject(e);
|
|
}
|
|
reader.onabort = function (e) {
|
|
reject(e);
|
|
}
|
|
reader.readAsArrayBuffer(blob);
|
|
}).then(function (arrayBuffer) {
|
|
// 非公式のメンバー load を使用
|
|
// @ts-ignore
|
|
return new ExcelJS.Workbook().xlsx.load(arrayBuffer);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* ワークブックを Blob に変換します。
|
|
* @param {ExcelJS.Workbook} workbook ワークブック
|
|
* @return {Promise<Blob>} Blob
|
|
*/
|
|
saveWorkbookToBlob: function (workbook) {
|
|
// writeBuffer の戻り値 ExcelJS.Buffer はブラウザ環境では Uint8Array となる
|
|
return workbook.xlsx.writeBuffer().then(function (ua) {
|
|
return new Blob([ua], {
|
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 行をコピーします。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
* @param {number} topRowNumber コピー元の開始行番号(1から開始)
|
|
* @param {number} [bottomRowNumber] コピー元の終了行番号(1から開始)。省略時は開始行番号と同じ値となります。
|
|
* @returns {pvc.lib.exceljsUtil.CopiedRowsInfo} 行のコピー情報
|
|
*/
|
|
copyRows: function (worksheet, topRowNumber, bottomRowNumber) {
|
|
const _bottomRowNumber = (bottomRowNumber == null ? topRowNumber : bottomRowNumber);
|
|
|
|
if (topRowNumber < 0 || _bottomRowNumber < topRowNumber) {
|
|
throw new Error('コピー元の領域とが不正です。');
|
|
}
|
|
|
|
// コピー元の領域の情報を収集
|
|
const rows = [];
|
|
for (let r = topRowNumber; r <= _bottomRowNumber; r++) {
|
|
const row = worksheet.getRow(r);
|
|
const cells = [];
|
|
for (let c = 1; c <= worksheet.columnCount; c++) {
|
|
const cell = row.getCell(c);
|
|
cells.push({
|
|
value: cell.value,
|
|
style: cell.style,
|
|
});
|
|
}
|
|
rows.push({
|
|
cells: cells,
|
|
height: row.height,
|
|
// 非公式のメンバー style を使用
|
|
// @ts-ignore
|
|
style: row.style,
|
|
});
|
|
}
|
|
|
|
// コピー元の領域の内部に収まるマージを収集
|
|
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
|
|
return (topRowNumber <= merge.top && merge.bottom <= _bottomRowNumber);
|
|
}).map(function (merge) {
|
|
return {
|
|
top: merge.top - topRowNumber,
|
|
right: merge.right,
|
|
bottom: merge.bottom - topRowNumber,
|
|
left: merge.left,
|
|
};
|
|
}));
|
|
|
|
return deepCopyObject({
|
|
rows: rows,
|
|
merges: merges
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 指定された開始行を起点に、コピーした行を貼り付けます。
|
|
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
|
|
* コピー先の領域を一部でも含むマージは全て解除されます。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
|
|
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
|
|
*/
|
|
pasteRows: function (worksheet, topRowNumber, copiedRowsInfo) {
|
|
const bottomRowNumber = topRowNumber + copiedRowsInfo.rows.length - 1;
|
|
|
|
if (topRowNumber < 0) {
|
|
throw new Error('コピー先の領域とが不正です。');
|
|
}
|
|
|
|
// コピー先の行のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
|
|
getWorksheetMerges(worksheet).filter(function (merge) {
|
|
return (topRowNumber <= merge.bottom && merge.top <= bottomRowNumber);
|
|
}).forEach(function (merge) {
|
|
worksheet.unMergeCells(merge.range);
|
|
});
|
|
|
|
// マージをペースト
|
|
copiedRowsInfo.merges.forEach(function (mergeInfo) {
|
|
worksheet.mergeCells(
|
|
topRowNumber + mergeInfo.top,
|
|
mergeInfo.left,
|
|
topRowNumber + mergeInfo.bottom,
|
|
mergeInfo.right);
|
|
});
|
|
|
|
// セルをペースト
|
|
copiedRowsInfo.rows.forEach(function (rowInfo, i) {
|
|
const row = worksheet.getRow(topRowNumber + i);
|
|
row.height = rowInfo.height;
|
|
// 非公式のメンバー style を使用
|
|
// @ts-ignore
|
|
row.style = rowInfo.style;
|
|
for (let c = 1; c <= worksheet.columnCount; c++) {
|
|
const cell = row.getCell(c);
|
|
const cellInfo = rowInfo.cells[c - 1];
|
|
if (cellInfo) {
|
|
cell.value = cellInfo.value;
|
|
cell.style = cellInfo.style;
|
|
} else {
|
|
exceljs.clearCell(cell);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 指定された開始行を起点に、コピーした行を挿入します。
|
|
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
* @param {number} topRowNumber コピー先の開始行番号(1から開始)
|
|
* @param {pvc.lib.exceljsUtil.CopiedRowsInfo} copiedRowsInfo 行のコピー情報
|
|
*/
|
|
insertRows: function (worksheet, topRowNumber, copiedRowsInfo) {
|
|
const bottomRows = (
|
|
topRowNumber <= worksheet.rowCount ?
|
|
exceljs.copyRows(worksheet, topRowNumber, worksheet.rowCount) :
|
|
null);
|
|
exceljs.pasteRows(worksheet, topRowNumber, copiedRowsInfo);
|
|
if (bottomRows) {
|
|
exceljs.pasteRows(worksheet, topRowNumber + copiedRowsInfo.rows.length, bottomRows);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 列をコピーします。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
* @param {number | string} leftColumn コピー元の開始列の列番号(1から開始)または列ラベル。終了列の引数を省略して、代わりに "A:B" の形式でコピー元の列の範囲を指定することも可能。
|
|
* @param {number | string} [rightColumn] コピー元の終了列の列番号(1から開始)または列ラベル。省略時は開始列と同じとみなされます(開始列を "A:B" の形式で指定された場合を除く)。
|
|
* @returns {pvc.lib.exceljsUtil.CopiedColumnsInfo} 列のコピー情報
|
|
*/
|
|
copyColumns: function (worksheet, leftColumn, rightColumn) {
|
|
if (rightColumn == null) {
|
|
rightColumn = leftColumn;
|
|
if (typeof leftColumn === 'string') {
|
|
const sp = leftColumn.split(':');
|
|
if (sp.length === 2) {
|
|
leftColumn = sp[0];
|
|
rightColumn = sp[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
|
|
const rightColumnNumber = ((typeof rightColumn === 'number') ? rightColumn : exceljs.columnLetterToColumnNumber(rightColumn));
|
|
|
|
if (leftColumnNumber < 0 || rightColumnNumber < leftColumnNumber) {
|
|
throw new Error('コピー元の領域とが不正です。');
|
|
}
|
|
|
|
// コピー元の領域の情報を収集
|
|
const columns = [];
|
|
for (let c = leftColumnNumber; c <= rightColumnNumber; c++) {
|
|
const column = worksheet.getColumn(c);
|
|
const cells = [];
|
|
for (let r = 1; r <= worksheet.rowCount; r++) {
|
|
const cell = worksheet.getRow(r).getCell(c);
|
|
cells.push({
|
|
value: cell.value,
|
|
style: cell.style,
|
|
});
|
|
}
|
|
columns.push({
|
|
cells: cells,
|
|
width: column.width,
|
|
style: column.style,
|
|
});
|
|
}
|
|
|
|
// コピー元の領域の内部に収まるマージを収集
|
|
const merges = deepCopyObject(getWorksheetMerges(worksheet).filter(function (merge) {
|
|
return (leftColumnNumber <= merge.left && merge.right <= rightColumnNumber);
|
|
}).map(function (merge) {
|
|
return {
|
|
top: merge.top,
|
|
right: merge.right - leftColumnNumber,
|
|
bottom: merge.bottom,
|
|
left: merge.left - leftColumnNumber,
|
|
};
|
|
}));
|
|
|
|
return {
|
|
columns: columns,
|
|
merges: merges
|
|
};
|
|
},
|
|
|
|
/**
|
|
* 指定された開始列を起点に、コピーした列を貼り付けます。
|
|
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
|
|
* コピー先の領域を一部でも含むマージは全て解除されます。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
|
|
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedColumnsInfo 列のコピー情報
|
|
*/
|
|
pasteColumns: function (worksheet, leftColumn, copiedColumnsInfo) {
|
|
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
|
|
const rightColumnNumber = leftColumnNumber + copiedColumnsInfo.columns.length - 1;
|
|
|
|
if (leftColumnNumber < 0) {
|
|
throw new Error('コピー先の領域とが不正です。');
|
|
}
|
|
|
|
// コピー先の列のマージを解除(コピー先の領域を一部でも含むマージは全て解除)
|
|
getWorksheetMerges(worksheet).filter(function (merge) {
|
|
return (leftColumnNumber <= merge.right && merge.left <= rightColumnNumber);
|
|
}).forEach(function (merge) {
|
|
worksheet.unMergeCells(merge.range);
|
|
});
|
|
|
|
// マージをペースト
|
|
copiedColumnsInfo.merges.forEach(function (mergeInfo) {
|
|
worksheet.mergeCells(
|
|
mergeInfo.top,
|
|
leftColumnNumber + mergeInfo.left,
|
|
mergeInfo.bottom,
|
|
leftColumnNumber + mergeInfo.right);
|
|
});
|
|
|
|
// セルをペースト
|
|
copiedColumnsInfo.columns.forEach(function (columnInfo, i) {
|
|
const column = worksheet.getColumn(leftColumnNumber + i);
|
|
column.width = columnInfo.width;
|
|
column.style = columnInfo.style;
|
|
for (let r = 1; r <= worksheet.rowCount; r++) {
|
|
const cell = worksheet.getRow(r).getCell(leftColumnNumber + i);
|
|
const cellInfo = columnInfo.cells[r - 1];
|
|
if (cellInfo) {
|
|
cell.value = cellInfo.value;
|
|
cell.style = cellInfo.style;
|
|
} else {
|
|
exceljs.clearCell(cell);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* 指定された開始列を起点に、コピーした列を挿入します。
|
|
* コピー元の領域と領域外の両方にまたがるセルのマージは貼り付けされません。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
* @param {number | string} leftColumn コピー先の開始列の列番号(1から開始)または列ラベル
|
|
* @param {pvc.lib.exceljsUtil.CopiedColumnsInfo} copiedRowsInfo 列のコピー情報
|
|
*/
|
|
insertColumns: function (worksheet, leftColumn, copiedRowsInfo) {
|
|
const leftColumnNumber = ((typeof leftColumn === 'number') ? leftColumn : exceljs.columnLetterToColumnNumber(leftColumn));
|
|
const rightColumns = (
|
|
leftColumnNumber <= worksheet.columnCount ?
|
|
exceljs.copyColumns(worksheet, leftColumnNumber, worksheet.columnCount) :
|
|
null);
|
|
exceljs.pasteColumns(worksheet, leftColumnNumber, copiedRowsInfo);
|
|
if (rightColumns) {
|
|
exceljs.pasteColumns(worksheet, leftColumnNumber + copiedRowsInfo.columns.length, rightColumns);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* セルをクリアします。
|
|
* @param {ExcelJS.Cell} cell セル
|
|
*/
|
|
clearCell: function (cell) {
|
|
cell.value = null;
|
|
/// @ts-ignore
|
|
cell.style = cell._mergeStyle(cell._row.style, cell._column.style, {});
|
|
},
|
|
|
|
/**
|
|
* 列ラベルから列番号(1から開始)を取得します。
|
|
* @param {string} columnLetter 列ラベル
|
|
* @return {number} 列番号(1から開始)
|
|
*/
|
|
columnLetterToColumnNumber: function (columnLetter) {
|
|
const letter = columnLetter.toUpperCase();
|
|
const l = letter.length;
|
|
const charCodeA = 65;
|
|
let result = 0;
|
|
for (let i = 0, j = l - 1; i < l; i++, j--) {
|
|
result += (letter.charCodeAt(j) - charCodeA + 1) * (i === 0 ? 1 : Math.pow(26, i));
|
|
}
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* 日付のセルの値を作成します。
|
|
* @param {Date} date
|
|
* @returns {any}
|
|
*/
|
|
createDateCellValue: function (date) {
|
|
// UTC にしないと正しく Excel に反映されない
|
|
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()));
|
|
},
|
|
|
|
deepCopyObject: deepCopyObject,
|
|
};
|
|
|
|
/**
|
|
* ワークシートのマージを取得します。
|
|
* @param {ExcelJS.Worksheet} worksheet ワークシート
|
|
*/
|
|
function getWorksheetMerges(worksheet) {
|
|
// 非公式のメンバー _merges を使用
|
|
// @ts-ignore
|
|
const mergesDic = worksheet._merges;
|
|
return Object.keys(mergesDic).map(function (address) {
|
|
return mergesDic[address];
|
|
});
|
|
}
|
|
|
|
/**
|
|
* オブジェクのコピーします。ディープコピーとなります。
|
|
* @template T
|
|
* @param {T} obj
|
|
* @returns {T}
|
|
*/
|
|
function deepCopyObject(obj) {
|
|
return copyObject(obj);
|
|
|
|
//@ts-ignore
|
|
function copyObject(obj) {
|
|
return (
|
|
obj == null ? null :
|
|
Array.isArray(obj) ? copyArray(obj) :
|
|
isDate(obj) ? copyDate(obj) :
|
|
isDic(obj) ? copyDic(obj) :
|
|
obj);
|
|
}
|
|
|
|
//@ts-ignore
|
|
function copyArray(array) {
|
|
//@ts-ignore
|
|
return array.map(function (item) {
|
|
return copyObject(item);
|
|
});
|
|
}
|
|
|
|
//@ts-ignore
|
|
function isDate(obj) {
|
|
return (obj != null && Object.prototype.toString.call(obj) === '[object Date]');
|
|
}
|
|
|
|
//@ts-ignore
|
|
function copyDate(date) {
|
|
return (isNaN(date) ? null : new Date(date.getTime()));
|
|
}
|
|
|
|
//@ts-ignore
|
|
function isDic(obj) {
|
|
return (obj != null && typeof obj === 'object');
|
|
}
|
|
|
|
//@ts-ignore
|
|
function copyDic(dic) {
|
|
let result = {};
|
|
for (let key in dic) {
|
|
//@ts-ignore
|
|
result[key] = copyObject(dic[key]);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return exceljs;
|
|
}());
|
|
})(); |