Sleazy Fork is available in English.

ImageBoard Viewer/Downloader

A simple quick and dirty image viewer for gelbooru.com and rule34.xxx supports all formats from gif to webm.

Versión del día 15/05/2017. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         ImageBoard Viewer/Downloader
// @version      1.3
// @description  A simple quick and dirty image viewer for gelbooru.com and rule34.xxx supports all formats from gif to webm.
// @author       PineappleLover69
// @include      https://gelbooru.com*
// @include      https://rule34.xxx*
// @include      http://rule34.xxx*

// @namespace https://greasyfork.org/users/120106
// ==/UserScript==


(function () {

    //Settings
    var StartImageHeight = 650;
    var AutoShowImageView = false;
    var DisableImageLinks = true;


    var siteInfo = {
        hostName: window.location.hostname,
        siteIndex_: "",
        get siteIndex() {
            if (!this.siteIndex_) {
                if (this.hostName == "gelbooru.com" || this.hostName == "rule34.xxx") {
                    this.siteIndex_ = "gel+r34";
                } else if (this.hostName == "chan.sankakucomplex.com") {
                    this.siteIndex_ = "sankaku";
                }
            }
            return this.siteIndex_;
        }
    };

    var siteObj, siteSwitchObj;
    SetUpSiteSwitchObjs();

    //this group of vars is to be set by SetVars() and depends on the current website
    var buttonInsertionPoint, posts, imgList, tagEntry, tagTypeLookup, postsJson, tagArray, postSources;


    var tagDictionary = {};

    siteObj.SetVars();
    BatchPostApiCall();

    var imgIndex = 0;
    var imgOpened = false;

    if (DisableImageLinks) {
        for (let i = 0; i < imgList.length;) {
            try {
                imgList[i].setAttribute("openRef", imgList[i].childNodes[0].getAttribute("href"));
                imgList[i].childNodes[0].removeAttribute("href");
                imgList[i].childNodes[0].addEventListener("click", ImgClick);
                i++;
            } catch (ex) {
                imgList[i].remove();
            }
        }
    }


    function ImgClick(e) {
        if (!imgOpened)
            ImgView();

        var parentchildObj = {};
        siteObj.ImgClickGetChildAndParent(parentchildObj, e);

        // The equivalent of parent.children.indexOf(child)
        imgIndex = Array.prototype.indexOf.call(parentchildObj.parent.children, parentchildObj.child);
        SetImg();
        imgViewBtn.scrollIntoView();
    }

    var imgViewBtn = document.createElement("button");
    imgViewBtn.innerHTML = "Image View";
    imgViewBtn.onclick = ImgView;
    var dlAllBtn = document.createElement("button");
    dlAllBtn.innerHTML = "Download All";
    dlAllBtn.onclick = dlAll;

    //imgViewBtn.setAttribute("class", "active");
    buttonInsertionPoint.insertBefore(dlAllBtn, buttonInsertionPoint.childNodes[0]);
    buttonInsertionPoint.insertBefore(imgViewBtn, buttonInsertionPoint.childNodes[0]);

    var imgViewImg, videoImg, preloadImg1, preloadImg2, preloadImg3, preloadImg4;

    function ImgView() {
        if (imgOpened)
            return;

        var holdDiv = document.createElement("div");
        holdDiv.setAttribute("align", "center");
        buttonInsertionPoint.insertBefore(holdDiv, buttonInsertionPoint.childNodes[2]);

        imgViewImg = document.createElement("img");
        imgViewImg.setAttribute("height", StartImageHeight);
        holdDiv.appendChild(imgViewImg);
        videoImg = document.createElement("video");
        videoImg.setAttribute("height", StartImageHeight);
        videoImg.setAttribute("autoplay", true);
        videoImg.setAttribute("controls", true);
        videoImg.setAttribute("loop", true);
        videoImg.setAttribute("hidden", true);
        holdDiv.appendChild(videoImg);

        preloadImg1 = document.createElement("img");
        preloadImg2 = document.createElement("img");
        preloadImg1.setAttribute("hidden", true);
        preloadImg2.setAttribute("hidden", true);
        holdDiv.appendChild(preloadImg1);
        holdDiv.appendChild(preloadImg2);

        preloadImg3 = document.createElement("img");
        preloadImg4 = document.createElement("img");
        preloadImg3.setAttribute("hidden", true);
        preloadImg4.setAttribute("hidden", true);
        holdDiv.appendChild(preloadImg3);
        holdDiv.appendChild(preloadImg4);

        imgViewImg.addEventListener('load', DoPreload);

        imgViewImg.addEventListener('mousedown', ImageMouseDown);
        imgViewImg.addEventListener('mouseup', ImageMouseUp);
        imgViewImg.addEventListener('mousemove', ImageMouseMove);
        imgViewImg.addEventListener('mouseleave', ImageMouseLeave);

        videoImg.addEventListener('mousedown', ImageMouseDown);
        videoImg.addEventListener('mouseup', ImageMouseUp);
        videoImg.addEventListener('mousemove', ImageMouseMove);
        videoImg.addEventListener('mouseleave', ImageMouseLeave);

        prevBtn = document.createElement("button");
        prevBtn.innerHTML = "Prev";
        prevBtn.onclick = PrevImg;
        nextBtn = document.createElement("button");
        nextBtn.innerHTML = "Next";
        nextBtn.onclick = NextImg;
        dlBtn = document.createElement("button");
        dlBtn.innerHTML = "Download";
        dlBtn.onclick = DownloadCurrent;
        opBtn = document.createElement("button");
        opBtn.innerHTML = "Open Src";
        opBtn.onclick = OpenSrc;
        spacer = document.createElement("img");
        spacer.setAttribute("width", 30);
        spacer2 = document.createElement("img");
        spacer2.setAttribute("width", 30);
        spacer3 = document.createElement("img");
        spacer3.setAttribute("width", 30);
        holdDiv.appendChild(document.createElement("br"));
        holdDiv.appendChild(prevBtn);
        holdDiv.appendChild(spacer);
        holdDiv.appendChild(dlBtn);
        holdDiv.appendChild(spacer2);
        holdDiv.appendChild(opBtn);
        holdDiv.appendChild(spacer3);
        holdDiv.appendChild(nextBtn);

        imgOpened = true;
        let header = document.getElementById("header");
        if (header)
            header.remove();
        header = document.getElementsByClassName("header")[0];
        if (header)
            header.remove();

        document.addEventListener("keydown", keyInput);
        SetImg();
    }

    if (AutoShowImageView)
        ImgView();


    function BatchPostApiCall() {
        var apiCallObj = getJsonFromUrl();
        siteObj.posts.PostApiSelector(apiCallObj);

        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                postsJson = xmlToJson(this.responseXML);

                siteObj.posts.PostSourcesSelector(apiCallObj);

                CreateTagBase();
            }
        };
        xhttp.open("GET", apiCallObj.request, true);
        xhttp.send();
    }

    function CreateTagBase() {
        var uniqueTagList = [];
        siteObj.tags.GetSplitTagsPerPost(uniqueTagList);
        uniqueTagList = mergeDedupe(uniqueTagList);

        siteObj.tags.TagApiSelector(uniqueTagList);
    }

    function TagRequest(tagRequest) {
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4 && this.status == 200) {
                var tagPageJson = xmlToJson(this.responseXML);

                siteObj.tags.TagDictionarySetup(tagPageJson);

                if (imgOpened)
                    SetNewTags();
            }
        };
        xhttp.open("GET", tagRequest, true);
        xhttp.send();
    }


    function OpenSrc() {
        window.open(imgList[imgIndex].getAttribute("openRef"));
    }


    function SetCurrentSrc() {
        currentSrc = GetSrcForImg(imgIndex);
    }

    function GetSrcForImg(getIndex) {
        if (postSources[getIndex]) {
            return postSources[getIndex];
        } else {
            return siteObj.posts.SinglePostSrc(getIndex);
        }
    }

    function SetNewTags() {
        if (!tagArray)
            return;

        siteObj.tags.RemoveTags();
        siteObj.tags.AddTags();
        siteObj.tags.RemoveEmptyTags();
    }


    function SetImg() {
        SetCurrentSrc();
        var dI = currentSrc.lastIndexOf(".");
        var fileExt = currentSrc.substring(dI + 1);

        if (fileExt.toLowerCase() == "webm") {
            videoImg.setAttribute("src", currentSrc);
            videoImg.removeAttribute("hidden");
            videoImg.play();
            imgViewImg.setAttribute("hidden", true);
            setTimeout(DoPreload, 200);
        } else {
            imgViewImg.setAttribute("src", "");
            imgViewImg.removeAttribute("hidden");
            videoImg.setAttribute("hidden", true);
            videoImg.pause();

            setTimeout(SetImageAfterTimeout, 1);
        }

        SetNewTags();
    }

    function SetImageAfterTimeout() {
        imgViewImg.setAttribute("src", currentSrc);
    }


    function DoPreload() {
        var preIndex = imgIndex + 1;
        if (preIndex >= imgList.length)
            preIndex = 0;
        preloadImg1.src = GetSrcForImg(preIndex);

        preIndex++;
        if (preIndex >= imgList.length)
            preIndex = 0;
        preloadImg2.src = GetSrcForImg(preIndex);

        preIndex = imgIndex - 1;
        if (preIndex < 0)
            preIndex = imgList.length - 1;
        preloadImg3.src = GetSrcForImg(preIndex);

        //preIndex--;
        //if(preIndex < 0)
        //    preIndex = imgList.length - 1;
        //preloadImg4.src = GetSrcForImg(preIndex);
    }

    function DownloadCurrent() {
        SetCurrentSrc();
        var dI = currentSrc.lastIndexOf(".");
        var uI = currentSrc.lastIndexOf("/") + 5;
        var fileExt = currentSrc.substring(dI);
        var imgName = "tags-" + tagEntry.value + " ";
        if (tagEntry.value === "") {
            imgName = currentSrc.substring(uI, dI);
        } else {
            imgName += currentSrc.substring(uI, dI);
        }
        imgName += " id-" + imgList[imgIndex].childNodes[0].getAttribute("id");
        imgName += fileExt;
        //console.log(imgName);
        var dl = document.createElement("a");
        dl.setAttribute("href", currentSrc);
        dl.setAttribute("download", imgName);
        dl.click();
        dl.remove();

        document.body.focus();
    }

    function dlAll() {
        var prevIndex = imgIndex;
        for (imgIndex = 0; imgIndex < imgList.length;) {
            try {
                DownloadCurrent();
                imgIndex++;
            } catch (ex) {
                console.log(ex);
                imgIndex++;
                //imgList[imgIndex].remove();
            }
        }

        imgIndex = prevIndex;
    }


    function keyInput(e) {
        if (document.activeElement != tagEntry) {
            if (e.keyCode === 32) {
                e.preventDefault();
                return false;
            }
            if (e.keyCode === 37) {
                e.preventDefault();
                PrevImg();
                return false;
            }
            if (e.keyCode === 39) {
                e.preventDefault();
                NextImg();
                return false;
            }
            if (e.keyCode === 40) {
                e.preventDefault();
                DownloadCurrent();
                return false;
            }
        }
    }

    function SetUpSiteSwitchObjs() {
        ///this is the object that controls what should be done for each individual website supported
        siteSwitchObj = {
            //the default here serves as a master obj so that code bits can be reused if certain websites
            //use similar layouts in areas. it is based on the gelbooru design.
            default: {
                SetVars: function () {
                    buttonInsertionPoint = document.getElementsByClassName("content")[0];
                    posts = document.getElementById("post-list");
                    imgList = document.getElementsByClassName("thumb");
                    tagEntry = document.getElementById("tags");
                    postSources = Array(imgList.length);

                    tagTypeLookup = {
                        0: "tag-type-general",
                        1: "tag-type-artist",
                        2: "tag-type-copyright",
                        3: "tag-type-copyright",
                        4: "tag-type-character"
                    };
                },
                ImgClickGetChildAndParent: function (obj, e) {
                    obj.child = e.target.parentNode.parentNode;
                    obj.parent = obj.child.parentNode;
                },
                posts: {
                    PostApiSelector: function (apiObj) {
                        var pid = 0;
                        if (apiObj.pid)
                            pid = apiObj.pid / 42;
                        apiObj.postLimit = imgList.length;
                        var tags = encodeURIComponent(tagEntry.value);
                        apiObj.request = "/index.php?page=dapi&s=post&q=index&limit=" + apiObj.postLimit + "&tags=" + tags + "&pid=" + pid;
                    },
                    PostSourcesSelector: function (apiObj) {
                        for (var i = 0; i < apiObj.postLimit; i++) {
                            if (!postsJson.posts.post[i])
                                break;
                            postSources[i] = postsJson.posts.post[i]["@attributes"].file_url;
                        }
                    },
                    SinglePostSrc: function (getIndex) {
                        var tmpSrc = imgList[getIndex].id;
                        tmpSrc = tmpSrc.replace("s", "");

                        var thing = JsonHttpRequest("/index.php?page=dapi&s=post&q=index&id=" + tmpSrc.toString());

                        tmpSrc = thing.posts.post["@attributes"].file_url;
                        postSources[getIndex] = tmpSrc;
                        return tmpSrc;
                    }
                },
                tags: {
                    GetSplitTagsPerPost: function (uniqueTagList) {
                        for (var i = 0; i < imgList.length; i++) {
                            var currentPost = postsJson.posts.post[i];
                            var tags = currentPost["@attributes"].tags.toLowerCase();
                            var splitTags = tags.split(' ');

                            uniqueTagList.push(splitTags);
                        }
                    },
                    TagApiSelector: function (uniqueTagList) {
                        var uniqueTagString = "";
                        var uniqueStringArray = [];
                        var usCount = 0;
                        for (i = 0; i < uniqueTagList.length; i++) {
                            if (usCount === 0) {
                                uniqueTagString += uniqueTagList[i];
                            } else {
                                uniqueTagString += " " + uniqueTagList[i];
                            }
                            usCount++;
                            if (usCount > 99 || i == uniqueTagList.length - 1) {
                                usCount = 0;
                                uniqueStringArray.push(uniqueTagString);
                                uniqueTagString = "";
                            }
                        }

                        for (i = 0; i < uniqueStringArray.length; i++) {
                            var request = "/index.php?page=dapi&s=tag&q=index&names=" + encodeURIComponent(uniqueStringArray[i]);
                            TagRequest(request);
                        }
                    },
                    TagDictionarySetup: function (tagPageJson) {
                        let tmpArray = tagPageJson.tags.tag;
                        if (!tagArray)
                            tagArray = tmpArray;
                        else {
                            tagArray = tagArray.concat(tmpArray);

                        }
                        for (i = 0; i < tmpArray.length; i++) {
                            tagDictionary[tmpArray[i]["@attributes"].name.toLowerCase()] = tmpArray[i]["@attributes"];
                        }
                    },
                    AddTag: function (tagName, tagParent, tagToClone, stringToReplace) {
                        try {
                            var clonedTag = tagToClone.cloneNode(true);
                            tagParent.appendChild(clonedTag);
                            clonedTag.innerHTML = clonedTag.innerHTML.replaceAll(stringToReplace, encodeURIComponent(tagName));
                            clonedTag.childNodes[7].innerHTML = tagName.replace(/_/g, " ");

                            var jsonTag = tagDictionary[tagName];
                            var tagType = jsonTag.type;
                            clonedTag.setAttribute("class", tagTypeLookup[tagType]);
                            clonedTag.childNodes[9].innerHTML = jsonTag.count;
                        } catch (ex) {
                            console.log("Failed tag: " + tagName);
                            console.log(ex);
                            console.log(tagDictionary);
                        }
                    },
                    AddTags: function () {
                        var currentPost = postsJson.posts.post[imgIndex];
                        var tags = currentPost["@attributes"].tags;
                        var splitTags = tags.split(' ');

                        var tagBar = document.getElementById("tag-sidebar");
                        var firstTag = tagBar.childNodes[0];
                        var stringToReplace = firstTag.innerHTML.substring(firstTag.innerHTML.indexOf("search=") + 7, firstTag.innerHTML.indexOf('" title="Wiki"'));

                        for (var i = 1; i < splitTags.length; i++) {
                            this.AddTag(splitTags[i], tagBar, firstTag, stringToReplace)
                        }

                        firstTag.remove();
                    },
                    RemoveTags: function () {
                        var tagBar = document.getElementById("tag-sidebar");
                        for (var i = tagBar.childNodes.length - 1; i >= 1; i--) {
                            tagBar.childNodes[i].remove();
                        }
                    },
                    RemoveEmptyTags: function () {
                        var tagBar = document.getElementById("tag-sidebar");
                        for (var i = tagBar.childNodes.length - 1; i >= 0; i--) {
                            let tAg = tagBar.childNodes[i];
                            if (tAg.childNodes[7].innerHTML === "") {
                                tAg.remove();
                            }
                        }
                    }
                }
            },

            //this setup for gelbooru and r34 uses all the default calls
            "gel+r34": {
                SetVars: function () {
                    siteSwitchObj.default.SetVars();
                },
                ImgClickGetChildAndParent: function (obj, e) {
                    siteSwitchObj.default.ImgClickGetChildAndParent(obj, e);
                },
                posts: {
                    PostApiSelector: function (apiObj) {
                        siteSwitchObj.default.posts.PostApiSelector(apiObj);
                    },
                    PostSourcesSelector: function (apiObj) {
                        siteSwitchObj.default.posts.PostSourcesSelector(apiObj);
                    },
                    SinglePostSrc: function (getIndex) {
                        return siteSwitchObj.default.posts.SinglePostSrc(getIndex);
                    }
                },
                tags: {
                    GetSplitTagsPerPost: function (uniqueTagList) {
                        siteSwitchObj.default.tags.GetSplitTagsPerPost(uniqueTagList);
                    },
                    TagApiSelector: function (uniqueTagList) {
                        siteSwitchObj.default.tags.TagApiSelector(uniqueTagList);
                    },
                    TagDictionarySetup: function (tagPageJson) {
                        siteSwitchObj.default.tags.TagDictionarySetup(tagPageJson);
                    },
                    RemoveTags: function () {
                        siteSwitchObj.default.tags.RemoveTags();
                    },
                    AddTag: function (tagName, tagParent, tagToClone, stringToReplace) {
                        siteSwitchObj.default.tags.AddTag(tagName, tagParent, tagToClone, stringToReplace);
                    },
                    AddTags: function () {
                        siteSwitchObj.default.tags.AddTags();
                    },
                    RemoveEmptyTags: function () {
                        siteSwitchObj.default.tags.RemoveEmptyTags();
                    }
                }
            },
            "sankaku": {}
        };

        siteObj = siteSwitchObj[siteInfo.siteIndex];
    }


    //----------everything below here is either utility or is pretty set in stone-------------------

    Element.prototype.remove = function () {
        if (this)
            this.parentElement.removeChild(this);
    };

    //String.prototype.replaceAll = function (search, replacement) {
    //    var target = this;
    //    return target.replace(new RegExp(search, 'g'), replacement);
    //};

    String.prototype.replaceAll = function (search, replacement) {
        var target = this;
        return target.split(search).join(replacement);
    };

    function mergeDedupe(arr) {
        return [...new Set([].concat(...arr))];
    }

    function PrevImg() {
        imgIndex--;
        if (imgIndex < 0)
            imgIndex = imgList.length - 1;
        SetImg();
    }

    function NextImg() {
        imgIndex++;
        if (imgIndex >= imgList.length)
            imgIndex = 0;
        SetImg();
    }

    var imgMouseDown = false;
    var imgDownPosX, imgDownPosY, imgDownHeight = 0;

    function ImageMouseDown(e) {
        e.preventDefault();
        imgMouseDown = true;
        imgDownPosX = e.screenX;
        imgDownPosY = e.screenY;
        imgDownHeight = Number(imgViewImg.getAttribute("height"));
        return false;
    }

    function ImageMouseUp(e) {
        e.preventDefault();
        imgMouseDown = false;
        return false;
    }

    function ImageMouseMove(e) {
        if (imgMouseDown) {
            e.preventDefault();
            var moveDist = e.screenY - Number(imgDownPosY);
            imgViewImg.setAttribute("height", imgDownHeight + moveDist * 2);
            videoImg.setAttribute("height", imgDownHeight + moveDist * 2);
            return false;
        }
    }

    function ImageMouseLeave(e) {
        e.preventDefault();
        imgMouseDown = false;
        return false;
    }

    function getJsonFromUrl() {
        var query = location.search.substr(1);
        var result = {};
        query.split("&").forEach(function (part) {
            var item = part.split("=");
            result[item[0]] = decodeURIComponent(item[1]);
        });
        return result;
    }

    function JsonHttpRequest(urlRequest) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", urlRequest, false);
        xhr.send();
        return xmlToJson(xhr.responseXML);
    }

    // Changes XML to JSON
    function xmlToJson(xml) {

        // Create the return object
        var obj = {};

        if (xml.nodeType == 1) { // element
            // do attributes
            if (xml.attributes.length > 0) {
                obj["@attributes"] = {};
                for (var j = 0; j < xml.attributes.length; j++) {
                    var attribute = xml.attributes.item(j);
                    obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
                }
            }
        } else if (xml.nodeType == 3) { // text
            obj = xml.nodeValue;
        }

        // do children
        if (xml.hasChildNodes()) {
            for (var i = 0; i < xml.childNodes.length; i++) {
                var item = xml.childNodes.item(i);
                var nodeName = item.nodeName;
                if (typeof(obj[nodeName]) == "undefined") {
                    obj[nodeName] = xmlToJson(item);
                } else {
                    if (typeof(obj[nodeName].push) == "undefined") {
                        var old = obj[nodeName];
                        obj[nodeName] = [];
                        obj[nodeName].push(old);
                    }
                    obj[nodeName].push(xmlToJson(item));
                }
            }
        }
        return obj;
    }


})
();