2019-06-08 08:35:43 +02:00
import { Config } from "data" ;
2019-05-29 05:48:41 +02:00
import ContentManager from "./contentmanager" ;
import Utilities from "./utilities" ;
2019-05-31 07:53:11 +02:00
import { Toasts , Modals } from "ui" ;
2019-06-08 08:35:43 +02:00
import ContentError from "../structs/contenterror" ;
import Settings from "./settingsmanager" ;
import { SettingsPanel as SettingsRenderer } from "ui" ;
const path = require ( "path" ) ;
const electronRemote = require ( "electron" ) . remote ;
export default new class PluginManager extends ContentManager {
get name ( ) { return "PluginManager" ; }
get moduleExtension ( ) { return ".js" ; }
get extension ( ) { return ".plugin.js" ; }
get contentFolder ( ) { return path . resolve ( Config . dataPath , "plugins" ) ; }
get prefix ( ) { return "plugin" ; }
constructor ( ) {
super ( ) ;
this . onSwitch = this . onSwitch . bind ( this ) ;
this . observer = new MutationObserver ( ( mutations ) => {
for ( let i = 0 , mlen = mutations . length ; i < mlen ; i ++ ) {
this . onMutation ( mutations [ i ] ) ;
}
} ) ;
}
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
/* Aliases */
updatePluginList ( ) { return this . updateList ( ) ; }
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
enablePlugin ( idOrContent ) { return this . enableContent ( idOrContent ) ; }
disablePlugin ( idOrContent ) { return this . disableContent ( idOrContent ) ; }
togglePlugin ( id ) { return this . toggleContent ( id ) ; }
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
unloadPlugin ( idOrFileOrContent ) { return this . unloadContent ( idOrFileOrContent ) ; }
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
loadPlugin ( filename ) {
const error = this . loadContent ( filename ) ;
if ( error ) Modals . showContentErrors ( { themes : [ error ] } ) ;
}
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
reloadPlugin ( filename ) {
const error = this . reloadContent ( filename ) ;
if ( error ) Modals . showContentErrors ( { themes : [ error ] } ) ;
}
2019-05-28 20:19:48 +02:00
2019-06-08 08:35:43 +02:00
loadAllPlugins ( ) {
const errors = this . loadAllContent ( ) ;
this . setupFunctions ( ) ;
2019-06-09 22:30:33 +02:00
Settings . registerPanel ( "Plugins" , { element : ( ) => SettingsRenderer . getPluginsPanel ( this . contentList , this . contentFolder ) } ) ;
2019-06-08 08:35:43 +02:00
return errors ;
}
/* Overrides */
initializeContent ( content ) {
if ( ! content . type ) return new ContentError ( content . name , content . filename , "Plugin had no exports" , { message : "Plugin had no exports or no name property." , stack : "" } ) ;
try {
const thePlugin = new content . type ( ) ;
content . plugin = thePlugin ;
2019-06-09 22:30:33 +02:00
content . name = thePlugin . getName ( ) || content . name ;
2019-06-08 08:35:43 +02:00
content . author = content . author || thePlugin . getAuthor ( ) || "No author" ;
content . description = content . description || thePlugin . getDescription ( ) || "No description" ;
content . version = content . version || thePlugin . getVersion ( ) || "No version" ;
2019-05-28 20:19:48 +02:00
try {
2019-06-08 08:35:43 +02:00
if ( typeof ( content . plugin . load ) == "function" ) content . plugin . load ( ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
catch ( error ) {
this . state [ content . id ] = false ;
return new ContentError ( content . name , content . filename , "load() could not be fired." , { message : error . message , stack : error . stack } ) ;
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
catch ( error ) { return new ContentError ( content . name , content . filename , "Could not be constructed." , { message : error . message , stack : error . stack } ) ; }
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
getContentModification ( module , content , meta ) {
module . _compile ( content , module . filename ) ;
const didExport = ! Utilities . isEmpty ( module . exports ) ;
if ( didExport ) {
meta . type = module . exports ;
module . exports = meta ;
return "" ;
}
content += ` \n module.exports = ${ JSON . stringify ( meta ) } ; \n module.exports.type = ${ meta . exports || meta . name } ; ` ;
return content ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
startContent ( id ) { return this . startPlugin ( id ) ; }
stopContent ( id ) { return this . stopPlugin ( id ) ; }
startPlugin ( idOrContent ) {
const content = typeof ( idOrContent ) == "string" ? this . contentList . find ( p => p . id == idOrContent ) : idOrContent ;
if ( ! content ) return ;
const plugin = content . plugin ;
try {
plugin . start ( ) ;
this . emit ( "started" , content . id ) ;
Toasts . show ( ` ${ content . name } v ${ content . version } has started. ` ) ;
}
catch ( err ) {
this . state [ content . id ] = false ;
Toasts . error ( ` ${ content . name } v ${ content . version } could not be started. ` ) ;
Utilities . err ( "Plugins" , content . name + " could not be started." , err ) ;
return new ContentError ( content . name , content . filename , "start() could not be fired." , { message : err . message , stack : err . stack } ) ;
}
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
stopPlugin ( idOrContent ) {
const content = typeof ( idOrContent ) == "string" ? this . contentList . find ( p => p . id == idOrContent ) : idOrContent ;
if ( ! content ) return ;
const plugin = content . plugin ;
try {
plugin . stop ( ) ;
this . emit ( "stopped" , content . id ) ;
Toasts . show ( ` ${ content . name } v ${ content . version } has stopped. ` ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
catch ( err ) {
this . state [ content . id ] = false ;
Toasts . error ( ` ${ content . name } v ${ content . version } could not be stopped. ` ) ;
Utilities . err ( "Plugins" , content . name + " could not be stopped." , err ) ;
return new ContentError ( content . name , content . filename , "stop() could not be fired." , { message : err . message , stack : err . stack } ) ;
}
}
setupFunctions ( ) {
electronRemote . getCurrentWebContents ( ) . on ( "did-navigate-in-page" , this . onSwitch . bind ( this ) ) ;
this . observer . observe ( document , {
childList : true ,
subtree : true
} ) ;
2019-05-28 20:19:48 +02:00
}
2019-06-08 08:35:43 +02:00
onSwitch ( ) {
this . emit ( "page-switch" ) ;
for ( let i = 0 ; i < this . contentList . length ; i ++ ) {
const plugin = this . contentList [ i ] . plugin ;
if ( ! this . state [ this . contentList [ i ] . id ] ) continue ;
if ( typeof ( plugin . onSwitch ) === "function" ) {
try { plugin . onSwitch ( ) ; }
catch ( err ) { Utilities . err ( "Plugins" , "Unable to fire onSwitch for " + this . contentList [ i ] . name + "." , err ) ; }
}
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
onMutation ( mutation ) {
for ( let i = 0 ; i < this . contentList . length ; i ++ ) {
const plugin = this . contentList [ i ] . plugin ;
if ( ! this . state [ this . contentList [ i ] . id ] ) continue ;
if ( typeof plugin . observer === "function" ) {
try { plugin . observer ( mutation ) ; }
catch ( err ) { Utilities . err ( "Plugins" , "Unable to fire observer for " + this . contentList [ i ] . name + "." , err ) ; }
}
2019-05-28 20:19:48 +02:00
}
}
2019-06-08 08:35:43 +02:00
} ;