Updating plugin framework.

This commit is contained in:
Marat Fayzullin 2023-12-17 19:31:28 -05:00
parent cd6d80544f
commit a29c8d6ce8
3 changed files with 160 additions and 150 deletions

View File

@ -9,9 +9,9 @@
// Wait for the page to load, then load the plugins.
$(document).ready(function () {
// detect which plugins to load
Plugins._type = (typeof mapManager !== 'undefined') ? 'map' : 'receiver';
Plugins.init();
// detect which plugins to load
Plugins._type = (typeof mapManager !== 'undefined') ? 'map' : 'receiver';
Plugins.init();
});
// Initialize the Plugins class and some defaults
@ -23,114 +23,118 @@ Plugins._enable_debug = false; // print debug to the console
// Load plugin
Plugins.load = async function (name) {
var path = 'static/plugins/' + Plugins._type + "/" + name + "/";
var remote = false;
var path = 'static/plugins/' + Plugins._type + "/" + name + "/";
var remote = false;
// check if we are loading an url
if (name.toLowerCase().startsWith('https://') || name.toLowerCase().startsWith('http://')) {
path = name;
name = path.split('/').pop().split('.').slice(0, -1);
path = path.split('/').slice(0, -1).join('/') + '/';
remote = true;
}
// check if we are loading an url
if (name.toLowerCase().startsWith('https://') || name.toLowerCase().startsWith('http://')) {
path = name;
name = path.split('/').pop().split('.').slice(0, -1);
path = path.split('/').slice(0, -1).join('/') + '/';
remote = true;
}
if (name in Plugins) {
console.warn(Plugins._get_banner(name) + 'is already loaded' + (Plugins[name]._script_loaded ? ' from ' + Plugins[name]._script_loaded : ''));
return;
}
if (name in Plugins) {
console.warn(Plugins._banner(name) + 'is already loaded' + (Plugins[name]._script_loaded ? ' from ' + Plugins[name]._script_loaded : ''));
return;
}
Plugins._debug(Plugins._get_banner(name) + 'loading.');
Plugins._debug(Plugins._banner(name) + 'loading.');
var script_src = path + name + ".js";
var style_src = path + name + '.css';
var script_src = path + name + ".js";
var style_src = path + name + '.css';
// init plugin object with defaults
Plugins[name] = {
_version: 0,
_script_loaded: false,
_style_loaded: false,
_remote: remote,
};
// init plugin object with defaults
Plugins[name] = {
_version : 0,
_script_loaded : false,
_style_loaded : false,
_remote : remote
};
// try to load the plugin
await Plugins._load_script(script_src)
.then(function () {
// plugin script loaded successfully
Plugins[name]._script_loaded = script_src;
// try to load the plugin
await Plugins._load_script(script_src).then(function () {
// plugin script loaded successfully
Plugins[name]._script_loaded = script_src;
// check if the plugin has init() method and execute it
if (typeof Plugins[name].init === 'function') {
if (!Plugins[name].init()) {
console.error(Plugins._get_banner(name) + 'cannot initialize.');
return;
// check if the plugin has init() method and execute it
if (typeof Plugins[name].init === 'function') {
if (!Plugins[name].init()) {
console.error(Plugins._banner(name) + 'cannot initialize.');
return;
}
}
}
// check if plugin has set the 'no_css', otherwise, load the plugin css style
if (!('no_css' in Plugins[name])) {
Plugins._load_style(style_src)
.then(function () {
Plugins[name]._style_loaded = style_src;
Plugins._debug(Plugins._get_banner(name) + 'loaded.');
}).catch(function () {
console.warn(Plugins._get_banner(name) + 'script loaded, but css not found.');
});
} else {
// plugin has no_css
Plugins._debug(Plugins._get_banner(name) + 'loaded.');
}
// check if plugin has set the 'no_css', otherwise, load the plugin css style
if (!('no_css' in Plugins[name])) {
Plugins._load_style(style_src).then(function () {
Plugins[name]._style_loaded = style_src;
Plugins._debug(Plugins._banner(name) + 'loaded.');
}).catch(function () {
console.warn(Plugins._banner(name) + 'script loaded, but css not found.');
});
} else {
// plugin has no_css
Plugins._debug(Plugins._banner(name) + 'loaded.');
}
}).catch(function () {
// plugin cannot be loaded
Plugins._debug(Plugins._get_banner(name) + 'cannot be loaded (does not exist or has errors).');
// plugin cannot be loaded
Plugins._debug(Plugins._banner(name) + 'cannot be loaded (does not exist or has errors).');
});
}
// Check if plugin is loaded
Plugins.isLoaded = function (name, version = 0) {
if (typeof Plugins[name] === 'object')
return (Plugins[name]._script_loaded && Plugins[name]._version >= version);
return false;
if (typeof(Plugins[name]) === 'object') {
return (Plugins[name]._script_loaded && Plugins[name]._version >= version);
}
return false;
}
// Initialize plugin loading. We should load the init.js for the {type}. This init() is called onDomReady.
Plugins.init = function () {
Plugins._debug("Loading " + Plugins._type + " plugins.");
// load the init.js for the {type}... user should load their plugins there.
Plugins._load_script('static/plugins/' + Plugins._type + "/init.js").then(function () {
Plugins._initialized = true;
}).catch(function () {
Plugins._debug('no plugins to load.');
})
Plugins._debug("Loading " + Plugins._type + " plugins.");
// load the init.js for the {type}... user should load their plugins there.
Plugins._load_script('static/plugins/' + Plugins._type + "/init.js").then(function () {
Plugins._initialized = true;
}).catch(function () {
Plugins._debug('no plugins to load.');
})
}
// ---------------------------------------------------------------------------
// internal utility methods
Plugins._load_script = function (src) {
return new Promise(function (resolve, reject) {
var script = document.createElement('script');
script.onload = resolve;
script.onerror = reject;
script.src = src;
document.head.appendChild(script);
});
return new Promise(function (resolve, reject) {
var script = document.createElement('script');
script.onload = resolve;
script.onerror = reject;
script.src = src;
document.head.appendChild(script);
});
}
Plugins._load_style = function (src) {
return new Promise(function (resolve, reject) {
var style = document.createElement('link');
style.onload = resolve;
style.onerror = reject;
style.href = src;
style.type = 'text/css';
style.rel = 'stylesheet';
document.head.appendChild(style);
});
return new Promise(function (resolve, reject) {
var style = document.createElement('link');
style.onload = resolve;
style.onerror = reject;
style.href = src;
style.type = 'text/css';
style.rel = 'stylesheet';
document.head.appendChild(style);
});
}
Plugins._debug = function (msg) {
if (Plugins._enable_debug) console.debug(msg);
if (Plugins._enable_debug) console.debug(msg);
}
Plugins._get_banner = function (name) {
return 'PLUGINS: ' + (Plugins[name] && Plugins[name]._remote ? '[remote]' : '[local]') + ' "' + name + '" ';
Plugins._banner = function (name) {
return 'PLUGINS: ' + (Plugins[name] && Plugins[name]._remote ? '[remote]' : '[local]') + ' "' + name + '" ';
}

View File

@ -2,12 +2,11 @@
// First load the utils, needed for some plugins
Plugins.load('utils').then(function () {
// load local plugins
Plugins.load('example');
Plugins.load('example_theme');
Plugins.load('sort_profiles');
// load local plugins
Plugins.load('example');
Plugins.load('example_theme');
Plugins.load('sort_profiles');
// load remote plugins
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/keyboard_shortcuts/keyboard_shortcuts.js');
// load remote plugins
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/keyboard_shortcuts/keyboard_shortcuts.js');
});

View File

@ -12,7 +12,7 @@ Plugins.utils._version = 0.1;
* Wrap an existing function with before and after callbacks.
* @param {string} name The name of function to wrap with before and after callbacks.
* @param {function(orig, thisArg, args):boolean} before_cb Callback before original. Return true to call the original.
* @param {function(result):void} after_cb Callback after original, will receive the result of original
* @param {function(result, orig, thisArg, args):void} after_cb Callback after original, will receive the result of original
* @param {object} obj [optional] Object to look for function into. Default is 'window'
* @description
* - Before Callback:
@ -24,6 +24,8 @@ Plugins.utils._version = 0.1;
* - After Callback:
* - Params:
* - res: Result of the original function
* - thisArg: local 'this' for the original function
* - args: arguments passed to the original function
*
* @example
* // Using before and after callbacks.
@ -36,7 +38,7 @@ Plugins.utils._version = 0.1;
* }
* return true; // always return true, to call the original function
* },
* function (res) { // after callback
* function (res, thisArg, args) { // after callback
* console.log(res);
* }
* );
@ -51,80 +53,85 @@ Plugins.utils._version = 0.1;
* do_something_after_original(res);
* return false; // to prevent calling the original and after_cb
* },
* function (res) { // after callback
* function (res, thisArg, args) { // after callback
* // ignored
* }
* );
*
*/
Plugins.utils.wrap_func = function (name, before_cb, after_cb, obj = window) {
if (typeof obj[name] !== "function") {
console.error("Cannot wrap non existing function: '" + obj + '.' + name + "'");
return false;
}
var fn_original = obj[name];
var proxy = new Proxy(obj[name], {
apply: function (target, thisArg, args) {
if (before_cb(target, thisArg, args))
after_cb(fn_original.apply(thisArg, args));
if (typeof(obj[name]) !== "function") {
console.error("Cannot wrap non existing function: '" + obj + '.' + name + "'");
return false;
}
});
obj[name] = proxy;
var fn_original = obj[name];
var proxy = new Proxy(obj[name], {
apply: function (target, thisArg, args) {
if (before_cb(target, thisArg, args)) {
after_cb(fn_original.apply(thisArg, args), thisArg, args);
}
}
});
obj[name] = proxy;
}
// Init utils plugin
Plugins.utils.init = function () {
var send_events_for = {};
// trigger some events
var send_events_for = {
'sdr_profile_changed': { // function name to proxy.
name: 'profile_changed', // [optional] event name (prepended with 'event:'). Default is function name.
data: function () { // [optional] data to send with the event (should be function).
return $('#openwebrx-sdr-profiles-listbox').find(':selected').text()
}
},
'on_ws_recv': {
handler: function (orig, thisArg, args) { // if handler exist, it will replace the before_cb
if (typeof args[0].data === 'string' && args[0].data.substr(0, 16) !== "CLIENT DE SERVER") {
try {
var json = JSON.parse(args[0].data);
$(document).trigger('server:' + json.type + ":before", [json['value']]);
} catch (e) {}
// function name to proxy.
send_events_for['sdr_profile_changed'] = {
// [optional] event name (prepended with 'event:'). Default is function name.
name: 'profile_changed',
// [optional] data to send with the event (should be function).
data: function () {
return $('#openwebrx-sdr-profiles-listbox').find(':selected').text()
}
orig.apply(thisArg, args); // we handle original function here
if (typeof json === 'object')
$(document).trigger('server:' + json.type + ":after", [json['value']]);
return false; // do not call the after_cb
}
},
}
};
$.each(send_events_for, function (key, obj) {
Plugins.utils.wrap_func(key,
(typeof (obj.handler) === 'function') ?
obj.handler : function () {
return true;
},
function (res) {
var ev_data;
var ev_name = key;
if (typeof (obj.name) === 'string') ev_name = obj.name;
if (typeof (obj.data) === 'function') ev_data = obj.data(res);
$(document).trigger('event:' + ev_name, [ev_data]);
}
);
});
send_events_for['on_ws_recv'] = {
// if handler exist, it will replace the before_cb
handler: function (orig, thisArg, args) {
if (typeof(args[0].data) === 'string' && args[0].data.substr(0, 16) !== "CLIENT DE SERVER") {
try {
var json = JSON.parse(args[0].data);
$(document).trigger('server:' + json.type + ":before", [json['value']]);
} catch (e) {}
}
var interval = setInterval(function () {
if (typeof (clock) === 'undefined') return;
clearInterval(interval);
$(document).trigger('event:owrx_initialized');
}, 10);
// we handle original function here
orig.apply(thisArg, args);
return true;
if (typeof(json) === 'object') {
$(document).trigger('server:' + json.type + ":after", [json['value']]);
}
// do not call the after_cb
return false;
}
};
$.each(send_events_for, function (key, obj) {
Plugins.utils.wrap_func(
key,
typeof(obj.handler)==='function'? obj.handler : function () { return true; },
function (res) {
var ev_data;
var ev_name = key;
if (typeof(obj.name) === 'string') ev_name = obj.name;
if (typeof(obj.data) === 'function') ev_data = obj.data(res);
$(document).trigger('event:' + ev_name, [ev_data]);
}
);
});
var interval = setInterval(function () {
if (typeof(clock) === 'undefined') return;
clearInterval(interval);
$(document).trigger('event:owrx_initialized');
}, 10);
return true;
}