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 });
    }
})();