// ==UserScript==
// @author Tarsi210
// @id herringbone-planner@Tarsi210
// @name Herringbone Planner
// @category Layer
// @version 0.4.4
// @description Finds and draws an optimal visible-portal spine for herringbone multilayer planning, with CSV export.
// @downloadURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/Tarsi210/herringbone-planner.user.js
// @updateURL https://raw.githubusercontent.com/IITC-CE/Community-plugins/master/dist/Tarsi210/herringbone-planner.meta.js
// @homepageURL https://github.com/Tarsi210/iitc-herringbone-planner
// @issueTracker https://github.com/Tarsi210/iitc-herringbone-planner/issues
// @include http://intel.ingress.com/*
// @include https://intel.ingress.com/*
// @include http://intel-x.ingress.com/*
// @include https://intel-x.ingress.com/*
// @include http://www.ingress.com/intel*
// @include https://www.ingress.com/intel*
// @match http://intel.ingress.com/*
// @match https://intel.ingress.com/*
// @match http://intel-x.ingress.com/*
// @match https://intel-x.ingress.com/*
// @match http://www.ingress.com/intel*
// @match https://www.ingress.com/intel*
// @grant unsafeWindow
// ==/UserScript==
function wrapper(plugin_info, w) {
'use strict';
w = w || window;
var $ = w.$;
var doc = w.document || document;
var plugin = {};
var ns = 'herringboneSpinePlanner';
if (!w.plugin) w.plugin = {};
w.plugin[ns] = plugin;
var STORAGE_KEY = 'plugin-herringbone-spine-planner-settings';
var DEFAULT_SETTINGS = {
maxDeviationMeters: 35,
maxSegmentAngleDegrees: 25,
minPortals: 4,
maxSpinePortals: 0,
maxVisiblePortals: 180,
maxExecutionSeconds: 30,
autoRefresh: true,
showCorridor: true,
showRejectedPortals: true,
showAnchorLinks: true,
anchorA: null,
anchorB: null
};
plugin.settings = loadSettings();
plugin.lastResult = null;
plugin.layerGroup = null;
plugin.refreshTimer = null;
plugin.computeTimer = null;
plugin.refreshSerial = 0;
plugin.control = null;
function setup() {
try {
plugin.isSetup = true;
w.console.log('[Herringbone Planner] setup starting');
if (!$) throw new Error('jQuery is not available in IITC page context yet.');
injectCss();
plugin.layerGroup = new w.L.LayerGroup();
plugin.layerGroup.addTo(w.map);
if (w.layerChooser && w.layerChooser.addOverlay) {
w.layerChooser.addOverlay(plugin.layerGroup, 'Herringbone Planner');
}
addControls();
addToolboxLink();
bindMapEvents();
addPortalDetailsButtons();
plugin.refresh();
w.console.log('[Herringbone Planner] setup complete');
} catch (err) {
w.console.error('[Herringbone Planner] setup failed', err);
w.setTimeout(function () {
w.alert('Herringbone Planner failed during setup. Open the browser console for details: ' + err.message);
}, 100);
}
}
plugin.refresh = function () {
if (!w.map || !w.portals) return;
var serial = ++plugin.refreshSerial;
var portals = getVisiblePortals();
if (portals.length > plugin.settings.maxVisiblePortals) {
plugin.lastResult = null;
drawResult(null);
updateStatus(null, portals.length, true);
return;
}
if (plugin.computeTimer) w.clearTimeout(plugin.computeTimer);
updateProcessingStatus(portals.length);
setDialogStatus('Processing herringbone plan...');
plugin.computeTimer = w.setTimeout(function () {
plugin.computeTimer = null;
if (serial !== plugin.refreshSerial) return;
var startedAt = Date.now();
var deadlineMs = startedAt + plugin.settings.maxExecutionSeconds * 1000;
var search = findBestSpine(portals, plugin.settings, deadlineMs);
var result = search.result;
if (result) {
result.timedOut = search.timedOut;
result.elapsedMs = Date.now() - startedAt;
}
rememberKeyCount(result);
plugin.lastResult = result;
drawResult(result);
updateStatus(result, portals.length, false, search.timedOut);
setDialogStatus(formatResultSummary(result, search.timedOut));
updateDialogAnchorLabels();
}, 25);
};
plugin.scheduleRefresh = function () {
if (!plugin.settings.autoRefresh) return;
if (plugin.refreshTimer) w.clearTimeout(plugin.refreshTimer);
plugin.refreshTimer = w.setTimeout(function () {
plugin.refreshTimer = null;
plugin.refresh();
}, 350);
};
plugin.openDialog = function () {
var html = [
'
')
.text('Herringbone Planner')
.attr('title', 'Open Herringbone Planner')
.on('click', plugin.openDialog)
.appendTo('#toolbox');
}
function addPortalDetailsButtons() {
w.setTimeout(function () {
var details = $('#portaldetails');
if (!details.length || details.find('#hbsp-portal-buttons').length) return;
var box = $('');
$('