// ==UserScript==
// @name Game Highlighter
// @author Timo Gebauer
// @namespace [email protected]
// @version 0.2.4
// @grant none
// @include http://makemehost.com/games.php*
// @description Searches the games table for settable game names and highlights them.
// ==/UserScript==
/******************************************************************************/
// use MakeMeHosts jQuery version
$ = unsafeWindow.jQuery;
// option, to restrict search on games column (only mmh table)
var searchGamesOnly = false;
var highlights = [];
var sounds = {
solemn: 'http://www.oringz.com/oringz-uploads/sounds-882-solemn.mp3',
comm: 'http://www.oringz.com/oringz-uploads/sounds-917-communication-channel.mp3'
};
/******************************************************************************/
/*
* Ajax Request "hijacken" nach jeder erfolgreichen Übertragung die Funktion highlightArray() ausführen
* Das Ergebnis des Requests kann hier nicht abgefangen werden, da der Eventlistener erst aufgerufen wird,
* nachdem der eigentliche Aufrufer das Ergebnis bereits verarbeitet hat
*/
// backup original "send" function reference
XMLHttpRequest.prototype.oldSend = XMLHttpRequest.prototype.send;
var newSend = function(a) {
var xhr = this;
var onload = function() {
highlightArray();
};
xhr.addEventListener("load", onload, false);
this.oldSend(a);
};
XMLHttpRequest.prototype.send = newSend;
/*******************************************************************************/
/*
* TODO:
* - color-picker ?
* - mute sound checkbox
* - "mute" elements
* - Rückgangig button
* - erst beim schließen vollständig anwenden
* - sound-fenster (neu, entfernen, etc..)
* - lag verringern
* - input dialog verschönern
*/
function initGui() {
$('.refreshMe').next().after('<div align="center"><a class="changelink-unactive" id="btn_options" title="Open highlight settings dialog."> Highlight Settings</a></div>');
document.getElementById('btn_options').addEventListener('click', showGui, true);
$("body").append('<div id="popup_overlay" class="overlay"></div>');
$("body").append('<div id="popup" class="popup"></div>');
document.getElementById('popup_overlay').addEventListener('click', hideGui, true);
// popup window
var html = '<div align="center"><a class="changelink-active"><span>Highlighting Rules</span></a></div><br><p>'
+ 'Enter one or more keywords, insert a color value (blue, red, #fff, #ff00ff, ...) '
+ 'and select a sound if you want one.'
+ '<br>All table fields are searched. Searching is case-insensitive.'
+ '<br>Lower indexed rules are applied first.'
+ '<br>Highlights that trigger a sound will be intensively highlighted for a short duration.</p>'
+ '<div id="buttons_top">'
+ '<a id="btn_add"class="changelink-unactive" title="Inserts a new highlighting rule.">New Highlight</a>'
+ '<a id="btn_load" class="changelink-unactive" title="Load rules from a previously saved JSON formated string.">Load (JSON)</a>'
+ '</div><div id="buttons_bottom">'
+ '<a id="btn_save" class="changelink-unactive" title="Save your current rules as JSON formated string.">Save (JSON)</a>'
+ '<a id="btn_remove_all" class="changelink-unactive" title="Delete all rules. Backup before?">Remove all</a>'
+ '</div><div id="highlight_container">Something is not working correctly.</div>';
$("#popup").append(html);
// load / save
$("#btn_load").click(function() {
showInputDialog("", function() {
highlights = JSON.parse($("#inputDialogInput").attr("value"));
updateGui();
});
});
$("#btn_save").click(function() {
alert("Save this stringified options somewhere \n\n"
+ JSON.stringify(highlights));
});
// add a new highlight
$("#btn_add").click(function() {
highlights.splice(0,0,{search:[]});
updateGui();
});
// remove all
$("#btn_remove_all").click(function() {
highlights = [];
updateGui();
});
updateGui();
// append custom stylesheet
$("head").append('<style type="text/css"><!--\n'
+ "#btn_options {"
+ "cursor: pointer;"
+ "font-size: 20px;"
+ "}"
+ ".overlay {"
+ "left: 0;"
+ "top: 0;"
+ "bottom: 0;"
+ "right: 0;"
+ "z-index: 100;"
+ "position: fixed;"
+ "background-color: #000;"
+ "filter: alpha(opacity=20);"
+ "opacity: 0.2;"
+ "cursor: pointer;"
+ "display: none;"
+ "}"
+ ".popup {"
+ "display: none;"
+ "background: #fff;"
+ "padding: 1%;"
+ "width: 50%;"
+ "position: fixed;"
+ "top: 10%;"
+ "left: 50%;"
+ "margin: 0 0 0 -20%;" /* add negative left margin for half the width to center the div */
+ "cursor: default;"
+ "z-index: 200;"
+ "box-shadow: 0 0 5px rgba(0,0,0,0.9);"
+ "max-height: 80%;"
+ "overflow: auto;"
+ "}"
+ ".highlight_wrapper {"
+ "width: 550px;"
+ "margin: 20px;"
+ "margin-left: auto;"
+ "margin-right: auto;"
+ "clear: both;"
// + "border: 1px solid grey;"
+ "overflow: hidden;"
+ "}"
+ ".index {"
+ "width: 20px;"
+ "margin-right: 30px;"
+ "float:left;"
+ "font-family: helvetica neue, helvetica, arial, sans-serif;"
+ "font-size: 14px;"
+ "text-shadow: 1px 1px 1px #000;"
+ "}"
+ ".btn_hl {"
+ "font-family: helvetica neue, helvetica, arial, sans-serif;"
+ "width: 22px;"
+ "height: 22px;"
+ "margin-right: 5px;"
+ "margin-left: 5px;"
+ "border: 1px solid grey;"
+ "text-decoration: none;"
+ "font-weight: bold;"
+ "cursor:pointer;"
+ "float:left;"
+ "}"
+ ".btn_color {"
+ "margin-right: 20px;"
+ "margin-left: 20px;"
+"}"
+ ".btn_remove {"
+ "background-color: #f5f5f5;"
+ "}"
+ ".highlight_search {"
+ "width: 200px;"
+ "resize: none;"
+ "float: left;"
+ "font-family: helvetica neue, helvetica, arial, sans-serif;"
+ "white-space: nowrap;"
+ "word-wrap: normal;"
+ "border: none;"
+ "border-left: 2px solid grey;"
+ "padding-left: 10px;"
+ "overflow: hidden;"
+ "}"
+ ".sound_select {"
+ "width: 100px;"
+ "border: 1px solid grey;"
+ "float: left;"
+ "}"
+ ".end_wrapper {"
+ "clear:both;"
+ "}"
+ "#inputDialog {"
+ "display: none;"
+ "position: fixed;"
+ "top: 20%;"
+ "left: 50%;"
+ "margin-left: -10%;"
+ "z-index: 300;"
+ "}"
+ "#inputDialogOverlay {"
+ "z-index: 250;"
+ "}"
+ "#btn_save {"
+ "float:right;"
+ "font-size: 18px;"
+ "cursor: pointer;"
+ "}"
+ "#btn_load {"
+ "float:right;"
+ "font-size: 18px;"
+ "cursor: pointer;"
+ "}"
+ "#btn_add {"
+ "font-size: 18px;"
+ "cursor: pointer;"
+ "}"
+ "#btn_remove_all {"
+ "font-size: 18px;"
+ "cursor: pointer;"
+ "}"
+ '\n--></style>');
// input dialog
var html = '<div id="inputDialogOverlay" class="overlay"></div>'
+ '<div id="inputDialog">'
+ '<input id="inputDialogInput" type="text"></input>'
+ '<button id="inputDialogButton">Ok</button>'
+ '</div>';
$("body").append(html);
$("#inputDialog").hide();
$("#inputDialogOverlay").click(function() {
$("#inputDialog").hide();
$("#inputDialogOverlay").hide();
});
$("#inputDialogInput").focus(function(){
var that = this;
setTimeout(function(){$(that).select();}, 10);
});
$("#inputDialogInput").keypress(function(event){
if(event.keyCode == 13){
$("#inputDialogButton").click();
}
});
}
function updateGui() {
// save changes
saveLocalData();
// reset all previous highlights
resetHighlights();
// apply new highlights
highlightArray();
$("#highlight_container").empty();
$.each(highlights, function(index, elem) {
var html = '<div class="highlight_wrapper" align="center">'
+ '<span class="index">' + index + '</span>'
+ '';
// make sure, elem.search is an array
elem.search = elem.search instanceof Array ? elem.search : [elem.search];
var text = "";
$.each(elem.search, function(index, search_value) {
if(index != 0)
text += "\n";
text += search_value;
});
html += '<textarea class="highlight_search" title="Search keywords. One per line." index="'+index+'" rows="'
+ elem.search.length + '">' + text + '</textarea>';
html += '<button class="btn_hl btn_color" value="' + index
+ '" style="background-color:' + elem.color + ';"> </button>';
html += '<select class="sound_select" title="Notification sound.\nNew games will not only trigger the sound, but also be especially highlighted for a short duration." index="'+index+'">';
var afterOption = "";
var foundSound = false;
$.each(sounds, function(sound, url) {
afterOption += '<option';
if(elem.sound === sound) {
afterOption += ' selected=selected';
foundSound = true;
}
afterOption += '>' + sound + '</option>';
});
html += '<option'
if(!foundSound)
html += ' selected=selected';
html += '></option>';
html += afterOption;
html += '</select>';
html += '<button class="btn_hl btn_up" title="Move up" index="' + index + '">▲</button>';
html += '<button class="btn_hl btn_down" title="Move down" index="' + index + '">▼</button>';
html += '<button class="btn_hl btn_remove" title="Delete" index="' + index + '">X</button>';
html += '</div>';
$("#highlight_container").append(html);
});
// register events
$(".highlight_search").change(function() {
highlights[$(this).attr("index")].search = cleanArray($(this).val().split("\n")) ;
saveLocalData();
resetHighlights();
highlightArray();
});
$(".highlight_search").keyup(function(event) {
$(this).attr("rows", $(this).val().split("\n").length);
});
$(".btn_color").click(function() {
var index = $(this).attr("value");
showInputDialog(highlights[index].color, function() {
var color = $("#inputDialogInput").attr("value");
highlights[index].color = color;
updateGui();
$('.btn_color[value="'+index+'"]').focus();
});
});
$(".sound_select").change(function(){
highlights[$(this).attr("index")].sound = $(this).val();
// no need to update the gui here, only save the changes
saveLocalData();
});
$(".btn_remove").click(function() {
highlights.splice($(this).attr("index"),1);
updateGui();
});
$(".btn_up").click(function() {
var index = parseInt($(this).attr("index"));
if(index == 0)
return;
var temp = highlights.splice(index, 1);
highlights.splice(index-1, 0, temp[0]);
updateGui();
});
$(".btn_down").click(function() {
var index = parseInt($(this).attr("index"));
if(index == highlights.length-1)
return;
var temp = highlights.splice(index, 1);
highlights.splice(index+1, 0, temp[0]);
updateGui();
});
}
function showInputDialog(text, okFunction) {
$("#inputDialogInput").attr("value", text);
$("#inputDialogInput").focus();
$("#inputDialog").show();
$("#inputDialogOverlay").show();
$("#inputDialogButton").unbind("click")
$("#inputDialogButton").click(function() {
$("#inputDialog").hide();
$("#inputDialogOverlay").hide();
});
$("#inputDialogButton").click(okFunction);
}
function showGui() {
$("#popup_overlay").show();
$("#popup").show();
$("body").css("overflow", "hidden");
}
function hideGui() {
$("#popup_overlay").css("display", "none");
$("#popup").css("display", "none");
$("body").css("overflow", "auto");
// reset all previous highlights
resetHighlights();
// apply new highlights
highlightArray();
}
/******************************************************************************/
function saveLocalData() {
localStorage.setItem("highlights", JSON.stringify(highlights));
}
function loadLocalData() {
highlights = JSON.parse(localStorage.getItem("highlights"));
if(highlights === null) {
highlights = [];
}
}
/******************************************************************************/
$(document).ready(function () {
// Riesigen Header entfernen
$('#rt-header-overlay').remove();
// Sounds vorladen
$.each(sounds, function(name, url) {
$("body").append("<audio id='sound_"+name+"' src='"+url+"' hidden=true preload=auto></audio>");
});
// Einstellungen laden
loadLocalData();
// eigenes GUI einfügen
initGui();
});
/******************************************************************************/
// enthalten die Namen der gefundenen Spiele
var knownGames = {};
var foundGames = {};
function resetHighlights() {
$("#mmh td").css({
backgroundColor: "transparent",
color: "black"
});
}
function highlightArray() {
foundGames = {};
$.each(highlights, function(index, elem) {
if (elem === null)
return;
var arr = elem.search instanceof Array ? elem.search : [elem.search];
if(!(arr.length == 1 && arr[0] === ""))
$.each(arr, function(index, text) {
highlight(text, elem.color, elem.sound);
});
});
knownGames = foundGames;
}
function highlight(search, color, sound) {
getNodes(search).each(function(){
// Schrift färben?
if(color && !$(this).attr("hl")) {
$(this).css("color", color);
}
// Spiel unbekannt?
if(!($(this).text() in knownGames)) {
// Sound abspielen?
if(sound in sounds) {
// neu gefundene Spiele kurzzeitig hervorheben
$(this).css("background-color", color);
$(this).css("color", "white");
// mark this element as highlighted
$(this).attr("hl", "true");
document.getElementById("sound_" + sound).play();
}
}
foundGames[$(this).text()] = $(this).text();
});
}
function getNodes(text) {
// Nur in Games Spalten suchen?
if (searchGamesOnly) {
var tds = $('#mmh table:first-child td:nth-child(4)')
.add('#mmh table:last-child td:nth-child(2)');
} else {
var tds = $('td');
}
return tds.filter(':Contains(' + text + ')');
}
/******************************************************************************/
/*
* removes every "falsy" value: undefined, null, 0, false, NaN and '':
*/
function cleanArray(actual){
var newArray = new Array();
for(var i = 0; i<actual.length; i++){
if (actual[i]){
newArray.push(actual[i]);
}
}
return newArray;
}
/******************************************************************************/
// jQuery :contains Case-Insensitive
// http://css-tricks.com/snippets/jquery/make-jquery-contains-case-insensitive/
jQuery.expr[':'].Contains = function(a, i, m) {
return jQuery(a).text().toUpperCase()
.indexOf(m[3].toUpperCase()) >= 0;
};