- // ==UserScript==
- // @name MassiveFap (2023 Fix)
- // @author Ryan Thaut + patches from Elandoris and Arti
- // @description A complete ImageFap.com gallery conversion script featuring customization options and multiple viewing modes. Original author is Ryan Thaut, but it looks like abandoned.
- // @include https://*imagefap.com/*
- // @version 1.7.8
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_registerMenuCommand
- // @grant GM_addStyle
- // @namespace https://greasyfork.org/users/705794
- // ==/UserScript==
-
-
- /* ===== Integration =====
- This is where the initial/basic eventListeners are added.
- */
- window.addEventListener('load', init, false);
- document.addEventListener('DOMContentLoaded', init, false);
-
-
- /* ===== Global Variables =====
- Changing these is a terrible idea; they are NOT for configuration
- */
- var fullscreen = false;
- var loaded = false;
- var images = [];
- var imagesMap = new Map();
- var head, body, title, addFavLink;
- var author = {name: '', url: ''};
- var activeImage = (parseInt(getHashParam('image'), 10) - 1) || 0;
- var totalImages = 0;
- var hotkeys = [
- {
- action: displayHelp,
- codes: [191],
- keys: '?',
- label: 'Display Help',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: true}
- },
- {
- action: displayAbout,
- codes: [90],
- keys: 'z',
- label: 'Display About',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: toggleFullScreen,
- codes: [70],
- keys: 'f',
- label: 'Toggle Full Screen',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: switchGalleryMode,
- codes: [71],
- keys: 'g',
- label: 'Switch Gallery Mode',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: changeSettings,
- codes: [83],
- keys: 's',
- label: 'Change Settings',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: toggleThumbs,
- codes: [84],
- keys: 't',
- label: 'Change Thumbnails',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: hideDialog,
- codes: [27],
- keys: 'esc',
- label: 'Hide Dialog Window',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: prevImage,
- codes: [37, 38, 65, 87],
- keys: '← ↑ a w',
- label: 'Previous Image',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: nextImage,
- codes: [39, 40, 68, 83],
- keys: '→ ↓ d s',
- label: 'Next Image',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- },
- {
- action: toggleAutoplay,
- codes: [32],
- keys: '[space]',
- label: 'Start/Stop Autoplay',
- modif: {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
- }
- ];
- var settings = {
- 'autoplay': {
- name: 'autoplayDelay',
- label: 'Auto Play Delay',
- hint: 'in seconds',
- type: 'integer',
- size: 3,
- min: 0,
- max: null,
- def: 2
- },
- 'inifiteScrolling': {
- name: 'inifiteScrolling',
- label: 'Infinite Scrolling',
- type: 'boolean',
- def: true
- },
- 'mode': {
- name: 'galleryMode',
- label: 'Gallery Mode',
- type: 'select',
- opts: ['scrolling', 'slideshow'],
- def: 'slideshow'
- },
- 'pagination': {
- name: 'imageLimit',
- label: 'Pagination Limit',
- hint: 'use <b>0</b> to disable',
- type: 'integer',
- size: 3,
- min: 0,
- max: null,
- def: 0
- },
- 'preloading': {
- name: 'preloadingEnabled',
- label: 'Preloading reach',
- hint: 'use <b>0</b> to disable preloading',
- type: 'integer',
- size: 3,
- def: 10
- },
- 'minLoadingTime': {
- name: 'autoplayDelay',
- label: 'Min loading time',
- hint: 'in ms',
- type: 'integer',
- size: 4,
- min: 0,
- max: null,
- def: 4
- },
- 'theme': {
- name: 'theme',
- label: 'Gallery Theme',
- type: 'select',
- opts: ['default', 'classic', 'green', 'blue'],
- def: 'default'
- },
- 'thumbnails': {
- name: 'showThumbs',
- label: 'Show Thumbnails',
- type: 'boolean',
- def: true
- },
- 'thumbnailsSize': {
- name: 'thumbnailsSize',
- label: 'Thumbnails Size',
- type: 'select',
- opts: ['small', 'medium', 'large'],
- def: 'medium'
- }
- };
-
- // objects for features that need multiple settings and calculated properties
- var autoplay = {
- active: false,
- count: parseInt(GM_getValue(settings.autoplay.name, settings.autoplay.def), 10),
- delay: parseInt(GM_getValue(settings.autoplay.name, settings.autoplay.def), 10),
- paused: false,
- timer: undefined
- };
- var pagination = {
- append: GM_getValue(settings.inifiteScrolling.name, settings.inifiteScrolling.def),
- active: false,
- limit: parseInt(GM_getValue(settings.pagination.name, settings.pagination.def), 10),
- page: parseInt(getHashParam('page'), 10) || 1
- };
- var preloading = {
- active: GM_getValue(settings.preloading.name, settings.preloading.def) > 0,
- pos: activeImage,
- reach: parseInt(GM_getValue(settings.preloading.name, settings.preloading.def), 10),
- };
-
-
- /* ===== Core Functions =====
- Where the magic happens...
- */
-
- /**
- * Crawls the normal gallery page and finds all thumbnail images
- * @return Array locations of all thumbnail images
- */
- function findImages() {
- var imgs = document.getElementById('gallery').getElementsByTagName('img');
- var thumbRegex = /^(.*\/images\/)(thumb)(\/.*\/)(\d+)(\..*)$/i;
-
- var count = 0;
- var ret = [];
- var match;
- var image;
-
- for (var i = 0; i < imgs.length; i++) {
- if (thumbRegex.test(imgs[i].src)) {
- match = thumbRegex.exec(imgs[i].src);
- image = {
- id: match[4],
- pos: count++,
- thumb: match[0],
-
- loadedUrl: null,
- singlePageUrl: imgs[i].parentNode.href,
- fullUrlRequest: null,
- callback: () => {
- },
- full(callback) {
- if (this.loadedUrl) {
- callback(this.loadedUrl);
- } else {
- let old = this.callback;
- this.callback = url => {
- old(url);
- callback(url);
- }
- fetchFullImageUrl(this);
- }
- }
- };
- ret.push(image);
- imagesMap.set(image.id, image);
- }
- }
-
- totalImages = ret.length;
- return ret;
- }
-
- function fetchFullImageUrl(image) {
- var fullImgRegex = /^(.*\/images\/)(full)(\/.*\/)(\d+)(\..*)$/i;
- var xmlHttp = new XMLHttpRequest();
- xmlHttp.onreadystatechange = function () {
- if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
- var el = document.createElement( 'html' );
- el.innerHTML = xmlHttp.responseText;
- console.log('-1',el);
- let srcs = el.querySelectorAll('#_navi_cavi > ul > li > a');
- console.log('1',srcs[0]);
- console.log('2',el.querySelector('#navigation'));
- let requestedReference
- for (let i in srcs) {
- let href = srcs[i].href;
- let match = fullImgRegex.exec(href);
- if (match && match.length >= 2) {
- let id = match[4];
- if (id === image.id) {
- requestedReference = href;
- }
- let otherImage = imagesMap.get(id);
- if (otherImage && !otherImage.loadedUrl) {
- otherImage.loadedUrl = href;
- if (otherImage.callback) {
- otherImage.callback(otherImage.loadedUrl);
- }
- }
- }
- }
- if (requestedReference) {
- image.loadedUrl = requestedReference;
- if (image.callback) {
- image.callback(image.loadedUrl)
- }
- }
- }
- };
- xmlHttp.open("GET", image.singlePageUrl, true); // true for asynchronous
- xmlHttp.send(null);
- return xmlHttp;
- }
-
- function smartUrlLoading() {
-
- }
-
- /**
- * Sets the global variables needed for pagination in other functions
- */
- function initPagination() {
- initSetting('pagination');
-
- // reset pagination object properties to default
- pagination = {
- append: settings.inifiteScrolling.value,
- active: (settings.mode.value === 'slideshow'),
- limit: parseInt(settings.pagination.value, 10),
- page: parseInt(getHashParam('page'), 10) || 1
- };
-
- if ((pagination.limit <= 0) || (settings.mode.value === 'slideshow')) {
- // in slideshow mode pagination is handled as if it is disabled
- pagination.active = false;
- pagination.limit = totalImages;
- pagination.page = 1;
- } else {
- pagination.active = true;
- if (!pagination.page || (pagination.page < 1))
- pagination.page = 1;
- }
-
- // ensure the user is on the correct page
- var page = findImagePage();
- if (page !== pagination.page) {
- setHashParam('page', page);
- pagination.page = page;
- }
- }
-
- /**
- * Finds the page number that the active image should be on
- * @param Int (Optional) The number of the image to find (default: value of activeImage internal variable)
- * @return Int The page number containing the active image
- */
- function findImagePage(pos) {
- if ((typeof activeImage === 'undefined') || !activeImage || (activeImage <= 0))
- return 1;
-
- return parseInt(((activeImage / pagination.limit) + 1), 10);
- }
-
- /**
- * Returns the images that will be used on the current page
- * @param Array Objects representing all images from the original gallery
- * @return Array Multi-dimensional array of objects representing all images on each page
- */
- function paginateImages(imgs) {
- // if images have been paginated previously, they must first be un-paginated
- if (typeof imgs[0] === 'object' && typeof imgs[0][0] === 'object')
- imgs = resetImages(imgs);
-
- var page = 0;
- var ret = [];
- for (var i = 0; i < imgs.length; i++) {
- if (typeof ret[page] === 'undefined')
- ret[page] = [];
- ret[page].push(imgs[i]);
-
- if (((i + 1) % pagination.limit) === 0)
- page++;
- }
- return ret;
- }
-
- /** Flattens a paginated multi-dimensional array of images
- * @param Array Multi-dimensional array of objects representing all images on multiple page
- * @return Array Multi-dimensional array of objects representing all images on one page
- */
- function resetImages(imgs) {
- var ret = [];
- for (var i = 0; i < imgs.length; i++) {
- for (var j = 0; j < imgs[i].length; j++) {
- ret.push(imgs[i][j]);
- }
- }
- // if the supplied array was only 1-dimensional, then the new array will be empty
- if (ret.length === 0)
- ret = imgs;
- return ret;
- }
-
- /**
- * Loads the next "page" of images in Infinite Scrolling mode
- * Updates the position text and the pagination links
- */
- function loadNextPage() {
- if (pagination.page < images.length) {
- pagination.page++;
- showNotification('Loading images from page ' + pagination.page);
-
- updatePosition(undefined, (pagination.limit * pagination.page), undefined, undefined);
- updatePagination(pagination.page);
-
- populateScrollingGallery(images[(pagination.page - 1)], false);
- populateThumbnails(images[(pagination.page - 1)], false);
-
- setHashParam('page', pagination.page);
- } else {
- var loader = document.getElementById('loader');
- if (loader)
- loader.parentNode.removeChild(loader);
- }
- }
-
- /**
- * Generates HTML for the help dialog
- * @return String HTML to be placed in the dialog
- */
- function getAbout() {
- var about = '';
- about += '<p>' + GM_info.script.name + ' v' + GM_info.script.version + '. Automatic script updates are ' + ((GM_info.scriptWillUpdate) ? 'enabled' : 'disabled') + '. </p>';
- about += '<p>' + GM_info.script.description + '</p>';
-
- return about;
- }
-
- /**
- * Generates HTML for the help dialog
- * @return String HTML to be placed in the dialog
- */
- function getHelp() {
- var help = '';
- help += '<table>';
- help += '<tr><th></th><th>Hotkeys</th></tr>';
- for (var h in hotkeys)
- help += '<tr><td class="key"><b>' + hotkeys[h].keys + '</b></td><td class="command">' + hotkeys[h].label + '</td></tr>';
-
- help += '</table>';
-
- return help;
- }
-
- /**
- * Generates the DOM objects needed for the thumbnail images
- * @param Array The objects of all images to be displayed
- * @param Bool (Optional) If the existing thumbnails should be removed (default: false)
- */
- function populateThumbnails(imgs, reset) {
- reset = (typeof reset === "undefined") ? false : reset;
-
- var thumbs = document.getElementById('thumbnails');
- if (!thumbs)
- return false;
-
- if (reset)
- thumbs.innerHTML = '';
- thumbs.className = settings.thumbnailsSize.value;
-
- var img, link;
- for (var i = 0; i < imgs.length; i++) {
- img = document.createElement('img');
- img.src = imgs[i].thumb;
-
- link = document.createElement('a');
- link.addEventListener('click', clickThumbnail);
- link.className = 'thumbnail';
- link.id = 'thumb_' + imgs[i].pos;
- link.rel = imgs[i].pos;
- link.appendChild(img);
-
- thumbs.appendChild(link);
- }
- }
-
- /**
- * Generates the DOM objects needed for the scrolling mode
- * @param Array The objects of all images to be displayed
- * @param Bool (Optional) If the existing gallery images should be removed (default: false)
- */
- function populateScrollingGallery(imgs, reset) {
- reset = (typeof reset === "undefined") ? false : reset;
-
- var gallery = document.getElementById('gallery');
- if (!gallery)
- return false;
-
- if (reset)
- gallery.innerHTML = '';
-
- var container, spinner;
- for (var i = 0; i < imgs.length; i++) {
- const img = document.createElement('img');
- img.alt = '';
- img.rel = imgs[i].pos;
- imgs[i].full(url => img.src = url);
-
- spinner = document.createElement('span');
- spinner.className = 'spinner';
-
- const link = document.createElement('a');
- link.className = 'image';
- imgs[i].full(url => link.href = url);
- link.id = 'full_' + (((pagination.page - 1) * pagination.limit) + i);
-
- link.appendChild(img);
- link.appendChild(spinner);
-
- container = document.createElement('p');
- container.appendChild(link);
-
- gallery.appendChild(container);
- }
-
- if (pagination.append && (imgs.length < totalImages)) {
- var loader = document.getElementById('loader');
- if (!loader) {
- loader = document.createElement('a');
- loader.id = 'loader'
- loader.innerHTML = 'Load Next Page of Images';
- loader.addEventListener('click', loadNextPage);
- }
- gallery.appendChild(loader);
- }
- }
-
- /**
- * Generates the DOM objects needed for the slideshow mode
- * @param Object The object representing the active image
- */
- function buildSlideshowGallery(active) {
- var gallery = document.getElementById('gallery');
- if (!gallery)
- return false;
-
- gallery.innerHTML = '';
-
- var prev = document.createElement('a');
- prev.addEventListener('click', prevImage);
- prev.id = 'prev';
- prev.className = 'nav';
- prev.innerHTML = '<span class="arrow"><</span>';
-
- var next = document.createElement('a');
- next.addEventListener('click', nextImage);
- next.id = 'next';
- next.className = 'nav';
- next.innerHTML = '<span class="arrow">></span>';
-
- var img = document.createElement('img');
- img.alt = '';
- img.id = 'slideshowImage';
- active.full(url => img.src = url);
-
- var spinner = document.createElement('span');
- spinner.className = 'spinner';
-
- var link = document.createElement('a');
- link.className = 'image';
- active.full(url => link.href = url);
- link.id = 'slideshowLink';
-
- link.appendChild(img);
- link.appendChild(spinner);
-
- gallery.appendChild(link);
- gallery.appendChild(prev);
- gallery.appendChild(next);
-
- // resize the slideshow area
- resizeSlideshowGallery();
- }
-
- /**
- * Resizes the DOM object containing the slideshow image to accomodate non-fixed header and footer sizes
- */
- function resizeSlideshowGallery() {
- var content = document.getElementById('content');
- if (!content)
- return false;
-
- // header
- var header = document.getElementById('header');
- content.style.top = (header) ? header.offsetHeight + 'px' : '';
-
- // footer
- var footer = document.getElementById('footer');
- content.style.bottom = (footer) ? footer.offsetHeight + 'px' : '';
- }
-
- /**
- * Generates the DOM objects for the header of rebuilt pages
- */
- function buildHeader() {
- var header = document.getElementById('header');
- if (!header)
- return false;
-
- header.innerHTML = '';
-
- // logo
- var logo = document.createElement('a');
- logo.href = window.location.protocol + '//' + window.location.host;
- logo.id = 'logo';
- logo.innerHTML = '<span class="image">Image</span><span class="fap">Fap</span>';
- header.appendChild(logo);
-
- // heading
- var heading = document.createElement('h2');
- heading.id = 'heading';
- heading.innerHTML = '"<span class="title">' + stripslashes(title) + '</span>"';
- if (author.name && author.url)
- heading.innerHTML += ' <small>by</small> <a href="' + author.url + '">' + unescape(stripslashes(author.name)) + '</a>';
- header.appendChild(heading);
-
- // description
- if (desc) {
- var description = document.createElement('p');
- description.id = 'description';
- description.innerHTML = stripslashes(desc);
- header.appendChild(description);
-
- // if the description spans multiple lines, left-align the text
- if (description.offsetHeight > 16)
- description.style.textAlign = 'left';
- }
-
- // sub-heading
- var subheading = document.createElement('p');
- header.appendChild(subheading);
- // sub-heading > position
- var position = document.createElement('span');
- position.className = settings.mode.value;
- position.id = 'position';
- subheading.appendChild(position);
- // sub-heading > spacer
- var separator = document.createElement('span');
- separator.innerHTML = ' | ';
- subheading.appendChild(separator);
- // sub-heading > "toggle thumbnails" link
- var toggle = document.createElement('a');
- toggle.addEventListener('click', toggleThumbs);
- toggle.innerHTML = 'Toggle Thumbnails';
- subheading.appendChild(toggle);
- // sub-heading > spacer
- var separator = document.createElement('span');
- separator.innerHTML = ' | ';
- subheading.appendChild(separator);
- // sub-heading > "change settings" link
- var openSettings = document.createElement('a');
- openSettings.addEventListener('click', changeSettings);
- openSettings.innerHTML = 'Change Settings';
- subheading.appendChild(openSettings);
-
- // pagination
- if (settings.mode.value === 'scrolling' && pagination.active)
- buildPagination('header');
-
- // search form
- var form = document.createElement('form');
- form.id = 'search';
- form.method = 'POST';
- form.action = window.location.protocol + '//' + window.location.host + '/gallery.php';
- header.appendChild(form);
- // search form > text input
- var search = document.createElement('input');
- search.type = 'text';
- search.value = 'Enter search term(s)...';
- search.name = 'search';
- search.addEventListener('focus', function () {
- if (this.value === 'Enter search term(s)...') this.value = '';
- });
- search.addEventListener('blur', function () {
- if (this.value === '') this.value = 'Enter search term(s)...';
- });
- form.appendChild(search);
- // search form > submit button
- var submit = document.createElement('input');
- submit.type = 'submit';
- submit.value = 'Search';
- submit.name = 'submit';
- form.appendChild(submit);
- }
-
- /**
- * Generates the DOM objects for the footer of rebuilt pages
- */
- function buildFooter() {
- var footer = document.getElementById('footer');
- if (!footer)
- return false;
-
- footer.innerHTML = '';
-
- footer.appendChild(addFavLink);
-
- if (settings.mode.value === 'scrolling' && pagination.active)
- buildPagination('footer');
-
- buildInfo();
- buildAutoplay();
- }
-
- /**
- * Updates the HTML for the position of the current image(s) within the gallery
- * @param Int (Optional) The lower limit image number (default: use existing value from DOM)
- * @param Int (Optional) The upper limit image number (default: use existing value from DOM)
- * @param Int (Optional) The total image number (default: use existing value from DOM)
- * @param Int (Optional) The active image number (default: use existing value from DOM)
- */
- function updatePosition(lower, upper, total, active) {
- var position = document.getElementById('position');
- if (!position)
- return false;
-
- if (position.className !== settings.mode.value) {
- position.className = settings.mode.value;
- position.innerHTML = '';
- }
-
- if (settings.mode.value === 'scrolling') {
- if (position.innerHTML === undefined || position.innerHTML === '')
- position.innerHTML = 'Viewing image(s) <span id="position_lower">' + lower + '</span>-<span id="position_upper">' + upper + '</span> of <span id="position_total">' + total + '</span>';
-
- lower = (typeof lower === "undefined") ? parseInt(document.getElementById('position_lower').innerHTML, 10) : lower;
- upper = (typeof upper === "undefined") ? parseInt(document.getElementById('position_upper').innerHTML, 10) : upper;
- total = (typeof total === "undefined") ? parseInt(document.getElementById('position_total').innerHTML, 10) : total;
- if (upper > totalImages)
- upper = totalImages;
- document.getElementById('position_lower').innerHTML = lower;
- document.getElementById('position_upper').innerHTML = upper;
- document.getElementById('position_total').innerHTML = total;
- } else if (settings.mode.value === 'slideshow') {
- if (position.innerHTML === undefined || position.innerHTML === '')
- position.innerHTML = 'Viewing image <span id="position_active">' + lower + '</span> of <span id="position_total">' + total + '</span>';
-
- active = (typeof active === "undefined") ? parseInt(document.getElementById('position_active').innerHTML, 10) : active;
- total = (typeof total === "undefined") ? parseInt(document.getElementById('position_total').innerHTML, 10) : total;
- document.getElementById('position_active').innerHTML = active;
- document.getElementById('position_total').innerHTML = total;
- }
- }
-
- /**
- * Generates the DOM objects for the pagination of rebuilt gallery pages
- * @param String The ID of the DOM object of which to insert the pagination controls
- */
- function buildPagination(location) {
- var container = document.getElementById(location);
- if (!container)
- return false;
-
- var pages = Math.ceil(totalImages / pagination.limit);
-
- if (pages <= 1)
- return false;
-
- var wrapper = document.createElement('div');
- wrapper.className = 'pagination';
-
- // previous page
- var prev = document.createElement('a');
- prev.innerHTML = '« Prev';
- if (pagination.page > 1) {
- prev.addEventListener('click', clickPagination);
- prev.rel = (pagination.page - 1);
- } else {
- prev.className = 'disabled';
- }
- wrapper.appendChild(prev);
-
- // individual pages
- var link, lower, upper;
- for (var i = 1; i <= pages; i++) {
- link = document.createElement('a');
- link.rel = i;
-
- lower = (pagination.limit * (i - 1) + 1);
- upper = (i < pages) ? (pagination.limit * i) : totalImages;
- link.innerHTML = (lower === upper) ? lower : lower + '-' + upper;
-
- if (i === pagination.page) {
- link.className = 'current';
- } else {
- link.addEventListener('click', clickPagination);
- }
- wrapper.appendChild(link);
- }
-
- // next page
- var next = document.createElement('a');
- next.innerHTML = 'Next »';
- if (pagination.page < pages) {
- next.addEventListener('click', clickPagination);
- next.rel = (pagination.page + 1);
- } else {
- next.className = 'disabled';
- }
- wrapper.appendChild(next);
-
- container.appendChild(wrapper);
- }
-
- /**
- * Updates the pagination controls
- * @param Int The number of the current page
- * @param Bool (Optional) If all pagination controls should be reset (default: false)
- */
- function updatePagination(page, reset) {
- reset = (typeof reset === "undefined") ? false : reset;
-
- var containers = document.getElementsByClassName('pagination');
- if (containers.length === 0)
- return false;
-
- var links, rel;
- for (var i = 0; i < containers.length; i++) {
- links = containers[i].getElementsByTagName('a');
-
- // first handle all of the inner links (i.e. the numbered ones)
- if (reset) {
- // this is for when a page is loaded by itself
- // activate the target link and reset all of the other links to default
- for (var j = 1; j < (links.length - 1); j++) {
- if (j === page) {
- links[j].className = 'current';
- links[j].removeEventListener('click', clickPagination);
- } else {
- links[j].className = '';
- links[j].addEventListener('click', clickPagination);
- }
- }
- } else {
- // this is for when a page is appended
- // simply activate the target link
- links[page].className = 'current';
- links[page].removeEventListener('click', clickPagination);
- }
-
- // the first link is the "Prev" link, which needs to point to the page BEFORE the current page
- var prev = links[0];
- if (prev.nextSibling && prev.nextSibling.className === 'current') {
- prev.rel = page;
- prev.className = 'disabled';
- prev.removeEventListener('click', clickPagination);
- } else {
- prev.rel = (page - 1);
- prev.className = trim(links[0].className.replace('disabled', ''));
- prev.addEventListener('click', clickPagination);
- }
-
- // the last link is the "Next" link, which needs to point to the page AFTER the current page
- var next = links[(links.length - 1)];
- if (next.previousSibling && next.previousSibling.className === 'current') {
- next.rel = page;
- next.className = 'disabled';
- next.removeEventListener('click', clickPagination);
- } else {
- next.rel = (page + 1);
- next.className = trim(next.className.replace('disabled', ''));
- next.addEventListener('click', clickPagination);
- }
- }
- }
-
- /**
- * Generates the DOM objects for the information text at the bottom of the footer
- */
- function buildInfo() {
- var footer = document.getElementById('footer');
- if (!footer)
- return false;
-
- var info = document.createElement('p');
- info.id = 'info';
-
- // "about" link
- var about = document.createElement('a');
- about.addEventListener('click', displayAbout);
- about.innerHTML = GM_info.script.name + ' v' + GM_info.script.version;
- info.appendChild(about);
-
- // spacer
- var separator = document.createElement('span');
- separator.innerHTML = ' | ';
- info.appendChild(separator);
-
- // "help" link
- var help = document.createElement('a');
- help.addEventListener('click', displayHelp);
- help.innerHTML = 'Help (?)';
- info.appendChild(help);
-
- // spacer
- var separator = document.createElement('span');
- separator.innerHTML = ' | ';
- info.appendChild(separator);
-
- // "settings" link
- var openSettings = document.createElement('a');
- openSettings.addEventListener('click', changeSettings);
- openSettings.innerHTML = 'Settings';
- info.appendChild(openSettings);
-
- footer.appendChild(info);
- }
-
- /**
- * Generates the DOM objects for the autoplay indicator in the footer
- */
- function buildAutoplay() {
- var footer = document.getElementById('footer');
- if (!footer)
- return false;
-
- var autoplay = document.createElement('div');
- autoplay.id = 'autoplay';
- if (settings.mode.value === 'scrolling') {
- var disabled = document.createElement('span');
- disabled.innerHTML = 'Autoplay is only available in slideshow mode';
- autoplay.appendChild(disabled);
- } else if (settings.mode.value === 'slideshow') {
- // autoplay counter
- var counter = document.createElement('span');
- counter.id = 'counter';
- counter.innerHTML = (autoplay.count > 0) ? 'Advancing image in ' + autoplay.count + ' seconds' : 'Autoplay is disabled';
- autoplay.appendChild(counter);
-
- // spacer
- var separator = document.createElement('span');
- separator.innerHTML = ' | ';
- autoplay.appendChild(separator);
-
- // autplay control link
- var control = document.createElement('a');
- control.addEventListener('click', toggleAutoplay);
- control.id = 'control';
- control.innerHTML = 'Start Autoplay';
- autoplay.appendChild(control);
- }
-
- footer.appendChild(autoplay);
- }
-
- /**
- * Clears out and re-initializes the DOM with the basic HTML needed for the gallery
- */
- function initDOM() {
- document.removeChild(document.getElementsByTagName('html')[0]);
-
- var html = document.createElement('html');
- head = document.createElement('head');
- body = document.createElement('body');
- html.appendChild(head);
- html.appendChild(body);
- document.appendChild(html);
-
- head.innerHTML = '<title>' + stripslashes(title) + '</title>';
-
- // build the basic HTML structure to prevent missing elements
- body.innerHTML = '<div id="header"></div><div id="content"><div id="gallery"></div></div><div id="footer"></div>'
- }
-
- /**
- * Registers the GreaseMonkey Menu commands
- * Must be run after the gallery page is initially built
- */
- function initMenuCommands() {
- GM_registerMenuCommand('[' + GM_info.script.name + '] Help', displayHelp);
- GM_registerMenuCommand('[' + GM_info.script.name + '] About', displayAbout);
- GM_registerMenuCommand('[' + GM_info.script.name + '] Settings', changeSettings);
- }
-
- /**
- * Rebuilds the actual gallery page piece by piece
- */
- function rebuildGalleryPage() {
- hideDialog();
- // re-initialize all settings and feature packages
- initSettings();
- initAutoplay();
- initPagination();
- initPreloading();
-
- // manually remove existing CSS and apply the chosen theme's CSS
- var styles = head.getElementsByTagName('style');
- for (var i = 0; i < styles.length; i++) {
- head.removeChild(styles[i]);
- }
- GM_addStyle(getCSS());
-
- // paginate the images using current pagination settings
- images = paginateImages(images);
-
- // header
- var header = document.getElementById('header');
- if (!header) {
- header = document.createElement('div');
- header.id = 'header';
- body.appendChild(header);
- }
- buildHeader();
-
- // thumbnails
- var thumbs = document.getElementById('thumbnails');
- if (!thumbs) {
- thumbs = document.createElement('div');
- thumbs.id = 'thumbnails';
- header.appendChild(thumbs);
- }
-
- // content (gallery wrapper)
- var content = document.getElementById('content');
- if (!content) {
- content = document.createElement('div');
- content.id = 'content';
- body.appendChild(content);
- }
-
- // main gallery
- var gallery = document.getElementById('gallery');
- if (!gallery) {
- gallery = document.createElement('div');
- gallery.id = 'gallery';
- content.appendChild(gallery);
- }
-
- // footer
- var footer = document.getElementById('footer');
- if (!footer) {
- footer = document.createElement('div');
- footer.id = 'footer';
- body.appendChild(footer);
- }
- buildFooter();
-
- // populate the thumbnails
- if (settings.thumbnails.value === false)
- thumbs.style.display = 'none';
- if (settings.mode.value === 'slideshow')
- thumbs.addEventListener('DOMMouseScroll', scrollThumbs, false);
- populateThumbnails(images[(pagination.page - 1)], true);
-
- // populate the gallery
- if (settings.mode.value === 'scrolling') {
- gallery.style.marginBottom = (footer.offsetHeight + 10) + 'px';
- body.className = 'scrolling';
- var lower = ((pagination.limit * (pagination.page - 1)) + 1);
- var upper = (pagination.limit * pagination.page);
- updatePosition(lower, upper, totalImages, undefined);
- populateScrollingGallery(images[(pagination.page - 1)], true);
- } else if (settings.mode.value === 'slideshow') {
- body.className = 'slideshow';
- updatePosition(undefined, undefined, totalImages, activeImage);
- buildSlideshowGallery(images[(pagination.page - 1)][activeImage]);
- refreshNavigation();
- }
-
- // remove the page hash for slideshow mode
- if (settings.mode.value === 'slideshow')
- unsetHashParam('page');
-
- // show the active image, unless it is the first image of a scrolling gallery
- if ((settings.mode.value === 'slideshow') || (activeImage > 0))
- showImage();
-
- // start preloading images if preloading is enabled
- if ((settings.mode.value === 'slideshow') && preloading.active)
- preloadImage();
- }
-
- /**
- * main function; executes functionality based on page (via URL)
- * Redirects to the one-page version of galleries (if not already in one-page mode)
- */
- function init() {
- // prevent the initialization function from running multiple times
- if (loaded) {
- return false;
- } else {
- loaded = true;
- }
-
- var loc = window.location.href;
- if ((loc.indexOf('/gallery/') !== -1) || (loc.indexOf('/pictures/') !== -1)) {
- // this is a gallery page; make sure it is in "One Page" mode and then go to work
- if (loc.indexOf('view') === -1) {
- window.location.href += ((loc.indexOf('?') === -1) ? '?' : '&') + 'view=2';
- } else {
- // populate the global variables from the original gallery page
- title = document.title;
- if (title)
- title = trim(title.replace('Porn pics of ', '').replace(' (Page 1)', ''));
-
- var links = document.getElementsByTagName('a');
- if (links) {
- var i = 0;
- for (var i = 0; i < links.length; i++) {
- if (links[i].href.indexOf('profile.php?') !== -1) {
- author.name = links[i].href.split('=')[1];
- author.url = links[i].href;
- break;
- }
- }
- }
-
- desc = document.getElementById('cnt_description');
- if (desc)
- desc = trim(desc.textContent);
-
- addFavLink = document.getElementById('favorites_container');
-
- // FINALLY! grab the images, build the gallery, enable the hotkeys, and listen for changes to settings
- images = findImages();
- initDOM();
- rebuildGalleryPage();
- initMenuCommands();
- checkSettings(); // must run after the gallery page is built (otherwise the notification gets wiped out)
- document.addEventListener('keydown', onKeyDown, false);
- window.addEventListener('focus', onWindowFocus, false);
- window.addEventListener('scroll', onWindowScroll, false);
- window.addEventListener('mozfullscreenchange', onFullScreenChange, false);
- window.addEventListener('webkitfullscreenchange', onFullScreenChange, false);
- }
- } else {
- // this might be a page with links to galleries; change all of the links to galleries to "One Page" mode
- var links = document.getElementsByTagName('a');
- if (links) {
- for (var i = 0; i < links.length; i++) {
- if ((links[i].href.indexOf('gallery') !== -1) || (links[i].href.indexOf('pictures') !== -1)) {
- links[i].href += ((links[i].href.indexOf('?') === -1) ? '?' : '&') + 'view=2';
- }
- }
- }
- }
- }
-
- /**
- * Toggles visibility of the navigational arrows by adding/removing a "disabled" class
- */
- function refreshNavigation() {
- var nav = document.getElementsByClassName('nav');
- for (var i = 0; i < nav.length; i++) {
- nav[i].className = trim(nav[i].className.replace('disabled', ''));
- }
-
- if (activeImage === 0) {
- var prev = document.getElementById('prev');
- if (prev)
- prev.className += ' disabled';
- } else if (activeImage === (pagination.limit - 1)) {
- var next = document.getElementById('next');
- if (next)
- next.className += ' disabled';
- }
- }
-
- /**
- * Handles the click event for the pagination links
- * @param Event The click event
- */
- function clickPagination(evt) {
- if (this.rel) {
- var page = parseInt(this.rel, 10);
- activeImage = (((page - 1) * pagination.limit) + 1);
- unsetHashParam('image');
- updatePagination(page, true);
- goToPage(page);
- }
- }
-
- /**
- * Sets the values needed to change pages and rebuilds the gallery for the specified page
- * @param Int The number of the page to display
- */
- function goToPage(page) {
- pagination.page = page;
-
- var lower = ((pagination.limit * (pagination.page - 1)) + 1);
- var upper = (pagination.limit * pagination.page);
-
- setHashParam('page', page);
- setHashParam('image', lower);
-
- updatePosition(lower, upper, totalImages, undefined);
-
- populateScrollingGallery(images[(page - 1)], true);
- populateThumbnails(images[(page - 1)], true);
-
- window.scrollTo(0, 0);
- }
-
- /**
- * Captures mouse scrolling on the thumbnails and scrolls the images horizontally
- * @param Event mouse wheel scroll event
- */
- function scrollThumbs(evt) {
- if (!evt)
- evt = this;
- evt.preventDefault(); // prevent vertical scrolling on the page
-
- var delta = (evt.detail) ? evt.detail : 0;
- window.document.getElementById('thumbnails').scrollLeft += (delta * 10);
-
- evt.returnValue = false;
- }
-
- /**
- * Sets the active image based on the thumbnail image that was clicked
- * Expects to be executed as a click event for a DOM object with a "rel" value
- * @param Event The click event
- */
- function clickThumbnail(evt) {
- if (this.rel) {
- activeImage = parseInt(this.rel, 10);
- preloading.pos = activeImage;
- }
- showImage();
- }
-
- /**
- * Sets the active image to the next image
- * Ensures the active image is not the last one already
- */
- function nextImage() {
- if (settings.mode.value === 'slideshow' && autoplay.active) {
- autoplay.count = autoplay.delay;
- }
-
- if (activeImage < ((pagination.page * pagination.limit) - 1)) {
- activeImage++;
- showImage();
- }
- }
-
- /**
- * Sets the active image to the previous image
- * Ensures the active image is not the first one already
- */
- function prevImage() {
- if (settings.mode.value === 'slideshow' && autoplay.active) {
- stopAutoplay();
- }
-
- if (activeImage > ((pagination.page - 1) * pagination.limit)) {
- activeImage--;
- showImage();
- }
- }
-
- /**
- * Sets the internal activeImage pointer and sets the image hash parameter
- * Activates the correct thumbnail for the active image
- * @param Int (Optional) The number of the active image (default: value of activeImage internal variable)
- */
- function setActiveImage(pos) {
- pos = (typeof pos === "undefined") ? activeImage : pos;
-
- setHashParam('page', findImagePage());
- setHashParam('image', (pos + 1));
- showActiveThumb(pos);
- }
-
- /**
- * Highlights the thumbnail corresponding to the current image
- * and scrolls it into view in slideshow mode
- * @param Int (Optional) The number of the active image (default: value of activeImage internal variable)
- */
- function showActiveThumb(pos) {
- pos = (typeof pos === "undefined") ? activeImage : pos;
-
- var thumbs = document.getElementsByClassName('thumbnail');
- for (var i = 0; i < thumbs.length; i++) {
- thumbs[i].className = trim(thumbs[i].className.replace('active', ''));
- }
-
- var thumb = document.getElementById('thumb_' + pos);
- if (thumb) {
- thumb.className += ' active';
- if (settings.mode.value === 'slideshow')
- thumb.scrollIntoView();
- }
- }
-
- /**
- * Displays/navigates to the "active" image
- * In scrolling mode, this simply scrolls the page up/down to the active image,
- * but in slideshow mode, this shows the active image and hides the others
- * @param Int (Optional) The number of the active image (default: value of activeImage internal variable)
- */
- function showImage(pos) {
- pos = (typeof pos === "undefined") ? activeImage : pos;
-
- setActiveImage(pos);
-
- if (settings.mode.value === 'scrolling') {
- var target = document.getElementById('full_' + pos);
- if (target)
- target.scrollIntoView();
- } else if (settings.mode.value === 'slideshow') {
- var link = document.getElementById('slideshowLink');
- var img = document.getElementById('slideshowImage');
-
- if (link && img) {
- showNotification("Loading image " + (pos + 1), 1000);
- // first blank out the current image and link target from the previous image
- // then use a slight delay to set the next image and link target;
- // this causes the loading animation to be played while the image is loaded;
- // if the delay is removed, the previous image will remain visible until the new image is loaded
- // without any indication that the image has changed and the next image is loading
- img.src = link.href = '';
- setTimeout(function () {
- images[pagination.page - 1][pos].full(url => img.src = link.href = url);
- },
- settings.minLoadingTime.value
- );
-
- // always remove the autoplay function from load, and then re-add again if autoplay is still enabled
- img.removeEventListener('load', startAutoplay);
- if (autoplay.active && !autoplay.paused) {
- img.addEventListener('load', startAutoplay);
- }
-
- updatePosition(undefined, undefined, totalImages, (pos + 1));
- refreshNavigation();
- preloadImage();
- if (preloading.active && !preloading.done && (preloading.pos < activeImage))
- preloading.pos = activeImage;
- }
- }
- }
-
- /**
- * Event handler for keypresses
- * Used to handle hotkeys
- * @param Event The keypress event
- */
- function onKeyDown(evt) {
- if (!evt)
- evt = this;
-
- if ((evt.target.nodeName === 'INPUT') || (evt.target.nodeName === 'SELECT') || (evt.target.nodeName === 'TEXTAREA'))
- return false;
-
- var correct = true,
- hotkey;
- for (var h in hotkeys) {
- hotkey = hotkeys[h];
- for (var c in hotkey.codes) {
- if (evt.keyCode === hotkey.codes[c]) {
- for (var m in hotkey.modif) {
- correct = (evt[m] === hotkey.modif[m]) ? correct : false;
- }
- if (correct) {
- evt.preventDefault();
- return (typeof hotkey.action === 'function') ? hotkey.action.call() : eval(hotkey.action);
- }
- }
- }
- }
- }
-
- /**
- * Event handler for window scroll
- * Used to update the active image when manually scrolling in the scrolling gallery
- * and to determine if the next page of images should be loaded
- * @param Event The scroll event
- */
- function onWindowScroll(evt) {
- if (settings.mode.value === 'scrolling') {
- var imgs = document.getElementById('gallery').getElementsByTagName('img');
- var target = 0;
-
- // loop backwards through the images until the currently visible image is found
- for (var i = (imgs.length - 1); i >= 0; i--) {
- var current = imgs[i].parentNode.offsetTop;
-
- if (document.body.scrollTop >= current) {
- target = parseInt(imgs[i].rel, 10);
- break;
- }
- }
-
- // only update the active image if it changed
- if (target !== activeImage) {
- activeImage = target;
- setActiveImage();
- }
-
- if (pagination.append) {
- var last = imgs[(imgs.length - 1)];
- if ((last.parentNode.offsetTop - window.innerHeight) <= window.pageYOffset) {
- loadNextPage();
- }
- }
- }
- }
-
- /**
- * Event handler for window focus
- * Used to monitor setting changes and refresh the gallery when needed
- * @param Event The focus event
- */
- function onWindowFocus(evt) {
- var prevMode = settings.mode.value;
- var prevPagination = settings.pagination.value;
- initSettings();
-
- var refresh = false;
-
- var thumbs = document.getElementById('thumbnails');
- if (thumbs) {
- // thumbnails can be toggled without rebuilding the entire gallery page
- thumbs.className = settings.thumbnailsSize.value;
- thumbs.style.display = (settings.thumbnails.value === true) ? 'block' : 'none';
- if (settings.mode.value === 'slideshow')
- resizeSlideshowGallery();
- }
-
- if (prevMode !== settings.mode.value) {
- // current gallery mode does not match the stored preferences
- refresh = true;
- }
-
- if (settings.mode.value === 'scrolling') {
- if (prevPagination !== settings.pagination.value) {
- // current gallery pagination does not match the stored preferences
- refresh = true;
- }
- }
-
- if (refresh)
- rebuildGalleryPage();
- }
-
- /**
- * Displays the about dialog
- */
- function displayAbout() {
- showDialog(getAbout(), 'About');
- }
-
- /**
- * Displays the help dialog
- */
- function displayHelp() {
- showDialog(getHelp(), 'Help');
- }
-
- /**
- * Re-initialize the autoplay settings
- */
- function initAutoplay() {
- if (autoplay.timer)
- window.clearTimeout(autoplay.timer);
-
- initSetting('autoplay');
-
- // reset autoplay object properties to default
- autoplay = {
- active: false,
- count: parseInt(settings.autoplay.value, 10),
- delay: parseInt(settings.autoplay.value, 10),
- paused: false,
- timer: undefined
- };
- }
-
- /**
- * Starts the autoplay used timer for advancing to the next image
- * If the delay is not set correctly, the user is prompted to set it
- */
- function startAutoplay() {
- if (!autoplay.active)
- return false;
-
- if (autoplay.delay > 1000) {
- // the delay is likely in milliseconds, so convert it to seconds and save
- autoplay.delay /= 1000;
- GM_setValue(settings.autoplay.name, autoplay.delay);
- initAutoplay();
- }
-
- if (autoplay.delay < 0) {
- // the delay is invalid; inform the user
- var buttons = {0: {text: 'Change Settings', action: changeSettings}};
- showDialog('<p>The current value for the ' + settings.autoplay.label + ' is not valid.</p>', 'Invalid ' + settings.autoplay.label, buttons);
- } else {
- autoplay.count = autoplay.delay;
- if (activeImage < totalImages) {
- autoplayTimer();
- } else {
- stopAutoplay();
- }
- }
- }
-
- /**
- * Stops the autoplay timer used for advancing to the next image
- */
- function stopAutoplay() {
- window.clearTimeout(autoplay.timer);
- autoplay.active = false;
- autoplay.count = autoplay.delay;
-
- var counter = document.getElementById('counter');
- if (counter)
- counter.innerHTML = 'Autoplay is disabled';
-
- var control = document.getElementById('control');
- if (control)
- control.innerHTML = 'Start Autoplay';
- }
-
- /**
- * Pauses the autoplay timer
- */
- function pauseAutoplay() {
- window.clearTimeout(autoplay.timer);
- autoplay.paused = true;
-
- var counter = document.getElementById('counter');
- if (counter)
- counter.innerHTML = 'Autoplay is paused';
-
- var control = document.getElementById('control');
- if (control)
- control.innerHTML = 'Resume Autoplay';
- }
-
- /**
- * Resumes the autoplay timer
- */
- function resumeAutoplay() {
- var counter = document.getElementById('counter');
- if (counter)
- counter.innerHTML = 'Resuming autoplay...';
-
- var control = document.getElementById('control');
- if (control)
- control.innerHTML = 'Pause Autoplay';
-
- autoplay.paused = false;
- autoplayTimer();
- }
-
- /**
- * Starts the counter indicator for advancing to the next image
- */
- function autoplayTimer() {
- window.clearTimeout(autoplay.timer);
-
- var counter = document.getElementById('counter');
- if (!counter)
- return false;
-
- if (activeImage < (totalImages - 1)) {
- if (autoplay.count > 0) {
- autoplay.timer = window.setTimeout(autoplayTimer, 1000);
- counter.innerHTML = 'Advancing image in <b>' + autoplay.count + '</b> seconds';
- } else {
- counter.innerHTML = 'Loading image...';
- nextImage();
- }
- autoplay.count--;
- } else {
- counter.innerHTML = 'End of gallery';
- }
- }
-
- /**
- * Toggles autoplay (start and pause/resume)
- */
- function toggleAutoplay() {
- autoplay.active = !autoplay.active;
- if (autoplay.active) {
- resumeAutoplay();
- } else {
- pauseAutoplay();
- }
-
- if (fullscreen)
- showAutoplayIndicator();
- }
-
- /**
- * Displays an indicator when toggling autoplay
- */
- function showAutoplayIndicator() {
- var gallery = document.getElementById('gallery');
- if (!gallery)
- return false;
-
- var indicator = document.getElementById('indicator');
- if (indicator)
- hideAutoplayIndicator(0);
-
- indicator = document.createElement('div');
- indicator.id = 'indicator';
- indicator.style.opacity = 1;
- gallery.appendChild(indicator);
-
- if (autoplay.active) {
- indicator.innerHTML = '<span class="symbol play">►</span>';
- } else {
- indicator.innerHTML = '<span class="symbol pause">||</span>';
- }
- setTimeout(function () {
- hideAutoplayIndicator(100);
- }, 1000);
- }
-
- /**
- * Fades the autoplay indicator
- */
- function hideAutoplayIndicator(duration) {
- duration = (typeof duration === "undefined") ? 100 : duration;
-
- var indicator = document.getElementById('indicator');
- if (!indicator)
- return false;
-
- var timer = setInterval(function () {
- if (indicator === null || indicator.parentNode === null) {
- clearInterval(timer);
- } else {
- indicator.style.opacity -= 0.1
- if (indicator.style.opacity <= 0)
- indicator.parentNode.removeChild(indicator);
- }
- }, duration);
- }
-
- /**
- * Toggles the slideshow mode for gallery pages
- * A dialog is shown after toggling to allow the user to apply the change immediately
- * @param Bool (Optional) If a nofitication should be displayed (default: true)
- */
- function switchGalleryMode(notify) {
- notify = (typeof notify === "undefined") ? true : notify;
-
- if (settings.mode.value === 'slideshow')
- GM_setValue(settings.mode.name, 'scrolling');
- else if (settings.mode.value === 'scrolling')
- GM_setValue(settings.mode.name, 'slideshow');
- initSetting('mode');
-
- if (notify) {
- var buttons = {
- 0: {text: 'Apply Change Now', action: rebuildGalleryPage},
- 1: {text: 'Close', action: hideDialog}
- };
- showDialog('<p>Gallery mode has been changed to ' + settings.mode.value + '.</p>', 'Gallery Mode', buttons);
- }
- }
-
- /**
- * Toggles visibilty on thumbnails and stores the preference
- */
- function toggleThumbs() {
- GM_setValue(settings.thumbnails.name, !GM_getValue(settings.thumbnails.name, settings.thumbnails.def));
- initSetting('thumbnails');
-
- var visible = settings.thumbnails.value;
-
- var thumbs = document.getElementById('thumbnails');
- if (thumbs) {
- var thumbsHeight = thumbs.offsetHeight;
- thumbs.style.display = (visible) ? 'block' : 'none';
- showNotification('Thumbnails are now ' + ((visible) ? 'visible' : 'hidden'), 1000);
-
- if (settings.mode.value === 'slideshow') {
- resizeSlideshowGallery();
- } else if (settings.mode.value === 'scrolling') {
- if (visible) {
- // scroll the window up to the top of the page when thumbnails are visible
- window.scrollTo(0, 0);
- } else {
- // attempt to prevent the page from scrolling too much when thumbnails are hidden
- window.scrollTo(0, (window.pageYOffset - thumbsHeight - 10));
- }
- }
- }
- }
-
- /**
- * Re-initialize the preloading settings
- */
- function initPreloading() {
- initSetting('preloading');
-
- // reset autoplay object properties to default
- preloading = {
- active: settings.preloading.value > 0,
- pos: activeImage,
- reach: settings.preloading.value
- };
-
- }
-
- function performPreload(imageData) {
- const image = document.createElement('img');
- image.setAttribute('alt', "preloadin...");
- imageData.full(url => image.setAttribute('src', url));
- image.addEventListener('load', preloadImage);
- }
-
- /**
- * Preloads the next image in slideshow mode
- * Will be called continuously until the last image is loaded
- */
- function preloadImage() {
- if (!preloading.active)
- return false;
-
- if (preloading.pos < (activeImage + preloading.reach)) {
- showNotification("Preloading image " + (preloading.pos + 1), 1000);
-
- const imageData = images[pagination.page - 1][preloading.pos];
- if (imageData) {
- performPreload(imageData);
- }
- preloading.pos++;
- }
- }
-
- /**
- * Toggles full screen view in supported browsers
- * Full screen view is only available in slideshow modepreloading.pos <
- */
- function toggleFullScreen() {
- if (settings.mode.value !== 'slideshow')
- return false;
-
- if (!document.mozFullScreenElement && !document.webkitFullscreenElement) {
- var target = document.getElementById('content');
- if (target.mozRequestFullScreen) {
- target.mozRequestFullScreen();
- } else if (target.webkitRequestFullscreen) {
- target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
- } else {
- showDialog('Sorry, but it seems your browser does not support customized fullscreen HTML.', 'Fullscreen Error');
- }
- } else {
- if (document.mozCancelFullScreen) {
- document.mozCancelFullScreen();
- } else if (document.webkitCancelFullScreen) {
- document.webkitCancelFullScreen();
- }
- }
- }
-
- /**
- * Event handler for fullscreen enter/exit
- * Used to move the thumbnails container into the c#ontent container in fullscreen mode
- * and back to the #header container when not in fullscreen mode
- * @param Event fullscreenchange event
- */
- function onFullScreenChange(evt) {
- fullscreen = !(document.mozFullScreenElement === null || document.webkitFullscreenElement === null);
- if (fullscreen) {
- document.getElementById('content').insertBefore(document.getElementById('thumbnails'), document.getElementById('gallery'));
- } else {
- document.getElementById('header').appendChild(document.getElementById('thumbnails'));
- }
- resizeSlideshowGallery();
- }
-
-
- /* ===== Dialog Functions =====
- Rather than using boring alert() and prompt() boxes, which are finicky in Greasemonkey,
- this script creates a div, styled with CSS, to replicate that functionality.
- */
-
- /**
- * Generates the DOM elements for the dialog boxes
- */
- function buildDialog() {
- var dialog = document.getElementById('dialogBox');
- if (dialog) {
- resetDialog();
- return;
- }
-
- dialog = document.createElement('div');
- dialog.id = 'dialogBox';
- dialog.innerHTML = '<h3 id="dialogTitle"></h3><div id="dialogMessage"></div><div id="dialogButtons"></div>';
-
- var closeButton = document.createElement('a');
- closeButton.addEventListener('click', hideDialog, false);
- closeButton.id = 'dialogClose';
- closeButton.innerHTML = '✖';
- dialog.appendChild(closeButton);
-
- var dialogContainer = document.createElement('div');
- dialogContainer.addEventListener('click', function (e) {
- hideDialog(e, true);
- }, false);
- dialogContainer.id = 'dialogContainer';
- dialogContainer.style.display = 'none';
- dialogContainer.appendChild(dialog);
- body.appendChild(dialogContainer);
- }
-
- /**
- * Hides the dialog box
- * @param Event The click event
- * @param Bool (Optional) If true, will only hide the dialog if the target of the click event is the dialogContainer
- */
- function hideDialog(e, containerOnly) {
- containerOnly = (typeof containerOnly === "undefined") ? false : containerOnly;
- if (containerOnly && (typeof e !== "undefined") && (e.target.id !== 'dialogContainer'))
- return false;
-
- var dialogContainer = document.getElementById('dialogContainer');
- if (dialogContainer)
- dialogContainer.style.display = 'none';
- }
-
- /**
- * Vertically centers the dialog box on the screen
- */
- function positionDialog() {
- var dialogBox = document.getElementById('dialogBox');
- var dialogContainer = document.getElementById('dialogContainer');
-
- dialogContainer.style.display = 'block';
- var top = ((window.innerHeight / 2) - (dialogBox.offsetHeight / 2) - 20);
- dialogBox.style.top = (top < 0) ? '0' : top + 'px';
-
- dialogContainer.scrollTop = 0;
- }
-
- /**
- * Clears out the contents of the dialog box
- */
- function resetDialog() {
- document.getElementById('dialogTitle').innerHTML = '';
- document.getElementById('dialogMessage').innerHTML = '';
- document.getElementById('dialogButtons').innerHTML = '';
- }
-
- /**
- * Preloads the next image in slideshow mode
- * Will be called continuously until the last image is loaded
- * @param String The HTML content of the dialog box
- * @param String (Optional) A title for the dialog box (always prefixed with '[Script Name]')
- * @param Object (Optional) An object representing the buttons to display and their actions (default: close button)
- */
- function showDialog(content, title, buttons) {
- buttons = (typeof buttons === "undefined") ? {0: {text: 'Close', action: hideDialog}} : buttons;
- title = (typeof title === "undefined") ? '' : title;
-
- buildDialog();
-
- var dialogContainer = document.getElementById('dialogContainer');
- var titleContainer = document.getElementById('dialogTitle');
- var messageContainer = document.getElementById('dialogMessage');
- var buttonContainer = document.getElementById('dialogButtons');
-
- titleContainer.innerHTML = '[<b>' + GM_info.script.name + '</b>] ' + title;
- messageContainer.innerHTML = content;
-
- var btn;
- for (var button in buttons) {
- btn = document.createElement('button');
- btn.addEventListener('click', buttons[button].action, false);
- btn.innerHTML = buttons[button].text;
- if (button == 0)
- btn.className = 'default';
- buttonContainer.appendChild(btn);
- }
-
- positionDialog();
-
- buttonContainer.childNodes[0].focus();
- }
-
-
- /* ===== Notication Message Functions =====
- The are notification messages that display in the lower right corner and are
- automatically removed after a short duration without needing user interaction.
- */
-
- /**
- * Generates and displays a notification message
- * @param String The notification message text
- * @param Int (Optional) The delay (in milliseconds) before the message should be hidden (default: 5000)
- * @param Int (Optional) The duration (in milliseconds) over which the message should fade (default: 100)
- * @return Object The DOM object of the created notification
- */
- function showNotification(text, delay, duration) {
- delay = (typeof delay === "undefined") ? 5000 : delay;
-
- var notificationContainer = document.getElementById('notificationContainer');
- if (!notificationContainer) {
- notificationContainer = document.createElement('div');
- notificationContainer.id = 'notificationContainer';
- document.getElementById('content').appendChild(notificationContainer);
- }
- notificationContainer.style.bottom = (document.getElementById('footer').offsetHeight + 10) + 'px';
-
- var notifications = notificationContainer.getElementsByClassName('notification');
-
- var notification = document.createElement('div');
- notification.className = 'notification';
- notification.id = 'notification' + notifications.length;
- notification.innerHTML = text;
-
- if (notifications.length > 0) {
- notificationContainer.insertBefore(notification, notifications[0]);
- } else {
- notificationContainer.appendChild(notification);
- }
-
- if (delay > 0)
- setTimeout(function () {
- hideNotification(notification, duration);
- }, delay);
-
- return notification;
- }
-
- /**
- * Fades a notification message out and then removes it
- * @param Object The notification message to hide
- * @param Int (Optional) The duration (in milliseconds) over which the message should fade (default: 100)
- */
- function hideNotification(notification, duration) {
- duration = (typeof duration === "undefined") ? 100 : duration;
-
- if (!notification)
- return false;
-
- notification.style.opacity = 1;
- var timer = setInterval(function () {
- notification.style.opacity -= 0.1
- if (notification.style.opacity <= 0) {
- notification.parentNode.removeChild(notification);
- clearInterval(timer);
- }
- }, duration);
- }
-
-
- /* ===== Setting Functions =====
- Functions used for working with the individual settings in bulk
- */
-
- /**
- * Saves the values for all settings from the Settings Form Dialog Box
- * Verifies values are valid based on criteria of the individual settings (min, max, etc.)
- */
- function applySettings() {
- var setting, field, value;
- for (s in settings) {
- setting = settings[s];
- if (setting && (typeof setting === 'object')) {
- value = null;
- field = document.getElementById('setting-' + s);
- if (field) {
- switch (setting.type) {
- case 'integer':
- value = parseInt(field.value, 10);
- if (isNaN(value)) {
- var buttons = {0: {text: 'Try Again', action: changeSettings}};
- showDialog('<p>A numeric value must be provided for <b>' + setting.label + '</b>.</p>', 'Error', buttons);
- return false;
- }
- if ((typeof setting.min !== 'undefined') && (setting.min !== null) && (value < setting.min)) {
- var buttons = {0: {text: 'Try Again', action: changeSettings}};
- showDialog('<p>The value provided (<span class="error">' + value + '</span>) for <b>' + setting.label + '</b> must be greater than or equal to <b>' + setting.min + '</b>.</p>', 'Error', buttons);
- return false;
- }
- if ((typeof setting.max !== 'undefined') && (setting.max !== null) && (value > setting.max)) {
- var buttons = {0: {text: 'Try Again', action: changeSettings}};
- showDialog('<p>The value provided for (<span class="error">' + value + '</span>) <b>' + setting.label + '</b> must be less than or equal to <b>' + setting.max + '</b>.</p>', 'Error', buttons);
- return false;
- }
- break;
- case 'select':
- value = field.options[field.selectedIndex].value;
- break;
- case 'boolean':
- value = field.checked;
- break;
- default:
- value = field.value;
- break;
- }
- }
- if (value !== null) {
- settings[s].value = value;
- }
- }
- }
- saveSettings();
- var buttons = {0: {text: 'Apply Changes Now', action: rebuildGalleryPage}, 1: {text: 'Close', action: hideDialog}};
- showDialog('<p>Your changes have been saved successfully!</p>', 'Success', buttons);
- }
-
- /**
- * Checks if settings have been saved for the current script version.
- * For new installs, a notice is displayed forcing the user to save the settings for the first time;
- * for updates, the user can view the settings or dismiss the notice.
- */
- function checkSettings() {
- var lastSavedVersion = GM_getValue('lastSavedVersion', false);
- if (!lastSavedVersion) {
- var buttons = {0: {text: 'Continue', action: changeSettings}};
- showDialog('<p>Since this is your first time using ' + GM_info.script.name + ', you need to view (and modify) the settings to meet your needs.</p><p>Note that all settings have a default value set already for your convenience; you can simply click the "Save Settings" button on the next dialog box to continue.', 'First Run', buttons);
- } else if (lastSavedVersion !== GM_info.script.version) {
- GM_setValue('lastSavedVersion', GM_info.script.version);
- var buttons = {0: {text: 'Change Settings', action: changeSettings}, 1: {text: 'Close', action: hideDialog}};
- showDialog('<p>The version of this script has changed from ' + lastSavedVersion + ' to ' + GM_info.script.version + '. There may be new settings for you to utilize.', 'Version Change', buttons);
- }
- }
-
- /**
- * Initializes all settings from saved preferences
- * Uses the default values for settings that have not yet been saved
- */
- function initSettings() {
- for (s in settings) {
- initSetting(s)
- }
- }
-
- /**
- * Initializes the specified setting from saved preferences
- * Uses the default value for settings that have not yet been saved
- * @param String name (key) of the setting to initialize
- */
- function initSetting(s) {
- if (settings[s] && (typeof settings[s] === 'object')) {
- if (settings[s].name)
- settings[s].value = GM_getValue(settings[s].name, settings[s].def);
-
- if (settings[s].type === 'integer')
- settings[s].value = parseInt(settings[s].value, 10);
- else if ((settings[s].type === 'select') && (settings[s].opts.indexOf(settings[s].value) === -1))
- settings[s].value = settings[s].def;
- }
- }
-
- /**
- * Generates the HTML for the Settings Form that will be displayed via dialog box
- */
- function changeSettings() {
- initSettings();
- var setting;
- var html = '<form id="settingsForm">';
- for (s in settings) {
- setting = settings[s];
- if (setting && (typeof setting === 'object')) {
- html += '<fieldset><legend>' + setting.label + '</legend>';
- switch (setting.type) {
- case 'integer':
- case 'text':
- html += '<label for="setting-' + s + '">Enter a value for the ' + setting.label;
- if (setting.hint)
- html += ' (' + setting.hint + ')';
- html += ':<br/><span class="default">Default value: <b>' + setting.def + '</b></span></label>';
- html += '<input type="text" id="setting-' + s + '" name="' + s + '" value="' + setting.value + '" size="' + setting.size + '" maxlength="' + setting.size + '"/>';
- break;
- case 'select':
- html += '<label for="setting-' + s + '">Select a value for the ' + setting.label;
- if (setting.hint)
- html += ' (' + setting.hint + ')';
- html += ':<br/><span class="default">Default value: <b>' + capitalize(setting.def) + '</b></span></label>';
- html += '<select id="setting-' + s + '" name="' + s + '">';
- for (opt in setting.opts) {
- html += '<option value="' + setting.opts[opt] + '"' + ((setting.value === setting.opts[opt]) ? ' selected="selected"' : '') + '>' + capitalize(setting.opts[opt]) + '</option>';
- }
- html += '</select>';
- break;
- case 'boolean':
- html += '<input type="checkbox" id="setting-' + s + '" name="' + s + '" value="true"' + ((setting.value) ? ' checked="checked"' : '') + '/>';
- html += '<label for="setting-' + s + '">Enable ' + setting.label;
- if (setting.hint)
- html += ' (' + setting.hint + ')';
- html += '<br/><span class="default">Default value: <b>' + ((setting.def) ? 'Enabled' : 'Disabled') + '</b></span></label>';
- break;
- }
- html += '</fieldset>';
- }
- }
- showDialog(html, 'Settings', {
- 0: {text: 'Save Settings', action: applySettings},
- 1: {text: 'Cancel', action: hideDialog}
- });
- }
-
- /**
- * Saves the current value for each setting to local storage
- */
- function saveSettings() {
- var setting;
- for (s in settings) {
- setting = settings[s];
- if (setting && (typeof setting === 'object'))
- GM_setValue(setting.name, setting.value);
- }
- initSettings();
- GM_setValue('lastSavedVersion', GM_info.script.version);
- }
-
-
- /* ===== String Functions =====
- Utility functions for manipulating strings
- */
-
- /**
- * Escapes quotes and double-quotes in a string
- * @param String The string to escape
- * @return String The escaped string
- */
- function addslashes(str) {
- return str.replace(/\\/g, '\\\\').replace(/\'/g, '\\\'').replace(/\"/g, '\\"').replace(/\0/g, '\\0');
- }
-
- /**
- * Unescapes quotes and double-quotes in a string
- * @param String The escaped string
- * @return String The string to escape
- */
- function stripslashes(str) {
- return str.replace(/\\'/g, '\'').replace(/\\"/g, '"').replace(/\\0/g, '\0').replace(/\\\\/g, '\\');
- }
-
- /**
- * Removes leading and trailing whitepsace from a string
- * @param String The string to trim
- * @return String The trimmed string
- */
- function trim(str) {
- return str.replace(/^\s+|\s+$/g, '');
- }
-
- /**
- * Capitalizes the first character of a string
- * @param String The string to capitalize
- * @return String The capitalized string
- */
- function capitalize(str) {
- return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
- }
-
-
- /* ===== Hash Parameter Functions =====
- Utility functions for setting and retrieving data from the URL location hash
- */
-
- /**
- * Returns all key/value pairs stored in the location hash
- * @return Object The location hash parameters indexed by key name
- */
- function getHashParams() {
- var params = window.location.hash.replace('#!', '').split('/');
- var ret = [];
- for (var i = 0; i < params.length; i = i + 2) {
- if (params[(i + 1)] && params[(i + 2)])
- ret[params[(i + 1)]] = params[(i + 2)];
- }
- return ret;
- }
-
- /**
- * Returns the value of the supplied hash key
- * @param String The name of the hash key
- * @return String The value of the hash key
- */
- function getHashParam(key) {
- var params = getHashParams();
- return params[key] || undefined;
- }
-
- /**
- * Sets the value of the supplied hash key
- * @param String The name of the hash key
- * @param String The value of the hash key
- */
- function setHashParam(key, val) {
- var hashString = '#!';
- var params = getHashParams();
- params[key] = val;
- for (key in params) {
- hashString += '/' + key + '/' + params[key];
- }
- history.replaceState(null, null, hashString);
- }
-
- /**
- * Removes a hash key/value pair from the location hash
- * @param String The name of the hash key
- */
- function unsetHashParam(key) {
- var current = getHashParam(key);
- if (typeof current !== 'undefined')
- history.replaceState(null, null, window.location.hash.replace('/' + key + '/' + current, ''));
- }
-
-
- /* ===== Gallery CSS =====
- The CSS for the rebuilt gallery page with theme support
- */
-
- /**
- * Generates the CSS used for the rebuilt gallery page
- * @param String (Optional) Additional CSS
- * @return String The CSS for the rebuilt gallery page
- */
- function getCSS(css) {
- css = (typeof css === "undefined") ? '' : css;
-
- initSettings('theme');
- switch (getHashParam('theme') || settings.theme.value) {
- case 'blue':
- var bg1 = '#060D1A';
- var bg2 = '#03060D';
- var fg1 = '#557799';
- var fg2 = '#557799';
- var links = '#AABBCC';
- var accent1 = '#6699CC';
- var accent2 = '#6699CC';
- break;
-
- case 'classic':
- var bg1 = '#FFFFFF';
- var bg2 = '#3366CC';
- var fg1 = '#666666';
- var fg2 = '#AACCEE';
- var links = '#AACCEE';
- var accent1 = '#3366CC';
- var accent2 = '#FFFFFF';
- break;
-
- case 'green':
- var bg1 = '#FFFFFF';
- var bg2 = '#222222';
- var fg1 = '#888888';
- var fg2 = '#888888';
- var links = '#AAAAAA';
- var accent1 = '#33AA00';
- var accent2 = '#66CC33';
- break;
-
- case 'default':
- default:
- var bg1 = '#222222';
- var bg2 = '#111111';
- var fg1 = '#888888';
- var fg2 = '#888888';
- var links = '#AAAAAA';
- var accent1 = '#3380CC';
- var accent2 = '#3380CC';
- break;
- }
-
- /**
- * Darkens a color by a specified amount
- * @param String The RGB hex color code
- * @param Int The amount (decimal-format pertentage) by which to darken the color
- * @return String The color code for the darkened color
- */
- function darken(color, amount) {
- color = splitColor(color);
- var ret = [];
- for (var i = 0; i < color.length; i++) {
- ret[i] = (color[i] - Math.ceil(255 * amount));
-
- if (ret[i] < 0)
- ret[i] = 0;
- if (ret[i] > 255)
- ret[i] = 255;
-
- ret[i] = ret[i].toString(16);
- if (ret[i].length < 2)
- ret[i] = '0' + ret[i];
- }
- return '#' + ret.join('').toUpperCase();
- }
-
- /**
- * Lightens a color by a specified amount
- * @param String The RGB hex color code
- * @param Int The amount (decimal-format pertentage) by which to lighten the color
- * @return String The color code for the lightened color
- */
- function lighten(color, amount) {
- color = splitColor(color);
- var ret = [];
- for (var i = 0; i < color.length; i++) {
- ret[i] = (color[i] + Math.ceil(255 * amount));
-
- if (ret[i] < 0)
- ret[i] = 0;
- if (ret[i] > 255)
- ret[i] = 255;
-
- ret[i] = ret[i].toString(16);
- if (ret[i].length < 2)
- ret[i] = '0' + ret[i];
- }
- return '#' + ret.join('').toUpperCase();
- }
-
- /**
- * Converts a color code into a usable array for math-based functions
- * @param String The RGB hex color code
- * @return Int[] The array containing the decimal values of each color
- */
- function splitColor(color) {
- color = color.replace('#', '');
-
- var offset = Math.floor(color.length / 3);
- var ret = [];
- for (var i = 0; i < color.length; i += offset) {
- ret.push(parseInt(color.substring(i, (i + offset)), 16));
- }
- return ret;
- }
-
- /**
- * Returns the CSS for a vertical background gradient (with vendor-specific prefixes)
- * @param String The RGB hex color code of the top color
- * @param String The RGB hex color code of the bottom color
- * @return String The CSS for the background gradient
- */
- function gradient(top, bottom) {
- var ret = '';
- ret += 'background: -moz-linear-gradient(top, ' + top + ' 0%, ' + bottom + ' 100%);';
- ret += 'background: -webkit-linear-gradient(top, ' + top + ' 0%, ' + bottom + ' 100%);';
- return ret;
- }
-
- /**
- * Returns the CSS for rounded corners (with vendor-specific prefixes)
- * @param String The radius value (syntax: '#px' or '#px #px #px #px')
- * @return String The CSS for the rounded corners
- */
- function borderRadius(radius) {
- radius = 'border-radius: ' + radius;
- // returns: -moz-border-radius: <radius>; -webkit-border-radius: <radius>; border-radius: <radius>;
- return '-moz-' + radius + '; ' + '-webkit-' + radius + '; ' + radius + ';';
- }
-
- /**
- * Returns the CSS for box shadows (with vendor-specific prefixes)
- * @param String The shadow value (syntax: '#px #px [#px] [#px] color [inset]')
- * @return String The CSS for the box shadows
- */
- function boxShadow(shadow) {
- shadow = 'box-shadow: ' + shadow;
- // returns: -moz-box-shadow: <shadow>; -webkit-box-shadow: <shadow>; box-shadow: <shadow>;
- return '-moz-' + shadow + '; ' + '-webkit-' + shadow + '; ' + shadow + ';';
- }
-
- // basics
- css += '* { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; margin: 0; padding: 0; }';
- css += 'body { background-color: ' + bg1 + '; color: ' + fg1 + '; font: 13px Helvetica, Arial, sans-serif; }';
- css += 'a { color: ' + links + '; cursor: pointer; text-decoration: underline; }';
- css += 'a:hover, a:hover { color: ' + lighten(links, 0.133) + '; text-decoration: none; }';
- css += 'p { margin: 0 0 10px; }';
- css += 'table { font: 13px Helvetica, Arial, sans-serif; margin: auto; width: 100%; }';
-
- // layout
- css += '#header { background-color: ' + bg2 + '; border-bottom: 1px solid ' + lighten(bg1, 0.066) + '; color: ' + fg2 + '; padding: 10px 0 0; text-align: center; }';
- css += '.scrolling #header { margin-bottom: 10px; }';
- css += '.slideshow #header { min-height: 60px; position: fixed; top: 0; width: 100%; z-index: 2; }';
- css += '#header .title { color: ' + accent2 + '; }';
- css += '#header small { font-variant: small-caps; }';
- css += '#header p { margin: 10px 0; }';
- css += '#header #description { margin: 10px auto; max-width: 60%; text-align: center; }';
- css += '#search { position: absolute; right: 10px; top: 10px; }';
- css += '#footer { background-color: ' + bg2 + '; border-top: 1px solid ' + lighten(bg1, 0.066) + '; color: ' + fg2 + '; min-height: 60px; padding: 10px 0; position: relative; text-align: center; }';
- css += '.scrolling #footer { margin-top: 10px; position: fixed; bottom: 0; left: 0; right: 0; }';
- css += '.slideshow #footer { bottom: 0; height: 60px; position: fixed; width: 100%; z-index: 2; }';
- css += '#favorites_container { height: 40px; line-height: 40px; margin-bottom: 0 !important; position: absolute; left: 25px; bottom: 10px; }';
- css += '#autoplay { height: 20px; line-height: 20px; position: absolute; right: 25px; bottom: 20px; }';
- css += '#info { font-size: 11px; margin: 10px 0 0; }';
-
- // logo
- css += '#logo { text-decoration: none; position: absolute; left: 10px; top: 10px; }';
- css += '#logo span { font: 18px "Comic Sans MS"; padding: 0 2px; }';
- css += '#logo .image { background-color: ' + bg1 + '; color: ' + accent1 + '; }';
- css += '#logo:hover .image { background-color: ' + accent1 + '; color: ' + bg1 + '; }';
- css += '#logo .fap { background-color: ' + accent1 + '; color: ' + bg1 + '; }';
- css += '#logo:hover .fap { background-color: ' + bg1 + '; color: ' + accent1 + '; }';
-
- // forms
- css += 'form { margin: 0 0 10px; }';
- css += 'input[type="text"], select { background: ' + bg1 + '; border: 1px solid ' + fg1 + '; color: ' + fg1 + '; margin-right: 5px; padding: 5px; }';
- css += 'input[type="text"]:focus, select:focus { border-color: ' + accent1 + '; color: ' + fg1 + '; }';
- css += 'button, input[type="button"], input[type="submit"] { background: ' + lighten(bg2, 0.066) + '; ' + gradient(lighten(bg2, 0.133), lighten(bg2, 0.066)) + '; border: 1px solid ' + lighten(bg2, 0.200) + '; ' + borderRadius('5px') + ' color: ' + links + '; cursor: pointer; margin-left: 5px; padding: 5px; ' + boxShadow('0 0 0 1px ' + bg2) + '; }';
- css += 'button:hover, input[type="button"]:hover, input[type="submit"]:hover { ' + gradient(lighten(bg2, 0.200), lighten(bg2, 0.066)) + '; border: 1px solid ' + lighten(bg2, 0.266) + '; color: ' + lighten(links, 0.066) + '; }';
- css += 'button:focus, input[type="button"]:focus, input[type="submit"]:focus { ' + gradient(lighten(bg2, 0.266), lighten(bg2, 0.066)) + '; border: 2px solid ' + lighten(bg2, 0.333) + '; color: ' + lighten(links, 0.133) + '; padding: 4px; }';
-
- css += '#favorites_container input[type="button"], button.default, input[type="submit"] { border: 1px solid ' + accent1 + '; color: ' + lighten(links, 0.133) + '; }';
- css += '#favorites_container input[type="button"]:hover, button.default:hover, input[type="submit"]:hover { border: 1px solid ' + lighten(accent1, 0.066) + '; color: ' + lighten(links, 0.200) + '; }';
- css += '#favorites_container input[type="button"]:focus, button.default:focus, input[type="submit"]:focus { border: 2px solid ' + lighten(accent1, 0.066) + '; color: ' + lighten(links, 0.266) + '; padding: 4px; }';
-
- // pagination
- css += '.pagination { margin: 0; }';
- css += '.pagination a { border: 1px solid ' + darken(links, 0.133) + '; color: ' + darken(links, 0.133) + '; display: inline-block; margin: 0 2px 10px; padding: 2px 6px; text-decoration: none; }';
- css += '.pagination a:hover { border-color: ' + links + '; color: ' + links + '; display: inline-block; margin: 0 2px; padding: 2px 6px; text-decoration: none; }';
- css += '.pagination a.current { border: 1px solid ' + accent2 + '; color: ' + accent2 + '; }';
- css += '.pagination a.disabled { border: 1px solid ' + darken(fg2, 0.266) + '; color: ' + darken(fg2, 0.133) + '; cursor: default; display: inline-block; margin: 0 2px; padding: 2px 6px; }';
-
- // thumbnails
- css += '#thumbnails { margin-bottom: 10px; text-align: center; z-index: 2; }';
- css += '#thumbnails .thumbnail { border: 1px solid ' + darken(links, 0.133) + '; display: inline-block; margin: 2px; padding: 4px; vertical-align: middle; }';
- css += '#thumbnails .thumbnail:hover { border-color: ' + links + '; }';
- css += '#thumbnails .thumbnail.active { border: 2px solid ' + accent2 + '; padding: 3px; }';
- css += '#thumbnails.small img { max-height: 100px; }';
- css += '#thumbnails.medium img { max-height: 150px; }';
- css += '#thumbnails.large img { max-height: 200px; }';
- css += '.slideshow #thumbnails { background-color: ' + bg2 + '; padding-bottom: 10px; overflow-y: hidden; width: 100%; white-space: nowrap; }';
- css += '.slideshow #thumbnails .thumbnail { margin: 0 5px; }';
-
- // gallery basics
- css += '#gallery { position: relative; text-align: center; }';
- css += '#gallery .image img { border: 1px solid ' + darken(links, 0.133) + '; padding: 4px; min-height: 100px; min-width: 100px; }';
- css += '#gallery .image:hover img { border-color: ' + accent1 + '; }';
- css += '#gallery .image .spinner { border: 10px solid ' + darken(fg2, 0.266) + '; border-left-color: ' + accent1 + '; position: absolute; left: calc(100% / 2 - 80px / 2); top: calc(100% / 2 - 80px / 2); -webkit-animation: spinning 1s infinite linear; animation: spinning 1s infinite linear; }';
- css += '#gallery .image .spinner, #gallery .image .spinner:after { ' + borderRadius('50%') + '; width: 80px; height: 80px; z-index: -1; }';
- css += '@-webkit-keyframes spinning { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }';
- css += '@keyframes spinning { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } }';
-
- // scrolling gallery
- css += '.scrolling #gallery { max-width: 100%; }';
- css += '.scrolling #gallery .image { clear: both; display: inline-block; max-width: 98%; position: relative; }';
- css += '.scrolling #gallery .image img { display: inline-block; max-width: 100%; }';
-
- css += '#loader { background: ' + lighten(bg2, 0.066) + '; ' + gradient(lighten(bg2, 0.133), lighten(bg2, 0.066)) + '; border: 1px solid ' + accent1 + '; border-radius: 5px; ' + boxShadow('0 0 0 1px ' + bg2) + '; color: ' + lighten(links, 0.133) + '; display: inline-block; margin-top: 10px; padding: 8px 16px; text-decoration: none; }';
- css += '#loader:hover { ' + gradient(lighten(bg2, 0.200), lighten(bg2, 0.066)) + '; border-color: ' + lighten(accent1, 0.066) + '; color: ' + lighten(links, 0.200) + '; }';
-
- // slideshow gallery
- css += '.slideshow #content { bottom: 81px; left: 0; padding: 10px; position: absolute; right: 0; top: 81px; }';
- css += '.slideshow #gallery { height: 100%; width: 100%; overflow: hidden; }';
- css += '.slideshow #gallery .image img { bottom: 0; left: 0; margin: auto; max-height: 100%; max-width: 100%; position: absolute; right: 0; top: 0; }';
- css += '.slideshow #gallery .nav { color: #FFFFFF; display: block; text-decoration: none; opacity: 0.5; position: absolute; top: 5px; bottom: 5px; height: 100%; width: 160px; z-index: 1; }';
- css += '.slideshow #gallery .nav.disabled { display: none; }';
- css += '.slideshow #gallery #next { min-height: 60px; min-width: 60px; right: 5px; }';
- css += '.slideshow #gallery #prev { min-height: 60px; min-width: 60px; left: 5px; }';
- css += '.slideshow #gallery .arrow { display: block; font-size: 120px; height: 160px; line-height: 160px; margin-top: -80px; position: relative; text-align: center; text-shadow: 0 0 5px #000000, 0 0 20px #FFFFFF; top: 50%; }';
- css += '.slideshow #gallery .nav:hover { background-color: rgba(0,0,0,0.5); opacity: 1.0; }';
- css += '.slideshow #gallery #indicator { background-color: rgba(0,0,0,0.5); ' + borderRadius('40px') + '; display: block; position: absolute; top: 50%; left: 50%; height: 160px; margin-top: -80px; margin-left: -80px; width: 160px; z-index: 1; }';
- css += '.slideshow #gallery #indicator .symbol { color: #FFFFFF; font-size: 120px; position: relative; text-align: center; text-shadow: 0 0 5px #000000, 0 0 20px #FFFFFF; }';
- css += '.slideshow #gallery #indicator .symbol.pause { font-weight: bold; }';
- css += '.slideshow #gallery #indicator .symbol.play { line-height: 160px; }';
-
- // full screen:: WebKit
- css += ':-webkit-full-screen { background-color: #000000; }';
- css += '#content:-webkit-full-screen { padding: 0; top: 0 !important; bottom: 0 !important; height: 100%; width: 100%; }';
- css += '#content:-webkit-full-screen .nav { ' + borderRadius('40px') + '; height: 160px; margin-top: -80px; top: 50%; }';
- css += '#content:-webkit-full-screen .image img { border: 0; padding: 0; }';
- css += '#content:-webkit-full-screen #thumbnails { background-color: #000000; position: absolute; top: 0; }';
- // full screen:: Mozilla
- css += '#content:-moz-full-screen { padding: 0; top: 0 !important; bottom: 0 !important; height: 100%; width: 100%; }';
- css += '#content:-moz-full-screen .nav { ' + borderRadius('40px') + '; height: 160px; margin-top: -80px; top: 50%; }';
- css += '#content:-moz-full-screen .image img { border: 0; padding: 0; }';
- css += '#content:-moz-full-screen #thumbnails { background-color: #000000; position: absolute; top: 0; }';
-
- // add to favorites
- css += '#favorites_container table { width: auto; }';
-
- // notification messages
- css += '#notificationContainer { bottom: 70px; position: fixed; right: 10px; }';
- css += '.notification { background: rgba(0, 0, 0, 0.8); border: 1px solid rgba(255, 255, 255, 0.2); ' + borderRadius('5px') + '; color: rgba(255, 255, 255, 0.5); display: block; font-size: 11px; margin-top: 10px; padding: 9px; }';
-
- // dialog box layout
- css += '#dialogContainer { background: rgba(0,0,0,0.8); display: block; text-align: center; position: fixed; top: 0; bottom: 0; left: 0; right: 0; z-index: 3; overflow-y: auto; }';
- css += '#dialogBox { background: ' + bg1 + '; border: 1px solid ' + lighten(bg2, 0.200) + '; ' + boxShadow('0 0 20px 0 #000000') + '; color: ' + fg1 + '; display: inline-block; margin: 20px auto; min-width: 300px; text-align: left; position: relative; z-index: 10; }';
- css += '#dialogClose { border-left: 1px solid ' + lighten(bg2, 0.200) + '; color: ' + links + '; font-size: 14px; line-height: 28px; position: absolute; right: 0; text-align: center; text-decoration: none; top: 0; width: 30px; }';
- css += '#dialogClose:hover { color: ' + lighten(links, 0.200) + '; }';
- css += '#dialogTitle { background: ' + bg1 + '; ' + gradient(lighten(bg2, 0.066), bg2) + '; border-bottom: 1px solid ' + lighten(bg2, 0.200) + '; color: ' + links + '; display: block; margin: 0; padding: 5px 10px; }';
- css += '#dialogTitle b { color: ' + accent2 + '; }';
- css += '#dialogMessage { display: block; padding: 10px; }';
- css += '#dialogButtons { clear: both; display: block; padding: 10px; text-align: right; }';
-
- // dialog box content
- css += '#dialogMessage table { margin: 0; }';
- css += '#dialogMessage table th { color: ' + accent1 + '; font-size: 14px; text-align: left; }';
- css += '#dialogMessage table b { color: ' + lighten(accent1, 0.066) + '; }';
- css += '#dialogMessage table td.name { padding-right: 5%; text-align: right; width: 20%; }';
- css += '#dialogMessage table td.key { text-align: center; width: 25%; }';
-
- // dialog box form elements
- css += '#dialogBox #settingsForm { width: 820px }';
- css += '#dialogBox .error { color: #C43131; font-weight: bold; }';
- css += '#dialogBox button { margin-right: 5px; }';
- css += '#dialogBox fieldset { border: 0; border-bottom: 1px solid ' + lighten(bg2, 0.200) + '; margin: 0 0 15px; padding-bottom: 10px; min-width: 400px; }';
- css += '#dialogBox fieldset:nth-child(1n) { float: left; }';
- css += '#dialogBox fieldset:nth-child(2n) { float: right; }';
- css += '#dialogBox legend { color: ' + accent1 + '; font-weight: bold; margin-bottom: 10px; }';
- css += '#dialogBox label { float: left; line-height: 20px; }';
- css += '#dialogBox label b { color: ' + accent1 + '; }';
- css += '#dialogBox label span.default { font-size: 11px; font-variant: small-caps; }';
- css += '#dialogBox input[type="text"], #dialogBox select { float: right; }';
- css += '#dialogBox input[type="checkbox"] { float: left; margin: 4px 10px 0 ; }';
-
- // insert the line breaks automatically before returning
- css = css.replace(/}/g, "}\n");
-
- css += `
- ::-webkit-scrollbar {
- width: 2px;
- height: 2px;
- background-color: transparent;
-
- }
- ::-webkit-scrollbar-button {
- width: 20px;
- height: 20px;
-
- }
-
- ::-webkit-scrollbar-thumb {
- background: rgba(255,255,255,0.6);
- opacity: 0.5;
- border: none;
- }
-
-
- ::-webkit-scrollbar-track {
- background: transparent;
-
- border-radius: 5px;
- }
-
- ::-webkit-scrollbar-corner {
- background: transparent;
- }
-
-
- #content{
- padding: 0;
- top: 0;
- bottom: 0;
-
- }
-
- img, .thumbnail, .slideshow, body, #header, #heading, #thumbnails, #logo, h2,p,form{
- border: none !important;
- padding: 0 ;
- margin: 0;
-
- }
-
- #logo{
-
- filter: grayscale(100%);
- opacity: 0.3;
- }
-
- p{
- opacity: 0.7;
- }
-
-
- #thumbnails{
- background-color: transparent;
- }
-
- a.image{
- max-height: 50px;
- }
-
- a.thumbnail{
- top: 10;
- margin-right: 1px;
- }
-
- input, button {
- border: 0px;
- color: grey ;
- }
-
-
-
- #footer {
- padding: 0;
- margin:0;
- height: 20px;
- position: fixed;
- bottom: 0;
- width:150px;
- right: 0;
- background-color: transparent;
- opacity: 0.3;
- border: 0;
-
- }
-
- #autoplay{
- background-color: rgba(0,0,0,1);
- height: 20px;
- width: 150px;
- padding: 0;
- margin: 0;
- bottom:10;
- right: 0px;
- color: rgba(255,255,255,0);
-
-
- }
-
- a#control{
- color: rgba(255,255,255,1);
- }
-
- #autoplay > #counter{
- display: none;
- }
-
- #info{
- display: none;
- }
-
- #favorites_container{
- display: none;
- }`
-
- console.log(css)
-
- return css
-
-
- }