ExHentai Viewer

manage your favorite tags, enhance searching, improve comic page

Від 03.03.2019. Дивіться остання версія.

// ==UserScript==
// @name ExHentai Viewer
// @namespace Violentmonkey Scripts
// @description manage your favorite tags, enhance searching, improve comic page
// @match *://exhentai.org/*
// @match *://e-hentai.org/*
// @grant       GM_setValue
// @grant       GM_getValue
// @version 0.40
// ==/UserScript==
// version info:
// 0.40: 
//     1. improve btn style;
//     2. tag value were saved as list instead of string;
//     3. add support for add / search / delete a set of tags by one name.
//     4. add zoomIn / zoomOut btn to MPV.


var custom_filter = GM_getValue('custom_filter', -1);
initScript();
if (window.location.href.includes("/s/")) {
    // Handle comic page
    EhViewer('s');
} else if (window.location.href.includes("/mpv/")){
    // Handle multi page mode
    EhViewer('mpv');
}else if (window.location.href.includes("/g/")) {
    // Handle gallery page
	addNewStyle('input{margin:2px 2px!important;}');
    filterForGallery();
} else if (document.getElementById('searchbox') !== null) {
    // Add tag management feature to searchbox
	addNewStyle('input{margin:2px 2px!important;}');
    addFilter(document.getElementsByClassName("nopm")[0]);
}

function initScript(){
    var script_config = GM_getValue('script_config', {});
    if (script_config.currentVersion == undefined){
        script_config.currentVersion = '0.40';
        if (custom_filter == -1){
            custom_filter = {};
        } else {
            updateTo040();
        }
    }
    GM_setValue('custom_filter', custom_filter);
    GM_setValue('script_config', script_config);

    function updateTo040(){
        script_config.oldVersionData = [];
        script_config.oldVersionData.push({'0.37': [custom_filter]});
        var new_custom_filter = {};
        custom_filter.forEach(function(v, i, a){
            // Handle wrong values in custom filter
            if (v == undefined || v.tag == undefined || v.name == undefined){
                a.splice(i, 1);
            } else{
                // save tags to list instead of str
                new_custom_filter[a[i].name] = a[i].tag.split("+");
            }
        });
        custom_filter = new_custom_filter;
    }
}

// Add style to current page
function addNewStyle(newStyle, target) {
    if(target == undefined){
        target = 'new-styles';
    }
    var styleElement = document.getElementById(target);
    if (!styleElement) {
        styleElement = document.createElement('style');
        styleElement.type = 'text/css';
        styleElement.id = target;
        document.getElementsByTagName('head')[0].appendChild(styleElement);
        styleElement.appendChild(document.createTextNode(newStyle));
    } else {
        styleElement.innerText = newStyle;
    }
}

// Add tag management and searchbox to gallery page
function filterForGallery(){
    var galleryFilter = document.body.insertBefore(document.createElement('form'), document.getElementsByClassName('gm')[0]);
    galleryFilter.innerHTML = '<p id="galleryFilter" class="nopm"><input type="text" name="f_search" placeholder="Search Keywords" value="" size="50"><input type="submit" name="f_apply" value="Apply Filter"></p>';
    galleryFilter.setAttribute('style', 'display: none; width: 30%; text-align: center; margin: 10px auto; border: 2px ridge black; padding: 10px;');
    galleryFilter.setAttribute('action', 'https://exhentai.org/');
    galleryFilter.setAttribute('method', 'get');
    addFilter(document.getElementById('galleryFilter'));
    var tb = document.getElementById('taglist').firstElementChild.firstElementChild;
    tb.innerHTML += '<tr><td class="tc">EHV:</td><td><div id="show_filter" class="gt" style="cursor:pointer">show filter</div></td></tr>';
    document.getElementById('show_filter').addEventListener('click', function(e){
    	if (e.target.innerText === 'show filter'){
    		galleryFilter.style.display = "block";
    		e.target.innerText = 'hide filter'
    	} else {
    		galleryFilter.style.display = "none";
    		e.target.innerText = 'show filter'
    	}
    })
	for(var i=0; i<document.all.length; i++){
		if (document.all[i].id.slice(0, 3) === 'ta_') {
			document.all[i].addEventListener('contextmenu', addGalleryTag, false);
		}
	}

    // Allow you to add tags of this gallery to searchbox
	function addGalleryTag(e) {
		e.preventDefault();
		var searchBox = galleryFilter.firstElementChild.firstElementChild;
		var tagValue = '"'+e.target.innerText+'"';
		if (searchBox.value.includes(tagValue)){
			// console.log('del?');
			searchBox.value = searchBox.value.replace(tagValue, '');
		}else {
			searchBox.value += tagValue;
		}
	}
}

// Add tag management feature to specific position (i.e. boxPos)
function addFilter(boxPos){
    var ipColor1;
    var ipColor2;
    if (window.location.host === 'e-hentai.org'){
        ipColor1 = 0xedeada;
        ipColor2 = ipColor1 - 0x202020;
    } else {
        ipColor1 = 0x34353b;
        ipColor2 = ipColor1 + 0x202020;
    }
    ipColor1 = '#' + ipColor1.toString('16');
    ipColor2 = '#' + ipColor2.toString('16');
    var search_box = boxPos.firstElementChild;
    var p = document.getElementById('custom_filter');
    if(p == undefined) {
    	boxPos.appendChild(document.createElement('br'));
        boxPos.appendChild(document.createElement('br'));
        p = boxPos.appendChild(document.createElement('p'));
        boxPos.appendChild(document.createElement('br'));
        p.setAttribute('class', 'nopm');
        p.setAttribute('id', 'custom_filter');
    } else {
        p.innerHTML = '';
    }
    for (var tagName in custom_filter) {
        var t = p.appendChild(document.createElement('input'));
        t.setAttribute('type', 'button');
        t.setAttribute('value', tagName);
        // t.setAttribute('tag', filter.tag);
        t.addEventListener('click', searchTags, false);
        t.addEventListener('contextmenu', delTags, false);
        if (tagsExist(custom_filter[tagName])){
            t.style.backgroundColor=ipColor2;
        } else {
            t.style.backgroundColor=ipColor1;
        }
    }
    t=p.appendChild(document.createElement('input'));
    t.setAttribute('type', 'button');
    t.setAttribute('value', "+");
    t.addEventListener('click', newTag, false);
    
    function tagsExist(tags){
        var exist = true;
        for(var i=0; i < tags.length; i++){
            if(search_box.value.includes(tags[i])){
                ;
            } else {
                exist = false;
                break;
            }
        }
        return exist;
    }

    function searchTags(e){
        // get tags
        var tagName = e.target.value;
        var tags = custom_filter[tagName];
        // handle searchbox value and btn color
        if (tagsExist(tags)){
            tags.forEach(function(tag){
                search_box.value = search_box.value.replace(('"'+tag+'"'), "");
            });
            e.target.style.backgroundColor = ipColor1;
        } else {
            tags.forEach(function(tag){
                if (search_box.value.includes(tag)){
                    ;
                } else {
                    search_box.value += ('"'+tag+'"');
                }
            });
            e.target.style.backgroundColor=ipColor2;
        }
    }
    function delTags(e){
        e.preventDefault();
        if (window.confirm('Delete this tag?') == true){
            var tagName = e.target.value;
            delete custom_filter[tagName];
            GM_setValue('custom_filter', custom_filter);
            addFilter(e.target.parentElement.parentElement);
        }
    }
    function newTag(e){
        var tagStr = window.prompt("Add filter like format below", "[tag] or [name@tag] or [name@tag+tag+tag+tag]").split("@");
        if (tagStr.length == 1 && tagStr[0] != ''){
            // custom_filter.push({'name':tagStr[0], 'tags':tagStr});
            custom_filter[tagStr[0]] = tagStr;
        } else if (tagStr.length == 2){
            var tags = tagStr[1].split('+');
            // custom_filter.push({'name':tagStr[0], 'tag':tags}); 
            custom_filter[tagStr[0]] = tags;
    	} else {
    	    window.alert("Invalid input... :(");
    	}
        GM_setValue('custom_filter', custom_filter);
        addFilter(e.target.parentElement.parentElement);
	}
}

// Optimize the comic page style, add functional buttons
function EhViewer(mode){
    console.log('EhV start...'); 
    var isFirstRun = true;
    var current_scale = 1;
    var zoomInterval;
    var setScale;
    var float_list = document.createElement("ul");
    float_list.setAttribute("class", "float_list");
    document.body.appendChild(float_list);
    var float_btn = new Array([]);
    for(var i=0; i<2; i++){
        float_btn[i]=document.createElement("li");
        float_list.appendChild(float_btn[i]);
    }
    float_btn[0].setAttribute("class", "float_btn zoom_in");
    float_btn[1].setAttribute("class", "float_btn zoom_out");
    float_btn[0].innerText="➕";
    float_btn[1].innerText="➖";
    float_btn[0].addEventListener('mousedown', zoomIn, false);
    float_btn[1].addEventListener('mousedown', zoomOut, false);
    document.addEventListener('mouseup', function(){clearInterval(zoomInterval);});
    if (mode == 's'){   
        var oldSi = si;
        var firstPage = document.getElementsByClassName("sn")[0].firstChild.href;
        var lastPage = document.getElementsByClassName("sn")[0].lastChild.href;
        setScale = s;
        // add extra btn for this mode
        for(var i=2; i<5; i++){
            float_btn[i]=document.createElement("li");
            float_list.appendChild(float_btn[i]);
        }
        float_btn[2].setAttribute("class", "float_btn prev_page");
        float_btn[3].setAttribute("class", "float_btn next_page");
        float_btn[4].setAttribute("class", "float_btn gallery");
        float_btn[2].innerText="👈";
        float_btn[3].innerText="👉";
        float_btn[4].innerText="📚";
        float_btn[2].addEventListener('click', prevPage, false);
        float_btn[3].addEventListener('click', nextPage, false);
        float_btn[4].setAttribute("onclick", "window.open(document.getElementsByClassName('sb')[0].firstChild.href,'_self');");
        document.addEventListener("keydown", keyDown);
        setNewPage(); // initial when user enter comic page from elsewhere;
    } else if (mode == 'mpv'){
        setScale = mpv;
        document.addEventListener("keydown", function(e){
            var keycode = e.which;
            switch(keycode){
                case 187: {
                    setScale("zoomIn");
                    break;
                }
                case 189: {
                    setScale("zoomOut");
                    break;
                }
                default: {
                    ;
                }  
            }
        })
    }
    setBtnStyle();
    function setBtnStyle(){
        var btnStyle = '.float_list{display:block; position:fixed; bottom:10px; right:10px; list-style:none;z-index:1005;}'+
                '.float_btn{position:relative; user-select:none;background-color:btColor1; margin:5px; width:50px; height:50px; line-height:50px; font-size:12px; border-radius:50%; cursor:pointer;}'+
                '.float_btn:hover{transition-duration:250ms; background-color:btColor2; box-shadow:0 0 3px 1px shadowColor;}'+
                '.float_btn:active{transition-duration:250ms; top:1px; box-shadow:0 0 3px 0px shadowColor;}';
        var btColor1;
        var btColor2;
        var shadowColor;
        if (window.location.host === 'e-hentai.org'){
            btColor1 = 0xe3e0d1 - 0x101010;
            btColor2 = btColor1 - 0x050505;
            shadowColor = btColor1 + 0x080808;
        } else {
            btColor1 = 0x34353b + 0x101010;
            btColor2 = btColor1 + 0x050505;
            shadowColor = btColor1 - 0x080808;
        }
        btnStyle = btnStyle.replace('btColor1', '#'+btColor1.toString('16'));
        btnStyle = btnStyle.replace('btColor2', '#'+btColor2.toString('16'));
        btnStyle = btnStyle.replace('shadowColor', '#'+shadowColor.toString('16'));
        btnStyle = btnStyle.replace('shadowColor', '#'+shadowColor.toString('16'));
        addNewStyle(btnStyle, 'btn-style');
    }
    function keyDown(e) {
       var keycode = e.which;
        switch(keycode){
            case 37: {
                setNewPage();
                break;
            }
            case 39: {
                setNewPage();
                break;
            }
            case 187: {
                setScale("zoomIn");
                break;
            }
            case 189: {
                setScale("zoomOut");
                break;
            }
            case 188: {
                window.scrollBy(0, -window.innerHeight);
                break;
            }
            case 190: {
                window.scrollBy(0, window.innerHeight);
                break;
            }
            case 219: {
                window.scrollBy(0, -window.innerHeight*0.3);
                break;
            }
            case 221: {
                window.scrollBy(0, window.innerHeight*0.3);
                break;
            }
            default: {
                // console.log(keycode);
            }
        }
    }
    function setNewPage() {
                var listenChange = setInterval(function(){
                    if((oldSi != si) || isFirstRun){
                        isFirstRun = false;
                        var newStyle = 'h1, #i2, #i5, #i6, #i7, .ip, .sn{display:none!important;} ::-webkit-scrollbar{display:none;}';
                        addNewStyle(newStyle);
                        var pic = document.getElementById("img");
                        var width = Number(pic.style.width.replace("px", ""));
                        var height = Number(pic.style.height.replace("px", ""));
                        var page = document.getElementsByTagName('span');
                        var footMark = document.getElementById('i4').firstChild;
                        var currentPage = page[0].innerText;
                        var totalPage = page[1].innerText;
                        footMark.innerHTML = currentPage +"P / "+ totalPage +"P :: "+ footMark.innerText +" :: ";
                        var originDlLink = document.getElementById('i7').lastChild;
                        if (originDlLink != null){
	                        var dlLink = document.createElement('a');
	                        dlLink.href = originDlLink.href;
	                        dlLink.innerText = originDlLink.innerText;
                        	footMark.appendChild(dlLink);
                        } else {
                        	footMark.innerHTML += 'No download';
                        }
                        width *= current_scale;
                        height *= current_scale;
                        pic.style.width = width + "px";
                        pic.style.height = height + "px";
                        oldSi = si;
                        clearInterval(listenChange);
                    }
                }, 200);
    }
    function prevPage(e){
        e.preventDefault();
        if (window.location.href !== firstPage){
            document.getElementById('prev').onclick();
        } else {
            window.alert("The first page (⊙_⊙)")
        }
        setNewPage();
    }
    function nextPage(e){
        e.preventDefault();
        if (window.location.href !== lastPage){
            document.getElementById('next').onclick();
        } else {
            window.alert("The last page (⊙ω⊙)")
        }
        setNewPage();
    }
    function zoomIn(e){
        e.preventDefault();
        setScale('zoomIn');
        zoomInterval = setInterval(function(){
            setScale('zoomIn');
        }, 300);
    }
    function zoomOut(e){
        e.preventDefault();
        setScale('zoomOut');
        zoomInterval = setInterval(function(){
            setScale('zoomOut');
        }, 300);
    }
    function s(cmd){
        var pic = document.getElementById("img");
        var width = Number(pic.style.width.replace("px", ""));
        var height = Number(pic.style.height.replace("px", ""));
        switch(cmd){
            case 'zoomIn': {
                var max_width = Number(pic.style.maxWidth.replace("px", ""));
                if (width < max_width){
                    width *= 1.1;
                    height *= 1.1;
                    current_scale *= 1.1;
                } else{
                }
                break;
            }
            case 'zoomOut': {
                if (height >= window.innerHeight){
                    width /= 1.1;
                    height /= 1.1;
                    current_scale /= 1.1;
                } else {
                }
                break;
            }
        }
        pic.style.width = Math.round(width) + "px";
        pic.style.height = Math.round(height) + "px";
    }
    function mpv(cmd){
        var mpvStyle = 'img[id^="imgsrc"], div[id^="image"]{width:mpvWidth!important;height:auto!important; max-width:100%!important;}"';
        var max_width = Number(document.getElementById('pane_images').style.width.replace("px", "")) - 20;
        var min_width = 200;
        var original_width = Number(document.getElementById('image_1').style.maxWidth.replace("px", ""));
        var current_width = original_width * current_scale;
        switch(cmd){
            case 'zoomIn': {
                if(current_width*1.1 < max_width){
                    current_scale *= 1.1;
                    current_width = Math.round(original_width * current_scale);
                }
                break;
            }
            case 'zoomOut': {
                if(current_width/1.1 > min_width){
                    current_scale /= 1.1;
                    current_width = Math.round(original_width * current_scale);
                }           
                break;
            }
        }
        addNewStyle(mpvStyle.replace("mpvWidth", current_width+"px"));
    }
}