/* cfb-editor (C) 2017-present SheetJS -- http://sheetjs.com */ /* eslint-env browser */ import { easyStore } from 'react-easy-state'; import * as CFB from 'cfb'; import * as JSZIP from './vendor/jszip'; import { SaveBArray, SaveBString } from './utils/SaveData'; import { fix_string, unfix_string, ab2a, s2a, a2s } from './utils/misc'; export default easyStore({ fname: "", file: null, dirty: false, loading: false, isDirty() { return this.file && this.dirty; }, setLoading(x) { return this.loading = x; }, getLoading() { return this.loading; }, /* Type tests */ isEmpty() { return !this.file; }, isCFB() { return this.file && this.file.FullPaths; }, isZIP() { return this.file && !this.isCFB(); }, getType() { return this.isCFB() ? "CFB" : this.isZIP() ? "ZIP" : "???"; }, /* File-level Accessors */ getFileList() { return this.keys().map((x, i) => [x, this.getFileEntryById(i), i]).filter(x => this.isCFB() ? x[1].type == 2 : !x[1].dir); }, getNextName(id) { return !id ? this.getNextName(1) : this.find(`SheetJS${id}`) ? this.getNextName(id+1) : `SheetJS${id}`; }, getRootName() { return !this.isCFB() ? "" : this.getFileNameById(0); }, getCLSID() { return !this.isCFB() ? "" : this.getFileEntryById(0).clsid; }, /* File-level Mutators */ setName(name) { this.fname = name; }, setBStr(bstr, cb) { this.loading = true; setTimeout(() => {try { switch(bstr.slice(0,4)) { case "\xD0\xCF\x11\xE0": this.type = "CFB"; this.file = CFB.read(bstr, {type: "binary"}); break; case "PK\x03\x04": case "PK\x05\x06": this.type = "ZIP"; this.file = new JSZIP(bstr, {base64: false}); break; default: throw new Error(`Invalid file (magic ${bstr.slice(0,4).split("").map(x => x.charCodeAt(0).toString(16).padStart(2,"0"))})`); } this.loading = false; } catch(e) { this.loading = false; if(cb) cb(e); } }, 100); }, addNewFile(name) { const fname = name || this.getNextName(1); if(this.isCFB()) CFB.utils.cfb_add(this.file, fname, [0x37, 0x32, 0x36, 0x32]); else if(this.isZIP()) this.file.file(fname, "7262"); const fi = this.find(fname); this.dirty = true; return this.isCFB() ? this.getFileNameById(this.entries().indexOf(fi)) : fname; }, /* Entry-level Accessors */ getFileNameById(id) { return this.keys()[id] || ""; }, getFileEntryById(id) { return this.entries()[id] || null; }, getFixedName(id) { return this.isEmpty() ? "" : fix_string(this.getFileNameById(id)); }, getFixedPath(id) { return this.isEmpty() ? "" : this.isCFB() ? fix_string(this.getFileNameById(id)).replace(/^[^/]*/,"") : fix_string(this.getFileNameById(id)); }, getFileTime(id) { return (this.isEmpty() || !this.getFileEntryById(id)) ? new Date(NaN) : (this.getFileTimeEntry(id) || this.getFileTimeEntry(0) || new Date(1980,0,1)); }, /* Entry-level Mutators */ setContentAB(id, ab) { const payload = ab2a(ab), fn = this.getFileNameById(id); if(this.isCFB()) { CFB.utils.prep_blob(payload); CFB.utils.cfb_add(this.file, fn, ab2a(payload)); } else if(this.isZIP()) { this.file.file(fn, payload.map(x => String.fromCharCode(x)).join(""), {binary:"true"}); } this.dirty = true; }, renFileById(id, name) { const oldfn = this.getFileNameById(id), newfn = unfix_string(name).replace(/^\//,""); if(this.isCFB()) { CFB.utils.cfb_mov(this.file, oldfn, this.getFileNameById(0) + newfn); CFB.utils.cfb_gc(this.file); } else if(this.isZIP()) { this.file.file(newfn, a2s(this.getContentById(id)), {binary: true}); this.file.remove(oldfn); } this.dirty = true; }, delFileById(id) { const fn = this.getFileNameById(id); if(this.isCFB()) CFB.utils.cfb_del(this.file, fn); if(this.isZIP()) this.file.remove(fn); this.dirty = true; }, /* Download data */ exportBStrById(id) { this.loading = true; setTimeout(() => {try { const FI = this.getFileEntryById(id); SaveBArray(this.getContentByEntry(FI), FI.name); this.loading = false; } catch(e) { this.loading = false; throw e; } }, 100); }, exportBStr(/*name*/) { this.loading = true; setTimeout(() => {try { this.dirty = false; let o; if(this.isCFB()) o = SaveBString(CFB.write(this.file, {type:"binary"}), this.fname || "SheetJS.cfb"); if(this.isZIP()) o = SaveBString(this.file.generate({type:"string", compression:"DEFLATE"}), this.fname || "SheetJS.zip"); this.loading = false; return o; } catch(e) { this.loading = false; throw e; } }, 100); }, /* Initialization */ newFile(name) { this.fname = name || "sheetjs.cfb"; if(this.fname.match(/\.zip$/)) { const out = new JSZIP(); out.file("Sh33tJ5", "7262"); this.file = out; } else this.file = CFB.utils.cfb_new(); this.dirty = true; }, reset() { this.file = null; this.fname = ""; this.dirty = false; }, /* Utils */ getContentById(id) { return this.getContentByEntry(this.getFileEntryById(id)); }, getContentByEntry(fi) { if(!fi) return []; if(fi._data) return typeof fi._data == "string" ? s2a(fi._data) : ab2a(fi._data.getContent()); return fi.content || []; }, getContentSliceByEntry(fi, s, e) { return !fi ? [] : fi.content ? fi.content.slice(s, e) : this.getContentByEntry(fi).slice(s, e); }, getSizeByEntry(fi) { if(this.isCFB() && (!fi.content || !fi.content.length)) return 0; return fi && (fi.size || fi._data && (fi._data.uncompressedSize) || fi._data.length) || 0; }, getTextByEntry(fi) { if(!fi || (!fi._data && !fi.content)) return ""; if(fi._data) return (typeof fi._data == "string") ? fi._data : a2s(fi._data.getContent()); return a2s(fi.content); }, getFileTimeEntry(id) { const FI = this.getFileEntryById(id); return !FI ? new Date(NaN) : (FI.ct || FI.mt || FI.date || id != 0 && this.getFileTime(0) || new Date(1980,0,1)); }, find(path) { return this.isEmpty() ? null : this.isCFB() ? CFB.find(this.file, path) : this.file.filter((rp, f) => (path == rp || path == f.name))[0]; }, keys() { return this.isEmpty() ? [] : this.isCFB() ? this.file.FullPaths : Object.keys(this.file.files).filter(x => !this.file.files[x].dir); }, entries() { return this.isEmpty() ? [] : this.isCFB() ? this.file.FileIndex : this.keys().map(x => this.file.files[x]); }, getIdByName(name) { if(this.isEmpty()) return -1; if(this.isZIP()) { const entries = Object.keys(this.file.files).filter(x => !this.file.files[x].dir); return entries.indexOf(name); } return this.entries().indexOf(this.find(name)); }, Sheet: "JS" });