Sleazy Fork is available in English.

e-hentai base history recommand gallerys

record your favorite tags, use to search gallerys

// ==UserScript==
// @name        e-hentai base history recommand gallerys
// @name:ja        e-hentai base history recommand gallerys
// @name:zh-TW         e-hentai base history recommand gallerys
// @name:zh-CN        e-hentai base history recommand gallerys
// @namespace   e-hentai_preference_push
// @supportURL  https://github.com/zhuzemin
// @description record your favorite tags, use to search gallerys
// @description:zh-CN record your favorite tags, use to search gallerys
// @description:zh-TW  record your favorite tags, use to search gallerys
// @description:ja record your favorite tags, use to search gallerys
// @include     https://exhentai.org/
// @include     https://e-hentai.org/
// @include     https://exhentai.org/?*
// @include     https://e-hentai.org/?*
// @include     https://exhentai.org/tag/*
// @include     https://e-hentai.org/tag/*
// @include     https://exhentai.org/g/*
// @include     https://e-hentai.org/g/*
// @version     1.42
// @grant       GM_xmlhttpRequest
// @grant         GM_registerMenuCommand
// @grant         GM_setValue
// @grant         GM_getValue
// @run-at      document-start
// @author      zhuzemin
// @license     Mozilla Public License 2.0; http://www.mozilla.org/MPL/2.0/
// @license     CC Attribution-ShareAlike 4.0 International; http://creativecommons.org/licenses/by-sa/4.0/
// @connect-src e-hentai.org
// @connect-src exhentai.org
// ==/UserScript==
var config = {
    'debug': false
}
var debug = config.debug ? console.log.bind(console)  : function () {
};

//default matching tags number
var defaultMatchTagNum=8;
var defaultBlockTags='multi-work series;translated;original;chinese;english;mosaic censorship;anthology';
var hostname;
var ContentPane;
var ContentPaneChildNum;
var FilledChildNum;
var ObjectGalleryPage;
var ObjectGallery;
var VisitTags;
var FavTags;
var VisitLinks;
var BlackTags;
var DivCount;
var btn;
class Gallery{
    constructor(href,other=null) {
        this.method = 'GET';
        this.url = href;
        this.headers = {
            'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
            'Accept': 'application/atom+xml,application/xml,text/xml',
            'Referer': window.location.href,
        };
        this.charset = 'text/plain;charset=utf8';
        this.other=other;
    }
}
class GalleryPage{
    constructor(keyword) {
        this.method = 'GET';
        this.url = window.location.href.replace(/&page=\d*/,'').replace(/$|(\?|page=\d*)/,"?page="+keyword);
        this.headers = {
            'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
            'Accept': 'application/atom+xml,application/xml,text/xml',
            'Referer': window.location.href,
        };
        this.charset = 'text/plain;charset=utf8';
        this.other=null;
    }
}

// prepare UserPrefs
setUserPref(
    'BlockTags',
    defaultBlockTags,
    "Tags that won't be fctor for reacommand",
    `These Tags will not be factor for recommand. split with ";". Example: multi-work series;translated;original`,
    ','
);

// prepare UserPrefs
setUserPref(
    'matchTagsNum',
    defaultMatchTagNum,
    'How many tags has be matching',
    `Number more bigger then result will be more precise, but more slower.`,
    ','
);

function init() {
    debug("init");
    VisitTags;
    VisitLinks;
    try{
        VisitTags=JSON.parse(GM_getValue("VisitTags"));
        VisitLinks=GM_getValue("VisitLinks").split(",");
        BlackTags=GM_getValue("BlockTags");
    }catch(e){
        debug("Not VisitTags.");
    }
    if(BlackTags==undefined||BlackTags.length ==0){
        BlackTags=defaultBlockTags;

    }
    if(VisitTags==undefined||VisitTags.length ==0){
        VisitTags={};

    }
    if(VisitLinks==undefined||VisitLinks.length ==0){
        VisitLinks=[];

    }
    debug("BlackTags: "+BlackTags);
    if(window.location.href.match(/(https:\/\/e(-|x)hentai\.org\/g\/[\d\w]*\/[\d\w]*\/)/)!=null){
        if(!VisitLinks.includes(window.location.href)){
            VisitLinks.push(window.location.href);
            GM_setValue("VisitLinks",VisitLinks.toString());
            var taglist = document.querySelector('#taglist');
            var links=taglist.querySelectorAll("a");
            for(var link of links) {
                var tag = link.innerText;
                if(Object.keys(VisitTags).length>0){
                    var count=1;
                    for(var VisitTag of Object.keys(VisitTags)){
                        if(tag==VisitTag){
                            VisitTags[tag]+=1;
                            break;
                        }
                        else if(count==Object.keys(VisitTags).length){
                            VisitTags[tag]=1;
                        }
                        count++;
                    }
                }
                else{
                    VisitTags[tag]=1;
                }
            }
            GM_setValue("VisitTags",JSON.stringify(VisitTags));
        }
        debug("VisitTags: "+JSON.stringify(VisitTags));
    }
    else{
        CreateButton();
    }
}

function CreateButton(){
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = `
[data-tooltip]:before {
    /* needed - do not touch */
    content: attr(data-tooltip);
    position: absolute;
    opacity: 0;
    
    /* customizable */
    transition: all 0.15s ease;
    padding: 10px;
    color: #333;
    border-radius: 10px;
    box-shadow: 2px 2px 1px silver;    
}

[data-tooltip]:hover:before {
    /* needed - do not touch */
    opacity: 1;
    
    /* customizable */
    background: yellow;
    margin-top: -50px;
    margin-left: 20px;    
}
    `;
    document.getElementsByTagName('head')[0].appendChild(style);
    btn=document.createElement("button");
    btn.type="button";
    btn.onclick="";
    btn.setAttribute('data-tooltip','Also you can use the filter')
    btn.innerHTML=`You may like`;
    btn.addEventListener('click',ShowRecommand);
    var p=document.querySelector("p.nopm");
    p.insertBefore(btn,null);
}
function SetExtended(){
    hostname=getLocation(window.location.href).hostname;
    var select=document.querySelector("select");
    var options=select.querySelectorAll("option");
    for(var option of options){
        var value=option.getAttribute("value");
        var selected=option.getAttribute("selected");
        if(value=="e"){
            if(selected=="selected"){
                break;
            }
            else{
                alert("Page will set to Extended view, After refresh click button again.")
                window.location.href="https://"+hostname+"/?inline_set=dm_"+value;
            }
        }
    }

}
function ShowRecommand(e) {
    e.target.innerHTML="Searching...";
    matchTagNum=GM_getValue('matchTagsNum')||defaultMatchTagNum;
    debug('matchTagNum: '+matchTagNum)
    debug("ShowRecommand");
    //window.location.href+="#E-Hentai_Display_Tag_with_thumb";
    FavTags=[];
    GetFavTag();
    debug('FavTag: '+FavTags);
    if(FavTags.length==0){
        alert('Current record tags number: 0, so will just recommand ramdom gallery.')
    }
    CreateStyle();
    SetExtended();
    var div=document.querySelector("div.ido");
    div.style="max-width:1370px";
    var table=document.querySelector("table.itg.glte");
    var tbody=table.querySelector("tbody");
    tbody.className="itg gld";
    tbody.style="width:1323px";
    ContentPane=tbody;
    ContentPaneChildNum=ContentPane.childNodes.length;
    FilledChildNum=0;
    //clear ContentPane
    while (ContentPane.firstChild) {
        ContentPane.removeChild(ContentPane.firstChild);
    }
    FillPane();
}

function FillPane(TotalPage){
    var table=document.querySelector("table.ptt");
    var tds=table.querySelectorAll("td");
    var TotalPage=parseInt(tds[tds.length-2].firstChild.innerText);
    debug('TotalPage: '+TotalPage)
    var RandomPage = Math.floor(Math.random() * (TotalPage+1 - 0));
    ObjectGalleryPage=new GalleryPage(RandomPage);
    debug("RandomPage: "+ObjectGalleryPage.url);
    request(ObjectGalleryPage,SearchGallery);
}

function GetGalleryTag(responseDetails,divs) {
    debug("GetGalleryTag");
    try{
        var tr=divs[DivCount];
        var count=0;
        var Break;
        //shuffle array
        var shuffle=function (sourceArray) {
            for (var i = 0; i < sourceArray.length - 1; i++) {
                var j = i + Math.floor(Math.random() * (sourceArray.length - i));

                var temp = sourceArray[j];
                sourceArray[j] = sourceArray[i];
                sourceArray[i] = temp;
            }
            return sourceArray;
        }
        var div = tr.querySelector("div.gl4e.glname");
        table = div.childNodes[1].querySelector("table");
        var href=tr.querySelector("a").href;
        debug("href: "+href);
        if (table != null) {
            FavTags=shuffle(FavTags);
            //debug("FavTags: "+FavTags);
            var links = table.querySelectorAll("div");
            for (var link of links) {
                if (FavTags == 0) {
                    break;
                }
                var tag = link.innerText;
                for (var FavTag of FavTags) {
                    if (count >= parseInt(matchTagNum) || count == FavTags.length) {
                        if (!VisitLinks.includes(href)) {
                            tr.className = "gl1t";
                            tr.style = "min-width:250px !important;width:263px !important;";
                            var detail = tr.querySelector("div.gl3e");
                            detail.className = "gl3t";
                            var star = detail.querySelector("div.ir");
                            star.style.margin = "auto";
                            var thumb = tr.querySelector("td.gl1e");
                            thumb.firstChild.style = "height:340px;";
                            thumb.insertBefore(detail, null);
                            ContentPane.insertBefore(tr, null);
                            debug("Insert div");
                            count = 0;
                            FilledChildNum++;
                            Break = true;
                            break;
                        }
                    }
                    else if (tag == FavTag.trim()) {
                        //debug("FavTag: " + FavTag);
                        link.className += " glowbox";
                        count++;
                    }
                }
                if (Break) {
                    break;
                }
            }
        }
    }
    catch(e){
        debug("Error: "+e);
    }
    if(FilledChildNum<=ContentPaneChildNum) {
        if (DivCount < divs.length-1) {
            //for (var i = 0; i < divs.length; ++i) {
            //var div=divs[i];
            if (FilledChildNum == ContentPaneChildNum) {
                btn.innerHTML="You may like";
                debug("finish");
                return;
            }
            else if (FavTags.length == 0) {
                debug("Insert divs");
                for(div of divs){
                    ContentPane.insertBefore(div, null);

                }
                btn.innerHTML="You may like";
                debug("finish");
                return;

            }
            else {
                debug("DivCount: " + DivCount);
                DivCount++;
                GetGalleryTag(null,divs);
            }
        }
        else {
            FillPane();
        }
    }
}

function GetFavTag(){
    //convert object to array
    var sortable = [];
    for (var VisitTag in VisitTags) {
        if(VisitTag.match(/^\d*$/)==null){
        sortable.push([VisitTag, VisitTags[VisitTag]]);
        }
    }

    //sort by reverse
    sortable.sort(function(a, b) {
        return a[1] - b[1];
    }).reverse();

    //shuffle array
    var shuffle=function (sourceArray) {
        for (var i = 0; i < sourceArray.length - 1; i++) {
            var j = i + Math.floor(Math.random() * (sourceArray.length - i));

            var temp = sourceArray[j];
            sourceArray[j] = sourceArray[i];
            sourceArray[i] = temp;
        }
        return sourceArray;
    }

    //convert array to object
    var ArrayToObj=function (sortable){
        VisitTags={};
        sortable.forEach(function(item){
            VisitTags[item[0]]=item[1]
        })
        return VisitTags;
    }

    VisitTags=ArrayToObj(sortable);
    var count=0;
    for(var VisitTag of Object.keys(VisitTags)){
        if(VisitTags[VisitTag]==1){
            return;
        }
        else if(!BlackTags.includes(VisitTag.trim())){
            FavTags.push(VisitTag);
            if(count==Math.floor(Object.keys(VisitTags).length/3)) {
                //VisitTags too many, need shuffling
                if(VisitTags[VisitTag]>=Math.floor(Object.keys(VisitTags).length/3)){
                    VisitTags=ArrayToObj(shuffle(sortable));
                    GM_setValue("VisitTags",JSON.stringify(VisitTags));
                }
                return;
            }
        }
        count++;
    }
}



function getLocation(href) {
    var l = document.createElement("a");
    l.href = href;
    return l;
}

function SearchGallery(responseDetails) {
    debug("SearchGallery");
    var responseText=responseDetails.responseText;
    //var href=responseText.match(/(https:\/\/e(-|x)hentai\.org\/g\/[\d\w]*\/[\d\w]*\/)/)[1];
    //debug("href: "+href);
    var dom = new DOMParser().parseFromString(responseText, "text/html");
    var table=dom.querySelector("table.itg.glte");
    var tbody=table.querySelector("tbody");
    var CurrentContentPane=tbody;
    var divs = CurrentContentPane.childNodes;
    debug('CurrentContentPane.childNodes.length: '+CurrentContentPane.childNodes.length)
    for(var div of divs){
        debug("tr.innerHTML: "+div.innerHTML)
        var ir=div.querySelector("div.ir");
        if(ir!=null){
            var backgroundPosition=ir.style.backgroundPosition;
            if(!["0px -21px","0px -1px"].includes(backgroundPosition)){
                CurrentContentPane.removeChild(div);
            }

        }
    }
    divs = CurrentContentPane.childNodes;
    debug("divs.length: "+divs.length);
    DivCount=0;
    GetGalleryTag(null,divs);
}

function request(object,func) {
    var retries = 10;
    GM_xmlhttpRequest({
        method: object.method,
        url: object.url,
        headers: object.headers,
        overrideMimeType: object.charset,
        //synchronous: true
        onload: function (responseDetails) {
            if (responseDetails.status != 200) {
                // retry
                if (retries--) {          // *** Recurse if we still have retries
                    setTimeout(request,2000);
                    return;
                }
            }
            //debug(responseDetails);
            //Dowork
            func(responseDetails,object.other);
        }
    })
}
function CreateStyle(){
    debug("Start: CreateStyle");
    var style=document.createElement("style");
    style.setAttribute("type","text/css");
    style.innerHTML=`
.glowbox {
     background: #4c4c4c; 
    //width: 400px;
    margin: 40px 0 0 40px;
    padding: 10px;
    -moz-box-shadow: 0 0 5px 5px #FFFF00;
    -webkit-box-shadow: 0 0 5px 5px #FFFF00;
    box-shadow: 0 0 5px 5px #FFFF00;
}
`;
    debug("Processing: CreateStyle");
    var head=document.querySelector("head");
    head.insertBefore(style,null);
    debug("End: CreateStyle");
}

// setting User Preferences
function setUserPref(varName, defaultVal, menuText, promtText, sep){
    GM_registerMenuCommand(menuText, function() {
        var val = prompt(promtText, GM_getValue(varName, defaultVal));
        if (val === null)  { return; }  // end execution if clicked CANCEL
        // prepare string of variables separated by the separator
        if (sep && val){
            var pat1 = new RegExp('\\s*' + sep + '+\\s*', 'g'); // trim space/s around separator & trim repeated separator
            var pat2 = new RegExp('(?:^' + sep + '+|' + sep + '+$)', 'g'); // trim starting & trailing separator
            //val = val.replace(pat1, sep).replace(pat2, '');
        }
        //val = val.replace(/\s{2,}/g, ' ').trim();    // remove multiple spaces and trim
        GM_setValue(varName, val);
        // Apply changes (immediately if there are no existing highlights, or upon reload to clear the old ones)
        //if(!document.body.querySelector(".THmo")) THmo_doHighlight(document.body);
        //else location.reload();
    });
}

window.addEventListener('load', init);