From 2926d5613d5679e760ff3f754edaf3e4c97d6df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Mon, 17 Feb 2020 18:06:45 +0100 Subject: [PATCH] [ivette] remove multitool support --- ivette/backup/Configure.js | 155 ----------- ivette/backup/Display.js | 34 --- ivette/backup/FileMenu.js | 37 --- ivette/backup/Home.js | 134 ---------- ivette/backup/Project.js | 101 ------- ivette/backup/ivette/index.js | 455 -------------------------------- ivette/backup/ivette/plugins.js | 247 ----------------- ivette/backup/ivette/stores.js | 152 ----------- ivette/backup/ivette/views.js | 42 --- ivette/backup/style.css | 50 ---- 10 files changed, 1407 deletions(-) delete mode 100644 ivette/backup/Configure.js delete mode 100644 ivette/backup/Display.js delete mode 100644 ivette/backup/FileMenu.js delete mode 100644 ivette/backup/Home.js delete mode 100644 ivette/backup/Project.js delete mode 100644 ivette/backup/ivette/index.js delete mode 100644 ivette/backup/ivette/plugins.js delete mode 100644 ivette/backup/ivette/stores.js delete mode 100644 ivette/backup/ivette/views.js delete mode 100644 ivette/backup/style.css diff --git a/ivette/backup/Configure.js b/ivette/backup/Configure.js deleted file mode 100644 index b91206cb19d..00000000000 --- a/ivette/backup/Configure.js +++ /dev/null @@ -1,155 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Module Configuration View -// -------------------------------------------------------------------------- - -import _ from 'lodash' ; -import React from 'react' ; -import Dome from 'dome' ; -import Dialogs from 'dome/dialogs' ; -import { Catch } from 'dome/errors' ; -import { Label } from 'dome/controls/labels' ; -import Box from 'dome/layout/boxes' ; -import Toolbar from 'dome/layout/toolbars' ; -import Dispatch from 'dome/layout/dispatch' ; -import Form from 'dome/layout/forms' ; -import Ivette from '@ivette' ; -import Project from './Project' ; - -// -------------------------------------------------------------------------- -// --- Configuration View -// -------------------------------------------------------------------------- - -export default function Configure() -{ - - const [ state, setState ] = Project.useState(); - const configure = state.configure ; - if (!configure) return null; - - const { module, value, error, modified=false, copied } = configure ; - const valueId = value && value.id ; - const savedId = module && module.id ; - const isFresh = Ivette.isFresh(valueId); - const borrowed_label = copied && module.label ; - const borrowed_title = copied && module.title ; - - const validIdent = (id) => - id === savedId || - (!Ivette.isValid(id) ? 'Invalid identifier (not a filename)' : - !Ivette.isFresh(id) ? 'Already used identifier' : - true) ; - - const validLabel = borrowed_label && ((label) => label !== borrowed_label) ; - const validTitle = borrowed_title && ((title) => title !== borrowed_title) ; - - const onChange = (value,error) => { - Object.assign( configure, { modified: true, value, error } ); - setState({ configure }); - }; - - const onReload = () => { - setState({ configure: { - module, - copied: configure.copied, - value: Project.initConfigValue(configure.module) - }}); - }; - - const closeWith = (current) => { - setState({ - current, - configure: undefined, - frame: configure.onClose || 'home' - }); - }; - - const onClose = () => closeWith( state.current ); - - const onApply = () => { - if ( value && !error && !module.locked()) { - let commit = Ivette.commitModule( module, value ); - module.synchronize(commit) - .catch(onError) - .finally(() => closeWith( configure.copied ? module : undefined )); - } else { - closeWith( undefined ); - } - }; - - const onError = (err) => Dialogs.showMessageBox({ - kind: 'warning', - title: 'Module Management Error', - message: 'An error occurs during a module operation:\n' + err, - buttons: [{ label:'Ok' }] - }); - - const onRemove = () => Dialogs.showMessageBox({ - kind: 'warning', - title: 'Remove Module', - message: 'Definitively remove this Module from your Project ?', - defaultValue: false, - cancelValue: false, - buttons: [ - { value:false, label:'No' }, - { value:true, label:'Remove' } - ] - }).then( (ok) => { - if (ok) { - let removal = Ivette.removeModule( configure.module ); - module.synchronize(removal) - .catch(onError) - .finally(() => closeWith( undefined )); - } - }); - - return ( - <Box.Vfill> - <Dispatch.Item id='ivette.toolbar.configure'> - <Toolbar.Separator/> - <Toolbar.Button - kind='positive' icon='CHECK' - label='Apply' title='Apply modifications' - enabled={modified || isFresh} - disabled={error || module.locked()} - onClick={onApply} /> - <Toolbar.Inset/> - <Label>{ isFresh && 'New' } Module Configuration </Label> - <Toolbar.Filler/> - <Toolbar.Button - kind='negative' icon='TRASH' - label='Remove' title='Remove module' - disabled={isFresh} - enabled={valueId === savedId} - onClick={onRemove} /> - <Toolbar.Button - icon='RELOAD' title='Reset configuration' - enabled={modified} - onClick={onReload} /> - <Toolbar.Button - kind='cancel' - icon='CIRC.CLOSE' title='Cancel modifications' - onClick={onClose} /> - </Dispatch.Item> - <Form.Form - value={value} - error={error} - onChange={onChange} > - <Form.FieldCode - path='id' label='Module' - validate={validIdent} - placeholder={valueId} /> - <Form.FieldText - path='label' label='Display Name' - validate={validLabel} /> - <Form.FieldTextArea - path='title' label='Description' - validate={validTitle} rows={3} /> - <Form.Select path='config'> - <Catch label='Plugin Config'>{ module.renderConfig() }</Catch> - </Form.Select> - </Form.Form> - </Box.Vfill> - ); -} - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/Display.js b/ivette/backup/Display.js deleted file mode 100644 index 140f47ea5a9..00000000000 --- a/ivette/backup/Display.js +++ /dev/null @@ -1,34 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Module Control View -// -------------------------------------------------------------------------- - -import _ from 'lodash' ; -import React from 'react' ; -import Dome from 'dome' ; -import { Catch } from 'dome/errors' ; -import { Label } from 'dome/controls/labels' ; -import Toolbar from 'dome/layout/toolbars' ; -import Dispatch from 'dome/layout/dispatch' ; -import Project from './Project' ; -import { ProvideModule } from '@ivette' ; - -// -------------------------------------------------------------------------- -// --- Current Module Display -// -------------------------------------------------------------------------- - -export default function Display() -{ - const [ { current } ] = Project.useState(); - if (!current) - return <Label>No module selected.</Label> ; - else - return ( - <ProvideModule module={current}> - <Catch label='Plugin Main View'> - <Dome.Render>{current.renderMain}</Dome.Render> - </Catch> - </ProvideModule> - ); -} - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/FileMenu.js b/ivette/backup/FileMenu.js deleted file mode 100644 index c2bf7131c3f..00000000000 --- a/ivette/backup/FileMenu.js +++ /dev/null @@ -1,37 +0,0 @@ -// -------------------------------------------------------------------------- -// --- File Menu -// -------------------------------------------------------------------------- - -import fs from 'fs' ; -import path from 'path' ; -import Dome from 'dome' ; -import Dialogs from 'dome/dialogs' ; -import Project from './Project' ; -import Ivette from '@ivette' ; - -// -------------------------------------------------------------------------- -// --- Actions -// -------------------------------------------------------------------------- - -function openProject() -{ - console.log('OPEN PROJECT…'); -} - -// -------------------------------------------------------------------------- -// --- Menu Items -// -------------------------------------------------------------------------- - -Dome.addMenuItem({ - menu: 'File', - id: 'ivette.menu.file.open', - label: 'Open Project…', - onClick: openProject -}); - -Dome.onUpdate(() => { - let enabled = Ivette.isProjectLocked(); - Dome.setMenuItem({ id: 'ivette.menu.file.open', enabled }); -}); - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/Home.js b/ivette/backup/Home.js deleted file mode 100644 index 93c98808a6c..00000000000 --- a/ivette/backup/Home.js +++ /dev/null @@ -1,134 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Project Home View -// -------------------------------------------------------------------------- - -import React from 'react' ; -import Dome from 'dome' ; -import Toolbar from 'dome/layout/toolbars' ; -import { Scroll, Hpack, Vbox, Vfill, Hfill } from 'dome/layout/boxes' ; -import { Splitter } from 'dome/layout/splitters' ; -import { Title, Label, Code, Descr } from 'dome/controls/labels' ; -import { Button, IconButton } from 'dome/controls/buttons' ; -import Ivette from '@ivette' ; -import Project from './Project' ; - -// -------------------------------------------------------------------------- -// --- Generic Dongle -// -------------------------------------------------------------------------- - -function Dongle({ id, label, title, selected, onSelect, children }) -{ - const wident = id && <Code className='ivette-dongle-id' label={id}/> ; - const wlabel = label && <Label label={label} /> ; - const wdescr = title && <Descr label={title} /> ; - const classes = 'ivette-dongle' - + (onSelect ? ' ivette-dongle-selectable' : '') - + (selected ? ' ivette-dongle-selected' : '') ; - return ( - <Hfill className={classes} - onClick={onSelect} > - {wident} - <Vfill> - {wlabel} - {wdescr} - </Vfill> - { children } - </Hfill> - ); -} - -// -------------------------------------------------------------------------- -// --- Module Dongle -// -------------------------------------------------------------------------- - -function ModuleDongle({ module, current, configure }) -{ - let selected = module === current ; - let configuring = configure && configure.modified ; - let configured = configuring && configure.value && configure.value.id === module.id ; - let SELECT = () => Project.setState({ current: module }); - let DISPLAY = () => Project.setState({ current: module, frame:'display' }); - let CONFIGURE = () => { - if (configured) Project.setConfigure(); - else if (!configuring) Project.setConfiguring(module); - }; - return ( - <Dongle id={module.id} - label={module.label} - title={module.title} - selected={selected} - onSelect={SELECT} > - <Toolbar.ButtonGroup> - <Toolbar.Button - icon='DISPLAY' title={'Display ' + module.id} - onClick={DISPLAY} /> - <Toolbar.Button - icon='SETTINGS' title={'Configure ' + module.id} - disabled={configuring && !configured} - onClick={CONFIGURE} /> - </Toolbar.ButtonGroup> - </Dongle> - ); -} - -// -------------------------------------------------------------------------- -// --- Plugin Dongle -// -------------------------------------------------------------------------- - -function PluginDongle({ plugin, configure }) -{ - const CREATE = () => Project.setConfiguring( Ivette.newModule(plugin) ); - return ( - <Dongle label={plugin.label} title={plugin.title} > - <Button icon='CIRC.PLUS' label='New' - title='Create a new Module from this plugin' - kind='positive' - style={{ width:80, height: 32 }} - disabled={configure && configure.modified} - onClick={CREATE} /> - </Dongle> - ); -} - -// -------------------------------------------------------------------------- -// --- Main View -// -------------------------------------------------------------------------- - -export default function Home() -{ - const [ { current,configure } , setState ] = Project.useState(); - const PROJECT = Ivette.getProjectDirectory(); - - const MODULES= Ivette.getModules().map((module) => ( - <ModuleDongle - key={'M' + module.id} - module={module} - current={current} - configure={configure} - /> - )); - - const PLUGINS= Ivette.getPlugins().map((plugin) => ( - <PluginDongle - key={'P' + plugin.id} - plugin={plugin} - configure={configure} - /> - )); - - return ( - <Scroll> - <Vbox> - {PROJECT ? <Title label='Project'/> : null} - {PROJECT ? <div className='ivette-project'><Code>{PROJECT}</Code></div> : null} - {MODULES.length ? <Title label='Modules'/> : null} - {MODULES} - {MODULES.length ? <div style={{height: 32}}/> : null} - {PLUGINS.length ? <Title label='Plugins'/> : null} - {PLUGINS} - </Vbox> - </Scroll> - ); -} - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/Project.js b/ivette/backup/Project.js deleted file mode 100644 index 67e6fa68443..00000000000 --- a/ivette/backup/Project.js +++ /dev/null @@ -1,101 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Current State of Project -// -------------------------------------------------------------------------- - -import _ from 'lodash' ; -import React from 'react' ; -import Dome from 'dome' ; -import Dialogs from 'dome/dialogs' ; -import System from 'dome/system' ; -import Ivette from '@ivette' ; -import { Module } from '@ivette/plugins' ; - -// -------------------------------------------------------------------------- -// --- Global State -// -------------------------------------------------------------------------- - -/* - - 'frame': currently displayed frame in main window - - 'current': currently selected module object - - 'configure.module': original configured module - - 'configure.value': edited module properties - - 'configure.error': edited module errors - - 'configure.copied': true if the the target is a fresh copy from another module - - 'configure.modified': true if the value has been edited - */ - -const project = new Dome.State({ frame: 'home' }); - -export const setState = project.setState ; -export const useState = project.useState ; - -const mkSetFrame = (frame) => () => project.setState({frame}); -export const setHome = mkSetFrame('home'); -export const setDisplay = mkSetFrame('display'); -export const setConfigure = mkSetFrame('configure'); - -// -------------------------------------------------------------------------- -// --- Module Helpers -// -------------------------------------------------------------------------- - -export function initConfigValue( module ) -{ - let { id, label, descr } = module ; - let config = _.cloneDeep( module.config ); - return { id, label, descr, config }; -} - -export function setConfiguring( module, onClose ) -{ - if (module instanceof Module) { - let value = initConfigValue( module ); - setState({ - frame: 'configure', - configure: Object.assign( { onClose }, { value, module } ) - }); - } else - console.err( 'Not a plugin instance' , module ); -} - -// -------------------------------------------------------------------------- -// --- Module Helpers -// -------------------------------------------------------------------------- - -export function lookupProject() { - - let argv = System.argv(); - let commandProject = Ivette.lookupProject( argv[0] ); - if (commandProject) return commandProject ; - - let previousProject = Ivette.lookupProject( Dome.getGlobalSetting('ivette.working.project') ); - if (previousProject) return previousProject ; - - let workingProject = Ivette.lookupProject( System.getWorking() ); - if (workingProject) return workingProject ; - - return undefined ; -} - -export function loadProject( dir ) -{ - if (!dir) return ; - Dome.setGlobalSetting('ivette.working.project',dir); - let setup = Ivette.loadProject( dir ); - setup.then( (errors) => errors && errors.length && Dialogs.showMessageBox({ - kind: 'error', - message: 'Error(s) when loading project:\n' + errors.join('\n'), - buttons: [{ label: 'Ok' }] - })).finally(Dome.update); -} - -// -------------------------------------------------------------------------- -// --- Exports Default -// -------------------------------------------------------------------------- - -export default { - useState, setState, setHome, setConfigure, setDisplay, - setConfiguring, initConfigValue, - lookupProject, loadProject -}; - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/ivette/index.js b/ivette/backup/ivette/index.js deleted file mode 100644 index c9520ab74cf..00000000000 --- a/ivette/backup/ivette/index.js +++ /dev/null @@ -1,455 +0,0 @@ -/** @module @ivette */ - -import _ from 'lodash' ; -import React from 'react' ; -import Dome from 'dome' ; -import System from 'dome/system' ; -import { Module, Factory, CONFIG, UPDATE } from './plugins' ; -import { Registry } from './stores' ; - -// -------------------------------------------------------------------------- -// --- Plugin & Module Registries -// -------------------------------------------------------------------------- - -const pluginsRegistry = new Registry('plugin'); -const modulesRegistry = new Registry('module'); - -/** - @method - @summary Registered Plugins. - @return {Class[]} an array of `Plugin` classes - */ -export const getPlugins = () => pluginsRegistry.elements() ; - -/** - @method - @summary Registered Modules. - @return {Plugin[]} an array of `Plugin` instances - */ -export const getModules = () => modulesRegistry.elements() ; - -/** - @method - @summary Returns a module by id. - */ -export const getModule = (id) => modulesRegistry.get(id); - -// -------------------------------------------------------------------------- -// --- Register Plugin -// -------------------------------------------------------------------------- - -const checkModule = ( m ) => { - if (!(m instanceof Module)) - throw `Invalid Plugin '${m}'` ; -}; - -const checkPlugin = ( P ) => checkModule( P.prototype ); -const getPluginId = ( m ) => m.constructor.id ; - -/** - @summary Register a (new) plugin. - @param {PLUGIN|Class<Module>} Plugin - - [plugin specification](module-@ivette_plugins.html#~PLUGIN) or - [module class](module-@ivette_plugins.html#Module) - @description - - Register a new plugin in the Ivette system. - - It is recommand to use a [PLUGIN](module-@ivette_plugins.html#~PLUGIN) - object for plugin specification. - If a class is provided, it shall extends the [Module](module-@ivette_plugins.html#Module) - base class and shall have static properties `id`, `label` - and `title` defined. - - Unless your are in development mode (which implies hot module reloading), - it is not permitted to register two plugins with the same identifier, - and an error would be emitted on the console in turn. - */ -function registerPlugin( spec ) -{ - const ThePlugin = typeof(spec)==='object' ? Factory(spec) : spec ; - checkPlugin( ThePlugin ); - const id = ThePlugin.id ; - if (!id) - throw `Invalid plugin: missing identifier` ; - if (!System.DEVEL && pluginsRegistry.get(id)) - throw `Duplicate plugin '${id}'` ; - pluginsRegistry.add( ThePlugin ); - if (System.DEVEL) modulesRegistry.forEach( (module) => { - // Propagates changes from HMR - if (module.constructor.id === ThePlugin.id) Object.setPrototypeOf( module , ThePlugin.prototype ); - }); -} - -// -------------------------------------------------------------------------- -// --- Project Directory -// -------------------------------------------------------------------------- - -let projectDirectory ; - -const getProjectDir = () => { - if (!projectDirectory) return projectDirectory; - let cwd = System.getWorkingDir(); - return cwd ? System.join( cwd , '.ivette' ) : undefined ; -}; -const getProjectIndex = () => System.join( getProjectDir(), 'index.json' ); -const getModuleSession = (mid) => System.join( getProjectDir(), 'modules', mid ); -const getModuleConfig = (mid) => System.join( getProjectDir() , 'config.json' ); - -/** @method - @summary Check is fome module is locked. - @return {boolean} */ -export const isProjectLocked = () => ( - modulesRegistry.find((m) => m.locked()) !== undefined -); - -/** - @method - @summary Returns current project directory. - @return {string} absolute path or undefined */ -export const getProjectDirectory = () => projectDirectory; - -/** - @summary Find enclosing project. - @param {string} path - a file path to starts with - @return {string} project directory holding that path - @description - Looks for the closest directory with name `"*.ivette"` - containing the given path, or the closest directory with a `.ivette` - file at root. - Returns `undefined` if not found. -*/ -export function lookupProject( path ) { - try { - path = System.resolve(path); - let prev ; - while( prev !== path ) - { - if (System.extname(path) === '.ivette' && System.isDirectory(path)) - return path; - let here = System.join( path, '.ivette' ); - if (System.isDirectory(here)) - return here; - prev = path ; - path = System.dirname(path); - } - } catch(_err) { } - return undefined ; -} - -// -------------------------------------------------------------------------- -// --- Project Index -// -------------------------------------------------------------------------- - -const saveIndex = async () => { - let modules = modulesRegistry.elements().map((m) => m.id); - let indexFile = getProjectIndex(); - let indexJSON = JSON.stringify({ modules }); - await System.mkDir( projectDirectory , { recursive: true }); - await System.writeFile( indexFile , indexJSON ); -}; - -const loadIndex = () => { - let indexFile = getProjectIndex(); - if (System.isFile(indexFile)) { - return System.readFile( indexFile ).then((content) => { - try { - return JSON.parse(content); - } catch(err) { - console.warn('[Ivette] invalid index.json:',err); - return {}; - } - }); - } else { - return Promise.resolve({}); - } -}; - -// -------------------------------------------------------------------------- -// --- Module Helpers -// -------------------------------------------------------------------------- - -export const isFresh = (id) => modulesRegistry.isFresh(id); -export const isValid = (id) => modulesRegistry.isValid(id); - -/** - @summary Creates a fresh Plugin instance. - @param {Class} Plugin - a class extending Plugin - @return {Plugin} a fresh module instance - @description - The returned module has a fresh identifier but is not yet registered. - */ -export function newModule( Plugin ) -{ - checkPlugin( Plugin ); - let module = new Plugin(); - module.id = modulesRegistry.fresh( Plugin.id ); - return module; -} - -/** - @summary Creates a fresh duplicate of a Plugin instance. - @param {Module} module - the instance to duplicate - @return {Module} a fresh module instance - @description - The returned module has a fresh identifier but is not yet registered. - It inherits from the original `module` configuration, with tagged label and title. - */ -export function duplicateModule( module ) -{ - let { label, title, constructor: Plugin } = module ; - checkPlugin( Plugin ); - let duplicate = new Plugin(); - duplicate.id = modulesRegistry.fresh( module.id ); - duplicate.label = label && label + " (copy)" ; - duplicate.title = title && title + " (duplicated)" ; - duplicate.config = _.cloneDeep(module.config); - return duplicate ; -} - -/** - @summary Unregister a module (if registered). - @param {Module} module - the module to remove - @return {Promise} resolved when the module has been removed (from disk) - @description - - Remove a module from the current project. - If the module is locked, an exception if raised. - The function does nothing if the module is not registered. - The promise is resolved when the module session directory has been fully removed. - */ -function removeModule( module ) -{ - checkModule( module ); - if (module.locked()) throw `Try to remove locked module '${module}'` ; - modulesRegistry.remove( module.id ); - if (module.session) - return System.rmDir( module.session, { recursive: true } ).then(saveIndex); - else - return saveIndex(); -} - -// -------------------------------------------------------------------------- -// --- Commit Module -// -------------------------------------------------------------------------- - -/** - @summary Commit module updates. - @param {Module} module - the module to update - @param {object} update - the module fields to update with - @param {string} [update.id] - new identifier - @param {string} [update.label] - new display name - @param {string} [update.title] - new short description - @param {object} [update.config] - new configuration - @return {Promise} resolved when the module has been commited (on disk) - @description - - The module is registered in the current project, or updated if already present. - Its session directory is created or renamed if necessary. - Finally, the module is emitted a `Plugin.CONFIG` event. - - */ -export function commitModule( module, { id, label, title, config } ) -{ - checkModule( module ); - if (module.locked()) throw `Try to commit locked module '${id}'` ; - - const oldId = module.id ; - const newId = id || oldId ; - - // Update registry - let index ; // Promise - if ( newId !== oldId ) { - module.id = newId ; - modulesRegistry.remove( oldId ); - modulesRegistry.add( module ); - index = saveIndex(); - } else { - if (!modulesRegistry.get( newId )) { - modulesRegistry.add( module ); - index = saveIndex(); - } - } - - // Update module - if (label !== undefined) module.label = label; - if (title !== undefined) module.title = title; - if (config !== undefined) module.config = config ; - - // Setup session directory - let setup ; // Promise - let oldSession = module.session ; - let newSession = System.join( projectDirectory, 'modules', newId ); - if ( newSession !== oldSession ) { - module.session = newSession ; - if (System.exists( oldSession )) - setup = System.rename( oldSession, newSession ); - else - setup = System.mkDir( newSession, { recursive:true } ); - } else { - if (!System.exists( newSession )) - setup = System.mkDir( newSession , { recursive:true } ); - else - setup = Promise.resolve(); - } - - // Backup config - let configFile = System.join( module.session , 'config.json' ); - if (!id || !label || !title || !config || !System.exists(configFile)) - { - let plugin = getPluginId( module ); - let configJSON = JSON.stringify({ id, label, title, plugin, config }); - setup.then(() => System.writeFile( configFile , configJSON )); - } - - // Batch updates and finally emit config event - return Promise.all([index,setup]).finally(() => module.emit( CONFIG )); -} - -// -------------------------------------------------------------------------- -// --- Project Management -// -------------------------------------------------------------------------- - -function loadProjectModule(id) -{ - if (modulesRegistry.get(id)) - return Promise.reject('Duplicate module'); - let session = System.join( projectDirectory, 'modules', id ); - let configFile = System.join( session , 'config.json' ); - return System.readFile( configFile ) - .then( (configJSON) => { - let moduleConfig = JSON.parse(configJSON); - let { label, title, plugin, config } = moduleConfig ; - let P = pluginsRegistry.get( plugin ); - if (!P) throw `Invalid plugin identifier ('${plugin}')` ; - let module = new P(); - module.id = id ; - module.label = label ; - module.title = title ; - module.config = config ; - modulesRegistry.add( module ); - module.emit( CONFIG ); - }); -} - -function reportLoadModule(id) { - return loadProjectModule(id) - .then(() => undefined) - .catch((err) => `Error with module ${id}: ${err}`); -} - -/** Change project directory. - @param {string} path - the directory of the project - @return {Promise} returning an array of errors -*/ -export function loadProject( path ) { - if (isProjectLocked()) throw 'Try to close locked project' ; - if (!path) throw 'Try to open empty project' ; - projectDirectory = path ; - modulesRegistry.clear(); - return loadIndex() - .then(({ modules=[] }) => Promise.all(modules.map(reportLoadModule))) - .then((errors) => errors.filter((e) => e !== undefined)); -} - -// -------------------------------------------------------------------------- -// --- Plugin Hooks -// -------------------------------------------------------------------------- - -const ModuleContext = React.createContext(); - -/** - @class - @summary Set the current module for children components. - @property {Module} module - the current module (can be `'undefined'`) - @property {React.Children} children - content to be rendered with the current module - */ -export const ProvideModule = ({ module, children }) => ( - <ModuleContext.Provider value={module}> - {children} - </ModuleContext.Provider> -); - -/** - @method - @summary Use the current module (Custom React Hook). - @return {Module} the current module - @description - Convenient method to obtain the current module. Does _not_ update on events. -*/ -export const useModule = () => React.useContext(ModuleContext); - -/** - @method - @summary Listen to (current) module event (Custom React Hook). - @parameter {string} event - the plugin event to listen on - @parameter {function} callback - the callback on event - @return {Plugin} the plugin instance of the current module - @description - Same as `Dome.useEmitter(module,event,callback)` with the current module. - The hook itself does not force update, unless the callback does. -*/ -export const useEvent = (event,callback) => { - let module = React.useContext(ModuleContext); - Dome.useEmitter( module, event, callback ); -}; - -/** - @method - @summary Use the current module with optional update on events (Custom React Hook). - @parameter {string} [events...] - the event(s) to listen on, in addition to `UPDATE` - @return {Module} the current module - @description - Convenient method to obtain the current module and connect to updating events. -*/ -export const useUpdate = (...events) => { - let module = React.useContext(ModuleContext); - let callback = Dome.useForceUpdate(); - React.useEffect(() => { - if (module) { - events.push(UPDATE); - events.forEach((evt) => module.on(evt,callback)); - return () => events.forEach((evt) => module.off(evt,callback)); - } else - return undefined; - }); - return module ; -}; - -/** - @method - @summary Returns the current module configuration (Custom React Hook). - @return {Module} the plugin configuration (up-to-date) - @description - Equivalent to `useUpdate(CONFIG).config` unless there is no current module. -*/ -export const useConfig = () => { - let m = useUpdate(CONFIG); - return m && m.config; -}; - -// -------------------------------------------------------------------------- -// --- Export Default -// -------------------------------------------------------------------------- - -export default { - registerPlugin, - getPlugins, - getModules, - isProjectLocked, - getProjectDirectory, - loadProject, - lookupProject, - isFresh, isValid, - newModule, - duplicateModule, - commitModule, - removeModule, - ProvideModule, - useModule, - useEvent, - useUpdate, - useConfig -}; - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/ivette/plugins.js b/ivette/backup/ivette/plugins.js deleted file mode 100644 index 90cd687daf8..00000000000 --- a/ivette/backup/ivette/plugins.js +++ /dev/null @@ -1,247 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Plugin Base Class -// -------------------------------------------------------------------------- - -/** @module @ivette/plugins */ - -import _ from 'lodash' ; -import React from 'react' ; -import Dome from 'dome' ; -import EventEmitter from 'events' ; - -/** - @event - @description - Triggered when a module is asked for killing its pending jobs. -*/ -export const KILL = 'ivette.kill' ; - -/** - @event - @description - Triggered when a module enters in locked state -*/ -export const LOCK = 'ivette.lock' ; - -/** - @event - @description - Triggered when a module is back to unlocked state -*/ -export const UNLOCK = 'ivette.unlock' ; - -/** - @event - @description - Triggered when a plugin has been configured -*/ -export const CONFIG = 'ivette.config' ; - -/** - @event - @description - Triggered when a plugin has been updated -*/ -export const UPDATE = 'ivette.update' ; - -/** - @summary Base class for Module instances. - @property {string} id - Module identifier - @property {string} [label] - Module display name - @property {string} [title] - Module short description - @property {object} config - Module configuration object - @property {string} session - Module session directory - @description - Each Ivette module is associated to a global object maintaining its internal state. - A `Plugin` is a class responsible for creating and managing the internal state of a module. - It is also responsible for rendering the module main view, its toolbar and sidebar items, - and configuration fields. -*/ -export class Module extends EventEmitter { - - constructor() { - super(); - this.config = {} ; - let _lock = { sem: 0 } ; - let _LOCKED = () => _lock.sem > 0 ; - let _LOCK = () => { - if (_lock.sem == 0) this.emit(LOCK); - _lock.sem++; - } ; - let _UNLOCK = () => { - _lock.sem--; - if (_lock.sem == 0) this.emit(UNLOCK); - if (_lock.sem < 0) { - console.warn(`[Ivette:${this.id}] unbalanced lock/unlock`,this.id); - _lock.sem = 0 ; - } - } ; - let _SYNCHRO = (p,k) => { - _LOCK() ; - if (k) this.once(KILL,k); - p.finally(() => { - if (k) this.off(KILL,k); - _UNLOCK(); - }); - return p; - } ; - this.lock = _LOCK ; - this.unlock = _UNLOCK ; - this.locked = _LOCKED ; - this.synchronize = _SYNCHRO ; - this.renderMain = this.renderMain.bind(this); - this.renderConfig = this.renderConfig.bind(this); - } - - // -------------------------------------------------------------------------- - // --- Locking - // -------------------------------------------------------------------------- - - /** - @summary Lock the plugin. - @description - Prevents the module from being re-configured. - Nested calls to `lock/unlock` shall be well balanced. - It is preferrable to use `this.synchronize()` instead. - Emits the signal `'ivette.lock'` unless already locked. - */ - lock() {} // privately defined by constructor - - /** - @summary Unlock the plugin. - @description - Cancel the last `lock()` call effect. - Nested calls to `lock/unlock` shall be well balanced. - It is preferrable to use `this.synchronize()` instead. - Emits the signal `'ivette.unlock'` when actually unlocked. - */ - unlock() {} // privately defined by constructor - - /** Whether the module is currenly locked or not. - @return {boolean} the locked status of the module. */ - locked() {} // privately defined by constructor - - /** - @summary Lock the module until a promise is pending. - @param {Promise} job - the promise to lock on - @param {function} kill - a callback to kill the job - @return {Promise} the job, for chaining - @description - Locks the module and wait for the promise to terminate - (normally or with an error) before unlocking. The callback is triggered - if the module is asked to be killed while the job is still running. - */ - synchronize() {} // privately defined by constructor - - /** - @summary Promise awaiting until unlocked. - @return {Promise} awaiting promise - @description - The returned promise is resolved once the - plugin is unlocked. It is immediately resolved - if not locked. - */ - wait() { - if ( !this.locked() ) { - return Promise.resolved(); - } else { - return new Promise((resolve,reject) => this.once(UNLOCK,resolve)); - } - } - - /** - @summary Interrupts pending tasks. - @description - Emits the `'ivette.kill'` signal. - Any currently running job shall be interrupted, - and the module shall eventually be unlocked. - @return {Promise} a promise resolved once unlocked - */ - kill() { - this.emit(KILL); - return this.wait(); - } - - // -------------------------------------------------------------------------- - // --- Render - // -------------------------------------------------------------------------- - - /** - @summary Emits the `@ivette.plugins.UPDATE` event. - */ - update() { - this.emit(UPDATE); - } - - /** - @summary Main view for the plugin. - @return {React.Children} the main view for the module - @description - See also the '@ivette/views' for defining sidebars and toolbars. - */ - renderMain() { - return null; - } - - /** - @summary Configuration view for the plugin. - @return {React.Children} form fields for configuring the plugin - */ - renderConfig() { - return null; - } - -} - -// -------------------------------------------------------------------------- -// --- Plugin Factory -// -------------------------------------------------------------------------- - -/** - @typedef PLUGIN - @summary Plugin Specification - @property {string} id - plugin identifier - @property {string} [label] - display name - @property {string} [title] - short description - @property {function} [initializer] - module initializer - @property {React.Element} [mainView] - main view - @property {React.Element} [configView] - configuration view - @description - - Properties for [registerPlugin](module-@ivette.html#registerPlugin) - specification. - - The `initializer` function, when specified, - is invoked once on each plugin instance. - The initializer function can be written as follows: - - `initializer() { ... this.XXX ... }` - - `initializer: function() { ... this.XXX ... }` - - `initializer: (module) => { ... module.XXX ... }` - - Typical usage of `initializer` is to initialize specific module properties - and register static callbacks on module events like `CONFIG` and `KILL`. -*/ - -export function Factory(spec) { - const { id, label, title, mainView, configView, initializer } = spec; - - class Plugin extends Module { - constructor() { - super(); - initializer && initializer.apply(this, [this]); - } - renderMain() { return mainView; } - renderConfig() { return configView; } - }; - Plugin.id = id; - Plugin.label = label; - Plugin.title = title; - - return Plugin; -} - -// -------------------------------------------------------------------------- - -export default { Module, Factory, KILL, LOCK, UNLOCK, CONFIG, UPDATE } ; - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/ivette/stores.js b/ivette/backup/ivette/stores.js deleted file mode 100644 index 201516c5d51..00000000000 --- a/ivette/backup/ivette/stores.js +++ /dev/null @@ -1,152 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Items Registry -// -------------------------------------------------------------------------- - -/** @module @ivette/stores */ - -import _ from 'lodash' ; - -const ALL = () => true ; -const IDENT = /^[a-zA-Z0-9_-]+$/ ; -const SUFFIX = /-[0-9]+$/ ; -const PADDING = (n) => n<10 ? "0"+n : n ; - -/** - @summary Base class for registries. - */ -export class Registry -{ - - /** @param {string} prefix - fresh identifier prefix */ - constructor( prefix ) { - this.items = {} ; - this.prefix = prefix ; - } - - /** - @summary Valid identifiers (letters, digits, dashes and underscores). - @return {boolean} `true` if the identifier is valid - */ - isValid(id) { - return typeof(id)=='string' && id.match( IDENT ); - } - - /** - @summary Test for freshness identifier. - @return {boolean} `true` if the identifier is not yet attributed - */ - isFresh(id) { - return !this.items[id] ; - } - - /** - @summary Creates an identifier. - @param {string} [id] - the base identifier to starts with - @return {string} a fresh identifier - @description - Returns a fresh identifier with format `<base>-<n>` where - the base is extracted from `id` (without its `-<n>` suffix, if any). - The default base is the registry prefix, if any. - */ - fresh(id) { - let base = this.prefix ; - if (id) { - let m = SUFFIX.exec(id); - base = m ? id.substring(0,m.index) : id ; - } - base += '-' ; - let kid = 1 ; - for( ;; ) { - var a = base + PADDING(kid++) ; - if (!this.items[a]) break ; - } - return a ; - } - - /** - @summary Iterates ovee the registry. - @param {function} job - called with `job(item,id)` in unspecified order - @description - If the iteratee returns `false`, the iteration is interrupted. - */ - forEach(job) { _.forEach( this.items , (item,id) => job(item,id) ); - } - - /** - @summary Iterates ovee the registry. - @param {function} test - called with `test(item,id)` in unspecified order - @description - Returns the first item such that `test(item,id)` returns a truthy value. - Returns `undefined` otherwise. - */ - find(test) { _.find( this.items , (item,id) => test(item,id) ); } - - /** - Select items from the registry - @param {object} [options] - filter and ordering options - @return {item[]} the selected array of items - @description -Available options are: -- `filter:(item) => boolean` to select some itmes (default: all) -- `sortBy:function | function[] | field | field[]` to sort items by (default: by identifier) - -Sorting is done by loadsh [sortBy](https://lodash.com/docs/4.17.11#sortBy) method. - */ - elements( options ) - { - const filter = (options && options.filter) || ALL ; - const sortBy = (options && options.sortBy) || 'id' ; - const pool = []; - _.forEach( this.items, (item) => { - if (filter(item)) pool.push(item); - }); - return _.sortBy(pool,sortBy); - } - - /** Register a new item - @param {object} - the item to register in - @param {once} - register only fresh items - @description -Each object shall define the following properties: -- `id:string` item identifier (default to `fresh()`) -- `label:string` _optional_ item display name -- `title:string` _optional_ item short description - */ - add( item, once=false ) { - if (!item.id) item.id = this.fresh(); - if (!this.isValid(item.id)) { - console.warn( `[Ivette:${this.prefix}] invalid identifier (${item.id})` ); - return ; - } - if (once && !this.isFresh(item.id)) { - console.warn( `[Ivette:${this.prefix}] duplicate identifier (${item.id})` ); - return ; - } - this.items[item.id] = item ; - } - - /** Retrieve an item by id - @param {string} id - item identifier - @return {object} the associated item - */ - get(id) { return id ? this.items[id] : undefined; } - - /** Remove an item by id - @param {string} id - item identifier - */ - remove( id ) { - if (id && this.items[id]) { - delete this.items[id]; - } - } - - /** Remove all items */ - clear() { - this.items = {}; - } - -} - -export default { Registry }; - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/ivette/views.js b/ivette/backup/ivette/views.js deleted file mode 100644 index 4b80dfc0ee5..00000000000 --- a/ivette/backup/ivette/views.js +++ /dev/null @@ -1,42 +0,0 @@ -// -------------------------------------------------------------------------- -// --- Lab View Component -// -------------------------------------------------------------------------- - -/** @module @ivette/views */ - -import _ from 'lodash' ; -import React from 'react' ; -import { Catch } from 'dome/errors' ; -import { Item } from 'dome/layout/dispatch' ; - -import Ivette from '@ivette' ; - -/** - @class - @summary Defines the plugin toolbar - @description - Shall be defined once inside main plugin rendering. -*/ -export const PluginToolbar = ({children}) => ( - <Item id='ivette.toolbar.display'> - <Catch label='Plugin Toolbar'>{ children }</Catch> - </Item> -); - -/** - @class - @summary Defines the plugin toolbar - @description - Shall be defined once inside main plugin rendering. -*/ -export const PluginSidebar = ({children}) => ( - <Item id='ivette.sidebar.display'> - <Catch label='Plugin Sidevar'>{ children }</Catch> - </Item> -); - -// -------------------------------------------------------------------------- - -export default { PluginSidebar, PluginToolbar }; - -// -------------------------------------------------------------------------- diff --git a/ivette/backup/style.css b/ivette/backup/style.css deleted file mode 100644 index 96b0490f3cd..00000000000 --- a/ivette/backup/style.css +++ /dev/null @@ -1,50 +0,0 @@ -/* -------------------------------------------------------------------------- */ -/* --- Ivette Styling --- */ -/* -------------------------------------------------------------------------- */ - -.ivette-dongle { - flex-wrap: nowrap ; - margin: 8px ; - padding: 6px ; - max-width: 480px ; - background: #ddd ; - border: thin solid darkgrey ; - border-radius: 8px ; -} - -.ivette-dongle-id { - min-width: 80px ; -} - -.ivette-project { - margin: 6px 6px 18px 6px ; - padding: 3px ; -} - -.ivette-dongle-selectable:hover { - background: orange ; -} - -.dome-window-inactive .ivette-dongle { - background: #eee ; -} - -.dome-window-active .ivette-dongle-selected { - background: #ffcd00 ; -} - -.ivette-plugin-item { - margin: 2px ; - padding: 0px 6px ; - background: lightgrey ; - border: thin solid darkgrey ; - border-radius: 10px ; - align-items: baseline ; -} - -.ivette-dongle-cancel { - position: relative ; - visibility: hidden ; - top: 15px ; - height: 18px ; -} -- GitLab