Skip to content

Commit

Permalink
Merge pull request #205 from johnd0e/plugins-priority
Browse files Browse the repository at this point in the history
Implement plugins priority control

Improvements related to #76:

* Plugin priority order is now controlled.
  Priority can be specified as `setup` property (see details in commit msg).
* On error in setup:
   - show plugin name in log
   - strike-through plugin name in About window
* `pluginCreateHook` function deprecated,
  it's now possible to use hooks without explicit creation.
* Related fixes and refactoring
  • Loading branch information
johnd0e committed Oct 17, 2019
2 parents 1d03f7b + 7c63c82 commit 60235ea
Show file tree
Hide file tree
Showing 15 changed files with 191 additions and 185 deletions.
113 changes: 71 additions & 42 deletions code/boot.js
Expand Up @@ -667,13 +667,82 @@ window.extendLeaflet = function() {

// BOOTING ///////////////////////////////////////////////////////////

function prepPluginsToLoad() {

var priorities = {
lowest: 100,
low: 75,
normal: 50,
high: 25,
highest: 0,
boot: -100
}

function getPriority (data) {
var v = data && data.priority || 'normal';
var prio = priorities[v] || v;
if (typeof prio !== 'number') {
console.warn('wrong plugin priority specified: ', v);
prio = priorities.normal;
}
return prio;
}

// executes setup function of plugin
// and collects info for About IITC
function safeSetup (setup) {
if (!setup) {
console.warn('plugin must provide setup function');
return;
}
var info = setup.info;
if (typeof info !== 'object' || typeof info.script !== 'object' || typeof info.script.name !== 'string') {
console.warn('plugin does not have proper wrapper:',setup);
info = { script: {} };
}

try {
setup.call(this);
} catch (err) {
var name = info.script.name || '<unknown>';
console.error('error starting plugin: ' + name + ', error: ' + err);
info.error = err;
}
pluginsInfo.push(info);
}

if (window.bootPlugins) { // sort plugins by priority
bootPlugins.sort(function (a,b) {
return getPriority(a) - getPriority(b);
});
} else {
window.bootPlugins = [];
}

var pluginsInfo = []; // for About IITC
bootPlugins.info = pluginsInfo;

// loader function returned
// if called with parameter then load plugins with priorities up to specified
return function (prio) {
while (bootPlugins[0]) {
if (prio && getPriority(bootPlugins[0]) > priorities[prio]) { break; }
safeSetup(bootPlugins.shift());
}
};
}

function boot() {
if(!isSmartphone()) // TODO remove completely?
window.debug.console.overwriteNativeIfRequired();

console.log('loading done, booting. Built: @@BUILDDATE@@');
if(window.deviceID) console.log('Your device ID: ' + window.deviceID);
window.runOnSmartphonesBeforeBoot();

var loadPlugins = prepPluginsToLoad();
loadPlugins('boot');

window.extendLeaflet();
window.extractFromStock();
window.setupIdle();
Expand Down Expand Up @@ -709,46 +778,7 @@ function boot() {

$('#sidebar').show();

if(window.bootPlugins) {
// check to see if a known 'bad' plugin is installed. If so, alert the user, and don't boot any plugins
var badPlugins = {
'arc': 'Contains hidden code to report private data to a 3rd party server: <a href="https://plus.google.com/105383756361375410867/posts/4b2EjP3Du42">details here</a>',
};

// remove entries from badPlugins which are not installed
$.each(badPlugins, function(name,desc) {
if (!(window.plugin && window.plugin[name])) {
// not detected: delete from the list
delete badPlugins[name];
}
});

// if any entries remain in the list, report this to the user and don't boot ANY plugins
// (why not any? it's tricky to know which of the plugin boot entries were safe/unsafe)
if (Object.keys(badPlugins).length > 0) {
var warning = 'One or more known unsafe plugins were detected. For your safety, IITC has disabled all plugins.<ul>';
$.each(badPlugins,function(name,desc) {
warning += '<li><b>'+name+'</b>: '+desc+'</li>';
});
warning += '</ul><p>Please uninstall the problem plugins and reload the page. See this <a href="https://iitc.modos189.ru/faq.html">FAQ entry</a> for help.</p><p><i>Note: It is tricky for IITC to safely disable just problem plugins</i></p>';

dialog({
title: 'Plugin Warning',
html: warning,
width: 400
});
} else {
// no known unsafe plugins detected - boot all plugins
$.each(window.bootPlugins, function(ind, ref) {
try {
ref();
} catch(err) {
console.error("error starting plugin: index "+ind+", error: "+err);
debugger;
}
});
}
}
loadPlugins();

window.setMapBaseLayer();
window.setupLayerChooserApi();
Expand All @@ -761,8 +791,7 @@ function boot() {
window.iitcLoaded = true;
window.runHooks('iitcLoaded');


if (typeof android !== 'undefined' && android && android.bootFinished) {
if (typeof android !== 'undefined' && android.bootFinished) {
android.bootFinished();
}

Expand Down
28 changes: 5 additions & 23 deletions code/hooks.js
Expand Up @@ -57,21 +57,14 @@
// by plugins
// artifactsUpdated: called when the set of artifacts (including targets)
// has changed. Parameters names are old, new.
// nicknameClicked:
// geoSearch:
// search:

window._hooks = {}
window.VALID_HOOKS = [
'portalSelected', 'portalDetailsUpdated', 'artifactsUpdated',
'mapDataRefreshStart', 'mapDataEntityInject', 'mapDataRefreshEnd',
'portalAdded', 'linkAdded', 'fieldAdded',
'portalRemoved', 'linkRemoved', 'fieldRemoved',
'publicChatDataAvailable', 'factionChatDataAvailable',
'requestFinished', 'nicknameClicked',
'geoSearch', 'search', 'iitcLoaded',
'portalDetailLoaded', 'paneChanged'];
window.VALID_HOOKS = []; // stub for compatibility

window.runHooks = function(event, data) {
if(VALID_HOOKS.indexOf(event) === -1) throw('Unknown event type: ' + event);

if(!_hooks[event]) return true;
var interrupted = false;
$.each(_hooks[event], function(ind, callback) {
Expand All @@ -88,20 +81,9 @@ window.runHooks = function(event, data) {
return !interrupted;
}

// helper method to allow plugins to create new hooks
window.pluginCreateHook = function(event) {
if($.inArray(event, window.VALID_HOOKS) < 0) {
window.VALID_HOOKS.push(event);
}
}

window.pluginCreateHook = function() {}; // stub for compatibility

window.addHook = function(event, callback) {
if(VALID_HOOKS.indexOf(event) === -1) {
console.error('addHook: Unknown event type: ' + event + ' - ignoring');
debugger;
return;
}

if (typeof callback !== 'function') {
throw new Error('Callback must be a function.');
Expand Down
120 changes: 73 additions & 47 deletions code/utils_misc.js
@@ -1,62 +1,88 @@
// UTILS + MISC ///////////////////////////////////////////////////////

window.aboutIITC = function() {
var v = (script_info.script && script_info.script.version || script_info.dateTimeVersion) + ' ['+script_info.buildName+']';
if (typeof android !== 'undefined' && android && android.getVersionName) {
v += '[IITC Mobile '+android.getVersionName()+']';
}

if (window.bootPlugins) {
var plugins = '<ul>';
for(var i in bootPlugins) {
var info = bootPlugins[ i ].info;
if(info) {
var pname = info.script && info.script.name || info.pluginId;
if(pname.substr(0, 13) == 'IITC plugin: ' || pname.substr(0, 13) == 'IITC Plugin: ') {
pname = pname.substr(13);
}
var pvers = info.script && info.script.version || info.dateTimeVersion;

var ptext = pname + ' - ' + pvers;
if(info.buildName != script_info.buildName) {
ptext += ' [' + (info.buildName || '<i>non-standard plugin</i>') + ']';
}

plugins += '<li>' + ptext + '</li>';
var pluginsInfo = window.bootPlugins.info;
var iitc = script_info;
var iitcVersion = iitc.script.version + ' [' + iitc.buildName + ']';
var verRE = /^([\d.]+?)(?:\.(2\d{7}\.\d*))?$/;
var descRE = /^\[.+?\-2\d\d\d\-\d\d\-\d\d\-\d+\] /; // regexps to match 'garbage' parts
function prepData (info,idx) {
var data = {
build: info.buildName ? ' [' + info.buildName + ']' : '',
date: '',
error: info.error,
standard: info.buildName === iitc.buildName && info.dateTimeVersion === iitc.dateTimeVersion,
version: ''
};
var script = info.script;
if (!script.name) {
data.name = '[unknown plugin: index ' + idx + ']';
data.description = "this plugin does not have proper wrapper; report to it's author";
return data;
}
data.name = script.name.substring(0, 13) === 'IITC plugin: '
? script.name.substring(13)
: script.name;
data.description = script.description
? script.description.replace(descRE,'') // remove buildname component from description
: '';
if (script.version) {
var ver = script.version.match(verRE);
if (ver) {
data.version = ver[1] || ver[2]; // remove date component from version
data.date = ver[1] && ver[2] || '';
} else {
// no 'info' property of the plugin setup function - old plugin wrapper code
// could attempt to find the "window.plugin.NAME = function() {};" line it's likely to have..?
plugins += '<li>(unknown plugin: index ' + i + ')</li>';
data.version = script.version;
}
}
plugins += '</ul>';
return data;
}

var a = ''
+ ' <div><b>About IITC</b></div> '
+ ' <div>Ingress Intel Total Conversion</div> '
+ ' <hr>'
+ ' <div>'
+ ' <a href="https://iitc.modos189.ru/" target="_blank">IITC Homepage</a> |' +
' <a href="https://t.me/iitc_news" target="_blank">Telegram channel</a><br />'
+ ' On the script’s homepage you can:'
+ ' <ul>'
+ ' <li>Find Updates</li>'
+ ' <li>Get Plugins</li>'
+ ' <li>Report Bugs</li>'
+ ' <li>Contribute!</li>'
+ ' </ul>'
+ ' </div>'
+ ' <hr>'
+ ' <div>Version: ' + v + '</div>';
if (window.bootPlugins && window.bootPlugins.length > 0) {
a += ' <div>Plugins: ' + plugins + '</div>';
var plugins = pluginsInfo.map(prepData)
.sort(function (a,b) { return a.name > b.name ? 1 : -1; })
.map(function (p) {
p.style = '';
p.title = p.description;
if (p.error) {
p.style += 'text-decoration:line-through;';
p.title = p.error;
} else if (p.standard) {
p.style += 'color:darkgray;';
}
p.sep = p.version ? ' - ' : '';
return L.Util.template('<li style="{style}" title="{title}">{name}{sep}<code title="{date}{build}">{version}</code></li>', p);
})
.join('\n');

var html = ''
+ '<div><b>About IITC</b></div> '
+ '<div>Ingress Intel Total Conversion</div> '
+ '<hr>'
+ '<div>'
+ ' <a href="https://iitc.modos189.ru/" target="_blank">IITC Homepage</a> |' +
' <a href="https://t.me/iitc_news" target="_blank">Telegram channel</a><br />'
+ ' On the script’s homepage you can:'
+ ' <ul>'
+ ' <li>Find Updates</li>'
+ ' <li>Get Plugins</li>'
+ ' <li>Report Bugs</li>'
+ ' <li>Contribute!</li>'
+ ' </ul>'
+ '</div>'
+ '<hr>'
+ '<div>Version: ' + iitcVersion + '</div>';

if (typeof android !== 'undefined' && android.getVersionName) {
html += '<div>IITC Mobile ' + android.getVersionName() + '</div>';
}
if (plugins) {
html += '<div><p>Plugins:</p><ul>' + plugins + '</ul></div>';
}

dialog({
title: 'IITC ' + v,
title: 'IITC ' + iitcVersion,
id: 'iitc-about',
html: a,
html: html,
dialogClass: 'ui-dialog-aboutIITC'
});
}
Expand Down
21 changes: 8 additions & 13 deletions plugins/bookmarks.user.js
Expand Up @@ -1245,12 +1245,12 @@
var setup = function() {
window.plugin.bookmarks.isSmart = window.isSmartphone();

// Fired when a bookmarks/folder is removed, added or sorted, also when a folder is opened/closed.
if($.inArray('pluginBkmrksEdit', window.VALID_HOOKS) < 0) { window.VALID_HOOKS.push('pluginBkmrksEdit'); }
// Fired when the "Bookmarks Options" panel is opened (you can add new options);
if($.inArray('pluginBkmrksOpenOpt', window.VALID_HOOKS) < 0) { window.VALID_HOOKS.push('pluginBkmrksOpenOpt'); }
// Fired when the sync is finished;
if($.inArray('pluginBkmrksSyncEnd', window.VALID_HOOKS) < 0) { window.VALID_HOOKS.push('pluginBkmrksSyncEnd'); }
// HOOKS:
// - pluginBkmrksEdit: fired when a bookmarks/folder is removed, added or sorted,
// also when a folder is opened/closed.
// - pluginBkmrksOpenOpt: fired when the "Bookmarks Options" panel is opened
// (you can add new options);
// - pluginBkmrksSyncEnd: fired when the sync is finished;

// If the storage not exists or is a old version
window.plugin.bookmarks.createStorage();
Expand Down Expand Up @@ -1294,7 +1294,7 @@

// Sync
window.addHook('pluginBkmrksEdit', window.plugin.bookmarks.syncBkmrks);
window.addHook('iitcLoaded', window.plugin.bookmarks.registerFieldForSyncing);
window.plugin.bookmarks.registerFieldForSyncing();

// Highlighter - bookmarked portals
window.addHook('pluginBkmrksEdit', window.plugin.bookmarks.highlightRefresh);
Expand All @@ -1308,13 +1308,8 @@
window.addHook('pluginBkmrksEdit', window.plugin.bookmarks.editStar);
window.addHook('pluginBkmrksSyncEnd', window.plugin.bookmarks.resetAllStars);

if(window.plugin.portalslist) {
if (window.plugin.portalslist) {
window.plugin.bookmarks.setupPortalsList();
} else {
setTimeout(function() {
if(window.plugin.portalslist)
window.plugin.bookmarks.setupPortalsList();
}, 500);
}
}

Expand Down
3 changes: 0 additions & 3 deletions plugins/cross-links.user.js
Expand Up @@ -282,9 +282,6 @@ var setup = function() {
return;
}

// this plugin also needs to create the draw-tools hook, in case it is initialised before draw-tools
window.pluginCreateHook('pluginDrawTools');

window.plugin.crossLinks.createLayer();

// events
Expand Down
7 changes: 3 additions & 4 deletions plugins/distance-to-portal.user.js
Expand Up @@ -103,8 +103,6 @@ window.plugin.distanceToPortal.setLocation = function() {
};

window.plugin.distanceToPortal.setupPortalsList = function() {
if(!window.plugin.portalslist) return;

window.plugin.portalslist.fields.push({
title: "Dist",
value: function(portal) { if (window.plugin.distanceToPortal.currentLoc) return window.plugin.distanceToPortal.currentLoc.distanceTo(portal.getLatLng()); else return 0; },
Expand All @@ -131,8 +129,9 @@ window.plugin.distanceToPortal.setup = function() {

addHook('portalDetailsUpdated', window.plugin.distanceToPortal.addDistance);

window.plugin.distanceToPortal.setupPortalsList();

if (window.plugin.portalslist) {
window.plugin.distanceToPortal.setupPortalsList();
}
};

var setup = window.plugin.distanceToPortal.setup;
Expand Down

0 comments on commit 60235ea

Please sign in to comment.