Merge branch 'master' of https://github.com/luarvique/openwebrx
This commit is contained in:
commit
980bae2c5d
|
|
@ -39,6 +39,7 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9, minimum-scale=0.9, shrink-to-fit=no, user-scalable=no, interactive-widget=overlays-content" />
|
||||
<meta name="theme-color" content="#222" />
|
||||
<script src="static/plugins.js"></script>
|
||||
</head>
|
||||
<body onload="openwebrx_init();">
|
||||
<div id="webrx-page-container">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<script src="compiled/map-google.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="static/css/map.css" />
|
||||
<script src="static/plugins.js"></script>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<script src="compiled/map-leaflet.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="static/css/map.css" />
|
||||
<script src="static/plugins.js"></script>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
/**
|
||||
* OpenWebRx+ Plugin loader
|
||||
*
|
||||
* You should load your plugins in "plugins/{type}/init.js",
|
||||
* where {type} is 'receiver' or 'map'.
|
||||
* See the init.js.sample.
|
||||
* And check the "plugins/{type}/example" folder for example plugin.
|
||||
*/
|
||||
|
||||
// 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();
|
||||
});
|
||||
|
||||
// Initialize the Plugins class and some defaults
|
||||
function Plugins () {}
|
||||
Plugins._initialized = false;
|
||||
Plugins._version = 0.1; // version of the plugin sub-system (keep it float)
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
Plugins._debug(Plugins._get_banner(name) + 'loading.');
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
// 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 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.');
|
||||
}
|
||||
|
||||
}).catch(function () {
|
||||
// plugin cannot be loaded
|
||||
Plugins._debug(Plugins._get_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;
|
||||
}
|
||||
|
||||
// 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.');
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
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);
|
||||
});
|
||||
}
|
||||
Plugins._debug = function (msg) {
|
||||
if (Plugins._enable_debug) console.debug(msg);
|
||||
}
|
||||
Plugins._get_banner = function (name) {
|
||||
return 'PLUGINS: ' + (Plugins[name] && Plugins[name]._remote ? '[remote]' : '[local]') + ' "' + name + '" ';
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* example UI plugin for OpenWebRx+
|
||||
*/
|
||||
|
||||
// Disable CSS loading for this plugin
|
||||
Plugins.example.no_css = true;
|
||||
|
||||
// Init function of the plugin
|
||||
Plugins.example.init = function () {
|
||||
|
||||
// Check if utils plugin is loaded
|
||||
if (!Plugins.isLoaded('utils', 0.1)) {
|
||||
console.error('Example plugin depends on "utils >= 0.1".');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Listen to profile change and print the new profile name to console.
|
||||
// NOTE: you cannot manipulate the data in events, you will need to wrap the original
|
||||
// function if you want to manipulate data.
|
||||
$(document).on('event:profile_changed', function (e, data) {
|
||||
console.log('profile change event: ' + data);
|
||||
});
|
||||
|
||||
// Another events:
|
||||
// event:owrx_initialized - called when OWRX is initialized
|
||||
|
||||
// Server events are triggered when server sends data over the WS
|
||||
// All server events have suffix ':before' or ':after', based on the original functoin call.
|
||||
// :before events are before the original function call,
|
||||
// :after events are after the original function call.
|
||||
// Some interesting server events:
|
||||
// server:config - server configuration
|
||||
// server:bookmarks - the bookmarks from server
|
||||
// server:clients - clients number change
|
||||
// server:profiles - sdr profiles
|
||||
// server:features - supported features
|
||||
|
||||
|
||||
// Modify an existing OWRX function with utils plugin.
|
||||
// See utils.js for documentation on wrap method.
|
||||
// This will wrap profile changing function
|
||||
Plugins.utils.wrap_func(
|
||||
// function to wrap around
|
||||
'sdr_profile_changed',
|
||||
|
||||
// before callback, to be run before the original function
|
||||
// orig = original function
|
||||
// thisArg = thisArg for the original function
|
||||
// args = the arguments for the original function
|
||||
// If you call the original function here (in the before_cb), always return false,
|
||||
// so the wrap_func() will not call it later again.
|
||||
// example of calling the original function: orig.apply(thisArg, args);
|
||||
function (orig, thisArg, args) {
|
||||
console.log("Before callback for: " + orig.name);
|
||||
|
||||
// check if newly selected profile is the PMR profile
|
||||
if ($('#openwebrx-sdr-profiles-listbox').find(':selected').text() === "[RTL] 446 PMR") {
|
||||
// prevent changing to this profile
|
||||
console.log('This profile is disabled by proxy function');
|
||||
|
||||
// restore the previous selected profile
|
||||
$('#openwebrx-sdr-profiles-listbox').val(currentprofile.toString());
|
||||
|
||||
// return false to prevent execution of original function
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true to allow execution of original function
|
||||
return true;
|
||||
},
|
||||
|
||||
// after callback, to be run after the original function,
|
||||
// but only if the before callback returns true
|
||||
// res = result of the original function, if any
|
||||
function (res) {
|
||||
console.log('profile changed.');
|
||||
}
|
||||
);
|
||||
|
||||
// this example will do the same (stop profile changing), but using another method
|
||||
// replace the "onchange" handler of the profiles selectbox
|
||||
// and call the original function "sdr_profile_changed"
|
||||
$('#openwebrx-sdr-profiles-listbox')[0].onchange = function (e) {
|
||||
|
||||
// check the index of the selected profile (0 is the first profile in the list)
|
||||
if (e.target.options.selectedIndex === 0) {
|
||||
// prevent changing to this profile
|
||||
console.log('This profile is disabled by onchange.');
|
||||
|
||||
// restore the previous profile
|
||||
$('#openwebrx-sdr-profiles-listbox').val(currentprofile.toString());
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise, call the original function
|
||||
sdr_profile_changed();
|
||||
};
|
||||
|
||||
// this example will manipulate bookmarks data when the server sends the bookmarks
|
||||
// We will wrap the bookmarks.replace_bookmarks() function, once OWRX is initialized.
|
||||
// We cannot wrap the replace_bookmarks() function before the bookmarks object is created.
|
||||
// So we wait for OWRX to initialize and then wrap the function.
|
||||
$(document).on('event:owrx_initialized', function () {
|
||||
|
||||
// Call the wrap method of utils plugin
|
||||
Plugins.utils.wrap_func(
|
||||
|
||||
// function to wrap
|
||||
'replace_bookmarks',
|
||||
|
||||
// before callback
|
||||
function (orig, thisArg, args) {
|
||||
|
||||
// check if the bookmarks are "server bookmarks"
|
||||
if (args[1] === 'server') {
|
||||
|
||||
// check if we have array of bookmarks (will be empty if the profile has no bookmarks to show)
|
||||
if (typeof (args[0]) === 'object' && args[0].length)
|
||||
|
||||
// replace the name of the first bookmark
|
||||
args[0][0].name = 'replaced';
|
||||
}
|
||||
|
||||
// now call the original function
|
||||
orig.apply(thisArg, args);
|
||||
|
||||
// and return false, so the wrap_func() will not call the original for second time
|
||||
return false;
|
||||
},
|
||||
|
||||
// after callback
|
||||
function (res) {
|
||||
/* not executed because the before function returns false always */
|
||||
},
|
||||
|
||||
// this is the object, where the repalce_boomarks() function should be found
|
||||
bookmarks
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// return true for plugin init()
|
||||
return true;
|
||||
} // end of init function
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* colors for the new theme
|
||||
*/
|
||||
body.theme-eye-piercer {
|
||||
--theme-color1: #ff6262;
|
||||
--theme-color2: #ff626252;
|
||||
--theme-gradient-color1: #ff6262;
|
||||
--theme-gradient-color2: #ff0000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* example plugin, creating a new theme for OpenWebRx+
|
||||
*/
|
||||
|
||||
// Add new entry in the Theme selectbox
|
||||
$('#openwebrx-themes-listbox').append(
|
||||
$('<option>').val(
|
||||
// give it a value. you will need this for the css styles
|
||||
"eye-piercer"
|
||||
).text(
|
||||
// lets name it
|
||||
'Eye-Piercer'
|
||||
)
|
||||
);
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Plugin loader.
|
||||
|
||||
// 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 remote plugins
|
||||
Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/keyboard_shortcuts/keyboard_shortcuts.js');
|
||||
});
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Plugin: sort profiles by name.
|
||||
*/
|
||||
|
||||
// do not load CSS for this plugin
|
||||
Plugins.sort_profiles.no_css = true;
|
||||
|
||||
// Initialize the plugin
|
||||
Plugins.sort_profiles.init = function () {
|
||||
|
||||
// Catch the event, when server sends us the profiles.
|
||||
$(document).on('server:profiles:after', function (e, data) {
|
||||
var sel = $('#openwebrx-sdr-profiles-listbox');
|
||||
|
||||
// if the list is empty, return
|
||||
if (!sel[0] || !sel[0].length)
|
||||
return;
|
||||
|
||||
var selected = sel.val();
|
||||
var list = sel.find('option');
|
||||
|
||||
// sort the list of options, alphanumeric and ignorring the case
|
||||
list.sort(function (a, b) {
|
||||
return $(a).text()
|
||||
.localeCompare(
|
||||
$(b).text(), undefined, {
|
||||
numeric: true,
|
||||
sensitivity: 'base'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// now reset the list and fill it with the new sorted one
|
||||
sel.html('').append(list);
|
||||
|
||||
// set the selected profile from our cached value
|
||||
sel.val(selected);
|
||||
});
|
||||
|
||||
// return true to validate plugin load
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
// This is the utils plugin
|
||||
// It provides function wrapping method
|
||||
// and some events for the rest plugins
|
||||
|
||||
// Disable CSS loading for this plugin
|
||||
Plugins.utils.no_css = true;
|
||||
|
||||
// Utils plugin version
|
||||
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 {object} obj [optional] Object to look for function into. Default is 'window'
|
||||
* @description
|
||||
* - Before Callback:
|
||||
* - Params:
|
||||
* - orig: Original function (in case you want to call it, you have to return false to prevent second calling)
|
||||
* - thisArg: local 'this' for the original function
|
||||
* - args: arguments passed to the original function
|
||||
* - Returns: Boolean. Return false to prevent execution of original function and the after callback.
|
||||
* - After Callback:
|
||||
* - Params:
|
||||
* - res: Result of the original function
|
||||
*
|
||||
* @example
|
||||
* // Using before and after callbacks.
|
||||
* Plugins.utils.wrap_func('sdr_profile_changed',
|
||||
* function (orig, thisArg, args) { // before callback
|
||||
* console.log(orig.name);
|
||||
* if (something_bad)
|
||||
* console.log('This profile is disabled by proxy function');
|
||||
* return false; // return false to prevent the calling of the original function and the after_cb()
|
||||
* }
|
||||
* return true; // always return true, to call the original function
|
||||
* },
|
||||
* function (res) { // after callback
|
||||
* console.log(res);
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* @example
|
||||
* // Using only before callback and handle original.
|
||||
* Plugins.utils.wrap_func('sdr_profile_changed',
|
||||
* function (orig, thisArg, args) { // before callback
|
||||
* // if we need to call the original in the middle of our work
|
||||
* do_something_before_original();
|
||||
* var res = orig.apply(thisArg, args);
|
||||
* do_something_after_original(res);
|
||||
* return false; // to prevent calling the original and after_cb
|
||||
* },
|
||||
* function (res) { // 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));
|
||||
}
|
||||
});
|
||||
obj[name] = proxy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Init utils plugin
|
||||
Plugins.utils.init = function () {
|
||||
|
||||
// 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) {}
|
||||
}
|
||||
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]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
var interval = setInterval(function () {
|
||||
if (typeof (clock) === 'undefined') return;
|
||||
clearInterval(interval);
|
||||
$(document).trigger('event:owrx_initialized');
|
||||
}, 10);
|
||||
|
||||
return true;
|
||||
}
|
||||
Loading…
Reference in New Issue