Greasy Fork is available in English.
Automatically highlight user-defined text with Seek function (2019-09-22)
// ==UserScript==// @name Text Highlight and Seek// @author erosman and Jefferson "jscher2000" Scher// @namespace JeffersonScher// @version 2.4.0// @description Automatically highlight user-defined text with Seek function (2019-09-22)// @include https://greasyfork.org/*// @include https://openuserjs.org/*// @include http*://www.jeffersonscher.com/*// @grant GM_registerMenuCommand// @grant GM_setValue// @grant GM.setValue// @grant GM_getValue// @grant GM.getValue// @grant GM_getResourceURL// @grant GM.getResourceUrl// @copyright Copyright 2019 Jefferson Scher. Portions created by erosman.// @license BSD-3-clause// @resource mycon http://www.jeffersonscher.com/gm/src/gfrk-THS-ver240.png// ==/UserScript==var script_about = "https://greasyfork.org/scripts/13007-text-highlight-and-seek";/* --------- Note ---------TO INCLUDE SITES (only Greasy Fork and OpenUserJS are initially included):Go to Add-ons - User Scripts (Ctrl+Shift+a/Cmd+Shift+a on Firefox Windows/Mac)Click on the Script's OptionUnder User Settings Tab, Add Included/Excluded Pages that you want the script to run onClick OKNote from erosman: If you find that another script #####es with this script, set Text Highlight and Seek to Execute first.Go to Add-ons - User Scripts ('Ctrl+ Shift + a' on Firefox)Right Click on the ScriptOn the context menu click: Execute firstOn Add-ons - User Scripts, you can also Click on the Execution Order (top Right) andchange the execution order so that Text Highlight and Seek runs before those scripts that #####es with it.*/var hlframe, hlobjDefault, kwhieditstyle, hljson, hlobj, hlkeys, kwold, hlold, hlbtnvis, hlprecode, hlnextset, hbtndisp;var GM4 = (typeof GM_getValue === "undefined") ? true : false;async function THS_init(){if (!GM4){hlframe = GM_getValue("hlframe", ""); // get iframe pref} else {hlframe = await GM.getValue("hlframe", "");}if (hlframe == ""){hlframe = "none";if (!GM4){GM_setValue("hlframe", hlframe);} else {await GM.setValue("hlframe", hlframe);}}if ((window.self !== window.top) && (hlframe != "any")) { // framed pageif (hlframe == "none") return;if (hlframe == "same") {console.log(window.self.location.hostname + " vs " + window.top.location.hostname);}}// sample keyword+style object to get startedhlobjDefault = {"set100" : {keywords : "scripts|script",type : "string",hlpat : "",textcolor : "rgb(0,0,0)",backcolor : "rgb(255,255,128)",fontweight : "inherit",custom : "",enabled : "true",visible : "true",updated : ""},"set099" : {keywords : "site",type : "word",hlpat : "",textcolor : "rgb(0,0,0)",backcolor : "rgb(255,192,255)",fontweight : "inherit",custom : "",enabled : "true",visible : "true",updated : ""},"set098" : {keywords : "^October \\d{1,2}",type : "regex",hlpat : "",textcolor : "rgb(0,0,0)",backcolor : "rgb(192,255,255)",fontweight : "inherit",custom : "",enabled : "true",visible : "true",updated : ""}};kwhieditstyle = ["rgb(0,0,255)","rgb(255,255,0)","inherit",""];// read pref storage: keyword-style setsif (!GM4){hljson = GM_getValue("kwstyles");} else {hljson = await GM.getValue("kwstyles");}if (!hljson || hljson.length == 0){hlobj = hlobjDefault;// check for legacy preferencesif (!GM4){kwold = GM_getValue("keywords");} else {kwold = await GM.getValue("keywords");}if (kwold) if(kwold.length > 0) {hlobj.set100.keywords = kwold.split(',').join('|');}if (!GM4){hlold = GM_getValue("highlightStyle");} else {hlold = await GM.getValue("highlightStyle");}if (hlold) if(hlold.length > 0) {// really should try to parse this, but for now...hlobj.set100.custom = hlold;}// save starting valueshljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}} else {hlobj = JSON.parse(hljson);}// global keys arrayhlkeys = Object.keys(hlobj);// read/set other prefsif (!GM4){hlbtnvis = GM_getValue("hlbtnvis", "");} else {hlbtnvis = await GM.getValue("hlbtnvis", "");}if (hlbtnvis == ""){hlbtnvis = "on";if (!GM4){GM_setValue("hlbtnvis", hlbtnvis);} else {await GM.setValue("hlbtnvis", hlbtnvis);}}if (!GM4){hlprecode = GM_getValue("hlprecode", "");} else {hlprecode = await GM.getValue("hlprecode", "");}if (hlprecode == ""){hlprecode = true;if (!GM4){GM_setValue("hlprecode", hlprecode);} else {await GM.setValue("hlprecode", hlprecode);}}if (!GM4){hlnextset = GM_getValue("hlnextset", "");} else {hlnextset = await GM.getValue("hlnextset", "");}if (hlnextset == ""){hlnextset = 101;if (!GM4){GM_setValue("hlnextset", hlnextset);} else {await GM.setValue("hlnextset", hlnextset);}}// Inject CSSinsertCSS(hlkeys);// first runTHmo_doHighlight(document.body,null);// Add MutationObserver to catch content added dynamicallyvar THmo_MutOb = (window.MutationObserver) ? window.MutationObserver : window.WebKitMutationObserver;if (THmo_MutOb){var THmo_chgMon = new THmo_MutOb(function(mutationSet){mutationSet.forEach(function(mutation){for (var i=0; i<mutation.addedNodes.length; i++){if (mutation.addedNodes[i].nodeType == 1){THmo_doHighlight(mutation.addedNodes[i],null);}}});});// attach chgMon to document.bodyvar opts = {childList: true, subtree: true};THmo_chgMon.observe(document.body, opts);}// Set up top highlight/seek barvar kwhibar = document.createElement("div");kwhibar.id = "thdtopbar";if (hlbtnvis == "on") var btnchk = " checked=\"checked\"";else var btnchk = "";if (hlprecode) var btnprecode = " checked=\"checked\"";else var btnprecode = "";kwhibar.innerHTML = "<form id=\"thdtopform\" onsubmit=\"return false\"><p id=\"thdtopbarhome\"><a href=\"" + script_about + "\" target=\"_blank\" title=\"Go to script install page\">JS</a></p>" +"<div id=\"thdtopcurrent\"><p id=\"thdtopkeywords\" title=\"Click to View, Edit, Seek, or Add Keywords\">Click to manage keyword/highlight sets • <em>Add New Set</em></p>" +"<div id=\"thdtopdrop\" style=\"display:none;\"><div id=\"thdtable\"><table cellspacing=\"0\"><tbody id=\"kwhitbod\"></tbody></table></div><p><button id=\"btnkwhiadd\">Add New Set</button>" +"<span style=\"float:right\"><button id=\"btnkwhiexport\">Export Sets</button> <button id=\"btnkwhiimport\">Import Sets</button> <button id=\"thdtopdropclose\">X</button></span></p></div></div>" +"<div id=\"thdtopfindbuttons\"><button title=\"First match\" thdaction=\"f\"><b>l</b>◀</button> <button title=\"Previous match\" thdaction=\"p\">◀</button> <span id=\"thdseekdesc\">Seek</span> <button title=\"Next match\" thdaction=\"n\">▶</button> <button title=\"Last match\" thdaction=\"l\">▶<b>l</b></button><div id=\"thdseekfail\"></div></div>" +"<div id=\"thdtopoptions\"><div>Options</div><ul><li><label title=\"Float a button in the upper right corner of the document to quickly access this panel\"><input type=\"checkbox\" id=\"chkhbtn\"" + btnchk +"> Show H button</label></li><li><label title=\"Highlight matches in <pre> and <code> tags\"><input type=\"checkbox\" id=\"chkprecode\"" + btnprecode +"> Match in pre/code</label></li><li><label style=\"padding-left:4px\">Framed pages:</label><br><select id=\"hlframeselect\" size=\"3\"><option value=\"none\">No highlighting</option><option value=\"same\">Same site only</option>" +"<option value=\"any\">Any site</option></select></li><li><button id=\"btnthsreread\" title=\"Update from and apply stored settings\" disabled>Re-Read Saved Prefs</button></li></ul></div>" +"<button class=\"btnkwhiclose\" onclick=\"document.getElementById('thdtopbar').style.display='none';document.getElementById('thdtopspacer').style.display='none';return false;\" style=\"float:right\">X</button></form>" +"<style type=\"text/css\">#thdtopbar{position:fixed;top:0;left:0;height:26px;width:100%;padding:0;color:#024;background:#ddd;font-family:sans-serif;font-size:16px;line-height:16px;border-bottom:1px solid #024;z-index:2500;display:none} " +"#thdtopbar,#thdtopbar *{box-sizing:content-box;} #thdtopform{display:block;position:relative;float:left;width:100%;margin:0;border:none;} " +"#thdtopbarhome,#thdtopcurrent,#thdtopfindbuttons,#thdtopoptions{float:left;top:0;left:0;margin:0;padding:5px 8px 4px;border-right:1px solid #fff;font-size:16px;} " +"#thdtopbarhome{width:22px;text-align:center;overflow:hidden;} #thdtopbarhome a{display:block;} #thdtopbarhome a img{display:block;border:none;border-radius:3px;padding:3px;margin:-3px 0 -4px 0;background-color:#fff} " +"#thdtopfindbuttons{padding-bottom:1px;position:relative} #thdtopfindbuttons button{margin:-5px 0 -2px 0;width:28px;height:18px;color:#024;background:#f0f0f0;border:1px solid #024;border-radius:4px;padding:1px 3px;} " +"#thdtopfindbuttons button:hover{background:#ffa;} #thdseekdesc{cursor:pointer} #thdtopkeywords{margin:0;width:500px;cursor:pointer;} #thdtopkeywords em{padding: 0 2px;} #thdtopkeywords em:hover{background:#ffa;}" +"#thdseekfail{display:none;position:absolute;top:30px;left:15px;z-index:2001;width:200px;color:#f8f8f8;background:#b00;border-radius:6px;text-align:center;font-size:12px;padding:3px}" +"#thdtopkeywords span{display:inline-block;width:100%;overflow:hidden;text-overflow:ellipsis;} #thdtable{max-height:600px;overflow-y:auto;overflow-x:hidden} " +"#thdtopdrop{position:absolute;top:26px;left:38px;width:500px;margin:0 -1px 0 -1px;padding:0 8px 8px 8px;background:#ddd;border:1px solid #024;border-top:none;border-radius:0 0 6px 6px;} " +"#thdtopdrop table{width:100%;background:#fff;border-top:1px solid #000;border-left:1px solid #000;table-layout:fixed} " +"#thdtopdrop td{padding:4px 4px; vertical-align:top;border-right:1px solid #000;border-bottom:1px solid #000;} #thdtopdrop td div{word-wrap:break-word} #thdtopdrop p{margin-top:8px;margin-bottom:0;} " +"#thdtopoptions{position:relative;width:160px;height:26px;padding:0 8px;} #thdtopoptions > div{padding:5px 0 4px;} " +"#thdtopoptions ul{position:absolute;top:26px;left:0;width:160px;margin:0 -1px 0 -1px;padding:0 8px 8px 8px;background:#ddd;border:1px solid #024;border-top:none;border-radius:0 0 6px 6px;list-style:none;} " +"#thdtopoptions li{width:100%;float:left;padding:2px 0;} #thdtopoptions ul{display:none;} #thdtopoptions:hover ul{display: block;border:1px solid #024;border-top:none;} #thdtopoptions li:hover{background:#eee;}" +".btnkwhiclose{float:right;font-size:11px;margin-top:2px;} .thdtype{color:#ccc;float:right;font-size:12px;padding-top:8px;} #thdtopbar label{font-weight:normal;display:inline;margin:0} #hlframeselect{margin:3px 0 3px 4px;border-radius:4px}</style>";document.body.appendChild(kwhibar);// Attach event handlersdocument.getElementById("thdtopkeywords").addEventListener("click",thddroptoggle,false);document.getElementById("kwhitbod").addEventListener("click",kwhiformevent,false);document.getElementById("kwhitbod").addEventListener("dblclick",kwhiformevent,false);document.getElementById("btnkwhiadd").addEventListener("click",kwhinewset,false);document.getElementById("btnkwhiexport").addEventListener("click",kwhiexport,false);document.getElementById("btnkwhiimport").addEventListener("click",kwhiimport,false);document.getElementById("thdtopfindbuttons").addEventListener("click",thdseek,false);document.getElementById("chkhbtn").addEventListener("click",kwhihbtn,false);document.getElementById("chkprecode").addEventListener("click",kwhiprecode,false);document.getElementById("btnthsreread").addEventListener("click",thsreread,false);document.getElementById("thdtopdropclose").addEventListener("click",kwhitopdropclose,false);// frame optionsdocument.getElementById("hlframeselect").addEventListener("change",thsframeselect,false);setthsframeopts();// Add spacer at top of bodyvar divsp = document.createElement("div");divsp.id = "thdtopspacer";divsp.setAttribute("style","clear:both;display:none");divsp.style.height = parseInt(27 - parseInt(window.getComputedStyle(document.body,null).getPropertyValue("margin-top"))) + "px";document.body.insertBefore(divsp, document.body.childNodes[0]);// Switch JS text to iconvar JSBTN = document.createElement("img");if (!GM4){JSBTN.src = GM_getResourceURL("mycon");} else { /* asynchronous*/JSBTN.src = await GM.getResourceUrl("mycon");}document.querySelector("#thdtopbar a").textContent = "";document.querySelector("#thdtopbar a").appendChild(JSBTN);// Add menu itemif (!GM4) GM_registerMenuCommand("Show Text Highlight and Seek Bar - View, Edit, Add Keywords and Styles", editKW);// Inject H buttonif (hlbtnvis == "off") hbtndisp = ' style="display:none"';else hbtndisp = '';var dNew = document.createElement("div");dNew.innerHTML = '<button id="btnshowkwhi"' + hbtndisp + '>H</button><style type="text/css">#btnshowkwhi{position:fixed;top:4px;right:4px;opacity:0.2;' +'color:#000;background-color:#ffa;font-weight:bold;font-size:12px;border:1px solid #ccc;border-radius:4px;padding:2px 3px;z-index:1999;min-width:22px;min-height:22px}' +'#btnshowkwhi:hover{opacity:0.8}@media print{#btnshowkwhi{display:none;}}</style>';document.body.appendChild(dNew);document.getElementById("btnshowkwhi").addEventListener("click",editKW,false);// Set up add/edit formvar kwhied = document.createElement("div");kwhied.id = "kwhiedit";kwhied.innerHTML = "<form onsubmit=\"return false;\"><p style=\"margin-top:0\"><b>Edit/Add Keywords/Highlighting</b>" +"<span class=\"btnkwhiclose\"><button id=\"btnkwhimax\" title=\"Maximize dialog size\">^</button> " +"<button onclick=\"document.getElementById('kwhiedit').style.display='none'; return false;\" title=\"Close dialog\">X</button></span>" +"</p><p>List longer forms of a word first to match both in full. Example: \"children|child\" will highlight both, but \"child|children\" " +"will only highlight child, it won't expand the selection to children.</p>" +"<table cellspacing=\"0\" style=\"table-layout:fixed\"><tbody><tr kwhiset=\"new\"><td style=\"width:calc(100% - 464px)\">" +"<p contenteditable=\"true\" style=\"border:1px dotted #000;word-wrap:break-word;display:block!important\" class=\"\">placeholder</p>" +"<p style=\"margin-top:2em\">Match type: <select id=\"kwhipattype\"><option value=\"string\" selected>Anywhere in a word</option>" +"<option value=\"word\">\"Whole\" words only</option><option value=\"regex\">Regular Expression (advanced)</option></select></p></td>" +"<td style=\"width:416px\" id=\"stylecontrols\"><p><span>Text color:</span> <input id=\"txtcolorinput\" type=\"color\" value=\"#000000\" title=\"Pop up color picker\"> " +"R:<input id=\"txtr\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" style=\"width:3.25em\" value=\"0\"> " +"G:<input id=\"txtg\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" style=\"width:3.25em\" value=\"0\"> " +"B:<input id=\"txtb\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" style=\"width:3.25em\" value=\"0\"> " +"<button id=\"btntxtreset\">Reset</button></p>" +"<p><span>Background:</span> <input id=\"bkgcolorinput\" type=\"color\" value=\"#ffff80\" title=\"Pop up color picker\"> " +"R:<input id=\"bkgr\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" style=\"width:3.25em\" value=\"255\"> " +"G:<input id=\"bkgg\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" style=\"width:3.25em\" value=\"255\"> " +"B:<input id=\"bkgb\" type=\"number\" min=\"0\" max=\"255\" step=\"1\" style=\"width:3.25em\" value=\"128\"> <button id=\"btnbkgreset\">Reset</button></p>" +"<p><span>Font-weight:</span> <select id=\"fwsel\"><option value=\"inherit\" selected>inherit</option>" +"<option value=\"bold\"><b>bold</b></option><option value=\"normal\">not bold</option></select></p><p><span>Custom:</span> <input type=\"text\" " +"id=\"kwhicustom\" style=\"width:55%\"> <button id=\"kwhicustomapply\">Apply</button></p></td></tr></tbody></table>" +"<p><button id=\"btnkwhisave\">Save Changes</button> <button id=\"btnkwhicancel\">Discard Changes</button> " +"<button id=\"btnkwhiremove\">Hide Set</button> <button id=\"btnkwhirevert\" disabled>Revert Last Keyword Edit</button></p></form><style type=\"text/css\">" +"#kwhiedit{position:fixed;top:1px;left:150px;width:800px;height:400px;border:1px solid #000;border-radius:6px;padding:1em;color:#000;" +"background:#fafafa;z-index:2501;display:none} #kwhiedit table{width:100%;background:#fff;border-top:1px solid #000;" +"border-left:1px solid #000;} #kwhiedit td{padding:0 12px; vertical-align:top;border-right:1px solid #000;border-bottom:1px solid #000;}" +"#kwhiedit td p{margin-top:12px;} #stylecontrols>p>span{display:inline-block;width:6.5em;} " +"#stylecontrols input[type=\"color\"]{padding:0; width:24px; height:1.25em; border:none;}</style><style type=\"text/css\" id=\"kwhiedittemp\"></style></div>";document.body.appendChild(kwhied);// Attach event handlersdocument.getElementById("btnkwhisave").addEventListener("click",kwhisavechg,false);document.getElementById("btnkwhicancel").addEventListener("click",kwhicancel,false);document.getElementById("btnkwhiremove").addEventListener("click",kwhiremove,false);document.getElementById("btnkwhirevert").addEventListener("click",kwhirevert,false);document.getElementById("stylecontrols").addEventListener("input",updatestyle,false);document.getElementById("stylecontrols").addEventListener("change",updatecolor,false);document.getElementById("btntxtreset").addEventListener("click",kwhicolorreset,false);document.getElementById("btnbkgreset").addEventListener("click",kwhicolorreset,false);document.getElementById("fwsel").addEventListener("change",kwhifwchg,false);document.getElementById("kwhicustomapply").addEventListener("click",kwhicustom,false);document.getElementById("btnkwhimax").addEventListener("click",kwhimaxrestore,false);// Context menu options -- do not replace any existing menu!if (!document.body.hasAttribute("contextmenu") && "contextMenu" in document.documentElement){var cmenu = document.createElement("menu");cmenu.id = "THDcontext";cmenu.setAttribute("type", "context");cmenu.innerHTML = '<menu label="Text Highlight and Seek">' +'<menuitem id="THDshowbar" label="Show bar"></menuitem>' +'<menuitem id="THDenableset" label="Enable matching set"></menuitem>' +'<menuitem id="THDdisableset" label="Disable this set"></menuitem>' +'<menuitem id="THDnewset" label="Add new set"></menuitem>' +'</menu>';document.body.appendChild(cmenu);document.getElementById("THDshowbar").addEventListener("click",editKW,false);document.getElementById("THDenableset").addEventListener("click",cmenuEnable,false);document.getElementById("THDdisableset").addEventListener("click",cmenuDisable,false);document.getElementById("THDnewset").addEventListener("click",cmenuNewset,false);// attach menu and create event for filteringdocument.body.setAttribute("contextmenu", "THDcontext");document.body.addEventListener("contextmenu",cmenuFilter,false);}if (!GM4) GM_registerMenuCommand("TEST ONLY - flush keyword sets for Text Highlight and Seek", flushData);}THS_init();// Main workhorse routinefunction THmo_doHighlight(el,subset){if (subset) var keyset = subset;else var keyset = hlkeys;for (var j = 0; j < keyset.length; ++j) {var hlset = keyset[j];if (hlobj[hlset].visible == "true" && hlobj[hlset].enabled == "true"){var hlkeywords = hlobj[hlset].keywords;if (hlkeywords.length > 0) {if (hlobj[hlset].type != "regex"){var rQuantifiers = /[-\/\\^$*+?.()[\]{}]/g;hlkeywords = hlkeywords.replace(rQuantifiers, '\\$&');if (hlobj[hlset].type == "word"){hlkeywords = "\\b" + hlkeywords.replace(/\|/g, "\\b|\\b") + "\\b";}}//console.log("hlset:"+hlset+"\nhlkeywords:"+hlkeywords);var pat = new RegExp('(' + hlkeywords + ')', 'gi');var span = document.createElement('thdfrag');span.setAttribute("thdcontain","true");// getting all text nodes with a few exceptionsif (hlprecode){var snapElements = document.evaluate('.//text()[normalize-space() != "" ' +'and not(ancestor::style) ' +'and not(ancestor::script) ' +'and not(ancestor::textarea) ' +'and not(ancestor::div[@id="thdtopbar"]) ' +'and not(ancestor::div[@id="kwhiedit"]) ' +'and not(parent::thdfrag[@txhidy15])]',el, null, XPathR###lt.UNORDERED_NODE_SNAPSHOT_TYPE, null);} else {var snapElements = document.evaluate('.//text()[normalize-space() != "" ' +'and not(ancestor::style) ' +'and not(ancestor::script) ' +'and not(ancestor::textarea) ' +'and not(ancestor::pre) ' +'and not(ancestor::code) ' +'and not(ancestor::div[@id="thdtopbar"]) ' +'and not(ancestor::div[@id="kwhiedit"]) ' +'and not(parent::thdfrag[@txhidy15])]',el, null, XPathR###lt.UNORDERED_NODE_SNAPSHOT_TYPE, null);}if (!snapElements.snapshotItem(0)) { break; }for (var i = 0, len = snapElements.snapshotLength; i < len; i++) {var node = snapElements.snapshotItem(i);// check if it contains the keywordsif (pat.test(node.nodeValue)) {// create an element, replace the text node with an elementvar sp = span.cloneNode(true);sp.innerHTML = node.nodeValue.replace(/</g, '<').replace(/>/g, '>').replace(pat, '<thdfrag class="THmo '+hlset+'" txhidy15="'+hlset+'">$1</thdfrag>');node.parentNode.replaceChild(sp, node);// try to un-nest containersif (sp.parentNode.hasAttribute("thdcontain")) sp.outerHTML = sp.innerHTML;}}}}}}function insertCSS(setkeys){for (var j = 0; j < setkeys.length; ++j){var hlset = setkeys[j];if (hlobj[hlset].visible == "true"){var rule = "."+hlset+"{display:inline!important;";if (hlobj[hlset].textcolor.length > 0) rule += "color:"+hlobj[hlset].textcolor+";";if (hlobj[hlset].backcolor.length > 0) rule += "background-color:"+hlobj[hlset].backcolor+";";if (hlobj[hlset].fontweight.length > 0) rule += "font-weight:"+hlobj[hlset].fontweight+";";if (hlobj[hlset].custom.length > 0) rule += hlobj[hlset].custom+";";rule += "}";var setrule = document.querySelector('style[hlset="' + hlset +'"]');if (!setrule){var s = document.createElement("style");s.type = "text/css";s.setAttribute("hlset", hlset);s.appendChild(document.createTextNode(rule));document.body.appendChild(s);} else {setrule.innerHTML = rule;}}}}function editKW(e){refreshSetList();// show formdocument.getElementById("thdtopbar").style.display = "block";document.getElementById("thdtopspacer").style.display = "block";}function thdDropSetList(e){refreshSetList();document.getElementById("thdtopdrop").style.display = "block";}function thddroptoggle(e){if (document.getElementById("thdtopdrop").style.display == "none"){thdDropSetList();if (e.target.nodeName == "EM") kwhinewset();} else if (e.target.nodeName == "EM"){kwhinewset();} else {document.getElementById("thdtopdrop").style.display = "none";}}function refreshSetList(e){// clear old rows from formdocument.getElementById("kwhitbod").innerHTML = "";// populate data - hlobj is globalfor (var j = 0; j < hlkeys.length; ++j){var hlset = hlkeys[j];if (hlobj[hlset].visible == "true"){if (hlobj[hlset].enabled == "true") var strchk = ' checked=\"checked\"';else var strchk = '';var newrow = document.createElement("tr");var thdtypenote = '';newrow.setAttribute("kwhiset", hlset);if(hlobj[hlset].type != "string"){thdtypenote = '<span class="thdtype">' + hlobj[hlset].type + '</span>';}if (j == 0){newrow.innerHTML = '<td style=\"width:286px\"><div class=\"' + hlset + '\">' + hlobj[hlset].keywords + '</div>' + thdtypenote + '</td>' +'<td style=\"width:195px\"><button kwhiset=\"' + hlset + '\" title=\"Bring matches into view\">Seek</button> ' +'<button kwhiset=\"' + hlset + '\">Edit</button> <label><input type=\"checkbox\" kwhiset=\"' + hlset +'\"' + strchk + '"> Enabled </label></td>';} else {newrow.innerHTML = '<td><div class=\"' + hlset + '\">' + hlobj[hlset].keywords + '</div>' + thdtypenote + '</td>' +'<td><button kwhiset=\"' + hlset + '\" title=\"Bring matches into view\">Seek</button> ' +'<button kwhiset=\"' + hlset + '\">Edit</button> <label><input type=\"checkbox\" kwhiset=\"' + hlset +'\"' + strchk + '"> Enabled </label></td>';}document.getElementById("kwhitbod").appendChild(newrow);}}}async function kwhiformevent(e){if (e.target.nodeName == "INPUT"){ // Enabled checkboxvar hlsetnum = e.target.getAttribute("kwhiset");kwhienabledisable(hlsetnum, e.target.checked);}if (e.target.nodeName == "BUTTON"){ // Call up edit form or find barvar hlset = e.target.getAttribute('kwhiset');if (e.target.textContent == "Edit"){// need to cancel in-place editor if it's openkwhicancelipe(hlset);// set set number attributedocument.querySelector('#kwhiedit tr').setAttribute('kwhiset', hlset);// set class for keywordsdocument.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').className = hlset;// enter placeholder text & typedocument.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').textContent = hlobj[hlset].keywords;document.getElementById("kwhipattype").selectedIndex = 0;if (hlobj[hlset].type == "word") document.getElementById("kwhipattype").selectedIndex = 1;if (hlobj[hlset].type == "regex") document.getElementById("kwhipattype").selectedIndex = 2;// set style editing to default and override with set ruleskwhieditstyle = ["rgb(0,0,255)","rgb(255,255,0)","inherit",""]; // defaultsif (hlobj[hlset].textcolor.length > 0) kwhieditstyle[0] = hlobj[hlset].textcolor;if (hlobj[hlset].backcolor.length > 0) kwhieditstyle[1] = hlobj[hlset].backcolor;if (hlobj[hlset].fontweight.length > 0) kwhieditstyle[2] = hlobj[hlset].fontweight;if (hlobj[hlset].custom.length > 0) kwhieditstyle[3] = hlobj[hlset].custom;kwhiShowEditForm();}if (e.target.textContent == "Seek"){// need to cancel in-place editor if it's openkwhicancelipe(hlset);// Enable set if not currently enabled (2.3.5)var chkbx = e.target.parentNode.querySelector('input[type="checkbox"]');if (!chkbx.checked) chkbx.click();// Populate current seek set to #thdtopkeywordsvar divDataTD = e.target.parentNode.previousElementSibling;document.getElementById("thdtopkeywords").innerHTML = "<i>Seeking:</i> " + divDataTD.firstChild.outerHTML;// Store set to seek in #thdtopfindbuttonsdocument.getElementById("thdtopfindbuttons").setAttribute("thdseek", hlset);// Close Keyword Sets formdocument.getElementById('thdtopdrop').style.display='none';// Send click event to the "seek first" buttondocument.getElementById('thdtopfindbuttons').children[0].click();}if (e.target.textContent == "Save"){ // Check and save in-place keyword edit// get set number attributevar hlset = e.target.getAttribute("kwhiset");var kwtext = document.querySelector('div.'+hlset+' p.'+hlset).textContent;if (kwtext == hlobj[hlset].keywords){ // Nothing to save, cancel the editkwhicancelipe(hlset);return;}// Save keyword changes WITHOUT user confirmationhlobj[hlset].prevkeyw = hlobj[hlset].keywords;hlobj[hlset].prevtype = hlobj[hlset].type;if (hlobj[hlset].type != "regex") hlobj[hlset].keywords = kwtext;else{hlobj[hlset].keywords = kwtext.replace(/\\/g, "\\");hlobj[hlset].hlpat = ""; //TODOLATER}// Set updated date/timehlobj[hlset].updated = (new Date()).toJSON();// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Update CSS rule and parent forminsertCSS([hlset]);refreshSetList();// Unhighlight, re-highlight, close in-place editorunhighlight(hlset);THmo_doHighlight(document.body,[hlset]);kwhicancelipe(hlset);}if (e.target.textContent == "Cancel"){ // Revert in-place editor// get set number attributevar hlset = e.target.getAttribute("kwhiset");kwhicancelipe(hlset);}if (e.target.textContent == "Revert"){ // Restore previous keywords// get set number attributevar hlset = e.target.getAttribute("kwhiset");// gray the buttondocument.getElementById('thsrevert' + hlset).setAttribute('disabled', 'disabled');// get the previous keywords (if any)if (hlobj[hlset].prevkeyw && hlobj[hlset].prevkeyw != '') var kwtext = hlobj[hlset].prevkeyw;if (!kwtext || kwtext == ''){ // uh-ohalert('Unable to undo, sorry!');document.getElementById('thsrevert' + hlset).setAttribute('disabled', 'disabled');return;}// Save keyword changes WITHOUT user confirmationhlobj[hlset].keywords = kwtext;hlobj[hlset].type = hlobj[hlset].prevtype;hlobj[hlset].prevkeyw = '';hlobj[hlset].prevtype = '';// Set updated date/timehlobj[hlset].updated = (new Date()).toJSON();// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Update CSS rule and parent forminsertCSS([hlset]);refreshSetList();// Unhighlight, re-highlightunhighlight(hlset);THmo_doHighlight(document.body,[hlset]);}}if (e.type == "dblclick" && e.target.nodeName == "DIV"){ // Set up in-place quick editorif (e.target.children.length == 0) { // Ignore the double-click if the editor was already set upvar hlset = e.target.className;e.target.innerHTML = '<p class="' + hlset +'" contenteditable="true" style="border:1px dotted #000">' + e.target.textContent + '</p>' +'<p style="background-color:#fff;font-size:0.8em"><button kwhiset="' + hlset + '" title="Update keywords for this set" style="font-size:0.8em">' +'Save</button> <button kwhiset="' + hlset + '" title="Keep saved keywords" style="font-size:0.8em">Cancel</button> <button kwhiset="' +hlset + '" id="thsrevert' + hlset + '" title="Revert last edit" style="font-size:0.8em" disabled>Revert</button></p>';var rng = document.createRange();rng.selectNodeContents(e.target.children[0]);var sel = window.getSelection();sel.removeAllRanges();sel.addRange(rng);if (hlobj[hlset].prevkeyw && hlobj[hlset].prevkeyw != '') {document.getElementById('thsrevert' + hlset).removeAttribute('disabled');document.getElementById('thsrevert' + hlset).setAttribute('title','Revert to "' + hlobj[hlset].prevkeyw + '"');}}}}async function kwhienabledisable(hlsetnum,enable){if (enable == false) {// Update object and persist to GM storagehlobj[hlsetnum].enabled = "false";hljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Unhighlightunhighlight(hlsetnum);// Clear seek info from bar if this set is therevar seekset = document.getElementById("thdtopfindbuttons").getAttribute("thdseek");if (seekset){if(seekset.indexOf("|") > -1) seekset = seekset.split("|")[0];if (hlsetnum == seekset){document.getElementById("thdtopfindbuttons").setAttribute("thdseek","");document.getElementById("thdseekdesc").textContent = "Seek";document.getElementById("thdtopkeywords").innerHTML = "Click to manage keyword/highlight sets • <em>Add New Set</em>";}}} else {// Update object and persist to GM storagehlobj[hlsetnum].enabled = "true";hljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// HighlightTHmo_doHighlight(document.body,[hlsetnum]);}}function kwhinewset(e,kwtext){ // call up new set form// set set number attributedocument.querySelector('#kwhiedit tr').setAttribute('kwhiset', 'new');// clear class for keywordsdocument.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').className = "";// enter placeholder text & default typeif (kwtext) document.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').textContent = kwtext;else document.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').textContent = "larry|moe|curly";document.getElementById("kwhipattype").selectedIndex = 0;// set style editing to defaultskwhieditstyle = ["rgb(0,0,255)","rgb(255,255,0)","inherit",""];kwhiShowEditForm();}function kwhiShowEditForm(){var rule = "#stylecontrols>p>span{";if (kwhieditstyle[0].length > 0) rule += "color:"+kwhieditstyle[0]+";";if (kwhieditstyle[1].length > 0) rule += "background-color:"+kwhieditstyle[1]+";";if (kwhieditstyle[2].length > 0) rule += "font-weight:"+kwhieditstyle[2]+";";if (kwhieditstyle[3].length > 0) rule += kwhieditstyle[3]+";";document.getElementById("kwhiedittemp").innerHTML = rule + "}";populateRGB("txt",kwhieditstyle[0]);populateRGB("bkg",kwhieditstyle[1]);document.getElementById("fwsel").value = kwhieditstyle[2];document.getElementById("kwhicustom").value = kwhieditstyle[3];updateColorInputs();// default the reversion button to disabledvar rbtn = document.getElementById("btnkwhirevert");rbtn.setAttribute('disabled','disabled');if (rbtn.hasAttribute('kwhiset')) rbtn.removeAttribute('kwhiset');rbtn.setAttribute('title','');// show formdocument.getElementById("kwhiedit").style.display = "block";// check for possible reversion optionvar hlset = document.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').className;if (hlset != "" && hlobj[hlset].prevkeyw && hlobj[hlset].prevkeyw != '') {rbtn.removeAttribute('disabled');rbtn.setAttribute('kwhiset',hlset);rbtn.setAttribute('title','Revert to "' + hlobj[hlset].prevkeyw + '"');}}function kwhiexport(e){prompt("JSON data\nPress Ctrl+c or right-click to copy\n ", JSON.stringify(hlobj));}async function kwhiimport(e){var txtImport = prompt("Paste in the exported data and click OK to start parsing", "");try{var objImport = JSON.parse(txtImport);} catch(err){alert("Sorry, data does not appear to be in the proper format. Here's the error according to Firefox: \n\n"+err+"\n\nHopefully you can resolve that!");return;}var keysImport = Object.keys(objImport);// Compare for duplicate set numbersvar keysString = "|" + hlkeys.join("|") + "|";var counter = 0;for (var j = 0; j < keysImport.length; ++j){if(keysString.indexOf("|"+keysImport[j]+"|") > -1) counter++;}if (counter > 0){var arc = prompt("Detected "+counter+" of "+keysImport.length+" set numbers to be imported already exist. Do you want to:\nAdd these sets [A]\nReplace existing sets [R]\nTotally replace all existing sets [T]\nCancel the import [C]?","A");if (!arc) return;if (arc.length == 0) return;if (arc.toLowerCase() == "c") return;if (arc.toLowerCase() == "t"){if(!confirm("Total replacement has no error checking. Click OK to confirm total replacement, or click Cancel to only Replace matching sets.")) arc = "R";}} else {var arc = "A";}if (arc.toLowerCase() == "t"){ // Total replacementhlobj = JSON.parse(txtImport);// Update the global key arrayhlkeys = Object.keys(hlobj);// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Apply to page (see below)} else {for (var j = 0; j < keysImport.length; ++j){ // Add/replace individual setsvar impset = keysImport[j];if(keysString.indexOf("|"+impset+"|") > -1 && arc.toLowerCase() == "r"){ // replacevar hlset = impset;hlobj[hlset].keywords = objImport[impset].keywords || "keywords|not|found";hlobj[hlset].type = objImport[impset].type || "string";hlobj[hlset].hlpat = objImport[impset].hlpat || "";hlobj[hlset].textcolor = objImport[impset].textcolor || "rgb(0,0,255)";hlobj[hlset].backcolor = objImport[impset].backcolor || "rgb(255,255,0)";hlobj[hlset].fontweight = objImport[impset].fontweight || "inherit";hlobj[hlset].custom = objImport[impset].custom || "";hlobj[hlset].enabled = objImport[impset].enabled || "true";hlobj[hlset].visible = objImport[impset].visible || "true";hlobj[hlset].updated = (new Date()).toJSON();} else { // addif(keysString.indexOf("|"+impset+"|") > -1 && arc.toLowerCase() == "a"){// create a new set number insteadvar hlset = "set" + hlnextset;hlnextset += 1;if (!GM4){GM_setValue("hlnextset",hlnextset);} else {await GM.setValue("hlnextset",hlnextset);}} else {var hlset = impset;}// add the sethlobj[hlset] = {keywords : objImport[impset].keywords || "keywords|not|found",type: objImport[impset].type || "string",hlpat : objImport[impset].hlpat || "",textcolor : objImport[impset].textcolor || "rgb(0,0,255)",backcolor : objImport[impset].backcolor || "rgb(255,255,0)",fontweight : objImport[impset].fontweight || "inherit",custom : objImport[impset].custom || "",enabled : objImport[impset].enabled || "true",visible : objImport[impset].visible || "true",updated : objImport[impset].updated || ""}}// Update the global key arrayhlkeys = Object.keys(hlobj);// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}}}// TODO: Could an error prevent reaching this point, for example, if the import object is missing properties due to bad editing?// Update CSS rule and command bar listinsertCSS(hlkeys);refreshSetList();// Unhighlight all, re-highlight all, close dialogunhighlight(null);THmo_doHighlight(document.body);}async function kwhihbtn(e){if (e.target.checked == false){hlbtnvis = "off";if (!GM4){GM_setValue("hlbtnvis",hlbtnvis);} else {await GM.setValue("hlbtnvis",hlbtnvis);}document.getElementById("btnshowkwhi").style.display = "none";} else {hlbtnvis = "on";if (!GM4){GM_setValue("hlbtnvis",hlbtnvis);} else {await GM.setValue("hlbtnvis",hlbtnvis);}document.getElementById("btnshowkwhi").style.display = "";}}async function kwhiprecode(e){if (e.target.checked == false){// Update var, persist the preference, unhighlight, rehighlighthlprecode = false;if (!GM4){GM_setValue("hlprecode",hlprecode);} else {await GM.setValue("hlprecode",hlprecode);}unhighlight(null);THmo_doHighlight(document.body);} else {// Update var, persist the preference, rehighlighthlprecode = true;if (!GM4){GM_setValue("hlprecode",hlprecode);} else {await GM.setValue("hlprecode",hlprecode);}THmo_doHighlight(document.body);}}function kwhicancelipe(setno){// clean up in-place editor(s)if (setno && setno != ''){var kwdiv = document.querySelector('#kwhitbod .'+setno);if (kwdiv){kwdiv.innerHTML = hlobj[setno].keywords;return;}} else { // Check 'em allvar divs = document.querySelector('#kwhitbod div');for (var n=0; n<divs.length; n++){if (divs[n].children.length > 0 && divs[n].className != '') kwhicancelipe(divs[n].className);}}}function kwhitopdropclose(e){kwhicancelipe('');document.getElementById('thdtopdrop').style.display='none';}function thsreread(e){//TODO}async function thsframeselect(e){var selopt = e.target.options[e.target.selectedIndex].value;if (hlframe != selopt) {hlframe = selopt;if (!GM4){GM_setValue("hlframe",hlframe);} else {await GM.setValue("hlframe",hlframe);}setthsframeopts();}}function setthsframeopts(){var sel = document.getElementById("hlframeselect");if (hlframe == "none"){sel.options[0].selected = true;sel.options[0].setAttribute("selected","selected");} else {sel.options[0].selected = false;if (sel.options[0].hasAttribute("selected")) sel.options[0].removeAttribute("selected");}if (hlframe == "same"){sel.options[1].selected = true;sel.options[1].setAttribute("selected","selected");} else {sel.options[1].selected = false;if (sel.options[1].hasAttribute("selected")) sel.options[1].removeAttribute("selected");}if (hlframe == "any"){sel.options[2].selected = true;sel.options[2].setAttribute("selected","selected");} else {sel.options[2].selected = false;if (sel.options[2].hasAttribute("selected")) sel.options[2].removeAttribute("selected");}}function thdseek(e){if (e.target.nodeName == "DIV") return; // ignore background clicksvar seekset = e.currentTarget.getAttribute("thdseek");if (!seekset){ // user needs to select a set to seek inthdDropSetList();} else {var seekparams = seekset.split("|");var seekmatches = document.querySelectorAll('thdfrag[txhidy15="'+seekparams[0]+'"]');// Update or add total size of set; FIGURE OUT LATER: what if this changed??seekparams[1] = seekmatches.length;if (seekmatches.length > 0){if (e.target.nodeName == "SPAN"){ // re-scroll to the current referencethdshow(seekmatches[parseInt(seekparams[2])]);} else { // BUTTONvar seekaction = e.target.getAttribute("thdaction");if (!seekaction) seekaction = "f";if (seekparams.length == 3){ // User has seeked in this setswitch (seekaction){case "f":seekparams[2] = 0;var rtn = thdshow(seekmatches[parseInt(seekparams[2])]);if (rtn == false) seekagain("n");break;case "p":if (parseInt(seekparams[2]) > 0) {seekparams[2] = parseInt(seekparams[2]) - 1;var rtn = thdshow(seekmatches[parseInt(seekparams[2])]);if (rtn == false){if (parseInt(seekparams[2]) > 0) seekagain("p");else seekfailnotc("No previous match visible");}} else {seekfailnotc("Already reached first match");}break;case "n":if (parseInt(seekparams[2]) < (seekmatches.length-1)) {seekparams[2] = parseInt(seekparams[2]) + 1;var rtn = thdshow(seekmatches[parseInt(seekparams[2])]);if (rtn == false){if (parseInt(seekparams[2]) < (seekmatches.length-1)) seekagain("n");else seekfailnotc("No later match visible");}} else {seekparams[2] = (seekmatches.length-1); // in case it's too high, fix that hereseekfailnotc("Already reached last match");}break;case "l":seekparams[2] = (seekmatches.length-1);var rtn = thdshow(seekmatches[parseInt(seekparams[2])]);if (rtn == false) seekagain("p");break;}} else {seekparams[2] = 0;thdshow(seekmatches[parseInt(seekparams[2])]);}document.getElementById("thdtopfindbuttons").setAttribute("thdseek", seekparams.join("|"));document.getElementById("thdseekdesc").textContent = (parseInt(seekparams[2])+1) + " of " + seekparams[1];}} else {document.getElementById("thdseekdesc").textContent = "0 of 0";}}}function thdshow(elt){ // this could be much prettier with animation! TODO: outline/box?elt.scrollIntoView();var rect = elt.getClientRects()[0];if (rect){ // scroll down one inch to avoid many fixed headersif (rect.top < 96) window.scroll(0, window.scrollY-96);return true;} else { // match is not visiblereturn false;}}function seekagain(dir){switch (dir){case "p":seekfailnotc("Hidden, trying previous match...");window.setTimeout(function(){document.querySelector('button[thdaction="p"]').click();},250);break;case "n":seekfailnotc("Hidden, trying next match...");window.setTimeout(function(){document.querySelector('button[thdaction="n"]').click();},250);break;}}var evttimer;function seekfailnotc(txt){var sfdiv = document.getElementById("thdseekfail");sfdiv.textContent = txt;sfdiv.style.display = "block";if (evttimer) window.clearTimeout(evttimer);evttimer = window.setTimeout(function(){document.getElementById("thdseekfail").style.display="none";}, 800);}function unhighlight(setnum){if (setnum) var tgts = document.querySelectorAll('thdfrag[txhidy15="' + setnum + '"]');else var tgts = document.querySelectorAll('thdfrag[txhidy15]'); // remove ALLfor (var i=0; i<tgts.length; i++){// Check for co-extant parent(s) to remove potentially stranded <span>svar parnode = tgts[i].parentNode, parpar = parnode.parentNode, tgtspan;if (parnode.hasAttribute("thdcontain") && parnode.innerHTML == tgts[i].outerHTML){parnode.outerHTML = tgts[i].textContent.replace(/</g, '<').replace(/>/g, '>');tgtspan = parpar;} else {tgts[i].outerHTML = tgts[i].textContent.replace(/</g, '<').replace(/>/g, '>');tgtspan = parnode;}tgtspan.normalize();if (tgtspan.hasAttribute("thdcontain")){parnode = tgtspan.parentNode;if (parnode){if (parnode.hasAttribute("thdcontain") && parnode.innerHTML == tgtspan.outerHTML && tgtspan.querySelectorAll('thdfrag[txhidy15]').length == 0){parnode.outerHTML = tgtspan.innerHTML;} else if (parnode.innerHTML == tgtspan.outerHTML && tgtspan.querySelectorAll('thdfrag[txhidy15]').length == 0) {parnode.innerHTML = tgtspan.innerHTML;}}}}}async function kwhisavechg(e){// Update object, regenerate CSS if applicable, apply to documentvar hlset = document.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').className;var kwtext = document.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').textContent;if (hlset == ""){// create a new set numbervar hlset = "set" + hlnextset;hlnextset += 1;if (!GM4){GM_setValue("hlnextset",hlnextset);} else {await GM.setValue("hlnextset",hlnextset);}// add the setif (document.getElementById("kwhipattype").value == "regex"){kwtext = kwtext.replace(/\\/g, "\\");var hlpattxt = ""; //TODOLATER} else {var hlpattxt = "";}hlobj[hlset] = {keywords : kwtext,type : document.getElementById("kwhipattype").value,hlpat : hlpattxt,textcolor : kwhieditstyle[0],backcolor : kwhieditstyle[1],fontweight : kwhieditstyle[2],custom : kwhieditstyle[3],enabled : "true",visible : "true",updated : ""}// Update the global key arrayhlkeys = Object.keys(hlobj);} else {var oldtype = hlobj[hlset].type;hlobj[hlset].type = document.getElementById("kwhipattype").value;// Save keyword changes after user confirmationif (kwtext != hlobj[hlset].keywords){if (confirm("Save updated keywords (and other changes)?")){hlobj[hlset].prevkeyw = hlobj[hlset].keywords;hlobj[hlset].prevtype = oldtype;if (hlobj[hlset].type != "regex"){hlobj[hlset].keywords = kwtext;} else {hlobj[hlset].keywords = kwtext.replace(/\\/g, "\\");hlobj[hlset].hlpat = ""; //TODOLATER}} else return;}// Save style changes without confirmationhlobj[hlset].textcolor = kwhieditstyle[0];hlobj[hlset].backcolor = kwhieditstyle[1];hlobj[hlset].fontweight = kwhieditstyle[2];hlobj[hlset].custom = kwhieditstyle[3];// Set updated date/timehlobj[hlset].updated = (new Date()).toJSON();}// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Update CSS rule and parent forminsertCSS([hlset]);refreshSetList();// Unhighlight, re-highlight, close dialogunhighlight(hlset);THmo_doHighlight(document.body,[hlset])document.getElementById('kwhiedit').style.display='none';}function kwhicancel(e){// Close dialog (fields will be refresh if it is opened again)document.getElementById('kwhiedit').style.display='none';}async function kwhiremove(e){var hlset = document.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').className;if (hlset == ""){alert("This set has not been saved and therefore does not need to be hidden, you can just close the dialog to discard it.");} else {if (confirm("Are you sure you want to hide this set instead of editing it to your own liking?")){hlobj[hlset].visible = "false";hlobj[hlset].updated = (new Date()).toJSON();// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Update set list, remove highlighting, close formrefreshSetList();unhighlight(hlset);document.getElementById('kwhiedit').style.display='none';}}}async function kwhirevert(e){// get set number attributevar hlset = e.target.getAttribute("kwhiset");// gray the buttone.target.setAttribute('disabled', 'disabled');// get the previous keywords (if any)if (hlobj[hlset].prevkeyw && hlobj[hlset].prevkeyw != '') var kwtext = hlobj[hlset].prevkeyw;if (!kwtext || kwtext == ''){ // uh-ohalert('Unable to undo, sorry!');return;}// Save keyword changes WITHOUT user confirmationhlobj[hlset].keywords = kwtext;hlobj[hlset].type = hlobj[hlset].prevtype;hlobj[hlset].prevkeyw = '';hlobj[hlset].prevtype = '';// Set updated date/timehlobj[hlset].updated = (new Date()).toJSON();// Persist the objecthljson = JSON.stringify(hlobj);if (!GM4){GM_setValue("kwstyles", hljson);} else {await GM.setValue("kwstyles", hljson);}// Update CSS rule and parent forminsertCSS([hlset]);refreshSetList();// Unhighlight, re-highlightunhighlight(hlset);THmo_doHighlight(document.body,[hlset]);// Refresh the keywords and typedocument.querySelector('#kwhiedit td:nth-of-type(1) p:nth-of-type(1)').textContent = hlobj[hlset].keywords;document.getElementById("kwhipattype").selectedIndex = 0;if (hlobj[hlset].type == "word") document.getElementById("kwhipattype").selectedIndex = 1;if (hlobj[hlset].type == "regex") document.getElementById("kwhipattype").selectedIndex = 2;}function kwhicolorreset(e){// what set is this?var set = document.querySelector('#kwhiedit tr').getAttribute('kwhiset');// check which button, reset the RGBif (e.target.id == "btntxtreset"){if (set == "new"){kwhieditstyle[0] = "rgb(0,0,255)";} else {kwhieditstyle[0] = hlobj[set].textcolor;}populateRGB("txt",kwhieditstyle[0]);setdivstyle(["txt"]);}if (e.target.id == "btnbkgreset"){if (set == "new"){kwhieditstyle[1] = "rgb(255,255,0)";} else {kwhieditstyle[1] = hlobj[set].backcolor;}populateRGB("bkg",kwhieditstyle[1]);setdivstyle(["bkg"]);}e.target.blur();}function populateRGB(prop,stylestring){var rgbvals = stylestring.substr(stylestring.indexOf("(")+1);rgbvals = rgbvals.substr(0,rgbvals.length-1).split(",");document.getElementById(prop+"r").value = parseInt(rgbvals[0]);document.getElementById(prop+"g").value = parseInt(rgbvals[1]);document.getElementById(prop+"b").value = parseInt(rgbvals[2]);}async function updatestyle(e){// validate value and apply changevar tgt;if (e.id != undefined) tgt = e;else tgt = e.target;if (tgt.id.indexOf("colorinput") > -1){// let's wait for the change event to fire before updating} else if (tgt.id.indexOf("txt") == 0 || tgt.id.indexOf("bkg") == 0){if (isNaN(tgt.value)){alert("Please only use values between 0 and 255");return;}if (parseInt(tgt.value) != tgt.value){tgt.value = parseInt(tgt.value);}if (tgt.value < 0){tgt.value = 0;}if (tgt.value > 255){tgt.value = 255;}if (tgt.id.indexOf("txt") == 0) setdivstyle(["txt"]);if (tgt.id.indexOf("bkg") == 0) setdivstyle(["bkg"]);} else {if (tgt.id == "kwhicustom") return;console.log("updatestyle on "+tgt.id);}}function setdivstyle(props){for (var i=0; i<props.length; i++){switch (props[i]){case "txt":kwhieditstyle[0] = "rgb(" + document.getElementById("txtr").value + "," +document.getElementById("txtg").value + "," + document.getElementById("txtb").value + ")";break;case "bkg":kwhieditstyle[1] = "rgb(" + document.getElementById("bkgr").value + "," +document.getElementById("bkgg").value + "," + document.getElementById("bkgb").value + ")";break;default:console.log("default?");}}var rule = "#stylecontrols>p>span{";if (kwhieditstyle[0].length > 0) rule += "color:"+kwhieditstyle[0]+";";if (kwhieditstyle[1].length > 0) rule += "background-color:"+kwhieditstyle[1]+";";if (kwhieditstyle[2].length > 0) rule += "font-weight:"+kwhieditstyle[2]+";";if (kwhieditstyle[3].length > 0) rule += kwhieditstyle[3]+";";document.getElementById("kwhiedittemp").innerHTML = rule + "}";updateColorInputs();}async function updateColorInputs(){document.getElementById('txtcolorinput').value = '#' + ('0' + parseInt(document.getElementById("txtr").value).toString(16)).slice(-2) +('0' + parseInt(document.getElementById("txtg").value).toString(16)).slice(-2) +('0' + parseInt(document.getElementById("txtb").value).toString(16)).slice(-2);document.getElementById('bkgcolorinput').value = '#' + ('0' + parseInt(document.getElementById("bkgr").value).toString(16)).slice(-2) +('0' + parseInt(document.getElementById("bkgg").value).toString(16)).slice(-2) +('0' + parseInt(document.getElementById("bkgb").value).toString(16)).slice(-2);}async function updatecolor(e){// duplicate colors to RBG input boxesif (e.target.id.indexOf("colorinput") > -1){var hexcolor = e.target.value;var prefix = e.target.id.slice(0,3);document.getElementById(prefix + 'r').value = parseInt(hexcolor.slice(1,3), 16);document.getElementById(prefix + 'g').value = parseInt(hexcolor.slice(3,5), 16);document.getElementById(prefix + 'b').value = parseInt(hexcolor.slice(5,7), 16);updatestyle(document.getElementById(prefix + 'r'));}}function kwhifwchg(e){kwhieditstyle[2] = e.target.value;setdivstyle([]);}function kwhicustom(e){kwhieditstyle[3] = document.getElementById("kwhicustom").value;setdivstyle([]);}async function kwhimaxrestore(e){var el = document.getElementById('kwhiedit');if (e.target.textContent == '^'){e.target.textContent = '_';e.target.setAttribute('title', 'Restore normal dialog size');el.style.left = '1px';el.style.width = 'calc(100% - 3px - 2em)';el.style.height = 'calc(100% - 4px - 2em)';} else {e.target.textContent = '^';e.target.setAttribute('title', 'Maximize dialog size');el.style.left = '';el.style.width = '';el.style.height = '';}}function cmenuFilter(e){document.getElementById("THDenableset").setAttribute("disabled","disabled");document.getElementById("THDenableset").setAttribute("THDtext","");document.getElementById("THDdisableset").setAttribute("disabled","disabled");document.getElementById("THDdisableset").setAttribute("THDset","");var s = window.getSelection();if (s.isCollapsed) document.getElementById("THDnewset").setAttribute("THDtext","");else document.getElementById("THDnewset").setAttribute("THDtext",s.getRangeAt(0).toString().trim());if (e.target.hasAttribute('txhidy15')){document.getElementById("THDdisableset").removeAttribute("disabled");document.getElementById("THDdisableset").setAttribute("THDset",e.target.getAttribute('txhidy15'));} else {document.getElementById("THDdisableset").setAttribute("disabled","disabled");if (!s.isCollapsed){document.getElementById("THDenableset").removeAttribute("disabled");document.getElementById("THDenableset").setAttribute("THDtext",s.getRangeAt(0).toString().trim());}}}function cmenuEnable(e){var kw = e.target.getAttribute("THDtext").toLowerCase();var toggled = false;for (var j = 0; j < hlkeys.length; ++j){var hlset = hlkeys[j];var kwlist = "|" + hlobj[hlset].keywords.toLowerCase() + "|";if(kwlist.indexOf("|" + kw + "|") > -1){if (hlobj[hlset].enabled == "true") break; // already enabledkwhienabledisable(hlset,true);refreshSetList();toggled = true;break;}}if (toggled == false){if (document.getElementById("thdtopbar").style.display != "block") editKW();if (document.getElementById("thdtopdrop").style.display != "block") thdDropSetList();}}function cmenuDisable(e){kwhienabledisable(e.target.getAttribute("THDset"),false);refreshSetList();}function cmenuNewset(e){//TODO - if there's a selection, get it into the formkwhinewset(e,e.target.getAttribute("THDtext"));}// TESTING ONLYasync function flushData(){if (!GM4){GM_setValue("kwstyles", "");} else {await GM.setValue("kwstyles", "");}}