您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Blocks headers and other sticky elements from wasting precious vertical screen estate by pinning them down.
当前为
// ==UserScript== // @name Blockhead // @namespace blockhead // @description Blocks headers and other sticky elements from wasting precious vertical screen estate by pinning them down. // @match *://*/* // @version 15 // @grant GM.getValue // @grant GM.setValue // @grant GM_getValue // @grant GM_setValue // @grant GM_getResourceText // @grant GM.getResourceText // @xhr-include * // @author elypter // @require https://greasyfork.org/scripts/36900-super-gm-setvalue-and-gm-getvalue-lib/code/Super_GM_setValue_and_GM_getValue%20lib.user.js // @resource black_keywords https://raw.githubusercontent.com/elypter/rule_keyword_generator/master/generic_rule_keywords.txt // ==/UserScript== //This script blocks headers and other sticky elements from wasting precious vertical screen estate by pinning them down. //It checks the computed style of each element. if the position attribute is fixed or absolute or ~relative~ //then it checks if its id or classes names contain provided in the list below. //this list is created with theis tool https://github.com/elypter/rule_keyword_generator by parsing the rules https://raw.githubusercontent.com/yourduskquibbles/webannoyances/master/ultralist.txt //for class names and ids. //There is also a whitelist for certain keywords and tag names to reduce flase positives and processing time. //currently the processing time is quite high because each element has to be checked and many have to be checked against the whole list. //on an old laptop it can take up to single digit seconds so pageloadtime noticable increases. //License: GPL3 //id and classes that contain any of these keywords will not be modified var white_names = ["side","guide","article","html5","story","main","left","right","content","account__section","container--wide","container__main","panel", "body","gutter","embed","watch","background","middleContainer","drag-and-drop"]; //var white_names = ["example-whitelist-entry"]; //tags that will not be cheked var ignore_tags = ["a","A","script","SCRIPT","body","BODY","li","LI","ul","UL","br","BR","h5","H5","b","B","strong","STRONG","svg","SVG","path","PATHH","h2","H2", "code","CODE","tr","TR","td","TD","h3","H3","h1","H1","h4","H4"];//,"noscript","NOSCRIPT" var count_element_walker=0; //debug:counts how often a page check is triggered var count_style_walking=0; //debug:counts how often checking stylesheets is triggered //search for floating elements that get added after page load. var mutation_check=GM_SuperValue.get ("mutation_check")==true?GM_SuperValue.get ("mutation_check"):1; //1=yes 2=no change value in memory tab GM_SuperValue.set ("mutation_check",mutation_check); //all elements of a site will be checked individually for their computed style and changed if there is a match var walk_elements=GM_SuperValue.get ("walk_elements")==true?GM_SuperValue.get ("walk_elements"):1; //1=yes 2=no change value in memory tab GM_SuperValue.set ("walk_elements",walk_elements); //all stylesheets will be checked for classes and changed if there is a match var walk_styles=GM_SuperValue.get ("walk_styles")==true?GM_SuperValue.get ("walk_styles"):0; //1=yes 2=no change value in memory tab GM_SuperValue.set ("walk_styles",walk_styles); //this will save the statistics of how often a keyword is being matched in a local variable that can be viewed in the memory tab var save_keyword_statistics=GM_SuperValue.get ("save_keyword_statistics")==true?GM_SuperValue.get ("save_keyword_statistics"):0; //1=yes 2=no change value in memory tab GM_SuperValue.set ("save_keyword_statistics",save_keyword_statistics); //this will save the rules generated based on the blocking in a local variable that can be viewed in the memory tab var save_generated_rules=GM_SuperValue.get ("save_generated_rules")==true?GM_SuperValue.get ("save_generated_rules"):0; //1=yes 2=no change value in memory tab GM_SuperValue.set ("save_generated_rules",save_generated_rules); //contained in black_keywords.txt var black_keywords=GM_getResourceText("black_keywords").toString().split("\n"); //list generated with rule_keyword_generator from webannoyances ultralist function counted_element_walker(elm,orig){ count_element_walker++; //this disables all javascript that is being triggered when scrolling window.addEventListener("scroll", function (event) { event.stopPropagation(); }, true); console.log("check number "+count_element_walker+" from: "+orig); element_walker_all(elm); //console.log(GM_SuperValue.get ("white_names_counter")); //console.log(GM_SuperValue.get ("black_keywords_counter")); //console.log(GM_SuperValue.get ("generated_rules")); } function keyword_walker(keyword_list){ var white_names_counter=GM_SuperValue.get ("white_names_counter")||{}; var black_keywords_counter=GM_SuperValue.get ("black_keywords_counter")||{}; //todo: switch order of for loops to detect whitelists earlier var state=-1; //console.log("list: ("+keyword_list.length+") "+keyword_list); for (var i=0; i < keyword_list.length; i++){ var subword_list=keyword_list[i].toString().split('-'); //console.log("sublen of: "+subword_list+" from "+keyword_list[i]+" = "+subword_list.length); for (var j=0; j < subword_list.length; j++){ var subsubword_list=subword_list[j].split('_'); //console.log("subsublen of: "+subsubword_list+" from "+subword_list[j]+" = "+subsubword_list.length); for (var k=0; k < subsubword_list.length; k++){ for (var l=0; l < white_names.length; l++){ //console.log("test white: l:"+white_names[l]+" in s:"+subsubword_list[k]+" of "+keyword_list); if (subsubword_list[k].indexOf(white_names[l]) != -1){ //console.log("whitelisted: l:"+white_names[l]+" in s:"+subsubword_list[k]+" of "+keyword_list); if(white_names_counter[white_names[l]]) white_names_counter[white_names[l]]++; else white_names_counter[white_names[l]]=1; GM_SuperValue.set ("white_names_counter", white_names_counter); return 0; } } for (var l=0; l < black_keywords.length; l++){ if (subsubword_list[k].indexOf(black_keywords[l]) != -1){ console.log("matched: l:"+black_keywords[l]+" in s:"+subsubword_list[k]+" of "+keyword_list); if(black_keywords_counter[black_keywords[l]]) black_keywords_counter[black_keywords[l]]++; else black_keywords_counter[black_keywords[l]]=1; state = 1; } } } } } GM_SuperValue.set ("black_keywords_counter", black_keywords_counter); return state; } function element_walker_all(startElem) { //walk over element list as opposed to an element tree if (!(startElem instanceof Element)) return; var generated_rules=GM_SuperValue.get ("generated_rules")||[]; //contains all adblock/ublock rules that the script creates based on what it modifies var elms = startElem.getElementsByTagName("*"); for (var x = elms.length; x--;) { elm=elms[x]; //console.log("checking: "+elm.tagName.toString()); if(elm instanceof Element && ignore_tags.indexOf(elm.tagName.toString()) == -1 && getComputedStyle(elm)) { //console.log("testing: "+elm.tagName.toString()+" with position= "+getComputedStyle(elm).getPropertyValue("position").toLowerCase()); if (/*(getComputedStyle(elm).getPropertyValue("position") == "absolute") ||*/ (getComputedStyle(elm).getPropertyValue("position").toLowerCase() == "fixed")/* || */ /*(getComputedStyle(elm).getPropertyValue("position") == "relative")*//* ||*/ /*(getComputedStyle(elm).getPropertyValue("top") != "")*/) { var keyword_list =[]; keyword_list=elm.className.toString().split(' '); if (typeof(elm.id)=="string"&&elm.id!="") keyword_list.push([elm.id]); if (typeof(elm.tagName)=="string"&&elm.tagName!="") keyword_list.push([elm.tagName]); var has_matched=-1; if (keyword_list!=[]&&keyword_list!=[""]){ //compare against black and whitelist has_matched=keyword_walker(keyword_list); } var rule; var class_list=elm.className.toString().split(' '); //check for rule writing if (has_matched==1){ console.log("pinning sticky: "+elm.id+","+elm.className+","+elm.tagName); if (elm.id){ rule=window.location.hostname+"###"+elm.id+":style(position: "+"fixed"+" !important;)"; if(generated_rules.indexOf(rule)==-1){ if(!(rule in generated_rules)) generated_rules.push(rule); //console.log(rule); } } if(elm.className){ for (var i=0; i < class_list.length; i++){ rule=window.location.hostname+"##."+class_list[i]+":style(position: "+"fixed" +" !important;)"; if(generated_rules.indexOf(rule)==-1){ if(!(rule in generated_rules)) generated_rules.push(rule); //console.log(rule); } } } elm.setAttribute('style', 'position:static !important'); elm.style.removeProperty('top'); //return; }else if(has_matched==0){ if (elm.id){ rule=window.location.hostname+"#@#"+elm.id; if(generated_rules.indexOf(rule)==-1){ if(!(rule in generated_rules)) generated_rules.push(rule); //console.log(rule); } } if(elm.className){ for (var j=0; j < class_list.length; j++){ rule=window.location.hostname+"#@."+class_list[j]; if(generated_rules.indexOf(rule)==-1){ if(!(rule in generated_rules)) generated_rules.push(rule); //console.log(rule); } } } console.log("whitelisted sticky: "+elm.id+","+elm.className+","+elm.tagName); }else{ console.log("ignoring sticky: "+elm.id+","+elm.className+","+elm.tagName); } } } } GM_SuperValue.set ("generated_rules", generated_rules); } function element_walker(elm) { //walk over element list as opposed to an element tree var node; //console.log("checking: "+elm.tagName.toString()); if(elm instanceof Element && ignore_tags.indexOf(elm.tagName.toString()) == -1 && getComputedStyle(elm)) { //console.log("testing: "+elm.tagName.toString()+" with position= "+getComputedStyle(elm).getPropertyValue("position").toLowerCase()); if (/*(getComputedStyle(elm).getPropertyValue("position") == "absolute") ||*/ (getComputedStyle(elm).getPropertyValue("position").toLowerCase() == "fixed")/* || */ /*(getComputedStyle(elm).getPropertyValue("position") == "relative")*//* ||*/ /*(getComputedStyle(elm).getPropertyValue("top") != "")*/) { var keyword_list =[]; keyword_list=elm.className.toString().split(' '); if (typeof(elm.id)=="string"&&elm.id!="") keyword_list.push([elm.id]); var has_matched=-1; if (keyword_list!=[]&&keyword_list!=[""]){ has_matched=keyword_walker(keyword_list); } var rule; var class_list=elm.className.toString().split(' '); //check for rule writing if (has_matched==1){ //console.log("pinning sticky: "+elm.id+","+elm.className+","+elm.tagName); if (elm.id){ rule=window.location.hostname+"###"+elm.id+":style(position: "+"fixed"+" !important;)"; if(generated_rules.indexOf(rule)==-1){ generated_rules.push(rule); console.log(rule); } } if(elm.className){ for (var i=0; i < class_list.length; i++){ rule=window.location.hostname+"##."+class_list[i]+":style(position: "+"fixed" +" !important;)"; if(generated_rules.indexOf(rule)==-1){ generated_rules.push(rule); console.log(rule); } } } elm.setAttribute('style', 'position:static !important'); elm.style.removeProperty('top'); //return; }else if(has_matched==0){ if(elm.className){ for (var j=0; j < class_list.length; j++){ rule="@@"+window.location.hostname+"##."+class_list[j]; if(generated_rules.indexOf(rule)==-1){ generated_rules.push(rule); console.log(rule); } } } //return; }else{ //console.log("ignoring sticky: "+elm.id+","+elm.className+","+elm.tagName); } } } // Handle child elements for (node = elm.firstChild; node; node = node.nextSibling) { if (node.nodeType === 1) { // 1 == Element element_walker(node); } } } function style_walker() { //this alternative mode of searching and modifying sticky elements by searching stylesheets directly //although i thought stylesheet checking would be faster it tends be slower and cannot be performed on external stylesheets var state=0; count_style_walking++; //console.log("checking stylesheets for the "+count_style_walking+"th time"); var styleSheets = window.document.styleSheets; for(var i = 0; i < styleSheets.length; i++){ //console.log("checking stylesheet #"+i);//+" named "+styleSheets[i].sheet); //try to get a list of classes for each stylesheet. //this will throw an error if the stylesheet is hosted on an external server because of cross site protection //these stylesheets cannot be processed at them moment/or never var classes; if (document.styleSheets[i].cssRules){ classes=document.styleSheets[i].cssRules; }else if (document.styleSheets[i].rules){ classes=document.styleSheets[i].rules; }//}catch(e){} if (!classes) continue; for (var j = 0; j < classes.length; j++) { //console.log("checking class "+classes[x].selectorText); state=0; for (var k=0; k < black_keywords.length; k++){ if (classes[j].selectorText) if (classes[j].selectorText.indexOf(black_keywords[k])!=-1){ //console.log("matched: l:"+black_keywords[k]+" in s:"+classes[j].selectorText+" of "+styleSheets[i].sheet); if (classes[j].position=="absolute" ||classes[j].position=="static"){ state = 1; } } } for (var k=0; k < white_names.length; k++){ if (classes[j].selectorText) if (classes[j].selectorText.indexOf(white_names[k])!=-1){ //console.log("whitelisted: l:"+white_names[k]+" in s:"+classes[j].selectorText+" of "+styleSheets[i].sheet); state=0; } } if (state==1){ stylesheet.deleteRule("position"); } } } } // show saved rules when opening a location containing "bloackhead-rules" if(window.location.toString().indexOf("blockhead-rules")!=-1){ var iDiv = document.createElement('div'); iDiv.id = 'blockhead-rules'; iDiv.style.margin = '0 auto'; iDiv.style.position= "absolute"; iDiv.style.backgroundColor ="white"; iDiv.style.zIndex=9001000; iDiv.style.opacity=1; document.getElementsByTagName('body')[0].appendChild(iDiv); rules=GM_SuperValue.get ("generated_rules"); for(var i=0;i<rules.length;i++){ iDiv.innerHTML+=rules[i]+'<br/>\n'; } throw new Error('This is not an error. This is just to abort javascript'); } // show saved rules when opening a location containing "bloackhead-statistics" if(window.location.toString().indexOf("blockhead-statistics")!=-1){ var iDiv = document.createElement('div'); iDiv.id = 'blockhead-statistics'; iDiv.style.margin = '0 auto'; iDiv.style.position= "absolute"; iDiv.style.backgroundColor ="white"; iDiv.style.zIndex=9001000; iDiv.style.opacity=1; document.getElementsByTagName('body')[0].appendChild(iDiv); iDiv.innerHTML='#white_names_counter:<br/>\ņ'; rules=GM_SuperValue.get ("white_names_counter"); for (var key in rules) { iDiv.innerHTML+=key+": "+rules[key]+'<br/>\n'; } iDiv.innerHTML+='<br/>\n############<br/>\n<br/>\n'; iDiv.innerHTML+='#black_keyword_counter:<br/>\ņ'; rules=GM_SuperValue.get ("black_keywords_counter"); for (var key in rules) { iDiv.innerHTML+=key+": "+rules[key]+'<br/>\n'; } throw new Error('This is not an error. This is just to abort javascript'); } // Kick it off starting with the `body` element if(walk_styles==1) style_walker(); if(walk_elements==1) counted_element_walker(document.body,"onstart"); // check elements again if the page code changes if(walk_elements==1) document.onload = counted_element_walker(document.body,"onload"); // check elements again if the page code changes if(walk_elements==1) document.onchange = counted_element_walker(document.body,"onchange"); // check elements again if the user scrolls the page(doesnt really do anything) if(walk_elements==1) document.onscroll = counted_element_walker(document.body,"onscroll"); //check if the dom is modified and checks the changed elements again if(mutation_check==1 && walk_elements==1){ MutationObserver = window.MutationObserver || window.WebKitMutationObserver; // define a new observer var obs = new MutationObserver(function(mutations, observer) { // look through all mutations that just occured for(var i=0; i<mutations.length; ++i) { for(var j=0; j<mutations[i].addedNodes.length; ++j) { //check this mutation counted_element_walker(mutations[i].addedNodes[j],"onmutation"); } } }); // have the observer observe for changes in children obs.observe(document.body, { attributes : true, //check for attribute changes childList: true, //use a list of occured changes subtree: false //also return all subelements of a change }); }