// ==UserScript==
// @name WebEraser
// @version 1.2.1
// @namespace sfswe
// @description Erase parts of any webpage --annoyances, ads, images, etc., permanently with just, Ctrl + Left-Click.
// @include *
// @require https://code.jquery.com/jquery-3.1.0.js
// @require https://code.jquery.com/ui/1.12.0/jquery-ui.js
// @resource jqueryUiCss https://code.jquery.com/ui/1.12.0/themes/base/jquery-ui.css
// @resource whiteCurtains https://github.com/SloaneFox/imgstore/raw/master/whiteCurtains.jpg
// @icon https://github.com/SloaneFox/imgstore/raw/master/WebEraserIcon.jpg
// @run-at document-start
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @grant GM_getResourceText
// @grant GM_getResourceURL
// ==/UserScript==
//
// History
//
// updated Oct 2016. v1.2.1 Adapted for use also in Google Chrome/Chromium web browser.
// updated Sept 2016. v.1.2 Added user option to turn on the monitoring for new nodes (node mutations).
if (!chromeInit()) $(main);
var iframe=window!=window.parent, border_width=6;
var host=window.document.location.host,
pathname=window.document.location.pathname, webpage=host+pathname, website=host;
var erasedElems=getHidElems(), askedAlready, gelem, gelems, gpre_elem, whiteCurtains_res="whiteCurtains",
bblinker, promptOpen, rbcl="sfswe-redborder", pbcl="sfswe-prevborder", tbcl="sfswe-transparentborder";
var tab="      "; // tab=5spaces, emsp=4spaces, but HTML tab in a <pre> wider hence extra emsp's.
var curtain_icon=getValue("ownImageAddr","")||GM_getResourceURL(whiteCurtains_res), curtain_cnt=0;
var zaplists=new zaplist_composite(), overlay=false;
var config=getValue("config",{keepLayout:"checked",monitor:{}}); if (!config.monitor) config.monitor={};
Number.prototype.in=function(){for (i of Array.from(arguments)) if (this==i) return true;}; // Use brackets with a literal, eg, (2).in(3,4,2);
Number.prototype.inRange=function(min,max){ if (this >=min && this<=max) return true;}; // Ditto.
Number.prototype.withinRangeOf=function(range,target){ return this.inRange(target-range,target+range); }; // Ditto.
String.prototype.truncate=function(maxsize) { if (this.length<=maxsize+5) return this; else return this.slice(0,maxsize/2)+"..."+webpage.slice(-maxsize/2); }; //sumarize with ellipsis
//thousand's comma, call Number.toLocaleString()
if (iframe) console.log=x=>null; //logger(); // Logs from doc start.
document.addEventListener("scroll", function(e){ if (!overlay) return; e.preventDefault();e.stopPropagation();e.stopImmediatePropagation();},true);
function main() {
//timer();
init_jquery();
inner_eraseElements();
//$(window).click(handleClick);
window.addEventListener("click",handleClick,true);
GM_addStyle( jqueryui_dialog_css //GM_getResourceText ("jqueryUiCss")
+".sfswe-prevborder { border-color:transparent !important;border-width:"+border_width+"px !important;border-style:double !important; }"
+".sfswe-transparentborder { border-color:transparent !important;border-width:"+border_width+"px !important;border-style:double !important; }"
+".sfswe-redborder { border-color:red !important; border-width:"+border_width+"px !important;border-style:double !important; }"
); // A later defined rule has precedence when both rules in effect.
//setTimeout(inner_eraseElements,1500);
GM_registerMenuCommand("Erase Web Elements ["+(erasedElems?"some erased":"none erased")+"]", eraseElementsCmd,"","", "E");
setTimeout(reattachTornCurtains,2000);
gelems=$();
} //main()
function keypressHandler(event) { try{ //while prompt is open.
//console.log("keypressHandler",event.key);
var ip=$("#sfswe-seledel:enabled");
if (ip.length) { //live typing of selector.
setTimeout(ip=>{
var cval=ip.val();
if ($(cval).length) { // may unwind.
highlightElement(0,"off",null,true);
highlightElement($(cval),null,null,true); }
},500,ip);
} else { // widen/narrow
switch(event.key) {
case "w": widen(); break;
case "n": narrow(); break;
default: return; }
return false;
}
} catch(e) {console.error("An key handler error:"+e+" "+e.lineNumber);} };
function handleClick(e,iframe_click) { try {
if (e.shiftKey || e.altKey || e.metaKey) return;
if (e.ctrlKey) { // cut item, delete element
if (e.preventDefault) {e.preventDefault(); e.stopPropagation();}
if (!iframe_click) {
var seltext_len=window.getSelection().toString().length;
window.status="webEraser, Ctrl-Click, on HTML element:"+e.target.tagName+" "+seltext_len+" "+iframe;
console.log(window.status,e.target);
if (seltext_len != 0) return;
if (e.target.blur) e.target.blur();
if (iframe) {
window.parent.postMessage("sfswe-iframe-click","*"); // msg,origin
return false;
}
}
var permrm, target=e.target;
while (/HTMLUnknownElement/.test(target.toString())) target=target.parentNode; //Avoid non HTML tags.
if ($(target).is(".WebEraserCurtain")) {
let reply=confirm("This will completely remove selected item, continue?");
if (reply!=null) openCurtains("zap",$(target).siblings("img").addBack());
}
else if (!askedAlready) checkIfPermanentRemoval(target,function(permrm){
if (permrm==false) { askedAlready=true;alert("You hit TEMP, ctrl-click from now until page is reloaded will merely remove elements from the page temporarily. ");}
if (permrm!=undefined) inner_eraseElements("from Click"); //undefined==>escape (cancel)
});
else target.style.setProperty("display","none","important");
return false;
} // endif e.ctrlKey
} catch(e) { console.error("Click handling error:"+e+" "+e.lineNumber); } }; //handleClick()
window.addEventListener('message', function(e){ //reads postMessage().
if (e.data!="sfswe-iframe-click") return;
var iframeEl=$("iframe").filter(function(){ return this.contentWindow==e.source; });
handleClick({target:iframeEl[0],ctrlKey:true},"iframe_click");
}, false);
function sprompt(pretext,initval,cb,cancelbtnText="Cancel",okbtnText="OK") { // "Cancel" returns null reply, empty "OK" returns "" reply, Escape key returns undefined reply. undefined==null is true. but not for ""
var input_tag, input_style="width:80%;font-size:small;";
if (initval!==undefined) input_tag=initval.length<50 ? "input" : (input_style="width:95%;height:100px;","textarea");
var content=$("<div class=sfswe-content tabindex=2 style='outline:none;white-space:pre-wrap;'><div >"+pretext+"</div>"
+(initval!==undefined ? "<"+input_tag+" spellcheck='false' style='"+input_style+"' tabindex='1'></"+input_tag+">":"")+"</div>");
content.find("input:not(:checkbox),textarea").val(initval);
var sp1=$(document).scrollTop();
var dfunc=content.dialog.bind(content);
var dialog=content.dialog({
modal: true, width:"60%", // position: { my: "center", at: "center center-25%", of: window }, // Greater percent further to top.
buttons: {
[cancelbtnText]: function() { cb(null, $(this).find("input,textarea").val()); dfunc("close");},
[okbtnText]: function() { cb($(this).find("input,textarea").val()||""); dfunc("close"); }
},
close: function(e) { dialog.off("keydown"); $(document).scrollTop(sp1); if (e.key=="Escape") cb(undefined);dfunc("destroy");} }).parent();
dialog.wrap("<div class=sfswe-sprompt></div>"); // allows css rules to exclude other jqueryUi css on webpage from own settings, a
dialog.keydown(function(e){ if (e.key == "Enter" && !/textarea/i.test(e.target.tagName)) $("button:contains("+okbtnText+")",this).click(); });
dialog.css({"z-index":2147483647, position:"fixed", top: "50px"});
dialog.find(".ui-dialog-titlebar").remove(); // No img in css for close 'x' at top right so remove. Title bar not in normal confirm anyhow.
dialog.draggable("option","handle", ".ui-dialog-buttonpane"); //
dialog.resizable();
setTimeout(function(){content.focus();},100);
return dialog; //.ui-dialog
}
function sconfirm(msg,cb,cancelbtnText,okbtnText) { return sprompt(msg,undefined,cb,cancelbtnText,okbtnText); }
function checkIfPermanentRemoval(target,cb) { // called from click handler.
var parent=target.parentNode, index=0;
var msg="Permanently erase selected element(s) — now seen on page with a red border that blinks?"
+"\nUse 'w' and 'n' keys freely, to widen and narrow your selection."
+"\nEscape quits, Enter OKs. Use the GM menu <a href='#abc"+Math.random().toString(36)+"'>Erase Web Elements</a> to edit internal code."
+"\nHit Temp button below for ctrl-click to erase element(s) temporarily and inhibit this prompting until reload."
+"\n\nInternal code for selected<span id=fsfpe-tagel></span>element is:<br>"
+"<div style='display:inline-block; position:relative;width:100%'><input disabled id=sfswe-seledel style='width:80%;margin:10px;'>"
+"<div style='position:absolute; left:0; right:0; top:0; bottom:0;'></div></div>";
$(document).keypress(keypressHandler);
var dialog=sconfirm(msg,function(reply) { // null for cancel button, undefined for escape, otherwise, "" or string.
$(document).off('keypress');
$(":data(pewiden-trace)").data("pewiden-trace",""); // remove trace
if (reply==="") reply=$("#sfswe-seledel").val().trim(); // reply=="" for ok (ok for confirm of seledel text.)
if (reply===undefined) { highlightElement(0,"off","restore"); cb(undefined); return; } //undefined for Escape.
if (reply) {
sconfirm("Click 'Site' to erase from any page visited on this website, "+website
+",\n\nClick 'Page' button to erase selected elements from webpage, "+webpage
+".\n\nInternal code for Element:\n\t"+reply,function(reply2, iptxt){
if (reply2===null) addToHidElements(reply); // btn1 -> null, btn2 -> "<string>" null==undefined
else if (reply2!=undefined) addToHidElements(reply+" site");
highlightElement(0,"off","restore");
if (reply2!=undefined && $("#sfswe-checkbox6:checked").length) zaplists.add(reply);
cb(true);
},"Page","Site");
return;
}// if reply
highlightElement(0,"off","restore"); cb(false);
},"Temp","OK"); //confirm(msg);
dialog.find(".ui-dialog-buttonpane").prepend("<input id=sfswe-checkbox6 type=checkbox style='margin-left:3px;'><label style='vertical-align: text-bottom;margin-left:7px;'>Completely delete element.</label>");
dialog.find("a").click(eraseElementsCmd);
$("#sfswe-seledel").next().click(e=>{
var that=$(e.target).prev(); //, ip=$("<input style='width:80%;' value='"+that.val().trim()+"'>");
that[0].disabled=false;
that.next().css("display","none");
that[0].setSelectionRange(999,999);
that.focus();
that.blur(e=>{e.target.disabled=true;$(e.target).next().css("display",""); });
});
highlightElement(target);
setTimeout(function(){dialog[0].scrollIntoView();},100);
}//end checkIfPermanentRemoval()
function eraseElementsCmd() { try{
// Called from GM script command menu and from click on prompt.
//
var sitewide, erasedElems, page_erasedElems=[], site_erasedElems=[], no_sels;
erasedElems=getHidElems("withSite");
no_sels=!erasedElems?0:erasedElems.split(/,/).length;
var dialog=sprompt(
"To erase elements on this page at "+website
+", give a selector for it eg, 'DIV#main_column site', optional word 'site' erases the element at the entire website. "
+"For more than one selector use commas to separate"+(no_sels?", currently there is/are "+no_sels+" below":"")+". To remove element hiding leave blank. Reload if necessary."
, erasedElems.replace(/,/g,", \n"),
function(reply){ try{
if (reply == null) return; //cancel ==> null, undefined==> escape. (null is == to undefined!)
config={monitor:config.monitor}; delete config.monitor[website];
if ($("#sfswe-checkbox:checked").length) config.noAnimation="checked";
if ($("#sfswe-checkbox2:checked").length) config.keepLayout="checked";
if ($("#sfswe-checkbox3:checked").length) config.hideCurtains="checked";
if ($("#sfswe-checkbox5:checked").length) config.monitor[website]="checked";
if ($("#sfswe-checkbox4:checked").length) {
toggleCurtains();
sprompt("Please enter http address of curtain image to be used. If giving left and right images sperate with a space. Leave empty to reset. Accepts base64 image strings.","",function(reply2){
if (reply2!=null) { setValue("ownImageAddr",reply2); curtain_icon=reply2||GM_getResourceURL(whiteCurtains_res);
$(".WebEraserCurtain").attr("src",curtain_icon); } toggleCurtains(); });
} else {
reply=reply.replace(/\s*,\s*/g,",").replace(/(?=[^,])\n(?=[^,])/g,",").split(/,/); // , newline->comma if none; if no comma all is put in [0]
$(reply).each((i,str)=>{ //!!TBD check for dups.
if (str=="") return;
str=str.trim();
if (/\ssite$/.test(str)) site_erasedElems.push(str.replace(/\ssite$/,""));
else page_erasedElems.push(str);
});
try{$(reply);} catch(e){alert("Bad selector given."); throw(e);}
setValue("config",config);
setValue(website+":erasedElems",site_erasedElems.toString());
setValue(webpage+":erasedElems",page_erasedElems.toString());
zaplists.update();
openCurtains();
$(".Web-Eraser-ed").each(function(){
var that=$(this);
that.css({display: that.data("sfswe-display"), visibility: that.data("sfswe-visibility")});
that.removeClass("Web-Eraser-ed");
});
$(".WebEraserCurtains").remove();
setTimeout(inner_eraseElements,1000,"fromPrompt"); //'cos openCurtains takes time
//inner_eraseElements("fromPrompt");
}
} catch(e){console.error("eraseElementsCmd,"+e+e.lineNumber+e.stack);}
}); //dialog=sprompt(...)
var keep_layout=config.keepLayout;
dialog.find(".ui-dialog-buttonpane").prepend(
"<div class=sfswe-ticks style='width:75%;float:left;font-size:10px;'>"
+"<input id=sfswe-checkbox2 type=checkbox style='float:left;"+(!keep_layout?" margin:0 3px;":"")+"' "+keep_layout+"><label>Preserve layout (in general).</label>"
+(keep_layout ? "<input id=sfswe-checkbox3 type=checkbox style='margin:0 3px 0 10px;height:12px' "+(config.hideCurtains||"")+"><label>Hide curtains (when preserving layout).</label>" : "")
+"<br><input id=sfswe-checkbox type=checkbox style='margin-left:3px;'"+(config.noAnimation||"")+"><label>Disable animation (in general).</label>"
+"<br><input id=sfswe-checkbox4 type=checkbox style='margin-left:3px;'><label>Set your own curtains' image.</label>"
+"<input id=sfswe-checkbox5 type=checkbox style='margin-left:55px;'"+(config.monitor[website]||"")+"><label>Monitor for new elements on this website.</label>"
+"</div>"
);
dialog.find("input:checkbox").css({"-moz-appearance":"none",height:12});
dialog.find(".ui-dialog-content").attr("title","Current element matches at this webpage:\n"+bodymsg());
} catch(e){console.error("eraseElementsCmd,"+e+e.lineNumber+e.stack);}
} //eraseElementsCmd()
function inner_eraseElements(from) { try{
//
// Called at page load and when user sets selector(s) for erasure.
// 1. Go through uncurtained elements for erasure and do curtainClose (or css display to none) on each.
// 2. Class each as "Web-Eraser-ed" and backup css values that might get changed.
// 3. If changes were made log details to console and to body attribute.
//
var erasedElems=getHidElems(), len=erasedElems.length, erasedElems_ar=erasedElems.split(/,/), count=0, nomatch=[];
if (erasedElems_ar[0]=="") erasedElems_ar.shift(); //fix split's creation of array length one for empty string.
var theErased=$(".Web-Eraser-ed"); theErased.removeClass("Web-Eraser-ed");
erasedElems_ar.forEach(function(sel,i){
erasedElems=$(sel); //Array.from(document.querySelectorAll(sel)); //$(sel), jQ cannot find duplicate ids.
erasedElems.each(function() {
var eld=this,el=$(eld); // 40msecs per 'each' loop.
markForTheCurtains(el,eld,sel);
var no_anima=config.noAnimation, keep_layout=config.keepLayout;
if (no_anima && !keep_layout) eld.style.setProperty("display","none","important");
else if (el.css("display")!="none") closeCurtains(el, no_anima, measureForCurtains);
count++;
}); //erasedElems.each()
if (erasedElems.length==0) nomatch.push(sel);
}); //forEach()
theErased=$(".Web-Eraser-ed");
if (len && config.monitor[website]) observeThings(); else observeThings("off");
if (theErased.length==0) { if (len) console.info("WebEraser msg: "+webpage.truncate(20)+" has no match for selectors:",getHidElems());
return; } ////////////////////
if (nomatch.length) console.info("No match for some selectors:",nomatch,"at",webpage);
var ieemsg="GM script WebEraser is using selectors to hide "+count+(count==1 ? " element that was":" elements that were")+" present on this webpage html: "+webpage
+".\nSee GM menu command Erase Web Elements to check and edit selector list. "
+(config.keepLayout ? "" : "Keep layout is not ticked.")
+(config.noAnimation ? "Animation is off." : "")
+(config.hideCurtains ? "Hide curtains is ticked." : "");
theErased.each(function(i){
var that=$(this);
var sel=that.attr("selmatch-sfswe");
var onzaplist=zaplists.which(sel); // 10 msecs to here from prev in closeCurtains() above.
ieemsg+="\n"+(i+1)+":"+sel;
ieemsg+=".\t\t"
+(onzaplist.zap ?
" => not displaying."
: onzaplist.keep_layout ? " => hidden."
: "" );
});
count=0;
console.info(ieemsg);
bodymsg(ieemsg.replace(/(.*\n){2}/,""),"init");
} catch(e){console.info("Error during inner_eraseElements(),"+e+", line:"+e.lineNumber+"\n\t\tStack:\n"+e.stack+" "+erasedElems_ar);}
}
function closeCurtains(el, noAnimKeepLayout, finishedCB=x=>x) { // called from inner_eraseElements()
var that=closeCurtains; if (!that.final_curtain) that.final_curtain=0;
var hide_curtains=config.hideCurtains, keep_layout=config.keepLayout;
var old_curtained=el.prev().data("covered-el");
if ( ! old_curtained || ! old_curtained.is(el))
var [curtainRod,lrcurtains]=createCurtains(el,noAnimKeepLayout);
else { var curtainRod=el.prev(), lrcurtains=curtainRod.children().children(); }
var onzaplist=zaplists.which(el); // 20 msecs from prev
if (noAnimKeepLayout) {
lrcurtains.css({width:"50%"});
if (onzaplist.zap) { curtainRod.css({display:"none"}); el[0].style.setProperty("display","none","important");} // "none" triggers monitor if on.
else if (onzaplist.keep_layout||hide_curtains||curtainRod.hasClass("sfsweOverlay")){
curtainRod.css({visibility:"hidden",display:""});
el[0].style.setProperty("visibility","hidden","important");
}
measureForCurtains();
}
else { // Do animated curtain closing, then, perhaps, fade out.
that.final_curtain++;
manimate(lrcurtains,["width",50,"%"],2500,6,function(){ ///////////////////////Animation
el=$(this).closest(".WebEraserCurtains").data("covered-el");
if (!keep_layout || curtainRod.hasClass("sfsweOverlay")||onzaplist.zap) {
el.add(curtainRod).delay(200).fadeOut(
500, function(){
this.style.setProperty("display","none","important"); // triggers monitor if on.
if (el[0]==this && --that.final_curtain==0) finishedCB();
});
}
else if (hide_curtains||onzaplist.keep_layout) {
el.add(curtainRod).delay(200).fadeOut(
1000, function(){
this.style.setProperty("visibility","hidden","important");
this.style.setProperty("display",$(this).data("sfswe-display"),"important"); //triggers monitor.
//curtainRod.css({visibility:"hidden",display:""});
curtainRod.remove();
if (el[0]==this && --that.final_curtain==0) finishedCB();
});
} else if (--that.final_curtain==0) finishedCB();
}); //animate()
}
} //closeCurtains()
function getSelectorWithNearestId(target,exclude_classes) {
var sel, nearestNonNumericId=target.closest(":regexp(id,^\\D+$)").attr("id"), nnmi=nearestNonNumericId; //closest also checks target
if (nnmi) nnmi=$("#"+nnmi).prop("tagName")+"#"+nnmi; //cos of jQ & multiple ids.
if ($(nnmi).is(target)) sel=nnmi;
else {
sel=selector(target,$(nnmi),true,0,exclude_classes); //ok if nnmi is undefined id.
if (!sel) sel=nnmi; //both target and $(nnmi) are same element.
else if(nnmi) sel=nnmi+sel;
}
return sel;
}
function getHidElems(withsite, justpels_ar){
var els, pels=getValue(webpage+":erasedElems","").trim(), sels=getValue(website+":erasedElems","").trim();
if (withsite && sels) {
sels=sels.replace(/,/g," site,")+" site"; // see reverse of this in addToHidElements() and eraseElementsCmd().
}
if (justpels_ar) return pels.split(",");
return pels + (sels && pels ? "," : "") + sels;
}
function rmFromHidElements(str) {
console.log("rmFromHidElements",str);
addToHidElements(str,"rm");
}
function addToHidElements(str,rm) { // called from checkIfPermanentRemoval() in handleClick on ctrl-click, "cut item" and 'x' del element.
var page_erasedElems=getValue(webpage+":erasedElems","").trim(),
site_erasedElems=getValue(website+":erasedElems","").trim(), sitewide;
console.log("mathc ",page_erasedElems.match(str));
if (!rm) {
if (/\ssite$/.test(str)) { sitewide=true; str=str.replace(/\s+site$/,""); }
if (sitewide) site_erasedElems += site_erasedElems ? ","+str : str;
else page_erasedElems += page_erasedElems ? ","+str : str;
} else {
if (page_erasedElems.includes(str))
page_erasedElems=$.map(page_erasedElems.split(/,/),el=>el.includes(str)?null:el.trim()).join(",");
else if (site_erasedElems.includes(str))
site_erasedElems=$.map(site_erasedElems.split(/,/),el=>el.includes(str)?null:el.trim()).join(",");
}
console.log("page_erasedElems",page_erasedElems);
setValue(website+":erasedElems",site_erasedElems);
setValue(webpage+":erasedElems",page_erasedElems);
zaplists.update();
}
//Blinks are double, one for selected elements, other is only when at top/bottom of narrow/widen chosen.
function highlightElement(elem, off, restore, merehl) { //also updates prompt with elem's selector.
if (!off) { // on
elem=$(elem);
if (elem.length==0) return;
gpre_elem=gelem;
gelem=$(elem);
if (!merehl) {
var seltext=$("#sfswe-seledel"); //sfs_pesel");
var newsel=getSelectorWithNearestId(gelem,tbcl+" "+rbcl+" Web-Eraser-ed");
gelems=$(newsel).not(gelem);
seltext.val(newsel); //+"<pre style='font-size:14.4px;'>\n\tHTML in pre</pre>");
}
$("#fsfpe-tagel").text(gelem.prop("tagName").toLowerCase());
gelem.parents().addBack().addClass(tbcl);
gelem.find(">:only-child").addClass(tbcl);
gelem.add(gelems).toggleClass(rbcl);
bblinker=setInterval(function(){ // normal "selected" blink.
if (gelems.length) gelems.toggleClass(rbcl);
else gelem.toggleClass(rbcl); //.css({borderColor:"red",borderWidth:"9px",borderStyle:"double"});
},1200);
gelem.data("pewiden-trace","true"); // if (!gelem.hasClass("pewiden-trace"))
//gelem[0].scrollIntoView();
}
else {
clearInterval(bblinker);
gelem.removeClass(rbcl);
if (restore) { $("."+tbcl).removeClass(tbcl); return; }
return;
}
}
function widen() { // .html() return > encodings, .text() does not. tab as @emsp must be set with html() not text()
var seltext=$("#sfswe-seledel");
if (/[:.][^>]+$/.test(seltext.val())) {
var newsel=seltext.val().trim().replace(/[:.][^:.]+$/,"");
seltext.val(newsel);
gelems=$(newsel);
gelems.addClass(rbcl);
return;
}
if (gelems.length) { gelems.removeClass(rbcl);gelems=$();}
var p=gelem.parent();
if (p.is("html")) {
blinkBorders(gelem); //blink double indicates top of hierarchy.
return;
}
highlightElement(0,"off");
highlightElement(p);
}
function narrow() {
if (gelems.length) {
widen();narrow(); // Follow trace back to el.
return;
}
var trace=gelem.find(":data(pewiden-trace):first"); // trace left by highlightElement()
if(trace.length==0) trace=gelem.find(">:only-child");
if (trace.length==0) {
blinkBorders(gelem);
return;
}
highlightElement(0,"off");
highlightElement(trace);
}
function blinkBorders(elem, interval=150, times=4) { // borders must already be set.
times*=2;
var cnt=0,i=setInterval(function(){
cnt++;
elem.toggleClass(rbcl);
//!!
elem.toggleClass(tbcl);
if (cnt==times) {clearInterval(i);elem.removeClass(rbcl);}// interference so rm class.
},interval);
}
function init_jquery() {
$.fn.reverse = Array.prototype.reverse;
$.fn.swap = function(to) {
var a=this.eq(0), b=$(to).eq(0);
var tmp = $('<span>').hide();
a.before(tmp);
b.before(a);
tmp.replaceWith(b);
return;
};
$.easing["stepper"] = function (x, t, b, c, maxt) { // eg, see, console.log($.easing) for other funcs.
// var y=c*(t/=maxt)*t + b;
// if (x<0.4) y=0.1;
//console.log(x);
//return y;
return x;
};
$.extend($.expr[':'], {
regexp: function(currentobj, i, params, d) { //filter type function.
params=params[3].split(/,/); //eg, [ 'regexp', 'regexp', '', 'className,promo$' ]
var attr=params[0], re=params[1]; //eg, className, promo$
if (attr=="class") attr="className";
var val=currentobj[attr]+""||"";
if (attr=="className") return val.split(/\s/).some(function(cl){return cl.match(re);});
else return val.match(re);
}}); //usage eg: $(“div:regexp(className,promo$)”);
(function($){
$.event.special.destroyed = {
remove: function(o) {
if (o.handler) {
o.handler();
}
}
}; })(jQuery); //Usage: $("#anid").bind('destroyed', function() {// do stuff}) // only for is jQ removed el.
} //init_jquery()
function selector(desc,anc,no_numerals,recursed,exclude_classes) { // descendent, ancestor, such that ancestor.find(ret.val) would return descendant. If no ancestor given it gives it relative to body's parent node. // See example usage in checkIfPermanentRemoval(). Numeraled classes/ids are excluded.
anc=$(anc).eq(0); //apply only to first ancestor.
if (anc.length==0) anc=$(document.body.parentNode); // !anc wouldnt work for a jq obj.
desc=$(desc);
if ( (desc.closest(anc).length==0 || desc.length!=1) && !recursed) {
console.info("Too many elements or descendant may not related to ancestor:");
console.info("Descendant is:"+selector(desc,0,0,true));
console.info("Ancestor is:"+selector(anc,0,0,true)+".");
return;
}
// Last element is highest in node tree for .parentsUntil();
var sel=
desc.add(desc.parentsUntil(anc)) // up to but not including.
.reverse() // see above, needs: $.fn.reverse = Array.prototype.reverse;
.map(function() { // works from bottom up to ancestor, hence need for reverse().
var t=$(this), tag=this.tagName.toLowerCase(), nth=t.prevAll(tag).length+1, id, cl, nthcl;
id=this.id ? "#"+ this.id : "";
cl=(this.className? "." + $.trim(this.className).replace(/\s/gi, ".") : "");
if (exclude_classes) cl=cl.replace(RegExp(".("+exclude_classes.replace(/ /g,"|")+")","g"),"");
if (no_numerals && /\d/.test(id)) id="";
if (no_numerals && /\d/.test(cl)) cl="";
if ( (cl && t.siblings(tag+cl).length==0)
|| id
|| t.siblings(tag).length==0)
nth=0;
else if (cl && t.siblings(tag+cl).length!=0) {
cl+=":eq("+t.prevAll(tag+cl).length+")"; //jQuery only has :eq()
nth=0;
}
return tag+(nth?":nth-of-type("+nth+")":"")+id+cl; ////////////////////nth-of-type is One-indexed.
}) //map()
.get() //
.reverse()
.join(">");
if (desc.is(anc.find(">"+sel))) {
if (anc.is(document.body.parentNode)) return "html>" + sel;
return ">"+sel;
} else {
console.info("Selector result:\n\t"+sel+" Not findable in ancestor, nor in body's parent.");
if ($(sel).length) return sel; //Its the very top element, <HTML>.
}
}
function markForTheCurtains(el,eld,sel) {
el.css({overflow:"hidden"}).addClass("Web-Eraser-ed").attr("selmatch-sfswe",sel)
.data({sfsweDisplay: eld.style.display, sfsweVisibility:eld.style.visibility, sfsweOverflow: eld.style.overflow}); // needed in case zero height element with floating contents. // To make it have dims, in case of zero height with sized contents.
}
function reattachTornCurtains(curtains=$(".WebEraserCurtains")) {
var torn=false;
curtains.each(function(){
var that=$(this), el=that.data("covered-el");
if (el.parent().length==0 || !el.hasClass("Web-Eraser-ed")) {
torn=true;
that.addClass("sfswe-delete","true");
//that.remove();
} });
$(".sfswe-delete").remove();
if (torn) inner_eraseElements();
}
function measureForCurtains(curtains=$(".WebEraserCurtains")) {
curtains.each(function(){
var that=$(this), el=that.data("covered-el"); //that.next(); // next is the covered elem.
var w=el.outerWidth(), h=el.outerHeight(); // Includes padding & border, margin included if 'true' passed. jQuery sets and unsets margin-left during this, provoking attrModifiedListener.
if (!el.hasClass("Web-Eraser-ed")) {
el.addClass("Web-Eraser-ed");
el.css({overflow:"hidden"});
}
var off=moffset(el);
that.css(off).css({height:h,width:w});
zoomToShape(that.children());
});
}
function bodymsg(str,init) {
var b=$("body");
var msg=b.attr("sfswe-message2")||"";
if (msg.length>1300) msg=bodymsg.init;
if (str) if (init) { b.attr("sfswe-message",str);bodymsg.init=str;}
else { b.attr("sfswe-message2",msg+(msg?" ":"")+str);console.info("WebEraser Monitor: "+str); }
return b.attr("sfswe-message");
}
function observeThings(off) {
var that=arguments.callee; that.off=[];
if (that.obs1) { that.obs1.disconnect(); that.obs2.disconnect(); }
if (off) return;
var sels=getHidElems(),
nomonitor=set=>{ if (set==1) { that.off.push(true); that.obs1.takeRecords();} // jquery get causes set, hence inf.loop.
if (set==0) { that.off.pop(); that.obs1.takeRecords();} return that.off.slice(-1)[0]; };
var parseCssText=str=>JSON.parse("{" + (str||"").replace(/[\w-]+(?=:)/g,'"$&"').replace(/:\s*(.+?)(?=;)/g,':"$1"').replace(/;/g,",").slice(0,-1) + "}");
console.info("WebEraser msg: Monitoring for creation and appearance of elements to be erased:"+sels);
obs1_connect(sels); var cnt=[0,0,0];
function obs1_connect(selectors) {
that.obs1=attrModifiedListener(document,selectors,["style"],function(mutrecs,b) {
if (nomonitor()) return;
nomonitor(1);
mutrecs.forEach( mutrec=>{
var target=$(mutrec.target), t=mutrec.target,oldCssDisplay=mutrec.oldValue?mutrec.oldValue.match(/(display:)\s*(\w*);?/):null;
var currentValue=target.css("display"); cnt[0]++;
var oldval=parseCssText(mutrec.oldValue), currval=parseCssText(t.style.cssText);
//console.log("Prev Changes:",csscmp(target.data("olderval"), oldval)+". Compared to current:",csscmp(oldval,currval));
target.data("olderval",oldval);
if (currval.display=="none" && oldval.display!="none") {
bodymsg("change-nodisplay:"+target.attr("selmatch-sfswe"));
target.prev().css("display","none");
measureForCurtains();
} else if (currval.display!="none" && oldval.display=="none" ) {
bodymsg("change-display:"+target.attr("selmatch-sfswe"));
target.prev().css("display","");
closeCurtains(target,true);
}
if (parseInt(currval.height||0)- parseInt(oldval.height||0)) {
bodymsg("change-height:"+nodeInfo(target));
measureForCurtains();
}
}); //forEach
nomonitor(0);
}); // obs1_connect()
}
that.obs2=nodeMutationListener(document,sels,function(foundArrayOfNodes, ancestorOfMutation,removed) {
if (nomonitor()) return;
nomonitor(1);
foundArrayOfNodes.forEach(node=>{
var jQnode=$(node); cnt[1]++;
if (!removed) { // node inserted.
var foundsel;
sels.split(/,/).forEach(sel=>{if($(sel).is(jQnode)) foundsel=sel;});
bodymsg("new-node:"+foundsel);
markForTheCurtains(jQnode,node,foundsel);
setTimeout(x=>{nomonitor(1);closeCurtains(jQnode,false,measureForCurtains);nomonitor(0);},300);
} else { // node removed
cnt[2]++;
//if(jQnode.attr("cc")) {
bodymsg("node-delete:"+jQnode.attr("selmatch-sfswe"));
$(".WebEraserCurtains[cc='"+jQnode.attr("cc")+"']").remove(); //.filter(function(){return $(this).data()})
measureForCurtains();
//}
}
});//forEach
nomonitor(0);
},true);//nodeMutationListener()
}
function zoomToShape(z,slope=5) {
var zoom_scale;
z.each(function(i,z){
z=$(z); var sfsdiv=z.parent(), w=sfsdiv.outerWidth(), h=sfsdiv.outerHeight();
zoom_scale=((x,s)=>10 + s*(1000-x)/90|0)(w,slope)/10; //(x=>(2900-2*x)/90|0)(w)/10;
zoom_scale*=((x,s)=>5 + s*0.025*(x-20)|0)(h,2)/10;
var img=z.children()[0];
z.attr("zoomed-w-h-z",w+"-"+h+"-"+zoom_scale+"-slope-"+slope);
zoom_scale = w>1000 ? [1,1] : [zoom_scale,1];
if (GM_getValue("ownImageAddr","")) {
var nratio=img.naturalWidth/img.naturalHeight, maxRatio=10,
iratio=w/2/h, asp_ratio=iratio/nratio;
zoom_scale=[1,Math.min(asp_ratio,maxRatio)];
if (asp_ratio<1) zoom_scale=[Math.min(1/asp_ratio,maxRatio),1];
}
z.css("transform","scale("+zoom_scale+")");
});
return zoom_scale;
}
function openCurtains(zap_or_keep="",curtains=$(".WebEraserCurtain")) { // called from ctrl-click with curtains, eraseElementsCmd() w/o curtains, and lrcurtains.click sets "keep"
console.log("openCurtains",zap_or_keep);
setTimeout(function() {
manimate(curtains,["width",0,"%"],500,3,function() {
var that=$(this), erased_el=that.parent().parent().next();
var sel=erased_el.attr("selmatch-sfswe");
switch(zap_or_keep[0]) {
case "z": zaplists.add(sel);erased_el.css("display","none");measureForCurtains();break;
case "k": zaplists.add(sel,"keep");erased_el[0].style.setProperty("visibility","hidden","important");break;
case "t": that.parent().parent().css("display","none"); console.log(that.css("display"),that);break;
case "a": console.log("altzap");that.parent().parent().remove();rmFromHidElements(sel);break;
}
//erased_el.prev().css({display:"none"});
});
},1000);
}
function createCurtains(el, noAnimKeepLayout) {
var h=el.outerHeight()|0,w=el.outerWidth()|0, area=h*w, iw=w/2, pos= moffset(el),
warea=window.innerHeight*window.innerWidth, csspos=el.css("position");
// 9 msecs to here from function start.
//console.log("closeCurtains h/w",h,w,"pos",pos);
var lcurtain=$("<img class='WebEraserCurtain pwe-left' style='left:0;position:relative;' src="+curtain_icon.split(/\s+/)[0]+"></img>"),
rcurtain=$("<img class='WebEraserCurtain pwe-right' style='right:0;position:relative;' src="+curtain_icon.split(/\s+/).slice(-1)+"></img>"),
curtainRod=$("<sfswediv class=WebEraserCurtains cc="+(++curtain_cnt)+" style='z-index:2147483647; position:absolute; display:block; overflow:hidden;opacity:0.94;'></sfswediv>"), //inline is default here, 'd take full width of parent.
zoomer=$("<div class='sfswe-zoomer' style=' height:100%; '></div>"),
lrcurtains=lcurtain.add(rcurtain), sel=el.attr("selmatch-sfswe");
el.attr("cc",curtain_cnt);
zoomer.append(lcurtain,rcurtain);
curtainRod.append(zoomer);
lrcurtains.click(function({ctrlKey:ctrl,shiftKey:shift,altKey:alt}) {
if (ctrl) return; if (shift) openCurtains("keep_layout",lrcurtains);
else if (alt) openCurtains("azap",lrcurtains); else openCurtains("tzap",lrcurtains); return false;});
curtainRod[0].title="Shift-Click here to persistently hide whilst keeping page layout.\nCtrl-click to persistently delete from layout.\nClick to reveal hidden area."
+"\nSelector: "+sel;
curtainRod.css({height:h,width:w}).css(pos).data({coveredEl:el,selmatchSfswe:sel});
lrcurtains.css({ width: (!noAnimKeepLayout ? 0 : "50%" )}); // Initial width of each curtain.
curtainRod.data("zoom",zoomToShape(zoomer));
var scale=curtainRod.data("zoom")[0], calc=50-50/scale; // 50% is fully closed.
lrcurtains.width(calc+"%").height("100%");
lrcurtains.data("init-width",calc|0);
var portions=area/warea*100|0; //curtainRod.attr("init-calc",(calc|0)+" "+portions);
if (portions>=60) { //>75% of window is covered.
var visible_area;
with (Math) {visible_area=min(w,window.innerWidth)*min(h,window.innerHeight);}
if (visible_area>=warea*0.6) {
lcurtain.css({left:"10%"});
curtainRod.css({width:"80%",top:"10%"}).addClass("sfsweOverlay");
lrcurtains.css({height:h*0.8});
setTimeout(x=>$("html, body").css("overflow",(i,v) => v=="hidden" ? "auto": null).css("position",(i,v) => v=="fixed" ? "static": null),4000);
overlay=true;
//First event listener can stop prop to ones added later, ideally would be added at doc-start.
console.info("This is an Overlay (>2/3 covered, "+portions+"%): ",sel);}}
el.before(curtainRod);
return [curtainRod,lrcurtains];
}
function toggleCurtains() {
var that=arguments.callee;
$(".WebEraserCurtains").each(function(){
if (!that.xor) {manimate($(".WebEraserCurtain",this),["width",50,"%"],2000,12);zoomToShape($(".sfswe-zoomer"));}
else manimate($(".WebEraserCurtain",this),["width", $(this).data("init-width"),"%"],4000,8);
});
}
function zaplist_composite() { // composite pattern
if (iframe) return;
var zlists=[new zaplist(webpage),new zaplist(website),new zaplist(webpage,"kl"),new zaplist(website,"kl")];
this.add=function(sel,keep_layout){
zlists.forEach(function(el) { el.add(sel,keep_layout);}); };
this.contains=function(el){ // may be a dom/jq object or a string selector.
return zlists.some(function(list) { return list.contains(el);}); };
this.which=function(el) {
if (this.contains(el)) {
var has_keep_layout=zlists.map(v => v.contains(el)).includes("kl");
return {keep_layout:has_keep_layout,zap:!has_keep_layout};
}
return {keep_layout:false,zap:false};
};
this.update=function(sel){ zlists.forEach(function(el) { el.update();}); };
this.toString=()=>"[object zaplist_composite]";
}
function zaplist(key,keytype) {
var fullkey=key+":zaplist"+(keytype? ":"+keytype : "");
var savelist=function() { setValue(fullkey,list); };
var readlist=function() { return getValue(fullkey,[]); };
var list=readlist();
//console.log("zaplist created key:",key,", keytype",keytype,", list",list);
this.add=function(str,kl) {
if (!!kl != !!keytype) return;
if(getValue(key+":erasedElems","").split(/,/).includes(str)) {
list.push(str);
savelist();
}
};
this.contains=function(jqobjOrStr){
if (list.length==0) return;
if (jqobjOrStr.attr) jqobjOrStr=jqobjOrStr.attr("selmatch-sfswe");
if (list.indexOf(jqobjOrStr) != -1)
return keytype||"zap";
};
this.rm=function(str) {
var i=list.indexOf(str);
if (i!=-1) list.splice(i,1);
savelist();
};
this.update=function() {
if (list.length==0) return;
var strs_ar=getHidElems().split(/,/);
list=list.filter(function (lel) {
return strs_ar.includes(lel);
});
savelist();
};
this.toString=()=>"[object zaplist]";
}
function moffset(elem, eld=elem[0]) {
if (elem.css("position").includes("fixed"))
return Object.assign(elem.position(),{position:"fixed"});
var dominPar=elem.offsetParent()[0];
return left_top(elem);
function left_top(elem) {
var {left,top}=elem.position(); // jQ sets & unset margintop during this for some reason, margins and floating els may disaffect calc!
let margl=parseInt(elem.css('margin-left')), margt=parseInt(elem.css('margin-top'));
//let bordl=parseInt(elem.css('border-left-width')), bordt=parseInt(elem.css('border-top-width'));
var x = left + margl, y = top + margt;
do {
//console.log("got y",y," from","top",top,"margt",margt,"bordt",bordt);
elem = elem.offsetParent();
if (elem.is(dominPar) || elem.is("html")) break;
let {left,top}=elem.position(); // jQ sets & unset margintop during this for some reason, margins and floating els may disaffect calc!
console.log(x,nodeInfo(elem),dominPar?nodeInfo(dominPar):"");
x += left; y += top;
console.log("got y",y," from +",top);
} while (true)
return { left: x, top: y };
}
}
//
// MutationObserver functions. Eg, var obs=nodeInsertedListener(document,"#results", myCBfunc); function myCBfunc(foundArrayOfNodes, ancestorOfMutation);
// Requires jQuery.
// See https://www.w3.org/TR/dom/#mutationrecord for details of the object sent to the callback for each change.
// Four functions available here:
// Parameter, include_subnodes is to check when .innerHTML add subnodes that do not get included in normal mutation lists, these lower nodes are checked when parameeter is true.
// Return false from callback to ditch out.
function nodeInsertedListener(target, selector, callback, include_subnodes) {
return nodeMutation(target,selector,callback,1, include_subnodes);
}
function nodeRemovedListener(target, selector, callback, include_subnodes) {
return nodeMutation(target,selector,callback,2, include_subnodes);
}
function nodeMutationListener(target, selector, callback, include_subnodes) { //inserted or removed, callback's 3rd parameter is true if nodes were removed.
return nodeMutation(target,selector,callback,3, include_subnodes);
}
function attrModifiedListener(target, selectors, attr, callback) { //attr is array or is not set.
var attr_obs=new MutationObserver(attrObserver);
var config={ subtree:true, attributes:true, attributeOldValue:true};
if (attr) config.attributeFilter=attr;
attr_obs.observe(target, config);
function attrObserver(mutations) {
var results=mutations.filter(v=>{ return $(v.target).is(selectors);});
if (results.length) callback(results);
//console.time("attrObserver"+mutations.length);
}
return attr_obs;
}
//
// Internal functions:
function nodeMutation(target, selectors, callback, type, include_subnodes) { //type new ones, 1, removed, 2 or both, 3.
var node_obs=new MutationObserver(mutantNodesObserver);
var jQcollection=$(selectors), cnt=0;
node_obs.observe(target, { subtree: true, childList: true } );
return node_obs;
function mutantNodesObserver(mutations, mu_obs) {
var sel_find, muts, node;
for(var i=0; i<mutations.length; i++) {
if (type!=2) testNodes(mutations[i].addedNodes, mutations[i].target); // target is node whose children changed
if (type!=1) testNodes(mutations[i].removedNodes, mutations[i].target,"rmed"); // no longer in DOM.
}
function testNodes(nodes, ancestor, rmed) {
if (nodes.length==0) return;
var results=[], subresults=$();
for (var j=0,node; node=nodes[j], j<nodes.length;j++) {
if (node.nodeType!=1) continue;
if (rmed && jQcollection.is(node)) {results.push(node);jQcollection=jQcollection.not(node);}
else if ($(node).is(selectors)) {results.push(node);jQcollection=jQcollection.add(node);}
if (include_subnodes) //.innerHTML can add subnodes that do not get included in mutations, these lower nodes are checked here.
if (rmed) subresults=$(node).find(jQcollection);
else subresults=$(node).find(selectors);
} //for
results=results.concat(subresults.toArray());
if (results.length) callback(results, ancestor,rmed);
} //testNodes()
};
}
//
// End MutationObserver functions. Usage example, var obs=nodeInsertedListener(document,"div.results", myCBfunc); function myCBfunc(foundArrayOfNodes, ancestorOfMutation);
//
function manimate(objs,[css_attr,target,suffix],interval,noOf_subintervals,CB) { // CB is invoked once, at end. $.animate max-ed out cpu for 30 secs or so.
var len=objs.length,cnt=0,i;
if (!len) return false;
var maxi=objs.length-1, subinterval=interval/noOf_subintervals,
init_int=parseInt(objs[0].style[css_attr]), // assume same initital position and same units/suffix for all objs.
m=(target-init_int)/noOf_subintervals,
linear=(v,i)=>init_int+m*(i+1), // quad=(v,i)=>Math.min(target_int,init_int+(5/3)*Math.pow(i+1,2)-(5/3)*(i+1)), // combo=(v,i)=>quad(v,i)/2+linear(v,i)/2,
plotvals=new Uint32Array(noOf_subintervals).map(linear);
subinterval+=random(-subinterval/5,subinterval/5),
i=setInterval(eppursimuove,subinterval,objs);
function eppursimuove(that) {
objs.css(css_attr,plotvals[cnt]+suffix);
if (++cnt==noOf_subintervals) {
clearInterval(i); CB && CB.call(that);}}
}
function setValue(n,v) { return GM_setValue(n,JSON.stringify(v));}
function getValue(n,v) { try { return JSON.parse(GM_getValue(n,JSON.stringify(v)));}
catch(e){ var mv=GM_getValue(n,v);setValue(n,mv);return mv;}} // just for migration of verions from non JSON strings.
function random(min,max) {
return Math.floor(Math.random() * ((max+1) - min)) + min;
}
function timer() { //console.time() and console.timeEnd() not working at mo.
if (window!=window.parent || timer.log) return;
var originalLogger = console.log;
timer.log=originalLogger;
console.log = function () {
if (!timer.begin) {
timer.begin=Date.now();
originalLogger.call(this,">>>>Init timer "+location+":");
}
var args=Array.from(arguments);
args.unshift((Date.now()-timer.begin)+"\t");
originalLogger.apply(this, args);
};
}
function logger() {
$(document).dblclick(outputlogger);
var originalLogger = console.log;
logger.log=originalLogger;
console.log = function () {
if (!logger.this) logger.this=this;
// Do your custom logging logic
var argq=$(document).data("loggerq");
var args=Array.from(arguments);
if (!argq) argq=[];
if (document.readyState!=logger.state) {
argq.push(document.readyState+":");
logger.state=document.readyState;
}
argq.push(args);
$(document).data("loggerq",argq);
args.push(document.readyState);
originalLogger.apply(this, args);
};
} //logger()
function csscmp(prevval, newval) { try{
var that=arguments.callee;
var covered={}, roll="";
for (let i in prevval) {
covered[i]=1;
if (newval[i]===undefined) roll+="Removed: "+i+"="+prevval[i]+" ";
else if (prevval[i]!=newval[i]) roll+="Changed: "+prevval[i]+" to: "+newval[i]+" ";
}
for (let i in newval) if (!covered[i]) roll+="Added: "+i+"="+newval[i]+" ";
return roll||"Same";
}catch(e) {console.error("csscmp Error",e.lineNumber,e);}}
function nodeInfo(node) { return selector(node,node.parentNode,0,0,"Web-Eraser-ed").replace(/^html>body>/,""); }
function outputlogger() {
var originalLogger=logger.log;
var that=logger.this;
var argq=$(document).data("loggerq");
originalLogger.call(that,"===============Logger Output==========================");
argq.forEach(function(v){
originalLogger.call(that,v); //this changes in forEach in this case!
}); // originalLogger.apply(this,argq);
originalLogger.call(that,"===============End Logger Output=======================");
return false;
};
function logStack(fileToo) { // deepest first.
var res="", e=new Error;
var s=e.stack.split("\n");
if (fileToo) res="Stack of callers:\n\t\t"; //+s[1].split("@")[0]+"():\n\t\t"
for (var i=1;i<s.length-1;i++)
res+=s[i].split("@")[0]+"() "+s[i].split(":").slice(-2)+"\n";
return !fileToo ? res : {Stack:s[0]+"\n"+res};
}
function Ppositions(el, incl_self,not_pos_break="") {
el=$(el); var roll="\n\n";
var els=el.parents();
if (incl_self) els=els.add(el).reverse();
els.each(function(){
var pos=$(this).css("position");
roll+=this.tagName+" "+pos+"\n";
if (! pos.includes(not_pos_break)) return false;
// /^((?!relative).)*$/ matches any string, or line w/o \n, not containing the str "relative"
});
return roll;
}
var jqueryui_dialog_css=(
""
+".ui-dialog-content,.ui-dialog,.ui-dialog textarea { font-size: 12px; font-family: Arial,Helvetica,sans-serif; border: 1px solid #c5c5c5; "
+"background:#fff; color:#333; padding:12px;margin:5px;} "
+".ui-dialog-buttonpane { background:whitesmoke; font-size: 10px; cursor:move; border: 1px solid #ddd; overflow:hidden; } "
+".ui-dialog-buttonset { float:right; } "
+".ui-widget-overlay { background: #aaaaaa none repeat scroll 0 0; opacity: 0.3;height: 100%; left: 0;position: fixed; top: 0; width: 100%;}"
+".ui-button,.ui-widget-content { text-align:left; color:#333; border: solid 1px #c5c5c5; padding: 6px 13px;margin: 4px 3px 4px 0;} "
+".ui-corner-all,.ui-dialog-buttonpane {border-bottom-left-radius:30px;}"
+".ui-button:hover { background-color: #ededed;color:#333; } "
+".ui-button { background-color: #f6f6f6;}"
+".ui-dialog {position:absolute;padding:3px;outline:none;}"
+".ui-resizable-handle { position:absolute; cursor: url(data:image/svg+xml;base64,"
+"iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAAAAACo4kLRAAAACXBIWXMAAAsTAAALEwEAmpwYAAABAUlEQVQY022RsWrCYACEvz9EK1HQCilYu+jWdNKlj9BNN1/Cp/IBFHTMExihdTCIm0sR26W6KNHkvw6lunjLjffdnXn7fG0/PzzeG0A/m+/Ve/REB3CL/laStn7RBTpOG0iTXgWg0ktSoO20DJDv5gBy3TxgWkQFAG+QSdnAAyhErMtuFSiN0nRUAqpuec2x2V/4YOr7fd2AH/ebR+zhbMOaCWJrF4GphfZ8sEhSNm3EVrJxY5pJkhGAkjtzNRxuyAGws2Ap0DKYWYDbQRek3e6K9A8/TNPhBf5mzbEBvDCTpCz0ADN25gJOkzPAeXICNL85spu8/N0B8LDafK0+ouQXfemVYVtdIewAAAAASUVORK5CYII="
+") 10 10, row-resize; } .ui-resizable-sw {bottom:5px;left:5px;}"
+".ui-resizable-w, .ui-resizable-e { width:10px;height:100%;top:-5px;} .ui-resizable-n, .ui-resizable-s { width:100%;height:10px;} .ui-resizable-n {top:-5px; } .ui-resizable-w {left:-5px; } .ui-resizable-e {right:-5px; }"
+".ui-tooltip { font-size: 7px; }"
+".sfswe-ticks * {font-size:11px;padding:0px;margin:2px;}"
+".sfswe-content :-moz-any(div,span,input) { font-size:13px;padding:6px;margin:4px 3px 4px 0;color:#333;}"
+".sfswe-content :-moz-any(a,a:visited) { color:#333;text-decoration:underline; padding:0;margin:0;}"
+".sfswe-content a:hover {opacity:0.5;}")
.replace(/\.ui/g,".sfswe-sprompt .ui");
function chromeInit() {
if (!this.GM_getValue || "Barychelidae"!=GM_getValue("arachnoidal","Barychelidae")){ //chromium
console.info("WebEraser userscript in non GM_ mode for chrome/safari etc.");
this.GM_getValue=function(a,b) { return JSON.parse(localStorage[a]||JSON.stringify(b)); };
this.GM_setValue=function(a,b) { localStorage[a]=JSON.stringify(b);};
this.GM_getResourceURL=function(url) { return "https://github.com/SloaneFox/imgstore/raw/master/whiteCurtains.jpg"; };
this.GM_registerMenuCommand=x=>null;
var xhr=new XMLHttpRequest();
xhr.onload = function() {
eval(this.response);
if (this.responseURL.endsWith("jquery-ui.js")) return;
$(main);
$(window).click(handleClick);
xhr.open('GET', "https://code.jquery.com/ui/1.12.0/jquery-ui.js"); xhr.send();
};
xhr.open('GET', "https://code.jquery.com/jquery-3.1.1.js"); xhr.send();
return true;
} else
return false;
}