- // ==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
- });
- }