namespace OS { export namespace application { const BUTTON_ICONS = { opendoc: "data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAD8UlEQVR42q1Va2gcVRQ+587s7mRItNskfaY1trRi2sZHNYoQHzGmQkAMlNZK1R+l0R+K/REhRAmzIEElaqX2R/tDhZamov4QbV0a6V9Nm9DiI8QEEtpI8zDbPPfh7sw93juzOzv7Ugge2L3nztz7fed859w7CHmGiKocYHVGwswcvLwFWnNzc3tLS8sjaTIvcX4kBZvD4fBAf3//aeEmShGUd3Z2nu7q6noBHZPABOjA2/9ijs4+wiwrydehUKivp6fnqPCjOQSHPhrTmKI2XP/uA33/k9s6Ot58rSkNnvPzZlKQkTDDMM4XJXjp08m3aiqV9xUex3sqZqCtqZ6QMSeDdKQeEoISGQiCvo9PfnY0tjBVQNC9d7vf0Fkcq2AK9jXeR8wrEQB6Ii8gkJWV/skz4ZlLI8posKbOQmQfnnl9y/f2gsMnbnbvuctnaCwOm9QZaH3ifmAlpCklj7RvLw/Bn8lqSPAy+PVGKnT2ja0hN4MdmxTDDzGs1Wbh+aYHXVm8kebNM5m4En3TfxXGY1WUBB3HblmGyCBLsLUaDZViuLN8DvY/s9cL+p8SZQi+DF+BkaW1ZKKON/+iLIGUaEMQDMZjsHtNBA7ue6hAnlISCfldv++HK/DL7SBwpsP0PGQlOnh8onttBRpgruDD6xbgcGtDTgb5EkmXc27XVozus7MXr8Lg7BoCtRznlyF0/litgfUdAxcCqnovAK/l3IRKnWBz9Z155ZMZQEHUcnRcZz41twyROAJj8rZhN5KmOYxtvcMLLz5egxanCimrIiT2qUpBAT1sztzGppw1psXBIlkj0aQIsYHRSAqffnd4fOeWDRoRXy8iYgGBXab5PKDu1SAxwQXOji5BPGlCirNMB94em5xdxGffGx3as23zRotbLoGu+XMzQEgDOagZAqE/SplknUzLwtjfFqDiJ9kTDNn8bxO3JvC53vEfG+o21okF68VWTBN4znnaIUpzkJ2JBBZ1BlVVIJlMwUrCBKb43E2MscXBkekhPHBi8qvHdlc3Eqd1Ygtqoj56ma94Dci5FmT3ZEZRO4jGUwTMZzebXCIlFXkt/Tw8dwlfPjV9qrE+2MYYVckMHAK/q3uGwBN9uj3JliUaN4UsPoK8QyeT+un3hXPY/nmk59FdFa8GfBSUb/2MQ1lALXrXULYOIMDB0bz4BzCaoMTgSPQ4Hju31LHrbu0dXaM7ZDmJmxmof29T+wCq6QNCdmtmw0BcXOHJPyaTb2PvxciRYGX5J5wsvQRocYLSa5yTbiGPLifasfXAkada2g59kTKpuC6rNJ+C1uULX78iPlxKgHPrAfGssqiYqzOZRURRlGv/F2BJ+wdsRP1yLA0KOQAAAABJRU5ErkJggg==", newdoc: "data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABmJLR0QA/wD/AP+gvaeTAAABmklEQVRIie2Vv0tbURTHP+fmJTGpIYM/KEoUHItOEtIu6uTk2r9BcNMWuro62X9D6OTWIhp00baLCI2giKKYVoMtJtTE93JPhyLSpyavLxE69Due+73nwzn3nnvhkSX+wNut2pugm9XapdkXicO/A3ysaZDk3UnD9yt77KETs9mOg4d8Jkiy+9TTCc8HIpmoSH7xU3Wo7QCA3k5DLtMY0hIgCKRlwB8QI6v+NSds0sKZpXBm/eFBf6AtFTTSf0BThT5kvxRw620G1FUREQxwUraUa3c9LbXoa0XZLbkUSpYBd4Pp+Bz6eTQJoPo7d0sVVD14FZ8mbc5JU8IRF6/CnpsfSXlr5iVsvw9VQa0Ox5eWRP0b/ZF9uqSII+7Nch+QwthJVSTUc31aUXqvN5npeE2Uexp/q5VQFfQkhCN9hquxZtb1f3PQzq+UQflCVK6bWcdCAboSQtHJMf/zHVV98pBt0Rnfmbx7TYWFZoC4A5m0oVh+mr2gP5viIpbUH7GIeAKcAilUPogQ6H9vqJuB8taGp9z88LJ/0H4BR3aNW1eB/4cAAAAASUVORK5CYII=" } /** * * @class LibreOffice * @extends {BaseApplication} */ export class LibreOffice extends BaseApplication { private access_token: string; private curr_file: API.VFS.BaseFileHandle; private placeholder: HTMLDivElement; private eid: string; private iframe: HTMLIFrameElement; private editor_meta: GenericObject>; private post_msg_handle: (e:any) => void; private mimes: string[]; private current_mode: string; static discovery_uri: string; constructor(args: AppArgumentsType[]) { super("LibreOffice", args); this.access_token = undefined; this.curr_file = undefined; this.eid = `id${Math.random().toString(36).replace(".","")}`; this.iframe = undefined; this.mimes = this.meta().mimes.map(e=>e); this.current_mode = undefined; this.post_msg_handle = (e) => { this.process_iframe_msg(e); } } main(): void { if(this.args && this.args.length > 0) { this.curr_file = this.args[0].path.asFileHandle(); } this.placeholder = this.find("editor-area") as HTMLDivElement; this.placeholder.id = this.eid; (this.find("btn-open-file") as GUI.tag.ButtonTag).onbtclick = (e) => { this.openFile(); } (this.find("btn-new-doc") as GUI.tag.ButtonTag).onbtclick = (e) => { this.create("word"); } (this.find("btn-new-cell") as GUI.tag.ButtonTag).onbtclick = (e) => { this.create("sheet"); } (this.find("btn-new-slide") as GUI.tag.ButtonTag).onbtclick = (e) => { this.create("slide"); } $(window).on("message", this.post_msg_handle); this.discover() .then((data) => { this.editor_meta = data; if(this.curr_file) this.open() }) .catch((e) =>{ this.error(__("Unable to discover LibreOffice service: {0}", e.toString()), e); this.quit(true); }); } menu(): OS.GUI.BasicItemType[]{ const nodes = [ { text: "__(New)", dataid :"new" }, { text: "__(Open)", dataid :"open" } ] if(this.current_mode == "edit") { nodes.push({ text: "__(Save)", dataid :"save"}); nodes.push({ text: "__(Save As)", dataid :"saveas"}); } return [ { text: "__(File)", nodes: nodes, onchildselect: (e) => { switch(e.data.item.data.dataid) { case "new": this.check_dirty().then((_)=>this.new_document()); break; case "open": this.check_dirty().then((_)=>this.openFile()); break; case "save": this.post_message("Action_Save", { DontTerminateEdit: true, DontSaveIfUnmodified: true, Notify: true }); break; case "saveas": this.check_dirty().then((_)=>this.save_as()); break; default: } } } ] } private update_title() { let title = this.curr_file.path; if(this.curr_file.dirty) { title += ` ${__("(modified)")}`; } (this.scheme as GUI.tag.WindowTag).apptitle = title; } private post_message(id:string, values?: GenericObject) { let msg:GenericObject = {MessageId: id,SendTime: Date.now()}; if(values) msg.Values = values; this.iframe.contentWindow.postMessage(JSON.stringify(msg),"*"); } private process_iframe_msg(e:any) { if(e.originalEvent.source != this.iframe.contentWindow) return; let orgevt = e.originalEvent; let data = JSON.parse(orgevt.data); switch(data.MessageId) { case 'Action_Load_Resp': if(!data.Values.success) { this.error(data.Values.errorMsg); } break; case 'App_LoadingStatus': if(data.Values.Status == "Document_Loaded") { this.post_message("Host_PostmessageReady"); this.trigger("document_file_loaded"); if(this.current_mode == "edit") { this.post_message("Insert_Button", { id:'lool_new_file', imgurl:BUTTON_ICONS.newdoc, label: __("New file").__(), hint: __("Create new document").__(), insertBefore: 'save' } ); this.post_message("Insert_Button", { id:'lool_open_file', imgurl:BUTTON_ICONS.opendoc, label: __("Open file").__(), hint: __("Open document").__(), insertBefore: 'lool_new_file' } ); } } if(data.Values.Status == "Frame_Ready") { $(this.iframe).css("visibility","visible"); } break; case 'Doc_ModifiedStatus': this.curr_file.dirty = data.Values.Modified; this.update_title(); break; case "Clicked_Button": switch(data.Values.Id) { case 'lool_open_file': this.check_dirty().then((_)=>this.openFile()); break; case 'lool_new_file': this.check_dirty().then((_)=>this.new_document()); break; default: } break; case "UI_SaveAs": this.check_dirty().then((_)=>this.save_as()); break; default: console.log(data); } //console.log(this.eid, e); } private save_as() { this.openDialog("FileDialog", { title: __("Save file as"), type: "dir", file: this.curr_file.asFileHandle() }) .then(async (d) => { const file = `${d.file.path}/${d.name}`.asFileHandle(); try { const r = await this.exec({ action: 'duplicate', args:{src: this.curr_file.path, dest: file.path} }); if(r.error) { throw r.error; } this.curr_file = file; this.open(); } catch(e) { this.error(__("Unable to save file as {0}: {1}", file.path, e.toString()),e); } }); } private new_document() { this.openDialog("SelectionDialog", { title: __("Create new"), data:[ { text: __("Document"), iconclass: "fa fa-file-word-o", type: "word" }, { text: __("Spreadsheet"), iconclass: "fa fa-file-excel-o", type: "sheet" }, { text: __("Presentation"), iconclass: "fa fa-file-powerpoint-o", type: "slide" }, ] }) .then((d) => { this.create(d.type); }); } private discover(): Promise> { return new Promise(async (resolve, reject) => { try{ let xml_text = await LibreOffice.discovery_uri.asFileHandle().read(); let parser = new DOMParser(); let xml_doc = parser.parseFromString(xml_text,"text/xml"); let apps = xml_doc.getElementsByTagName("app"); let meta = {}; if(apps) { for(let app of apps){ let name = app.getAttribute("name") if(name.match(/^[^\/]*\/[^\/]*$/g)) { if(!(this.mimes as any).includes(name)) { this.mimes.push(name); } } let actions = app.getElementsByTagName("action"); if(actions) { for(let action of actions) { let ext = action.getAttribute("ext"); let mode = action.getAttribute("name"); let urlsrc = action.getAttribute("urlsrc"); if(ext && ext != "" && urlsrc) { meta[ext] = { url: urlsrc, mode: mode } } } } } } resolve(meta); } catch(e) { reject(__e(e)); } }); } private openFile(): void { this.openDialog("FileDialog", { title: __("Open file"), type: "file", mimes: this.mimes }) .then((d) => { this.curr_file = d.file.path.asFileHandle(); this.open(); }); } private create(type: string): void { let ext = undefined switch (type) { case 'word': ext = "docx"; break; case 'sheet': ext = "xlsx"; break; case "slide": ext = "pptx"; break; default: this.error(__("Unknown doc type {0}", type)); } this.openDialog("FileDialog", { title: __("Save file as"), type: "dir", file: `home://Untitled.${ext}`.asFileHandle() }) .then(async (d) => { try{ let file = `${d.file.path}/${d.name}`.asFileHandle(); let model = `${this.path()}/templates/model.${ext}`.asFileHandle(); let data = await model.read("binary"); let blob = new Blob([data], { type: model.info.mime, }); file.cache = blob; await file.write(model.info.mime); file.cache = undefined; this.curr_file = file; this.open(); } catch(e) { this.error(__("Unable to create document {0}", e.toString()),e); } }); } private open(): void { (this.scheme as GUI.tag.WindowTag).apptitle = __("Libre Office Online"); if(!this.curr_file) return; this.exec({ action: 'token', args:{file: this.curr_file.path} }) .then((r) =>{ if(r.error) { this.error(r.error); return; } this.access_token = r.result.sid; let mt = this.editor_meta[this.curr_file.ext]; if(!mt || !mt.url) { return this.error(__("Unknown editor for extension {0}", this.curr_file.ext)); } this.current_mode = mt.mode; // refresh the file menu //this.appmenu.nodes = this.baseMenu() || []; $(this.placeholder).empty(); let el = $('