Skip to content

Instantly share code, notes, and snippets.

@johnd0e
Last active July 2, 2020 02:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnd0e/cabe8d9a59f0ede60f7f6c184fa131e3 to your computer and use it in GitHub Desktop.
Save johnd0e/cabe8d9a59f0ede60f7f6c184fa131e3 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @id iitc-plugin-control-history
// @name IITC plugin: History control
// @description Allows user to navigate back through previous extents
// @category Controls
// @version 0.1.0
// @author jd
// @namespace https://gist.github.com/johnd0e
// @homepageURL https://gist.github.com/johnd0e/cabe8d9a59f0ede60f7f6c184fa131e3
// @supportURL https://gist.github.com/johnd0e/cabe8d9a59f0ede60f7f6c184fa131e3#new_comment_field
// @updateURL https://gist.github.com/johnd0e/cabe8d9a59f0ede60f7f6c184fa131e3/raw/control-history.user.js
// @downloadURL https://gist.github.com/johnd0e/cabe8d9a59f0ede60f7f6c184fa131e3/raw/control-history.user.js
// @include https://intel.ingress.com/intel*
// @grant none
// ==/UserScript==
function wrapper(plugin_info) {
// ensure plugin framework is there, even if iitc is not yet loaded
if(typeof window.plugin !== 'function') window.plugin = function() {};
// PLUGIN START ////////////////////////////////////////////////////////
// https://github.com/cscott530/leaflet-history
let css = '\
.history-control.leaflet-bar.hidden {\
display: none;\
}\
.history-control.leaflet-bar.horizontal a {\
display: inline-block;\
border-bottom: none;\
border-radius: 0;\
border-right: 1px solid #ccc;\
}\
.history-control.leaflet-bar.horizontal a:first-child {\
border-top-left-radius: 4px;\
border-bottom-left-radius: 4px;\
}\
.history-control.leaflet-bar.horizontal a:last-child {\
border-top-right-radius: 4px;\
border-bottom-right-radius: 4px;\
border-right: none;\
}\
.history-control.leaflet-bar a {\
width: auto;\
font-size: 1.1em;\
min-width: 26px;\
}';
function setup () {
(function () {
L.HistoryControl = L.Control.extend({
options: {
position: 'topright',
maxMovesToSave: 10, //set to 0 for unlimited
useExternalControls: false, //set to true to hide buttons on map and use your own. Can still use goBack, goForward, and allow this to take care of storing history.
backImage: 'fa fa-caret-left',
backText: '',
backTooltip: 'Go to Previous Extent',
backImageBeforeText: true,
forwardImage: 'fa fa-caret-right',
forwardText: '',
forwardTooltip: 'Go to Next Extent',
forwardImageBeforeText: false,
orientation: 'horizontal',
shouldSaveMoveInHistory: function(zoomCenter) { return true; } //by default save everything
},
initialize: function(options) {
L.Util.setOptions(this, options);
this._state.maxMovesToSave = this.options.maxMovesToSave;
},
onAdd: function(map) {
this._map = map;
var container = L.DomUtil.create('div', 'history-control leaflet-bar leaflet-control ' + this.options.orientation);
if(!this.options.useExternalControls) {
this._backButton = this._createButton('back', container, this.goBack, this);
this._forwardButton = this._createButton('forward', container, this.goForward, this);
}
this._updateDisabled();
this._addMapListeners();
return container;
},
onRemove: function(map) {
map.off('movestart');
},
performActionWithoutTriggeringEvent: function(action) {
this._state.ignoringEvents = true;
if(typeof (action) === 'function') {
action();
}
},
moveWithoutTriggeringEvent: function(zoomCenter) {
var _this = this;
this.performActionWithoutTriggeringEvent(function() {
_this._map.setView(zoomCenter.centerPoint, zoomCenter.zoom);
});
},
goBack: function() {
return this._invokeBackOrForward('historyback', this._state.history, this._state.future);
},
goForward: function() {
return this._invokeBackOrForward('historyforward', this._state.future, this._state.history);
},
clearHistory: function() {
this._state.history.items = [];
this._updateDisabled();
},
clearFuture: function() {
this._state.future.items = [];
this._updateDisabled();
},
_map: null,
_backButton: null,
_forwardButton: null,
_state: {
backDisabled: null,
forwardDisabled: null,
ignoringEvents: false,
maxMovesToSave: 0,
history: {
items: []
},
future: {
items: []
}
},
_createButton: function (name, container, action, _this) {
var text = this.options[name + 'Text'] || '';
var imageClass = this.options[name + 'Image'] || '';
var tooltip = this.options[name + 'Tooltip'] || '';
var button = L.DomUtil.create('a', 'history-' + name + '-button', container);
if(imageClass) {
var imageElement = '<i class="' + imageClass + '"></i>';
if(this.options[name + 'ImageBeforeText']) {
text = imageElement + ' ' + text;
}
else {
text += ' ' + imageElement;
}
}
button.innerHTML = text;
button.href = '#';
button.title = tooltip;
var stop = L.DomEvent.stopPropagation;
L.DomEvent
.on(button, 'click', stop)
.on(button, 'mousedown', stop)
.on(button, 'dblclick', stop)
.on(button, 'click', L.DomEvent.preventDefault)
.on(button, 'click', action, _this)
.on(button, 'click', this._refocusOnMap, _this);
return button;
},
_updateDisabled: function () {
var backDisabled = (this._state.history.items.length === 0);
var forwardDisabled = (this._state.future.items.length === 0);
if(backDisabled !== this._state.backDisabled) {
this._state.backDisabled = backDisabled;
this._map.fire('historyback' + (backDisabled ? 'disabled' : 'enabled'));
}
if(forwardDisabled !== this._state.forwardDisabled) {
this._state.forwardDisabled = forwardDisabled;
this._map.fire('historyforward' + (forwardDisabled ? 'disabled' : 'enabled'));
}
if(!this.options.useExternalControls) {
this._setButtonDisabled(this._backButton, backDisabled);
this._setButtonDisabled(this._forwardButton, forwardDisabled);
}
},
_setButtonDisabled: function(button, condition) {
var className = 'leaflet-disabled';
if(condition) {
L.DomUtil.addClass(button, className);
}
else {
L.DomUtil.removeClass(button, className);
}
},
_pop: function(stack) {
stack = stack.items;
if(L.Util.isArray(stack) && stack.length > 0) {
return stack.splice(stack.length - 1, 1)[0];
}
return undefined;
},
_push: function(stack, value) {
var maxLength = this._state.maxMovesToSave;
stack = stack.items;
if(L.Util.isArray(stack)) {
stack.push(value);
if(maxLength > 0 && stack.length > maxLength) {
stack.splice(0, 1);
}
}
},
_invokeBackOrForward: function(eventName, stackToPop, stackToPushCurrent) {
var response = this._popStackAndUseLocation(stackToPop, stackToPushCurrent);
if(response) {
this._map.fire(eventName, response);
return true;
}
return false;
},
_popStackAndUseLocation : function(stackToPop, stackToPushCurrent) {
//check if we can pop
if(L.Util.isArray(stackToPop.items) && stackToPop.items.length > 0) {
var current = this._buildZoomCenterObjectFromCurrent(this._map);
//get most recent
var previous = this._pop(stackToPop);
//save where we currently are in the 'other' stack
this._push(stackToPushCurrent, current);
this.moveWithoutTriggeringEvent(previous);
return {
previousLocation: previous,
currentLocation: current
};
}
},
_buildZoomCenterObjectFromCurrent:function(map) {
return new L.ZoomCenter(map.getZoom(), map.getCenter());
},
_addMapListeners: function() {
var _this = this;
this._map.on('movestart', function(e) {
if(!_this._state.ignoringEvents) {
var current = _this._buildZoomCenterObjectFromCurrent(e.target);
if(_this.options.shouldSaveMoveInHistory(current)) {
_this._state.future.items = [];
_this._push(_this._state.history, current);
}
} else {
_this._state.ignoringEvents = false;
}
_this._updateDisabled();
});
}
});
}());
(function () {
L.ZoomCenter = L.Class.extend({
initialize: function(zoom, centerPoint) {
this.zoom = zoom;
this.centerPoint = centerPoint;
}
});
}());
$('<style>').prop('type', 'text/css').html(css).appendTo('head');
new L.HistoryControl({
backText: '<',
forwardText: '>',
position: isSmartphone() ? 'topright' : 'bottomleft'
}).addTo(map);
}
// PLUGIN END //////////////////////////////////////////////////////////
setup.info = plugin_info; //add the script info data to the function as a property
if(!window.bootPlugins) window.bootPlugins = [];
window.bootPlugins.push(setup);
// if IITC has already booted, immediately run the 'setup' function
if(window.iitcLoaded && typeof setup === 'function') setup();
} // wrapper end
// inject code into site context
var script = document.createElement('script');
var info = {};
if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description };
script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');'));
(document.body || document.head || document.documentElement).appendChild(script);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment