// ==UserScript==
// @name Roll20 Fixes
// @namespace http://statonions.com/
// @version 0.2.0
// @description Some silly fixes and 'improvements' to Roll20 because I'm impatient and a psycho
// @author Justice Noon
// @match https://app.roll20.net/editor/
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
//Features
var TMp = {
clickShow: true,
macroHide: true,
fullButt: true,
fixPing: true,
css: {
player: false,
zoom: false,
sidebar: false,
quick: false,
turns: false,
macroTok: true
},
pfCustomAttr: true,
splitTokName: true
};
try {
GM_info;
var stored = GM_getValue('TMp', 'nah');
if (stored == 'nah')
GM_setValue('TMp', JSON.stringify(TMp));
else
TMp = JSON.parse(stored);
checkLoaded();
}
catch(err) {
checkLoaded();
}
//For *my* players specifically. Activates all css tweaks. I'm too lazy to maintain separate code (in-person touchpad view)
////_.each(TMp.css, (c,i,l) => l[i] = true);
function checkLoaded() {
if (Campaign.gameFullyLoaded)
showtime();
else
setTimeout(checkLoaded, 1000);
};
function showtime() {
//Create greasemonkey preferences menu
try {
GM_info;
var prefLi = document.createElement('li');
prefLi.id = 'showfixes';
var prefSpan = document.createElement('span');
prefSpan.setAttribute('class', 'pictos');
prefSpan.appendChild(document.createTextNode('x'));
prefLi.appendChild(prefSpan);
prefLi.appendChild(document.createTextNode("\r\n 'Fixes' Preferences\r\n"));
document.getElementById('helpsite').childNodes[3].childNodes[1].appendChild(prefLi);
prefLi.addEventListener('click', promptPrefs);
function promptPrefs(e) {
e.stopPropagation();
var finished = false, response, ref = TMp, counter, changeKey;
while (!finished) {
counter = 1;
response = parseInt(prompt(_.reduce(ref, (memo, v, k) => memo + '\n' + counter++ + ':' + k + ' :: ' + (_.isObject(v) ? '[' + _.keys(v).length + ' properties]' : v), 'Enter a number:\n0:Back'), 'Enter a number to swap value.'));
if (_.isNaN(response) || response == 0 || response > counter) {
if (ref === TMp) {
GM_setValue('TMp', JSON.stringify(TMp));
finished = true;
alert('Reload to see changes');
}
else
ref = TMp;
}
else {
changeKey = _.keys(ref)[response-1];
if (_.isObject(ref[changeKey]))
ref = ref[changeKey];
else if (_.isBoolean(ref[changeKey]))
ref[changeKey] = !ref[changeKey];
else
ref[changeKey] = prompt('Enter a new value for ' + changeKey);
}
}
}
}
catch (err) {
console.log('No GM. No need for preferences.');
}
//Display tokenname on select
if (TMp.clickShow) {
var toks = [];
var callback = function(mutationsList) {
for(var mutation of mutationsList) {
if (!_.isEmpty(mutation.addedNodes)) {
let cct = currentcontexttarget.canvas.lastRenderedObjectWithControlsAboveOverlay.model.attributes;
if (!cct.showname) {
cct.showname = true;
toks.push([cct.page_id, cct.id]);
Campaign.activePage().loadPageIntoDOM();
}
if (cct.gmnotes != 'blank' && TMp.css.macroTok)
_.each(document.getElementsByClassName('tokenactions')[0].firstElementChild.children, ob => ob.setAttribute('data-blank', 'false'));
}
else if (!_.isEmpty(mutation.removedNodes)) {
_.each(toks, tok => {if (tok[0] == Campaign.activePage().id) _.find(Campaign.activePage().thegraphics.models, tokk => tokk.attributes.id == tok[1]).attributes.showname = false});
toks = [];
Campaign.activePage().loadPageIntoDOM();
}
}
};
var observer = new MutationObserver(callback);
observer.observe(document.getElementById('editor-wrapper'), {childList: true});
}
//Allow Macros to be hidden
if (TMp.macroHide) {
var toggleMacro = document.createElement('div');
toggleMacro.setAttribute('class', 'macrobox');
var toggleButton = document.createElement('button');
toggleButton.setAttribute('class', 'btn');
toggleButton.appendChild(document.createTextNode('Show/Hide'));
toggleButton.id = 'toggleMacros';
toggleMacro.appendChild(toggleButton)
//macrobar is defined by default
macrobar.appendChild(toggleMacro);
document.getElementById('toggleMacros').addEventListener('click', () => macrobar.style.left = (macrobar.style.left == '' ? (macrobar.offsetWidth * -1 + 100) + 'px' : ''));
}
//Create fullscreen button
if (TMp.fullButt) {
var fullScreenLi = document.createElement('li');
fullScreenLi.setAttribute('tip', 'Toggle Fullscreen');
fullScreenLi.id = 'fullscreener';
var fullScreenSpan = document.createElement('span');
fullScreenSpan.setAttribute('class', 'pictos');
fullScreenSpan.appendChild(document.createTextNode('`'));
fullScreenLi.appendChild(fullScreenSpan);
floatingtoolbar.childNodes[1].appendChild(fullScreenLi);
fullScreenLi.addEventListener('click', toggleFullScreen);
function toggleFullScreen() {
var doc = window.document;
var docEl = doc.documentElement;
var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
if(!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {
requestFullScreen.call(docEl);
}
else {
cancelFullScreen.call(doc);
}
}
}
//Fix Pings
if (TMp.fixPing) {
JSON.parse2 = JSON.parse;
JSON.parse = function(e) {
var intercept = JSON.parse2(e);
if (intercept.type == 'mapping') {
intercept.currentLayer = 'objects';
if (intercept.pageid != Campaign.activePage().id)
intercept.scrollto = false;
}
return intercept;
};
}
//CSS override block
if (_.contains(TMp.css, true)) {
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '';
//Remove player names/ icons
if (TMp.css.player)
style.innerHTML += '.player.ui-droppable {display: none;} ';
//Remove Zoom Slider
if (TMp.css.zoom)
style.innerHTML += '#zoomslider {display:none;} ';
//Remove sidebar show/ hide button
if (TMp.css.sidebar)
style.innerHTML += '#sidebarcontrol {display: none;} ';
//Increase quickmenu sizes
if (TMp.css.quick)
style.innerHTML += '.sheet-roll-cell>a{padding:.5em !important;} ';
//Permanently hide turn order
if (TMp.css.turns)
style.innerHTML += 'div.ui-dialog-buttons[style*="width: 160px;"] {display: none !important;} ';
//Hide macroTokens by default
if (TMp.css.macroTok)
style.innerHTML += 'button[data-type="macro"]:not([data-blank="false"]) {display: none !important;} ';
document.getElementsByTagName('head')[0].appendChild(style);
}
//Character sheets display custom attributes as name
if (TMp.pfCustomAttr) {
function cssInjection(charId) {
var list = [1,2,3,10,11,12];
var head = 'div.dialog.characterdialog[data-characterid=' + charId + '] {', foot = '';
_.each(list, function(cK) {
cK = 'customa' + cK;
let val = {};
if (!_.isUndefined(val = _.find(Campaign.characters._byId[charId].attribs.models, at => at.attributes.name == cK + '-name')))
head += `--${cK}: "${val.attributes.current}"; `
foot += `input[title="@{buff_${cK}-total}"] + span {visibility: hidden; top: -1.2rem;} input[title="@{buff_${cK}-total}"] + span::after {content: var(--${cK}, "${cK}"); visibility: visible; display: block;} `
});
return [head + '} ', foot];
}
function newShow(that) {
var customOver;
var modCss = cssInjection(that.model.attributes.id);
if (_.isNull(document.getElementById('customOver'))) {
customOver = document.createElement('style');
customOver.type = 'text/css';
customOver.id = 'customOver';
customOver.innerHTML = modCss[1];
}
else {
customOver = document.getElementById('customOver');
}
customOver.innerHTML += (modCss[0].indexOf('{}') > -1 ? '' : modCss[0]);
if (that.childWindow)
_.delay(function() {customOver.innerHTML = modCss.join(' '); window.allChildWindows[0].document.getElementsByTagName('head')[0].appendChild(customOver)}, 2000);
else
document.getElementsByTagName('head')[0].appendChild(customOver);
};
_.each(Campaign.characters.models, ch => {ch.view.showDialog2 = ch.view.showDialog; ch.view.showDialog = function(e, t) {this.showDialog2(e, t); newShow(this)}});
_.each(Campaign.characters.models, ch => {ch.view.showPopout2 = ch.view.showPopout; ch.view.showPopout = function(e, t) {this.showPopout2(e, t); newShow(this)}});
_.each(window.allChildWindows, win => {newShow(Campaign.characters._byId[win.document.getElementsByClassName('dialog characterdialog')[0].getAttribute('data-characterid')].view)});
}
//Tokens auto split their name to multiple lines
if (TMp.splitTokName) {
var can = document.getElementById('maincanvas').getContext('2d');
can.fillRect2 = can.fillRect;
can.fillRect = function(x, y, w, h) {if (this.font != 'bold 14px Arial' && this.fillStyle.indexOf('rgba(255,255,255,0.5') == -1) this.fillRect2(x, y, w, h);};
can.fillText2 = can.fillText;
can.fillText = function(t, x, y, m) {
var g = 14, r = y - 22, n = m;
var texes = t.split(' ');
for (var k = 0; k < texes.length; k++) {
n = this.measureText(texes[k]).width;
while (this.measureText(texes[k] + ' ' + texes[k+1]).width < r*2 && k+1 < texes.length) {
texes[k] += ' ' + texes[k+1];
n = this.measureText(texes[k]).width;
texes.splice(k+1, 1);
}
this.fillStyle = 'rgba(255, 255, 255, 0.50)';
this.fillRect2(-1 * Math.floor((n + 6) / 2), r + 8 + (g+6)*k, n + 6, g + 6);
this.fillStyle = 'rgb(0,0,0)';
this.fillText2(texes[k], x, y + (g+6)*k, n);
}
};
}
}
})();