src/basic-application.js
/*
* OS.js - JavaScript Cloud/Web Desktop Platform
*
* Copyright (c) Anders Evenrud <andersevenrud@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @author Anders Evenrud <andersevenrud@gmail.com>
* @license Simplified BSD License
*/
import {EventEmitter} from '@osjs/event-emitter';
import {basename, pathname} from './utils/vfs';
/**
* Basic Application Options
*
* @typedef {Object} BasicApplicationOptions
* @property {string[]} [mimeTypes] What MIME types to support (all/fallback)
* @property {string[]} [loadMimeTypes] What MIME types to support on load
* @property {string[]} [saveMimeTypes] What MIME types to support on save
* @property {string} [defaultFilename] Default filename of a new file
*/
/**
* Basic Application Helper
*
* A class for helping creating basic applications with open/load/create functionality.
* Also sets the internal proc args for sessions.
*/
export class BasicApplication extends EventEmitter {
/**
* Basic Application Constructor
* @param {Core} core OS.js Core API
* @param {Application} proc The application process
* @param {Window} win The main application window
* @param {BasicApplicationOptions} [options={}] Basic application options
*/
constructor(core, proc, win, options = {}) {
super('BasicApplication<' + proc.name + '>');
/**
* Core instance reference
* @type {Core}
* @readonly
*/
this.core = core;
/**
* Application instance reference
* @type {Application}
* @readonly
*/
this.proc = proc;
/**
* Window instance reference
* @type {Window}
* @readonly
*/
this.win = win;
/**
* Basic Application Options
* @type {BasicApplicationOptions}
* @readonly
*/
this.options = {
mimeTypes: proc.metadata.mimes || [],
loadMimeTypes: [],
saveMimeTypes: [],
defaultFilename: 'New File',
...options
};
}
/**
* Destroys all Basic Application internals
*/
destroy() {
this.off();
super.destroy();
}
/**
* Initializes the application
* @return {Promise<boolean>}
*/
init() {
if (this.proc.args.file) {
this.open(this.proc.args.file);
} else {
this.create();
}
return Promise.resolve(true);
}
/**
* Gets options for a dialog
* @param {string} type Dialog type
* @return {object}
*/
getDialogOptions(type, options = {}) {
const {
file,
...rest
} = options;
const {
defaultFilename,
mimeTypes,
loadMimeTypes,
saveMimeTypes
} = this.options;
const currentFile = file ? file : this.proc.args.file;
const defaultPath = this.core.config('vfs.defaultPath');
const path = currentFile ? currentFile.path : null;
let mime = type === 'open' ? loadMimeTypes : saveMimeTypes;
if (!mime.length) {
mime = mimeTypes;
}
return [{
type,
mime,
filename: path ? basename(path) : defaultFilename,
path: path ? pathname(path) : defaultPath,
...rest
}, {
parent: this.win,
attributes: {
modal: true
}
}];
}
/**
* Updates the window title to match open file
*/
updateWindowTitle() {
if (this.win) {
const {translatableFlat} = this.core.make('osjs/locale');
const prefix = translatableFlat(this.proc.metadata.title);
const title = this._createTitle(prefix);
this.win.setTitle(title);
}
}
/**
* Creates a new dialog of a type
* @param {string} type Dialog type
* @param {Function} cb Callback
* @param {object} [options] Override options
*/
createDialog(type, cb, options = {}) {
const [args, opts] = this.getDialogOptions(type, options);
if (this.core.has('osjs/dialog')) {
this.core.make('osjs/dialog', 'file', args, opts, (btn, item) => {
if (btn === 'ok') {
cb(item);
}
});
}
}
/**
* Opens given file
*
* Does not do any actual VFS operation
*
* @param {VFSFile} file A file
*/
open(item) {
this._setFile(item, 'open-file');
}
/**
* Saves given file
*
* Does not do any actual VFS operation
*
* @param {VFSFile} file A file
*/
save(item) {
this._setFile(item, 'save-file');
}
/**
* Create new file
*
* Does not do any actual VFS operation
*/
create() {
this.proc.args.file = null;
this.emit('new-file');
this.updateWindowTitle();
}
/**
* Create new file
* @see BasicApplication#create
*/
createNew() {
this.create();
}
/**
* Creates a new save dialog
* @param {object} [options] Dialog options
*/
createSaveDialog(options = {}) {
this.createDialog('save', item => this.save(item), options);
}
/**
* Creates a new load dialog
* @param {object} [options] Dialog options
*/
createOpenDialog(options = {}) {
this.createDialog('open', item => this.open(item), options);
}
/**
* Sets file from open/save action
*
* @private
* @param {VFSFile} item File
* @param {string} eventName Event to fire
*/
_setFile(item, eventName) {
this.proc.args.file = {...item};
this.emit(eventName, item);
this.updateWindowTitle();
}
/**
* Creates the window title
*
* @private
* @param {string} prefix Title prefix
* @return {string}
*/
_createTitle(prefix) {
const title = this.proc.args.file
? basename(this.proc.args.file.path)
: this.options.defaultFilename;
return title
? `${prefix} - ${title}`
: prefix;
}
}