blockbench/js/plugin_loader.js

513 lines
13 KiB
JavaScript
Raw Normal View History

2017-10-27 01:00:52 +08:00
var onUninstall, onInstall;
2019-01-09 22:54:35 +08:00
const Plugins = {
apipath: 'https://raw.githubusercontent.com/JannisX11/blockbench-plugins/master/plugins.json',
2017-10-27 01:00:52 +08:00
Vue: [], //Vue Object
installed: [], //Simple List of Names
json: undefined, //Json from website
2019-02-04 04:09:35 +08:00
all: [], //Vue Object Data
2019-07-18 00:02:07 +08:00
registered: {},
updateSearch() {
2017-10-27 01:00:52 +08:00
Plugins.Vue._data.showAll = !Plugins.Vue._data.showAll
Plugins.Vue._data.showAll = !Plugins.Vue._data.showAll
},
2019-07-18 00:02:07 +08:00
devReload() {
2018-03-29 02:48:11 +08:00
var reloads = 0;
2019-02-04 04:09:35 +08:00
for (var i = Plugins.all.length-1; i >= 0; i--) {
2020-04-26 02:25:07 +08:00
if (Plugins.all[i].source == 'file') {
2019-02-04 04:09:35 +08:00
Plugins.all[i].reload()
2018-03-29 02:48:11 +08:00
reloads++;
2017-10-27 01:00:52 +08:00
}
2019-02-04 04:09:35 +08:00
}
Blockbench.showQuickMessage(tl('message.plugin_reload', [reloads]))
2018-03-29 02:48:11 +08:00
console.log('Reloaded '+reloads+ ' plugin'+pluralS(reloads))
2019-07-18 00:02:07 +08:00
},
sort() {
Plugins.all.sort(function(a,b) {
return sort_collator.compare(a.title, b.title)
});
2017-10-27 01:00:52 +08:00
}
}
2020-04-26 02:25:07 +08:00
StateMemory.init('installed_plugins', 'array')
Plugins.installed = StateMemory.installed_plugins;
2017-10-27 01:00:52 +08:00
2019-02-04 04:09:35 +08:00
class Plugin {
constructor(id, data) {
2019-07-18 00:02:07 +08:00
this.id = id||'unknown';
2019-02-04 04:09:35 +08:00
this.installed = false;
this.expanded = false;
this.title = '';
this.author = '';
this.description = '';
this.about = '';
this.icon = '';
this.variant = '';
this.min_version = '';
2020-10-12 01:53:22 +08:00
this.max_version = '';
2020-04-26 02:25:07 +08:00
this.source = 'store'
2019-07-18 00:02:07 +08:00
this.extend(data)
Plugins.all.safePush(this);
2019-02-04 04:09:35 +08:00
}
extend(data) {
2019-07-18 00:02:07 +08:00
if (!(data instanceof Object)) return this;
2019-02-04 04:09:35 +08:00
Merge.boolean(this, data, 'installed')
Merge.boolean(this, data, 'expanded')
Merge.string(this, data, 'title')
Merge.string(this, data, 'author')
Merge.string(this, data, 'description')
Merge.string(this, data, 'about')
Merge.string(this, data, 'icon')
Merge.string(this, data, 'variant')
Merge.string(this, data, 'min_version')
2020-10-12 01:53:22 +08:00
Merge.string(this, data, 'max_version')
2019-07-18 00:02:07 +08:00
Merge.function(this, data, 'onload')
Merge.function(this, data, 'onunload')
Merge.function(this, data, 'oninstall')
Merge.function(this, data, 'onuninstall')
2019-02-04 04:09:35 +08:00
return this;
}
async install(first, cb) {
2019-02-04 04:09:35 +08:00
var scope = this;
2019-07-18 00:02:07 +08:00
Plugins.registered[this.id] = this;
return await new Promise((resolve, reject) => {
$.getScript(Plugins.path + scope.id + '.js', function() {
if (cb) cb.bind(scope)()
scope.bindGlobalData(first)
if (first && scope.oninstall) {
scope.oninstall()
}
resolve()
}).fail(function() {
if (isApp) {
console.log('Could not find file of plugin "'+scope.id+'". Uninstalling it instead.')
scope.uninstall()
reject()
}
})
this.remember()
scope.installed = true;
2019-02-04 04:09:35 +08:00
})
}
2019-07-18 00:02:07 +08:00
bindGlobalData() {
2019-02-04 04:09:35 +08:00
var scope = this;
if (onUninstall) {
2019-07-18 00:02:07 +08:00
scope.onuninstall = onUninstall
2019-02-04 04:09:35 +08:00
}
2019-07-18 00:02:07 +08:00
if (onUninstall) {
scope.onuninstall = onUninstall
2019-02-04 04:09:35 +08:00
}
window.onInstall = window.onUninstall = window.plugin_data = undefined
return this;
}
async download(first) {
2019-02-04 04:09:35 +08:00
var scope = this;
if (!isApp) {
return await scope.install()
2019-02-04 04:09:35 +08:00
}
return await new Promise((resolve, reject) => {
var file = originalFs.createWriteStream(Plugins.path+this.id+'.js')
var request = https.get('https://raw.githubusercontent.com/JannisX11/blockbench-plugins/master/plugins/'+this.id+'.js', function(response) {
response.pipe(file);
response.on('end', function() {
setTimeout(async function() {
await scope.install(first);
resolve()
}, 50)
})
});
2019-02-04 04:09:35 +08:00
});
return this;
}
async loadFromFile(file, first) {
2019-02-04 04:09:35 +08:00
var scope = this;
2019-07-19 23:31:22 +08:00
if (!isApp && !first) return this;
2019-07-18 00:02:07 +08:00
if (first) {
2019-02-04 04:09:35 +08:00
if (isApp) {
if (!confirm(tl('message.load_plugin_app'))) return;
} else {
if (!confirm(tl('message.load_plugin_web'))) return;
}
}
2019-07-19 23:31:22 +08:00
scope.id = pathToName(file.path)
Plugins.registered[this.id] = this;
localStorage.setItem('plugin_dev_path', file.path)
Plugins.all.safePush(this)
2020-04-26 02:25:07 +08:00
scope.source = 'file'
return await new Promise((resolve, reject) => {
if (isApp) {
$.getScript(file.path, () => {
if (window.plugin_data) {
scope.id = (plugin_data && plugin_data.id)||pathToName(file.path)
scope.extend(plugin_data)
scope.bindGlobalData()
}
scope.installed = true
scope.path = file.path
this.remember()
Plugins.sort()
resolve()
}).fail(reject)
} else {
try {
eval(file.content);
} catch (err) {
reject(err)
}
if (!Plugins.registered && window.plugin_data) {
scope.id = (plugin_data && plugin_data.id)||scope.id
2019-07-19 23:31:22 +08:00
scope.extend(plugin_data)
scope.bindGlobalData()
}
scope.installed = true
2020-04-26 02:25:07 +08:00
this.remember()
2019-07-19 23:31:22 +08:00
Plugins.sort()
resolve()
2019-07-19 23:31:22 +08:00
}
})
2019-02-04 04:09:35 +08:00
}
2020-04-26 02:25:07 +08:00
loadFromURL(url, first) {
if (first) {
if (isApp) {
if (!confirm(tl('message.load_plugin_app'))) return;
} else {
if (!confirm(tl('message.load_plugin_web'))) return;
}
}
this.id = pathToName(url)
Plugins.registered[this.id] = this;
localStorage.setItem('plugin_dev_path', url)
Plugins.all.safePush(this)
this.source = 'url';
$.getScript(url, () => {
if (window.plugin_data) {
this.id = (plugin_data && plugin_data.id)||pathToName(url)
this.extend(plugin_data)
this.bindGlobalData()
}
this.installed = true
this.path = url
this.remember()
Plugins.sort()
})
return this;
}
remember(id = this.id, path = this.path) {
if (Plugins.installed.find(plugin => plugin.id == this.id)) {
return this;
}
Plugins.installed.push({
id: id,
path: path,
source: this.source
})
StateMemory.save('installed_plugins')
return this;
}
2019-02-04 04:09:35 +08:00
uninstall() {
2019-12-18 00:44:27 +08:00
try {
this.unload();
if (this.onuninstall) {
this.onuninstall();
}
} catch (err) {
console.log('Error in unload or uninstall method: ', err);
2019-07-18 00:02:07 +08:00
}
delete Plugins.registered[this.id];
2020-04-26 02:25:07 +08:00
let in_installed = Plugins.installed.find(plugin => plugin.id == this.id);
Plugins.installed.remove(in_installed);
StateMemory.save('installed_plugins')
2019-07-18 00:02:07 +08:00
this.installed = false;
2020-04-26 02:25:07 +08:00
if (isApp && this.source !== 'store') {
2019-02-04 04:09:35 +08:00
Plugins.all.remove(this)
2019-07-18 00:02:07 +08:00
} else if (isApp) {
2020-04-26 02:25:07 +08:00
var filepath = Plugins.path + this.id + '.js'
2019-07-18 00:02:07 +08:00
if (fs.existsSync(filepath)) {
fs.unlink(filepath, (err) => {
if (err) {
console.log(err);
}
});
2019-02-04 04:09:35 +08:00
}
}
2020-04-26 02:25:07 +08:00
StateMemory.save('installed_plugins')
2019-02-04 04:09:35 +08:00
return this;
}
2019-07-18 00:02:07 +08:00
unload() {
if (this.onunload) {
this.onunload()
}
return this;
}
2019-02-04 04:09:35 +08:00
reload() {
2020-04-26 02:25:07 +08:00
if (!isApp && this.source == 'file') return this;
2019-07-18 00:02:07 +08:00
this.unload()
Plugins.all.remove(this)
2020-04-26 02:25:07 +08:00
if (this.source == 'file') {
this.loadFromFile({path: this.path}, false)
} else if (this.source == 'url') {
this.loadFromURL(this.path, false)
}
2019-02-04 04:09:35 +08:00
return this;
}
2020-04-26 02:25:07 +08:00
isReloadable() {
return (this.source == 'file' && isApp) || (this.source == 'url')
}
2019-02-04 04:09:35 +08:00
isInstallable() {
var scope = this;
var result =
scope.variant === 'both' ||
(
isApp === (scope.variant === 'desktop') &&
isApp !== (scope.variant === 'web')
);
if (result && scope.min_version) {
2020-10-12 01:53:22 +08:00
result = Blockbench.isOlderThan(scope.min_version) ? 'outdated_client' : true;
}
if (result && scope.max_version) {
result = Blockbench.isNewerThan(scope.max_version) ? 'outdated_plugin' : true
}
if (result === false) {
2019-02-04 04:09:35 +08:00
result = (scope.variant === 'web') ? 'web_only' : 'app_only'
}
return (result === true) ? true : tl('dialog.plugins.'+result);
}
toggleInfo(force) {
var scope = this;
Plugins.all.forEach(function(p) {
if (p !== scope && p.expanded) p.expanded = false;
})
if (force !== undefined) {
this.expanded = force === true
} else {
this.expanded = this.expanded !== true
}
}
get expandicon() {
return this.expanded ? 'expand_less' : 'expand_more'
}
}
2019-07-18 00:02:07 +08:00
Plugin.register = function(id, data) {
if (typeof id !== 'string' || typeof data !== 'object') {
console.warn('Plugin.register: not enough arguments, string and object required.')
return;
}
var plugin = Plugins.registered[id];
if (!plugin) {
plugin = Plugins.registered.unknown;
if (plugin) {
delete Plugins.registered.unknown;
plugin.id = id;
Plugins.registered[id] = plugin;
}
}
if (!plugin) return;
plugin.extend(data)
2019-08-18 00:26:14 +08:00
if (data.icon) plugin.icon = Blockbench.getIconNode(data.icon)
2020-10-12 01:53:22 +08:00
if (plugin.isInstallable() == true) {
if (plugin.onload instanceof Function) {
plugin.onload()
}
2019-07-18 00:02:07 +08:00
}
return plugin;
}
2019-02-04 04:09:35 +08:00
2017-10-27 01:00:52 +08:00
if (isApp) {
2019-01-09 22:54:35 +08:00
Plugins.path = app.getPath('userData')+osfs+'plugins'+osfs
2017-10-27 01:00:52 +08:00
fs.readdir(Plugins.path, function(err) {
2018-10-18 01:50:25 +08:00
if (err) {
fs.mkdir(Plugins.path, function(a) {})
}
2017-10-27 01:00:52 +08:00
})
2019-02-04 04:09:35 +08:00
} else {
Plugins.path = 'https://cdn.jsdelivr.net/gh/JannisX11/blockbench-plugins/plugins/';
2017-10-27 01:00:52 +08:00
}
Plugins.loading_promise = new Promise((resolve, reject) => {
$.getJSON(Plugins.apipath, function(data) {
Plugins.json = data
resolve();
Plugins.loading_promise.resolved = true;
}).fail(function() {
console.log('Could not connect to plugin server')
$('#plugin_available_empty').text('Could not connect to plugin server')
resolve();
Plugins.loading_promise.resolved = true;
})
2017-10-27 01:00:52 +08:00
})
async function loadInstalledPlugins() {
if (!Plugins.loading_promise.resolved) {
await Plugins.loading_promise;
2019-02-04 04:09:35 +08:00
}
const install_promises = [];
2020-04-26 02:25:07 +08:00
if (localStorage.getItem('installed_plugins')) {
var legacy_plugins = JSON.parse(localStorage.getItem('installed_plugins'))
if (legacy_plugins instanceof Array) {
legacy_plugins.forEach((string, i) => {
if (typeof string == 'string') {
if (string.match(/\.js$/)) {
Plugins.installed[i] = {
id: string.split(/[\\/]/).last().replace(/\.js$/, ''),
path: string,
source: 'file'
}
} else {
Plugins.installed[i] = {
id: string,
source: 'store'
}
}
}
})
}
StateMemory.save('installed_plugins')
localStorage.removeItem('installed_plugins')
2017-10-27 01:00:52 +08:00
}
2020-04-30 05:35:47 +08:00
Plugins.installed.replace(Plugins.installed.filter(p => p !== null))
2020-04-26 02:25:07 +08:00
if (Plugins.json instanceof Object) {
2019-02-04 04:09:35 +08:00
//From Store
2017-10-27 01:00:52 +08:00
for (var id in Plugins.json) {
2019-02-04 04:09:35 +08:00
var plugin = new Plugin(id, Plugins.json[id])
2020-04-30 05:35:47 +08:00
if (Plugins.installed.find(p => {
return p && p.id == id && p.source == 'store'
2020-04-26 02:25:07 +08:00
})) {
install_promises.push(plugin.download())
2017-10-27 01:00:52 +08:00
}
}
2019-07-18 00:02:07 +08:00
Plugins.sort();
2019-02-04 04:09:35 +08:00
} else if (Plugins.installed.length > 0 && isApp) {
2019-07-18 00:02:07 +08:00
//Offline
2020-04-26 02:25:07 +08:00
Plugins.installed.forEach(function(plugin) {
2019-02-04 04:09:35 +08:00
2020-04-26 02:25:07 +08:00
if (plugin.source == 'store') {
var promise = new Plugin(plugin.id).install(false, function() {
2019-07-19 23:31:22 +08:00
this.extend(window.plugin_data)
2019-07-18 00:02:07 +08:00
Plugins.sort()
2019-02-04 04:09:35 +08:00
})
install_promises.push(promise);
2019-02-04 04:09:35 +08:00
}
2017-10-27 01:00:52 +08:00
})
}
if (Plugins.installed.length > 0) {
2019-04-08 00:53:33 +08:00
var loaded = []
2020-04-26 02:25:07 +08:00
Plugins.installed.forEachReverse(function(plugin) {
2019-02-04 04:09:35 +08:00
2020-04-26 02:25:07 +08:00
if (plugin.source == 'file') {
2019-02-04 04:09:35 +08:00
//Dev Plugins
2020-04-26 02:25:07 +08:00
if (isApp && fs.existsSync(plugin.path)) {
var instance = new Plugin(plugin.id);
install_promises.push(instance.loadFromFile({path: plugin.path}, false));
2020-04-26 02:25:07 +08:00
loaded.push('Local: '+ plugin.id || plugin.path)
2019-07-18 00:02:07 +08:00
} else {
2020-04-26 02:25:07 +08:00
Plugins.installed.remove(plugin)
2019-07-18 00:02:07 +08:00
}
2020-04-26 02:25:07 +08:00
} else if (plugin.source == 'url') {
var instance = new Plugin(plugin.id);
install_promises.push(instance.loadFromFile({path: plugin.path}, false));
2020-04-26 02:25:07 +08:00
loaded.push('URL: '+ plugin.id || plugin.path)
} else {
loaded.push('Store: '+ plugin.id)
2019-02-04 04:09:35 +08:00
}
})
2019-04-08 00:53:33 +08:00
console.log(`Loaded ${loaded.length} plugin${pluralS(loaded.length)}`, loaded)
2017-10-27 01:00:52 +08:00
}
2020-04-26 02:25:07 +08:00
StateMemory.save('installed_plugins')
2017-10-27 01:00:52 +08:00
Plugins.Vue = new Vue({
el: '#plugin_list',
data: {
showAll: false,
2019-02-04 04:09:35 +08:00
items: Plugins.all
2017-10-27 01:00:52 +08:00
},
computed: {
2019-02-04 04:09:35 +08:00
plugin_search() {
2017-10-27 01:00:52 +08:00
var name = $('#plugin_search_bar').val().toUpperCase()
return this.items.filter(item => {
2018-03-29 02:48:11 +08:00
if (this.showAll !== item.installed) {
2017-10-27 01:00:52 +08:00
if (name.length > 0) {
return (
item.id.toUpperCase().includes(name) ||
item.title.toUpperCase().includes(name) ||
item.description.toUpperCase().includes(name) ||
item.author.toUpperCase().includes(name)
)
}
return true;
}
return false;
})
}
}
})
install_promises.forEach(promise => {
promise.catch(console.error);
})
return await Promise.allSettled(install_promises);
2017-10-27 01:00:52 +08:00
}
function switchPluginTabs(installed) {
2019-08-18 00:26:14 +08:00
$('#plugins .tab_bar > .open').removeClass('open')
2017-10-27 01:00:52 +08:00
if (installed) {
$('#installed_plugins').addClass('open')
Plugins.Vue._data.showAll = false
} else {
$('#all_plugins').addClass('open')
Plugins.Vue._data.showAll = true
}
2018-11-12 04:19:08 +08:00
}
BARS.defineActions(function() {
2019-08-18 00:26:14 +08:00
new Action('plugins_window', {
2018-12-27 21:03:04 +08:00
icon: 'extension',
category: 'blockbench',
click: function () {
showDialog('plugins')
$('#plugin_list').css('max-height', limitNumber($(window).height()-300, 80, 600)+'px')
}
})
2019-08-18 00:26:14 +08:00
new Action('reload_plugins', {
2019-02-04 04:09:35 +08:00
icon: 'sync',
category: 'blockbench',
keybind: new Keybind({ctrl: true, key: 74}),
click: function () {
Plugins.devReload()
}
})
2019-08-18 00:26:14 +08:00
new Action('load_plugin', {
2019-07-18 00:02:07 +08:00
icon: 'fa-file-code',
2018-11-12 04:19:08 +08:00
category: 'blockbench',
click: function () {
Blockbench.import({
2020-04-26 02:25:07 +08:00
resource_id: 'dev_plugin',
2019-02-04 04:09:35 +08:00
extensions: ['js'],
2018-11-12 04:19:08 +08:00
type: 'Blockbench Plugin',
}, function(files) {
2020-04-26 02:25:07 +08:00
new Plugin().loadFromFile(files[0], true)
})
}
})
new Action('load_plugin_from_url', {
icon: 'cloud_download',
category: 'blockbench',
click: function () {
Blockbench.textPrompt('URL', '', url => {
new Plugin().loadFromURL(url, true)
2018-11-12 04:19:08 +08:00
})
}
})
})