- // ==UserScript==
- // @name Gelbooru Paged Gallery
- // @namespace zixaphir
- // @description A simplified gallery for viewing gelbooru images in succession.
- // @match *://*.gelbooru.com/index.php?*
- // @version 1
- // @grant none
- // ==/UserScript==
-
- /*
- #
- * $ object largely based on 4chan X's $, which is largely based on jQuery.
- * non-chainable.
- #
- * Copyright (c) 2009-2011 James Campos <james.r.campos@gmail.com>
- * Copyright (c) 2012-2014 Nicolas Stepien <stepien.nicolas@gmail.com>
- #
- * Permission is hereby granted, free of charge, to any person
- * obtaining a copy of this software and associated documentation
- * files (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use,
- * copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following
- * conditions:
- #
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- #
- */
-
- (function() {
- "use strict";
- var $, FNLIMIT, LIMIT, PRELOAD, SimpleDict, THRESHOLD, cb, clickThumb, d, err, fer, g, loadGallery, mkImage, mkURL, preload, query, queryImages, setImage, setup, setupImages, updateImages,
- slice = [].slice;
-
- d = document;
-
- FNLIMIT = 255;
-
- PRELOAD = 1;
-
- THRESHOLD = 3;
-
- LIMIT = 100;
-
- g = {
- galleryCSS: "#a-gallery {\n position: fixed;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n z-index: 30;\n display: flex;\n flex-direction: row;\n background: rgba(0,0,0,0.7);\n}\n.gal-thumbnails {\n flex-basis: 170px;\n overflow-y: auto;\n overflow-x: hidden;\n display: flex;\n flex-direction: column;\n align-items: stretch;\n text-align: center;\n background: rgba(0,0,0,.5);\n border-left: 1px solid #222;\n order: 3;\n}\n.gal-hide-thumbnails .gal-thumbnails {\n display: none;\n}\n.gal-thumb img {\n max-width: 150px;\n max-height: 150px;\n height: auto;\n width: auto;\n}\n.gal-thumb {\n flex-basis: auto;\n padding: 3px;\n line-height: 0;\n transition: background .2s linear;\n}\n.gal-highlight {\n background: rgba(0, 190, 255, .8);\n}\n.gal-prev {\n order: 0;\n border-right: 1px solid #222;\n}\n.gal-next {\n order: 2;\n border-left: 1px solid #222;\n}\n.gal-prev,\n.gal-next {\n flex-basis: 20px;\n position: relative;\n cursor: pointer;\n opacity: 0.7;\n background-color: rgba(0, 0, 0, 0.3);\n}\n.gal-prev:hover,\n.gal-next:hover {\n opacity: 1;\n}\n.gal-prev::after,\n.gal-next::after {\n position: absolute;\n top: 50%;\n transform: translateY(-50%)\n line-height: 22px;\n display: inline-block;\n border-top: 11px solid transparent;\n border-bottom: 11px solid transparent;\n content: \"\";\n}\n.gal-prev::after {\n border-right: 12px solid #fff;\n right: 5px;\n}\n.gal-next::after {\n border-left: 12px solid #fff;\n right: 3px;\n}\n.gal-image {\n position: relative;\n order: 1;\n display: flex;\n align-items: flex-start;\n justify-content: space-around;\n overflow: hidden;\n flex-grow: 1;\n}\n:root:not(.gal-fit-height):not(.gal-pdf) .gal-image {\n overflow-y: auto !important;\n}\n:root:not(.gal-fit-width):not(.gal-pdf) .gal-image {\n overflow-x: auto !important;\n}\n.gal-image a {\n line-height: 0;\n}\n.gal-image > div {\n margin: auto;\n max-width: 100%;\n}\n:root.gal-pdf .gal-image a {\n width: 100%;\n height: 100%;\n}\n.gal-image video,\n.gal-image img {\n max-width: 100%;\n}\n.gal-fit-height .gal-image video,\n.gal-fit-height .gal-image img {\n max-height: 95vh;\n}\n.gal-image iframe {\n width: 100%;\n height: 100%;\n}\n.gal-buttons .menu-button {\n bottom: 2px;\n color: #ffffff;\n text-shadow: 0px 0px 1px #000000;\n}\n.gal-close {\n font-size: 2em;\n color: #ffffff;\n text-shadow: 0px 0px 1px #000000;\n top: 5px;\n cursor: pointer;\n}\n.gal-close,\n.gal-info {\n position: absolute;\n right: 5px;\n}\n.gal-info {\n bottom: 5px;\n background: rgba(0,0,0,0.6) !important;\n}\n.gal-info,\n.gal-ex-info {\n border-radius: 3px;\n padding: 1px 5px 2px 5px;\n color: #ffffff !important;\n}\n.gal-ex-info {\n display: none;\n position: absolute;\n bottom: 0;\n right: 0;\n font-size: 12px;\n font-family: calibri;\n min-width: 200px;\n padding-left: 15px;\n text-indent: -15px;\n background: rgba(0,0,0,0.8) !important;\n}\n.gal-ex-info p {\n margin: 3px;\n}\n.gal-info:hover .gal-ex-info {\n display: block;\n}\n:root:not(.gal-fit-width):not(.gal-pdf) .gal-name {\n bottom: 23px !important;\n}\n:root:not(.gal-fit-width):not(.gal-pdf) .gal-count {\n bottom: 44px !important;\n}\n:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-buttons,\n:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-name,\n:root.gal-fit-height:not(.gal-pdf):not(.gal-hide-thumbnails) .gal-count {\n right: 178px !important;\n}\n:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-buttons,\n:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-name,\n:root.gal-hide-thumbnails:.gal-fit-height:not(.gal-pdf) .gal-count {\n right: 28px !important;\n}\n.spinner {\n width: 30px;\n height: 30px;\n background-color: #aaa;\n -webkit-animation: rotateplane 1.2s infinite ease-in-out;\n animation: rotateplane 1.2s infinite ease-in-out;\n}\n@-webkit-keyframes rotateplane {\n 0% { -webkit-transform: perspective(120px) }\n 50% { -webkit-transform: perspective(120px) rotateY(180deg) }\n 100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) }\n}\n@keyframes rotateplane {\n 0% {\n transform: perspective(120px) rotateX(0deg) rotateY(0deg);\n -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)\n } 50% {\n transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);\n -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)\n } 100% {\n transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);\n -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);\n }\n}",
- nodes: {}
- };
-
- (function() {
- var z;
- z = 0;
- return Object.defineProperty(g, "currentImageIndex", {
- set: function(x) {
- return z = Math.min(+g.images.length, Math.max(x, 0));
- },
- get: function() {
- return z;
- }
- });
- })();
-
- $ = function(query, root) {
- if (!root) {
- root = d.body;
- }
- return root.querySelector(query);
- };
-
- $.$ = function(query, root) {
- if (!root) {
- root = d.body;
- }
- return slice.call(root.querySelectorAll(query));
- };
-
- $.asap = function(test, fn) {
- var callback;
- callback = function() {
- var err;
- try {
- return fn();
- } catch (_error) {
- err = _error;
- console.log(err.message);
- return console.log(err.stack);
- }
- };
- if (test()) {
- return callback();
- } else {
- return setTimeout($.asap, 25, test, callback);
- }
- };
-
- $.addStyle = function(css, id) {
- var style;
- style = $.el('style', {
- textContent: css
- });
- if (id) {
- style.id = id;
- }
- $.asap((function() {
- return d.head;
- }), function() {
- return $.add(d.head, style);
- });
- return style;
- };
-
- $.on = function(target, events, fun, once) {
- var event, fn, func, j, len1, ref;
- fn = function() {
- var err;
- try {
- return fun.apply(this, arguments);
- } catch (_error) {
- err = _error;
- console.log(err.message);
- return console.log(err.stack);
- }
- };
- func = once ? function() {
- $.off(target, events, func);
- return fn.apply(this, arguments);
- } : fn;
- ref = events.split(' ');
- for (j = 0, len1 = ref.length; j < len1; j++) {
- event = ref[j];
- target.addEventListener(event, func, false);
- }
- return func;
- };
-
- $.off = function(target, events, fn) {
- var event, j, len1, ref;
- ref = events.split(' ');
- for (j = 0, len1 = ref.length; j < len1; j++) {
- event = ref[j];
- target.removeEventListener(event, fn, false);
- }
- return target;
- };
-
- $.el = function(type, props) {
- var el, prop;
- el = d.createElement(type);
- for (prop in props) {
- if (props.hasOwnProperty(prop)) {
- el[prop] = props[prop];
- }
- }
- return el;
- };
-
- $.nodes = function(nodes) {
- var frag, j, len1, node;
- if (!(nodes instanceof Array)) {
- return nodes;
- }
- frag = d.createDocumentFragment();
- for (j = 0, len1 = nodes.length; j < len1; j++) {
- node = nodes[j];
- frag.appendChild(node);
- }
- return frag;
- };
-
- $.html = function(html) {
- var el;
- el = $.el('div', {
- innerHTML: html
- });
- return $.nodes(slice.call(el.children));
- };
-
- $.add = function(root, nodes) {
- root.appendChild($.nodes(nodes));
- return root;
- };
-
- $.replace = function(root, el) {
- return root.parentNode.replaceChild($.nodes(el), root);
- };
-
- SimpleDict = (function() {
- function SimpleDict() {
- this.keys = [];
- }
-
- SimpleDict.prototype.push = function(key, data) {
- key = "" + key;
- if (!this[key]) {
- this.keys.push(key);
- }
- this[key] = data;
- return this[key].key = key;
- };
-
- SimpleDict.prototype.contains = function(obj) {
- return this.indexOf(obj) !== -1;
- };
-
- SimpleDict.prototype.indexOf = function(obj) {
- var key;
- key = obj.key;
- if (key) {
- if (obj !== this[key]) {
- return -1;
- }
- return this.keys.indexOf(key);
- } else {
- return this.keys.indexOf(obj);
- }
- };
-
- SimpleDict.prototype.rm = function(key) {
- var i;
- key = "" + key;
- i = this.keys.indexOf(key);
- if (i !== -1) {
- this.keys.splice(i, 1);
- return delete this[key];
- }
- };
-
- SimpleDict.prototype.first = function() {
- return this[this.keys[0]];
- };
-
- SimpleDict.prototype.forEach = function(fn) {
- var j, key, len1, ref;
- ref = slice.call(this.keys);
- for (j = 0, len1 = ref.length; j < len1; j++) {
- key = ref[j];
- fn.call(this, this[key]);
- }
- };
-
- SimpleDict.prototype.forEachKey = function(fn) {
- var j, key, len1, ref;
- ref = slice.call(this.keys);
- for (j = 0, len1 = ref.length; j < len1; j++) {
- key = ref[j];
- fn.call(this, key);
- }
- };
-
- Object.defineProperty(SimpleDict.prototype, 'length', {
- get: function() {
- return this.keys.length;
- }
- });
-
- return SimpleDict;
-
- })();
-
- preload = function(image) {
- var galLength, i, len, results;
- galLength = g.images.length;
- i = g.currentImageIndex;
- len = Math.min(galLength, i + PRELOAD + 1);
- results = [];
- while (++i < len) {
- results.push($.el('img', {
- src: g.images[g.images.keys[i]].url
- }));
- }
- return results;
- };
-
- loadGallery = function() {
- var close, count, err, gal, next, nodes, prev, thumbs;
- try {
- g.gallery = gal = $.el('div', {
- id: 'a-gallery',
- innerHTML: "<div class=\"gal-prev\"></div>\n<div class=\"gal-image\">\n <div>\n <div class=\"gal-info\">\n INFO\n <div class=\"gal-ex-info\">\n </div>\n </div>\n <div class=\"gal-close\">✖</div>\n <a class=\"current\"></a>\n </div>\n</div>\n<div class=\"gal-next\"></div>\n<div class=\"gal-thumbnails\"></div>"
- });
- nodes = g.nodes;
- nodes.prev = prev = $('.gal-prev', gal);
- nodes.next = next = $('.gal-next', gal);
- nodes.count = count = $('.gal-count', gal);
- nodes.thumbs = thumbs = $('.gal-thumbnails', gal);
- nodes.close = close = $('.gal-close', gal);
- $.on(close, 'click', cb.hideGallery);
- $.on(prev, 'click', cb.prev);
- $.on(next, 'click', cb.next);
- $.on(d, 'keydown', cb.keybinds);
- cb.hideGallery();
- return d.body.appendChild(gal);
- } catch (_error) {
- err = _error;
- console.log(err.message);
- return console.log(err.stack);
- }
- };
-
- cb = {
- next: function() {
- ++g.currentImageIndex;
- return cb.updateImage();
- },
- prev: function() {
- --g.currentImageIndex;
- return cb.updateImage();
- },
- updateImage: function() {
- return setImage(g.images[g.images.keys[g.currentImageIndex]]);
- },
- showGallery: function() {
- g.gallery.style.display = 'flex';
- return d.body.style.overflow = 'hidden';
- },
- hideGallery: function() {
- cb.pause();
- g.gallery.style.display = 'none';
- g.currentImageIndex = 0;
- return d.body.style.overflow = 'auto';
- },
- highlight: function(image) {
- var gal, highlight, thumbs;
- if (!image) {
- if (!(image = g.images[g.images.keys[g.currentImageIndex]])) {
- return;
- }
- }
- gal = g.gallery;
- $('.gal-image', gal).scrollTop = 0;
- highlight = $('.gal-highlight', gal);
- if (highlight != null) {
- highlight.classList.remove('gal-highlight');
- }
- highlight = $("[data-id='" + image.id + "']", gal);
- if (highlight != null) {
- highlight.classList.add('gal-highlight');
- }
- thumbs = g.nodes.thumbs;
- return thumbs.scrollTop = highlight.offsetTop + highlight.offsetHeight / 2 - thumbs.clientHeight / 2;
- },
- toggleGallery: function() {
- return cb[g.gallery.style.display === 'block' ? 'hideGallery' : 'showGallery']();
- },
- keybinds: function(e) {
- var fn, key;
- if (!(key = e.keyCode)) {
- return;
- }
- fn = (function() {
- switch (key) {
- case 39:
- return cb.next;
- case 37:
- return cb.prev;
- case 27:
- return cb.hideGallery;
- }
- })();
- if (!fn) {
- return;
- }
- e.stopPropagation();
- e.preventDefault();
- return fn();
- },
- pause: function() {
- var current, el;
- current = $('.gal-image a', g.gallery);
- if (current) {
- el = current.firstElementChild;
- }
- if (el && el.pause) {
- return el.pause();
- }
- }
- };
-
- fer = function(arr, fn) {
- var item, j, len1;
- for (j = 0, len1 = arr.length; j < len1; j++) {
- item = arr[j];
- fn(item);
- }
- };
-
- clickThumb = function(e) {
- e.preventDefault();
- return $.asap((function() {
- return g.images.length;
- }), (function(_this) {
- return function() {
- var id, image, queryURL;
- id = _this.id.slice(1);
- image = g.images["" + id];
- cb.showGallery();
- if (!image) {
- queryURL = g.baseURL + "page=dapi&s=post&q=index&id=" + id;
- query("get", queryURL, function() {
- image = mkImage(this.response.childNodes[0].children[0]);
- return setImage(image);
- });
- return;
- }
- return setImage(image);
- };
- })(this));
- };
-
- setImage = function(image) {
- var a, el, err, gal, i, img, info, j, len1, placeHolder, rating, ratingText, ready, ref, source, tag, tags;
- try {
- gal = g.gallery;
- cb.pause();
- el = $('.gal-image .current', gal);
- g.currentImageIndex = i = g.images.indexOf(image);
- a = $.el('a', {
- href: image.download,
- download: image.filename,
- className: 'current'
- });
- switch (image.type) {
- case "jpg":
- case "jpeg":
- case "gif":
- case "png":
- img = $.el('img', {
- src: image.url,
- alt: image.tags
- });
- ready = function() {
- return img.complete;
- };
- break;
- default:
- img = $.el('video', {
- src: image.url,
- poster: image.thumb,
- autoplay: true,
- loop: true,
- width: image.width,
- height: image.height
- });
- ready = function() {
- return img.readyState > 2;
- };
- }
- if (ready()) {
- $.add(a, img);
- preload();
- } else {
- placeHolder = $.el('div', {
- className: 'spinner'
- });
- $.add(a, placeHolder);
- $.asap(ready, function() {
- if (i !== g.currentImageIndex) {
- return;
- }
- $.replace(placeHolder, img);
- return preload();
- });
- }
- $.replace(el, a);
- a.parentElement.click();
- info = $('.gal-ex-info', gal);
- info.textContent = "ID: " + image.id + "\nScore: " + (image.score || 0) + "\nPosted: " + image.age + "\nWidth: " + image.width + "\nHeight: " + image.height + "\nType: " + (image.type.toUpperCase());
- info.innerHTML = "<p>" + (info.textContent.split('\n').join('</p><p>')) + "</p>";
- tags = $.el('p', {
- textContent: "Tags: "
- });
- ref = image.tags.split(' ');
- for (j = 0, len1 = ref.length; j < len1; j++) {
- tag = ref[j];
- $.add(tags, $.el('a', {
- href: g.baseURL + "page=post&s=list&tags=" + tag,
- textContent: tag,
- target: "_blank"
- }));
- $.add(tags, d.createTextNode(' '));
- }
- $.add(info, tags);
- ratingText = (function() {
- switch (image.rating) {
- case 'e':
- return 'explicit';
- case 's':
- return 'safe';
- default:
- return 'questionable';
- }
- })();
- rating = $.el('p', {
- textContent: "Rating: "
- });
- $.add(rating, $.el('a', {
- href: g.baseURL + "page=post&s=list&tags=rating:" + ratingText,
- textContent: ratingText,
- target: "_blank"
- }));
- $.add(info, rating);
- if (image.source) {
- source = $.el('p', {
- textContent: "Source: "
- });
- $.add(source, $.el('a', {
- href: image.source,
- textContent: image.source,
- target: "_blank"
- }));
- $.add(info, source);
- }
- cb.highlight(image);
- if (i + THRESHOLD > g.images.length) {
- return updateImages();
- }
- } catch (_error) {
- err = _error;
- console.log(err.message);
- return console.log(err.stack);
- }
- };
-
- updateImages = function() {
- var queryURL;
- queryURL = mkURL(g.images.length / LIMIT);
- return queryImages(queryURL);
- };
-
- setupImages = function() {
- var err, j, len1, parser, post, ref, response, results;
- try {
- if (this.status !== 200) {
- g.error = true;
- return alert(this.status + ": " + this.statusText);
- }
- parser = new DOMParser();
- response = parser.parseFromString(this.response, 'text/xml');
- ref = response.childNodes[0].children;
- results = [];
- for (j = 0, len1 = ref.length; j < len1; j++) {
- post = ref[j];
- results.push(mkImage(post));
- }
- return results;
- } catch (_error) {
- err = _error;
- console.log(err.message);
- return console.log(err.stack);
- }
- };
-
- mkImage = function(post) {
- var a, download, extension, filename, image, item, j, len1, p, ref, ref1, tags, thumb, type;
- p = {};
- ref = post.attributes;
- for (j = 0, len1 = ref.length; j < len1; j++) {
- item = ref[j];
- p[item.name] = item.value;
- }
- a = $.el('a', {
- href: p.file_url
- });
- a.host = 'gelbooru.com';
- download = a.href;
- type = download.split('.');
- type = ("" + type[type.length - 1]).toLowerCase();
- extension = "." + type;
- tags = p.tags.split(' ');
- while (true) {
- filename = p.id + " - " + (tags.join(' ').trim());
- tags.pop();
- if (!(filename.length + extension.length > FNLIMIT)) {
- break;
- }
- }
- filename += extension;
- image = {
- thumb: p.preview_url,
- url: p.sample_url,
- rating: p.rating,
- source: p.source,
- width: p.width,
- height: p.height,
- score: p.score,
- tags: (ref1 = p.tags) != null ? ref1.trim() : void 0,
- id: p.id,
- age: p.created_at,
- filename: filename,
- download: download,
- type: type
- };
- thumb = $.el('a', {
- href: "javascript:;",
- className: 'gal-thumb',
- innerHTML: "<img src='" + image.thumb + "'>"
- });
- thumb.setAttribute('data-id', image.id);
- $.on(thumb, 'click', function() {
- g.currentImageIndex = g.images.indexOf(image);
- return setImage(image);
- });
- $.add(g.nodes.thumbs, thumb);
- g.images.push(p.id, image);
- return image;
- };
-
- query = function(method, URL, callback) {
- var r;
- r = new XMLHttpRequest();
- r.open("get", URL, true);
- $.on(r, "load error abort", callback, true);
- r.send();
- return r;
- };
-
- queryImages = function(URL) {
- if (g.error) {
- return;
- }
- return query("get", URL, setupImages);
- };
-
- mkURL = function(pid) {
- var j, key, len1, queryURL, ref;
- if (pid) {
- g.attr.pid = pid;
- }
- queryURL = g.baseURL;
- ref = g.attr.keys;
- for (j = 0, len1 = ref.length; j < len1; j++) {
- key = ref[j];
- queryURL += key + "=" + g.attr[key] + "&";
- }
- return queryURL = queryURL.slice(0, -1);
- };
-
- setup = function() {
- var attr, j, len1, ref;
- g.images = new SimpleDict();
- g.host = d.location;
- g.attr = new SimpleDict();
- ref = g.host.search.slice(1).split('&');
- for (j = 0, len1 = ref.length; j < len1; j++) {
- attr = ref[j];
- attr = attr.split('=');
- g.attr.push(attr[0].toLowerCase(), attr[1].toLowerCase());
- }
- if (g.attr.s === 'view') {
- return;
- }
- g.baseURL = g.host.protocol + "//" + g.host.hostname + "/index.php?";
- g.attr.push('page', 'dapi');
- g.attr.push('q', 'index');
- g.attr.push('s', 'post');
- g.attr.push('limit', 100);
- if (g.attr.tags === 'all') {
- g.attr.rm('tags');
- }
- if (g.attr.pid) {
- g.attr.pid = ~~(g.attr.pid / 100);
- } else {
- g.attr.push('pid', 0);
- }
- g.queryURL = mkURL();
- $.addStyle(g.galleryCSS);
- return $.asap((function() {
- var ref1;
- return (ref1 = d.readyState) === 'interactive' || ref1 === 'complete';
- }), function() {
- var k, len2, ref1, thumb;
- ref1 = $.$('.thumb a');
- for (k = 0, len2 = ref1.length; k < len2; k++) {
- thumb = ref1[k];
- $.on(thumb, 'click', clickThumb);
- }
- loadGallery();
- return queryImages(g.queryURL);
- });
- };
-
- try {
- setup();
- } catch (_error) {
- err = _error;
- console.log(err.message);
- console.log(err.stack);
- }
-
- }).call(this);