Sleazy Fork is available in English.

Gelbooru KonaStyle

Change gelbooru.com theme to darker, similar to konachan style. Added filtration image options, images with blacklist tags will be remove from search result. Also adden upvote&downvote button to preview page. Added buttons to hide submenu and red message in header.

// ==UserScript==
// @name         Gelbooru KonaStyle
// @namespace    http://Aestellar.homepage/
// @version      0.15
// @description  Change gelbooru.com theme to darker, similar to konachan style. Added filtration image options, images with blacklist tags will be remove from search result. Also adden upvote&downvote button to preview page. Added buttons to hide submenu and red message in header.
// @author       Aestellar
// @include       *://gelbooru.com/*
// @resource    gelbooruCss1 https://userstyles.org/styles/111063/gelbooru-konastyle-test.css
// @grant GM_getResourceText
// @grant GM_addStyle

// @run-at      document-start

// ==/UserScript==
/*jshint multistr: true */
(function(){
    var win = window;
    var ls = localStorage;
    var css = GM_getResourceText ("gelbooruCss1");

    var searcher;
    if(!win){
        console.log(win + "Failed to load window object");
        return;
    }

    if(win.self != win.top){
        return;
    }


    addStyle(css);
    document.addEventListener("DOMContentLoaded",updateUI, false);

    function loadPrereq(){
        var hrefVar = win.location.href;
        if(/(htt[ps]:\/\/gelbooru\.com[\/]*)$/.test(hrefVar)){
            document.body.className = 'main-page';
        }
        var fontAwesomeLoader = document.createElement('div');
        //Font awesome support Don't work with @resource
        fontAwesomeLoader.innerHTML = '<link rel="stylesheet" type="text/css" media="screen" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css" />';
        fontAwesomeLoader = fontAwesomeLoader.children[0];
        document.head.appendChild(fontAwesomeLoader);
    }


    function updateUI(){
        loadPrereq();
        checkLastVisit();
        searcher = new SearchWidget();
        searcher.init();
        createSettingsWindow();
        updateNav();
        updateThumbs();
    }

    function SearchWidget (){
        this.searchElt = undefined; 
        this.searchForm = undefined;
        this.filtersList = [];
        var self = this;
        this.submit = function(e){          
            var text = self.searchElt.value;
            text = self.applySearchFilters(text);
            self.searchElt.value = text;
        };

        this.applySearchFilters = function(text){
            for (var i = 0; i < this.filtersList.length; i++) {
                text = this.filtersList[i](text);
            }
            return text;
        };

        this.init = function(){
            this.searchElt = qS('#stags')||qS('#tags');
            if (!this.searchElt) {
                throw new Error('Search field not found');
            }
            this._createFilters();
            this.searchForm = qS('.sidebar3 form');
             if (!this.searchForm) {
                throw new Error('Search form not found');
            }           
            this.searchForm.onsubmit = this.submit;
        };
        this._createFilters = function(){
            var newImageFilter = function(text){
                if (ls.getItem('onlyNewSearch')==='true') {
                    if (!!getLastImageId()) {
                        var newId = ' id:>' + getLastImageId() + ' ';
                        if (text.indexOf(newId)===-1) {
                            text+=newId;
                        }
                    }
                }
                return text;
            };
            this.filtersList.push(newImageFilter);
        };       
    }

    function updateThumbs(){
        var c = qS('.thumb');
        if (!c){
            return;
        }

        var thumbsContainer = c.parentNode;
        c = thumbsContainer.parentNode;
        processThumbs(thumbsContainer);

        observeDOM(c, function(e){
            var temp =e.forEach;
            if (!!temp) {
                e.forEach(function(elt){
                    var test;
                    var  t = elt.addedNodes;
                    if (!!t) {
                        t = t[0];

                        if (!t) {
                            return;
                        }

                        if (!t.getElementsByClassName){
                            return;
                        }
                        test = t.getElementsByClassName('thumb');
                        if (!!test.length) {

                            processThumbs(t);
                        }
                    }
                });
            }
        });
    }

    function appendThumbPane(thumb){
        var test = makeDiv('thumb-pane');
        var url = getImageRefFromThumb(thumb);
        url = url.replace('thumbnails','images');
        url = url.replace('thumbnail_','');
        if(getTagListFromThumb(thumb).search('animated_gif')!=-1){
            makeRef('gif');
        }
        else if (getTagListFromThumb(thumb).search('webm')!=-1) {
            makeRef('webm');
        }
        else{
            var ext = ['png','jpeg','jpg'];
            for (var i = ext.length - 1; i >= 0; i--) {
                makeRef(ext[i]);
            }
        }

        function makeRef(extension){
            var ref = document.createElement('a');
            ref.textContent  = extension;
            var url2 = url.replace('jpg',extension);
            ref.setAttribute('href', url2);
            test.appendChild(ref);
        }


        var dataId = getIdFromThumb(thumb);
        test.setAttribute('data-id', dataId);
        test.setAttribute('data-voted', 'false');

        var upBtn = document.createElement('span');
        var downBtn = document.createElement('span');

        makeSimpleButton(upBtn,
            {name:'upBtn',
                glyph:'fa-thumbs-up',
                callback:voteUp
            });

        makeSimpleButton(downBtn,
            {name:'downBtn',
                glyph:'fa-thumbs-down',
                callback:voteDown
            });

        test.insertBefore(upBtn,test.firstChild);
        //test.appendChild(ref);
        test.appendChild(downBtn);

        thumb.appendChild(test);
    }

    function voteImage(e, vote){
        var parent = e.bindedElt;
        var pane = parent.parentNode;
        if (pane.getAttribute('data-voted')!=='false') {
            return;
        }
        var id = pane.getAttribute('data-id');

        handleXHRDoc('index.php?page=post&s=vote&id='+id+'&type='+vote, function(doc){
            var score = doc.body.textContent;
            parent.textContent=score;
            pane.setAttribute('data-voted', 'true');
        });
    }

    function voteUp(e){
        voteImage(e, 'up');
    }

    function voteDown(e){
        voteImage(e, 'down');
    }


    function isImageFiltering(){
        return ls.getItem('disableImageFiltering')==='false';
    }

    function isPreviewPaneEnabled(){
        return ls.getItem('disablePreviewPane')==='false';
    }


    function processThumbs(container){
        if (!!container) {
            for (var i = 0; i <container.children.length; i++) {
                var loop = container.children[i];

                if(filterThumb(loop)){
                    continue;
                }
                if (isPreviewPaneEnabled()) {
                    appendThumbPane(loop);
                }
            }
        }
    }

    function increaseFilterCount(){
        var countElt = qS('#filterCount');
        var count = countElt.textContent;
        count = parseInt(count);
        count+=1;
        countElt.textContent=count;
    }

    function filterThumb(thumb){
        if (!isImageFiltering()) {
            return false;
        }

        var thumbTags = getTagListFromThumb(thumb);
        if (!!thumbTags) {
            if (isBannedThumb(thumbTags)) {
                hide(thumb);
                increaseFilterCount();
                return true;
            }
        }
        return false;
    }

    function isBannedThumb(thumbList){
        var bannedList = getBannedList();
        var loop;

        if (!bannedList) {
            return false;
        }
        bannedList = bannedList.replace('\n', ' ');
        bannedList = bannedList.replace(/\s+/g, ' ');
        bannedList = bannedList.replace(/(\s+)$/g, '');
        bannedList = bannedList.replace(/^(\s+)/g, '');
        bannedList = bannedList.split(' ');
        for (var i = bannedList.length - 1; i >= 0; i--) {
            loop = bannedList[i];
            loop = new RegExp('\\b'+ loop + '\\b');
            if (thumbList.search(loop)!=-1) {
                return true;
            }
        }
        return false;
    }

    function getIdFromThumb(thumb){
        var id = thumb.getAttribute('id');
        if (!!id) {
            id = id.replace('s', '');
        }
        return id;
    }

    function getImageRefFromThumb(thumb){
        var img = thumb.getElementsByTagName('img')[0];
        if (!!img) {
            return img.getAttribute('src');
        }
    }

    function getTagListFromThumb(thumb){
        if (!thumb) {
            return;
        }
        var thumbImg = thumb.getElementsByClassName('preview')[0];
        if (!!thumbImg) {
            return thumbImg.getAttribute('alt');
        }
    }

    function getBannedList(){
        var bannedList = ls.getItem('bannedTagsList');
        return bannedList;
    }
    function getFavoritesTagsList(){
        var favoritesList = ls.getItem('favoritesTagsList');
        return favoritesList;        
    }


    function updateNav() {
        var navBar = qS('.flat-list');
        var settingsBtn = document.createElement("li");
        var navBtn = document.createElement("li");
        var advNav = document.createElement("li");
        navBar.insertBefore(settingsBtn, navBar.firstChild);
        navBar.insertBefore(advNav, navBar.firstChild);
        navBar.insertBefore(navBtn, navBar.firstChild);

        var s = {name:'settingsBtn',
            glyph:'fa-gear',
            callback:showSettingsWindow};

        makeUserNavBar();

        var sett = {name:'navCollapse', glyphOff:'fa-minus-square-o', glyphOn:'fa-plus-square-o',
            callbackOff:makeToggleFunc(['.submenu', '.noticeError'], show), callbackOn:makeToggleFunc(['.submenu', '.noticeError'], hide)};

        var sett2 = {name:'advNavCollapse', glyphOff:'fa-search-minus', glyphOn:'fa-search-plus',
            callbackOff:makeToggleFunc(['#usernavbar'], show), callbackOn:makeToggleFunc(['#usernavbar'], hide)};

        makeSimpleButton(settingsBtn, s);
        makeToggleButton(navBtn,sett);
        makeToggleButton(advNav,sett2);
    }

    function createSettingsWindow(){
        var mod = document.createElement('div');
        mod.className = 'modalDialog';
        mod.innerHTML = '<div class = modalDialogBox>\
		<span title="Close" class="close"></span>\
		</div>';
        document.body.appendChild(mod);
        mod.addEventListener('click',hideModalWindow,false);

        var close = qS('.modalDialog .close');

        makeSimpleButton(close,
            {name:'settingsBtn',
                glyph:'fa-times',
                callback:hideSettingsWindow
            });

        var dialog = qS('.modalDialogBox');
        dialog.appendChild(makeSettingsInner());

    }

    function makeSettingsInner(){
        var div = document.createElement('div');
        div.innerHTML = '<span class="modalName">Settings</span>';

        var settTab = new TabbedPane();
        div.appendChild(settTab.tabbedPane);

        var mainSetting = makeDiv('main-settings');

        var disableFilteringRule = makeDiv('settings-rule');
        disableFilteringRule.textContent = 'Disable image filtering: ';
        disableFilteringRule.appendChild(makeCheckBox('disableImageFiltering'));
        mainSetting.appendChild(disableFilteringRule);


        var disableThumbPane = makeDiv('settings-rule');
        disableThumbPane.textContent = 'Don\' show preview panel: ';
        disableThumbPane.appendChild(makeCheckBox('disablePreviewPane'));
        mainSetting.appendChild(disableThumbPane);

        var bannedTagsTab = document.createElement('textarea');
        bannedTagsTab.className = 'bannedList';
        bannedTagsTab.value = getBannedList();
        bannedTagsTab.addEventListener('input',function(e){
            ls.setItem('bannedTagsList',e.target.value);
        },false);

        var favoritesTagsTab = document.createElement('textarea');
        favoritesTagsTab.className = 'bannedList';
        favoritesTagsTab.value = getFavoritesTagsList();
        favoritesTagsTab.addEventListener('input',function(e){
            ls.setItem('favoritesTagsList',e.target.value);
        },false);


        settTab.addTab('sett1', 'Settings', mainSetting);
        settTab.addTab('sett2','Banned tags', bannedTagsTab);
        //settTab.addTab('sett3','Favorites tags', favoritesTagsTab);        
        //console.log(settTab);

        // var iTest = makeDiv('');
        // iTest.innerHTML = '<img src="http://i.nhentai.net/galleries/795920/cover.jpg">';
        // settTab.addTab('sett3','imageTest', iTest);
        settTab.selectDefault();


        return div;
    }

    function showSettingsWindow(e){
        var mod = qS('.modalDialog');
        mod.classList.add('modalActive');
    }

    function hideModalWindow(e){
        var mod = qS('.modalDialog');
        if (e && (e.target!== mod)) {
            return;
        }
        hideSettingsWindow(e);
    }

    function hideSettingsWindow(e){
        var mod = qS('.modalDialog');
        mod.classList.remove('modalActive');
    }


    function makeUserNavBar(){
        var userBar = document.createElement('ul');
        userBar.className = 'flat-list';
        userBar.setAttribute('id', 'usernavbar');
        var navBar = qS('.submenu');
        var navParent = navBar.parentNode;
        navParent.insertBefore(userBar,navBar.nextSibling);

        makeFitImageBtn(userBar);
        makeAdvSearchPane(userBar);

    }

    function makeToggleFunc(sel,action){

        var activeFoo = function(){
            for (var i = sel.length - 1; i >= 0; i--) {
                action(qS(sel[i]));
            }
        };
        return activeFoo;
    }

    function makeFitImageBtn(userBar){
        var fitBtn = document.createElement("li");
        userBar.appendChild(fitBtn);
        fitBtn.title = 'Autofit image or webm to page width';
        var imageFit = makeToggleButton(fitBtn, {name:'fitImage',
            glyphOff:'fa fa-compress',
            glyphOn:'fa fa-expand',
            callbackOff:fitImage,
            callbackOn:fitImage});
        function fitImage(){
            var img = qS('#image')||qS('#gelcomVideoPlayer');
            if (img === undefined) {
                return;
            }

            img.removeAttribute('width');
            img.removeAttribute('height');

            if ((localStorage.getItem('fitImage')==='true')) {
                img.style.maxWidth = '100%';
            }

            else{
                img.style.maxWidth = '';
            }

            if(!!img){
                img.addEventListener('load', fitImage, false);
            }
        }
    }

    function makeAdvSearchPane(userBar){
        var advSearch = document.createElement('div');
        advSearch.innerHTML = '<div id = "adv-search">\
	<li><span>Advanced Search: </span>\
	<span id = "only-new">Only new images </span></li>\
	<li><span>Filtered images: <span id="filterCount">0</span></span></li>\
	</div>';
        advSearch = advSearch.firstChild;
        userBar.appendChild(advSearch);
        var onlyNew = document.getElementById('only-new');

        makeToggleButton(onlyNew, {name:'onlyNewSearch',
            glyphOff:'fa-square-o',
            glyphOn:'fa-check-square-o'
        });
        return advSearch;
    }

    function makeCheckBox(rname){
        var box = makeSpan('checkbox-span');
        makeToggleButton(box,
            {name:rname,
                glyphOff:'fa-square-o',
                glyphOn:'fa-check-square-o'
            });
        return box;
    }

    function makeToggleButton(elem, settings){
        var btn = new ButtonToggle(settings);
        btn.bindElement(elem);
        btn.load();
        elem.addEventListener('click', btn.handlerClick, false);
        return btn;
    }

    function makeSimpleButton(elem, settings){
        var btn = new ButtonSimple(settings);
        btn.bindElement(elem);
        elem.addEventListener('click', btn.handlerClick, false);
        return btn;
    }


    function ButtonSimple(settings){
        var self = this;
        var modGliph = 'fa-lg';
        this.name = settings.name;

        this.glyphElt = document.createElement('i');
        this.active = false;
        this.bindedElt = null;
        this.callback = settings.callback;
        this.bindElement = function (elem) {
            self.bindedElt = elem;
            if (self.bindedElt.length > 0) {
                elem.insertChildBefore(self.glyphElt, elem.FirstChild);
            }
            else {
                elem.appendChild(self.glyphElt);
            }
        };
        this.glyph = 'fa ' + settings.glyph + ' ' + modGliph;
        self.glyphElt.className = self.glyph;

        this.handlerClick = function (e) {
            self.callback(self);
        };
    }

    function ButtonToggle(settings) {
        var self = this;

        var modGliph = 'fa-lg';
        this.name = settings.name;

        this.active = false;
        this.bindedElt = null;
        this.glyphElt = document.createElement('i');
        this.bindElement = function (elem) {
            self.bindedElt = elem;
            if (self.bindedElt.length > 0) {
                elem.insertChildBefore(self.glyphElt, elem.FirstChild);
            }
            else {
                elem.appendChild(self.glyphElt);
            }
        };

        this.glyphOn = 'fa ' + settings.glyphOn + ' ' + modGliph;
        this.glyphOff = 'fa ' + settings.glyphOff + ' ' + modGliph;
        this.callbackOn = settings.callbackOn;
        this.callbackOff = settings.callbackOff;
        this.save = function () {
            localStorage.setItem(self.name, self.active);
        };
        this.load = function () {
            var t = localStorage.getItem(self.name);
            if (t === 'true') {
                self.setActive(true);
            }
            else {
                self.setActive(false);
            }
        };
        this.handlerClick = function (e) {
            if (self.active) {
                self.setActive(false);
            }

            else {
                self.setActive(true);
            }
        };

        this.setActive = function (val) {
            val = !!val;
            self.active = val;
            self.save(val);

            if (val) {
                if (!!self.bindedElt) {
                    self.bindedElt.classList.add('activeBtn');
                    self.glyphElt.className = self.glyphOn;
                }

                if (!!self.callbackOn) {
                    self.callbackOn(self);
                }
            }

            else {
                if (!!self.bindedElt) {
                    self.bindedElt.classList.remove('activeBtn');
                    self.glyphElt.className = self.glyphOff;
                }

                if (!!self.callbackOff) {
                    self.callbackOff(self);
                }
            }
        };
    }


// LastVisit functions              
    function checkLastVisit(){
        var timer = 7200000;
        var lastVisit = parseInt(ls.getItem('lastVisit'));
        var prevVisit = parseInt(ls.getItem('preLastVisit'));

        var currentVisit = new Date().valueOf();
        if ((!lastVisit)||(!prevVisit)) {
            updateLastVisit();
        }
        if ((lastVisit + timer) < currentVisit) {
            updateLastVisit();
        }
    }

    function updateLastVisit(){
        var posts = 'http://gelbooru.com/index.php?page=post&s=list&tags=all';
        handleXHRDoc(posts, function(doc){
            var lastIdElt = doc.getElementsByClassName('thumb')[0];
            var lastID = lastIdElt.getAttribute('id');
            lastID = lastID.replace('s', '');

            if (!!ls.getItem('lastVisit')) {
                ls.setItem('preLastVisit',ls.getItem('lastVisit'));
            }
            else{
                ls.setItem('preLastVisit',new Date().valueOf());
            }
            if (ls.getItem('lastImageId')) {
                ls.setItem('preImageId',ls.getItem('lastImageId'));
            }
            else{
                ls.setItem('preImageId',lastID);
            }

            localStorage.setItem('lastVisit',new Date().valueOf());
            localStorage.setItem('lastImageId', lastID);
        });
    }

    function getLastImageId(){
        var t = localStorage.getItem('preImageId');
        return parseInt(t);
    }

})();

function handleXHRDoc(reqString, callback){
    var doc = document.implementation.createHTMLDocument("example");

    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open('GET', reqString, true);
    xmlhttp.send(null);
    xmlhttp.onreadystatechange = handle;

    function handle(){
        if (xmlhttp.readyState == 4) {
            if(xmlhttp.status == 200) {
                doc.documentElement.innerHTML = xmlhttp.responseText;
                console.log(doc);
                callback(doc);
            }

            else{
                console.log('Error xhr of ' + reqString);
            }
        }
    }
}


//from http://stackoverflow.com/questions/2246901/how-can-i-use-jquery-in-greasemonkey-scripts-in-google-chrome
function load(a, b, c) {
    var d;
    d = document.createElement("script"), d.setAttribute("src", a), b !== null && d.addEventListener("load", b), c !== null && d.addEventListener("error", c), document.body.appendChild(d);
    return d;
}
function execute(a) {
    var b, c;
    typeof a == "function" ? b = "(" + a + ")();" : b = a, c = document.createElement("script"), c.textContent = b, document.body.appendChild(c);
    return c;
}
function loadAndExecute(a, b) {
    return load(a, function () {
        return execute(b);
    });
}
//
function qS(selector){
    return document.querySelectorAll(selector)[0];
}

function hide(elt){
    if (!!elt) {
        elt.style.display = 'none';
    }
    else{
        console.log('Not found' + elt );
    }

}
function show(elt){
    if (!!elt) {
        elt.style.display = '';
    }
    else{
        console.log('Not found' + elt );
    }

}

function removeAllChild(elem){
    while (elem.hasChildNodes()) {
        elem.removeChild(elem.lastChild);
    }
}

function hasClass(elem, name) {
    if (!!elem) {
        if (!!elem.classList) {
            if (elem.classList.contains(name)) {
                return true;
            }
        }
    }
    return false;
}

function addStyle(css){
    if (typeof GM_addStyle != "undefined") {
        GM_addStyle(css);
    } else if (typeof PRO_addStyle != "undefined") {
        PRO_addStyle(css);
    } else if (typeof addStyle != "undefined") {
        addStyle(css);
    } else {
        var node = document.createElement("style");
        node.type = "text/css";
        node.appendChild(document.createTextNode(css));
        var heads = document.getElementsByTagName("head");
        if (heads.length > 0) {
            heads[0].appendChild(node);
        } else {
            // no head yet, stick it whereever
            document.documentElement.appendChild(node);
        }
    }
}


function applyCSSRulesFromJS(){
    var style = document.createElement("style");
    style.appendChild(document.createTextNode(getCSSText()));

    // Add the <style> element to the page
    document.head.appendChild(style);

    return style.sheet;
}

function makeDiv(className){
    var div = document.createElement('div');
    div.className = className;
    return div;
}

function makeSpan(className){
    var div = document.createElement('span');
    div.className = className;
    return div;
}

function TabbedPane (){
    var self = this;
    var tabbedPane = makeDiv("tabbed-Pane");
    var navPane = makeDiv("nav-Pane");
    var contentPane = makeDiv("content-Pane");
    tabbedPane.appendChild(navPane);
    tabbedPane.appendChild(contentPane);
    this.tabbedPane = tabbedPane;
    this.navPane = navPane;
    this.navMap= {};
    this.contentPane = contentPane;
    this.addTab = function(realName,visibleName,content){
        var head = makeDiv('tab-Head');
        head.textContent  = visibleName;
        var tab = new TabPane(self, realName, head, content);
        self.navMap[realName]= tab;
        self.navPane.appendChild(head);
        head.addEventListener('click', tab.clickHandler, false);
    };
    this.selectTab = function(tab){
        var loopTab;

        for (var key in self.navMap) {
            if (self.navMap.hasOwnProperty(key)){
                loopTab = self.navMap[key];
                loopTab.setSelected(false);
            }
        }

        removeAllChild(self.contentPane);
        tab.setSelected(true);
        self.contentPane.appendChild(tab.content);
    };
    this.selectDefault = function(){
        for (var key in self.navMap) {
            if (self.navMap.hasOwnProperty(key)){
                self.navMap[key].clickHandler();
                break;
            }
        }
    };
}

function TabPane(master,name,head,content){
    var self = this;
    this.name = name;
    this.masterPane = master;
    this.head = head;
    this.content = content;
    this.selected = false;
    this.setSelected = function(val){
        if (!!val) {
            self.head.classList.add('activeTab');
            self.selected = true;
        }
        else{
            self.head.classList.remove('activeTab');
            self.selected = false;
        }
    };
    this.clickHandler = function(e){
        self.setSelected(true);
        master.selectTab(self);
    };

}

var observeDOM = (function(){
    var MutationObserver = window.MutationObserver;

    return function(obj, callback){
        // define a new observer
        var obs = new MutationObserver(function(mutations, observer){
            if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
            //console.log(obj)
                callback(mutations);
        });
        // have the observer observe foo for changes in children
        obs.observe( obj, { childList:true, subtree:false });
    }
})();