// ==UserScript==
// @name Modified Booru Downloader + Viewer (Fixed for rule34.xxx as of February 2022)
// @description The original fullsize images downloader, and viewer for more than 20 booru imageboards
// @namespace https://greasyfork.org/users/155308
// @author se7en
// @version 1.1.0.modified
// @license MIT
// -------- INCLUDE
// @include *://gelbooru.com/*
// @include *://rule34.xxx/*
// @include *://yande.re/*
// @include *://*.donmai.us/*
// @include *://*.sankakucomplex.com/*
// @include *://behoimi.org/*
// @include *://youhate.us/*
// @include *://safebooru.org/*
// @include *://uberbooru.com/*
// @include *://bronibooru.com/*
// @include *://www.bronibooru.com/*
// @include *://mspabooru.com/*
// @include *://e926.net/*
// @include *://e621.net/*
// @include *://*.booru.org/*
// @include *://atfbooru.ninja/*
// @include *://lolibooru.moe/*
// @include *://hypnohub.net/*
// @include *://tbib.org/*
// @include *://konachan.net/*
// @include *://konachan.com/*
// @include *://rule34.paheal.net/*
// -------- EXCLUDE
// @exclude *://simg3.gelbooru.com*//images/*
// @exclude *://img.rule34.xxx*//images/*
// @exclude *://files.yande.re*/images/*
// @exclude *://files.yande.re*/jpeg/*
// @exclude *://*.donmai.us*/data/*
// @exclude *://*s.sankakucomplex.com*/data/*
// @exclude *://behoimi.org*/data/*
// @exclude *://safebooru.org*//images/*
// @exclude *://uberbooru.com*/data/*
// @exclude *://s3.amazonaws.com*/bronibooru/*
// @exclude *://mspabooru.com*//images/*
// @exclude *://static1.e926.net*/data/*
// @exclude *://static1.e621.net*/data/*
// @exclude *://img.booru.org*/*//images/*
// @exclude *://atfbooru.ninja*/data/*
// @exclude *://lolibooru.moe*/image/*
// @exclude *://hypnohub.net*//data/image/*
// @exclude *://tbib.org*//images/*
// @exclude *://konachan.net*/images/*
// @exclude *://konachan.net*/jpeg/*
// @exclude *://konachan.com*/images/*
// @exclude *://konachan.com*/jpeg/*
// @exclude *://*.paheal.net*/_images/*
// -------- CONNECT
// @connect gelbooru.com
// @connect rule34.xxx
// @connect yande.re
// @connect donmai.us
// @connect sankakucomplex.com
// @connect behoimi.org
// @connect safebooru.org
// @connect uberbooru.com
// @connect s3.amazonaws.com
// @connect bronibooru.com
// @connect mspabooru.org
// @connect e926.net
// @connect e621.net
// @connect booru.org
// @connect atfbooru.ninja
// @connect lolibooru.moe
// @connect hypnohub.net
// @connect tbib.org
// @connect konachan.net
// @connect konachan.com
// @connect paheal.net
// -------- GREASEMONKEY API
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_listValues
// @grant GM_deleteValue
// @grant GM_download
// @grant GM_info
// ------ GREASEMONKEY 4.0+ COMPATIBILITY
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @grant GM.xmlHttpRequest
// @grant GM.setValue
// @grant GM.getValue
// @grant GM.listValues
// @grant GM.deleteValue
// @grant GM.download
// @grant GM.info
// ==/UserScript==
/*
1.1.0.modified
+ new fork created due to Se7en no longer maintaining the script<br>
* (line 2873) fix DOMException: The play() request was interrupted <a href="https://developers.google.com/web/updates/2017/06/play-request-was-interrupted">(Fix detailed in this article)</a><br>
- (line 3956) kinda broke the viewer by forcing it to fit the image to the window, idk how to change it back<br>
1.1.0
+ added "close" button to viewer's navigation bar
* fix userOptions loader (TamperMonkey bug?)
* fix redirect on gelbooru's favorite pages
* disable video preloader
1.0.6
* fix viewer colors on safebooru.org
* fix url match on donmai.us post page
* fix sankaku viewer
1.0.4
* fix 'ignoredTags' option: trimmed them
1.0.3
* fix include's list
1.0.2
* fix cofiguration data on tampermonkey
1.0.1
* fix preloader, it fell into infinite recursion, when total amount of images was equal to 1
1.0.0
+ added tabs to user menu:
+ General: autoRun, createViewer, downloadJPEG, animateProgress
+ Filename: maxTagsInName, tagsOrder, ignoredTags, tagsDelim, addImgBrdName, prefixedName, imgIdAtNameEnd
+ Viewer: viewOriginal, viewJPEG, viewFirst, holdCtrl, fixedThumbs, fixedTags
+ new user options:
+ ignored tag names [''], in the Filename tab:
* underscored tag names, that are not included into file name
+ fixed thumb/tag list [false]/[true], in the Viewer tab
* if false, then fade out the thumb/tag list, when mouse is out, and fade in, when mouse is over them, otherwise fixe them
+ view original images [false], in the Viewer tab:
* the old option called viewSample is replaced by the viewOriginal keeping backward compatibility
+ show progress/status bar [true], in the Viewer tab
+ auto hide the viewer's navigation bar
+ auto focus on the viewer's thumb/tag list when the mouse cursor is over them, thus, making much easier to start scrolling them
+ simple image preloader
* change the default value of the 'holdCtrl' option to [false]
* horizontally, and vertically centered images on the viewer window
* set controls, and loop attributes on the video elements
* restore browser history URL after viewer exit (older versions remain history URL unchanged after manipulations)
* this is sankaku hack: simply replaces the current history URL by the image's post URL to enable the valid image loading
* hide main page scroll bar when viewer activated
* fixed viewer's 1st image thumbnail (post image thumb) on post page
* little refactoring (for further changes)
0.7.0
+ compatibility with Greasemonkey 4.0+
0.6.0
+ advanced viewer:
+ tag list on right sidebar
+ thumbs on left sidebar
* removed user options: maxWidth, maxHeight
0.5.0
+ image status/progress bar
+ user option:
+ animate initialization/downloading progress [true]
* fix main button events
0.4.2
+ hotkey:
+ 'Shift+A' - download all available images
* fix image source getter
- known issues:
- 'Download Mode', and 'Download All' buttons don't work on post page of rule34.xxx, use hotkeys instead
0.4.1
* fix exclude-list typo
* fix konachan jpeg images extension
0.4.0
+ supported imageboards:
+ atfbooru
+ lolibooru
+ hypnohub
+ tbib
+ konachan
+ paheal
* change name 'rule34' to 'rule34.xxx'
* fix bug on post page due to an empty viewer div
0.3.2
+ user option:
+ tag-types order in file name ['character', 'copyright', 'artist', 'species', 'model', 'idol', 'photo_set', 'circle', 'medium', 'metadata', 'general', 'faults']
0.3.1
+ user options:
+ hold Ctrl key to left/right navigate when viewing [true]
+ maximum width of image, px [1000]
+ maximum height of image, px [700]
* little changes
0.3.0
+ simple image viewer
+ user options:
+ create image viewer - [true]
+ view image sample - [true]
+ view jpeg image (yande.re option) - [false]
+ view 1st image on viewer activation - [true]
+ hotkeys:
+ 'Shift+V' - switch viewer on/off
+ 'Ctrl+left/right' arrows - view previous/next image
+ viewer buttons:
+ Prev
+ Source - open image file in a new tab
+ Number - index of the current image
+ Download
+ Next
+ @connect meta-data (to silence tampermonkey)
* fix wrong image hostname for uberbooru
0.2.7
* scrollable content of user menu window
* user menu window's size fitted to client's size
* move user menu 'close' button to the top right of the menu window (x sign)
* other little change
0.2.5
* fix typos
0.2.4
+ user option:
+ Image ID, and ImageBoard name at the end of the file name [true]
+ dynamically rename images on user options change
0.2.3
* fix image extensions on tampermonkey
0.2.2
* bugfixes
* little changes
0.2.0
+ image downloader for imageboards:
+ youhate.us
+ safebooru
+ uberbooru
+ bronibooru
+ mspabooru
+ e926/e621
+ *.booru.org
+ user option:
+ prefixed imageboard name [false]
0.1.1
+ user option:
+ auto initialize the script [true]
+ hotkey:
+ 'Shift+M' - open/close user menu dialog
* little changes
0.1.0
+ user menu
0.0.13
* refactoring
* fix button events
0.0.10
+ behoimi downloader
0.0.9
+ hotkey:
+ 'Shift+I' - (re)initializes imageBoard (usefull for the imageboards with auto paging)
* fix yande.re jpeg image extension
0.0.8
+ sankaku downloader
+ chan.sankakucomplex.com
+ idol.sankakucomplex.com
0.0.7
+ hotkey:
+ 'Shift+D' - toggle the Download Mode on/off
+ donmai downloader
+ safebooru.donmai.us
+ danbooru.donmai.us
+ sonohara.donmai.us
+ hijiribe.donmai.us
0.0.6
+ yande.re downloader
+ user option:
+ download jpeg image on yande.re [false]
0.0.5
+ rule34 downloader
+ user option:
+ add the imageboard name to the image name [true]
0.0.3
+ gelbooru downloader
+ user options:
+ maximum tags in the image name [10]
+ tags delimeter in the image name ['-']
*/
if( window.self !== window.top )
return;
var RANDOM = '1681238';//Math.floor(Math.random()*1e6 + 1e6);
var DEBUG = false;
console.log('start ' + GM.info.script.name + ' v' + GM.info.script.version + '..');
(async function(){
function consoleLog(){window.console.log.apply(this, arguments);}
function blank(){}
var clog = (DEBUG ? consoleLog : blank);
var userOptions = await initOptions(),
methodsObject = initMethodsObject(),
imageBoard = initImageBoard();
newCssClasses();
//------------------------------------------------------------------------------------//
//------------------------------------ IMAGE BOARD -----------------------------------//
function initImageBoard( d )
{
/*
var elmClass = initImageBoardClasses(d),
elmData = initImageBoardDataset(d),
siteList = initSiteList(),
download = initImageBoardDownloader(d),
userMenu = initUserMenu(),
view = initImageBoardViewer(d),
state = {'viewMode': false, 'userMenu': false, 'downloadMode': false},
divID = 'image-board-div-' + RANDOM;
*/
var imgBrdCl = initImageBoardClasses(d),
imgBrdDt = initImageBoardDataset(d),
siteList = initSiteList(),
imgBrdDw = initImageBoardDownloader(d),
userMenu = initUserMenu(),
imgBrdVw = initImageBoardViewer(d),
imgBrdSt = {'viewMode': false, 'userMenu': false, 'downloadMode': false},
imgBrdId = 'image-board-div-' + RANDOM;
var retVal = {
get siteList(){return siteList;},
get imgBrdCl(){return imgBrdCl;},
get imgBrdDt(){return imgBrdDt;},
get imgBrdId(){return imgBrdId;},
get imgBrdDw(){return imgBrdDw;},
get userMenu(){return userMenu;},
get imgBrdVw(){return imgBrdVw;},
get imgBrdSt(){return imgBrdSt;},
get images(){return this.data.images;},
get downloader(){return this.data.downloader;},
get viewer(){return this.data.viewer;},
data: {
'images': {
list: null,
init: function( doc, type ){
clog("imageBoard init..");
siteList.init(type);
imgBrdDt.init(doc);
imgBrdCl.init(doc);
this.list = this.list || [];
this.doc = doc || document;
var siteObj = siteList.val(type),
isPost = siteObj.isPost(),
imgD;
if( isPost )
{
var img = siteObj.getPostImage();
if( img && !imgBrdCl.hasClass( img, 'counted') )
imgD = this.addNewImage( img, isPost, siteObj );
}
var thumbs = siteObj.getImageThumbs( this.doc ),
_3ParentTypes = ['yande.re', 'lolibooru', 'hypnohub', 'konachan'],
name = siteObj.name,
num = (_3ParentTypes.indexOf(name) != -1 ? 3 : 2);
clog("thumbs.length: ", thumbs.length);
for( var i = 0, len = thumbs.length, thumb, par, h; i < len; ++i )
{
thumb = thumbs[i];
if( imgBrdCl.hasClass( thumb, 'counted' ) )
continue;
imgD = this.addNewImage( thumb, false, siteObj );
par = parent( thumb, num );
par.appendChild( this.createProgressBar(imgD.index) );
if( par.tagName === 'ARTICLE' )
{
try{
h = par.style.height;
h = parseInt(h.match(/\d+/)[0], 10);
h += 15;
h += 'px';
}catch(er){
console.error(er);
h = null;
}
par.style.height = h || '170px';
}
}
},
addNewImage: function( img, isPost, siteObj ){
this.list.push({});
var imgD = last(this.list), pdiv;
imgD.state = 'empty';
imgD.index = this.list.length - 1;
imgD.type = siteObj.name;
if( isPost )
{
imgD.postId = siteObj.getPostId();
imgD.postUrl = window.location.href;
siteObj.setImageDataDoc(imgD);
pdiv = this.createProgressBar(imgD.index);
if( img.parentNode.tagName != 'A' )
img.parentNode.insertBefore(pdiv, img.nextSibling);
else
img.parentNode.parentNode.appendChild(pdiv);
}else
siteObj.setImageDataThumb( imgD, img );
imgBrdDt.val( img, 'index', imgD.index);
imgBrdCl.addClass( img, 'counted' );
if( imgD.state === 'ready' )
{
siteObj.createDiv( imgBrdId, this.doc);
imgBrdDw.init(imgBrdId, this.doc);
setReadyImage( imgD, imgBrdCl, imgBrdDt, imgBrdDw, imgBrdVw );
}
return imgD;
},
createProgressBar: function(index ){
var div = document.createElement('div'),
html = '<div id="progress-stripe-' + index + '" ' +
'class="progress-stripe progress-counted"></div>';
div.setAttribute('class', 'progress-bar');
div.insertAdjacentHTML('beforeend', html);
return div;
},
getEmpty: function(){
var empty = [];
for( var i = 0; i < this.list.length; ++i )
{
if( this.list[i].state === 'empty' )
empty.push(i);
}
return empty;
},
fix: function()
{
var empty = this.getEmpty(), animate = userOptions.val('animateProgress');
clog("fix start..", empty.length);
for( var i = 0, idx, imgD; i < empty.length; ++i )
{
idx = empty[i];
imgD = this.list[idx];
imgD.state = 'busy';
this.getImageData(imgD, animate);
}
},
getImageData: function(imgD, animate)
{
if( siteList.needXHR(imgD.type) )
{
if( animate )
addClass(document.querySelectorAll('#progress-stripe-' + imgD.index), 'progress-animated');
GM.xmlHttpRequest({
url: imgD.postUrl,
method: 'GET',
context: {
'index': imgD.index,
'url': imgD.postUrl,
},
onload: xhrImageData,
});
}else{
console.log("TODO :D");
var siteObj = siteList.val(imgD.type);
//siteObj.setImageDataFull(imgD);// TODO (yande.re, donmai)
}
},
},
'downloader': {
init: function(doc, type){
clog("downloader init..");
siteList.init(type);
var siteObj = siteList.val(type);
siteObj.createDiv( imgBrdId, doc);
imgBrdDw.init(imgBrdId, doc);
},
isActive: function(){
//return imgBrdDw && imgBrdDw.isActive() || false;
return imgBrdSt.downloadMode;
},
activateImage: function(thumb){
if( !thumb )
return;
var a = thumb.parentNode;
if( !imgBrdCl.hasClass(thumb, 'ready' ) )
return;
else if( !imgBrdCl.hasClass( a, 'downloadAttach' ) )
{
a.addEventListener('click', handleDownloadEvent, false);
imgBrdCl.addClass( a, 'downloadAttach' );
}
imgBrdCl.addClass( a, 'downloadActive' );
},
activate: function(doc){
clog("[downloader] activate");
doc = doc || document;
imgBrdCl.init(doc);
var thumbs = imgBrdCl.queryAll('counted');
for( var i = 0, len = thumbs.length; i < len; ++i )
this.activateImage( thumbs[i] );
imgBrdDw.downloadOn();
imgBrdSt.downloadMode = true;
},
deactivate: function(doc){
clog("[downloader] deactivate");
doc = doc || document;
imgBrdCl.init(doc);
var activ = imgBrdCl.queryAll('downloadActive');
clog("active.length: ", activ.length);
for( var i = 0, len = activ.length; i < len; ++i )
imgBrdCl.removeClass( activ[i], 'downloadActive' );
imgBrdDw.downloadOff();
imgBrdSt.downloadMode = false;
},
downloadAll: function(){
imgBrdDw.downloadAll.click();// =)
},
},
'userMenu': {
init: function(doc, type){
clog("userMenu init..");
siteList.init(type);
var siteObj = siteList.val(type);
siteObj.createDiv( imgBrdId, doc);
userMenu.init(imgBrdId, doc);
},
},
'keyboard': {
val: null,
init: function(){
if( !this.isActive )
this.activate();
},
get isActive(){ return !!this.val;},
activate: function(){
activateKeyboard();
this.val = true;
},
deactivate: function(){
deactivateKeyboard();
this.val = false;
},
},
'viewer': {
init: function(doc, type){
clog("viewer init..");
siteList.init(type);
var siteObj = siteList.val(type);
siteObj.createDiv( imgBrdId, doc);
imgBrdVw.init(imgBrdId, doc, siteObj.viewDivInsertionPlace);
},
activateImage: function( thumb ){
if( !thumb )
return;
var a = thumb.parentNode;
if( !imgBrdCl.hasClass(thumb, 'ready' ) )
return;
else if( !imgBrdCl.hasClass( a, 'viewAttach' ) )
{
a.addEventListener('click', handleViewerEvent, false);
imgBrdCl.addClass( a, 'viewAttach' );
}
imgBrdCl.addClass( a, 'viewActive' );
},
activate: function(doc){
clog("viewer activate");
doc = doc || document;
imgBrdCl.init(doc);
var thumbs = imgBrdCl.queryAll('counted');
for( var i = 0, len = thumbs.length; i < len; ++i )
this.activateImage( thumbs[i] );
imgBrdVw.viewerOn();
imgBrdSt.viewMode = true;
},
deactivate: function(doc){
clog("viewer deactivate");
doc = doc || document;
imgBrdCl.init(doc);
var activ = imgBrdCl.queryAll('viewActive');
clog("active.length: ", activ.length);
for( var i = 0, len = activ.length; i < len; ++i )
imgBrdCl.removeClass( activ[i], 'viewActive' );
imgBrdVw.viewerOff();
imgBrdSt.viewMode = false;
},
isActive: function(){
//return imgBrdVw.isActive();
return imgBrdSt.viewMode;
},
},
},
init: function(doc){
for( var key in this.data )
this.data[key].init(doc);
},
fix: function(){
this.data.images.fix();
},
initDiv: function(doc){
doc = doc || document;
var div = doc.querySelector('#' + imgBrdId),
siteObj = siteList.val();
if( !div )
div = siteObj.createDiv(imgBrdId);
if( !hasClass(div, 'image-board-div-activated') )
{
div.addEventListener('click', handleImageBoardEvent, false);
addClass(div, 'image-board-div-activated');
}
},
};
retVal.init(d);
setTimeout(function(){retVal.initDiv(d);}, 100);
if( userOptions.val('autoRun') )
retVal.fix();
return retVal;
}
function handleImageBoardEvent(event)
{
var t = event.target,
dId = 'image-board-download-switch-' + RANDOM,
aId = 'image-board-download-all-' + RANDOM,
vId = 'image-board-viewer-button-' + RANDOM,
mId = 'image-board-user-menu-id-' + RANDOM;
if( t.tagName === 'SPAN' )
t = t.parentNode;
if( t.tagName !== 'BUTTON' )
return;
else if( t.id === dId )
{
handleDownloadSwitchEvent();
}
else if( t.id === aId )
{
handleDownloadAllEvent();
}
else if( t.id === vId )
{
handleViewerSwitchEvent();
}
else if( t.id === mId )
{
handleUserMenuEvent();
}else
console.error("unknown element: ", t);
}
//------------------------------------ IMAGE BOARD -----------------------------------//
//------------------------------------------------------------------------------------//
//----------------------------------- XRH IMAGE DATA ---------------------------------//
function xhrImageData(xhr)
{
var imgD = imageBoard.images.list[xhr.context.index];
if( xhr.status !== 200 )
{
var context = xhr.context;
console.error("xhr.status: ", xhr.status, xhr.statusText );
console.error("index: ", context ? context.index : null);
console.error("postUrl: ", context && context.url || null );
if( imgD.state !== 'ready' )
imgD.state = 'empty';
removeClass( document.querySelectorAll('#progress-stripe-' + context.index), 'progress-animated' );
return;
}
if( !imgD || imgD.state === 'ready' )
{
console.error("invalid context: ", imgD);
return;
}
var siteObj = imageBoard.siteList.val(imgD.type);
if( !siteObj )
{
console.error("invalid site type: ", imgD.type);
return;
}
var doc = document.implementation.createHTMLDocument("");
doc.documentElement.innerHTML = xhr.response;
siteObj.setImageDataDoc(imgD, doc);
clog("xhrImageData[" + imgD.index + "].state : " + imgD.state);
if( imgD.state === 'ready' )
{
setReadyImage( imgD );
}
}
function setReadyImage( imgD, imgBrdCl, imgBrdDt, imgBrdDw, imgBrdVw )
{
if( (!imgBrdCl || !imgBrdDt || !imgBrdDw || !imgBrdVw) && imageBoard )
{
imgBrdCl = imageBoard.imgBrdCl;
imgBrdDt = imageBoard.imgBrdDt;
imgBrdDw = imageBoard.imgBrdDw;
imgBrdVw = imageBoard.imgBrdVw;
}
var thumb = imgBrdDt.query('index', imgD.index + ''),
stripe = document.querySelectorAll('#progress-stripe-' + imgD.index);
addClass(stripe, 'image-ready');
removeClass(stripe, 'progress-animated');
imgBrdCl.addClass( thumb, 'ready' );
imgBrdDt.val( thumb, 'source', imgD.source );
if( imgD.bytes ) imgBrdDt.val( thumb, 'bytes', imgD.bytes );
imgBrdDw.total += 1;
imgBrdVw.total += 1;
clog("name: " + imgD.name);
if( imageBoard )
{
if( imageBoard.downloader.isActive() )
imageBoard.downloader.activateImage( thumb );
if( imageBoard.viewer.isActive() )
imageBoard.viewer.activateImage( thumb );
}
}
//----------------------------------- XRH IMAGE DATA ---------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------- SITE LIST ------------------------------------//
function initSiteList()
{
var retVal = {
settings: {
'gelbooru': getGelbooruSettings,
'rule34.xxx': getRule34Settings,
'yande.re': getYandereSettings,
'donmai': getDonmaiSettings,
'sankaku': getSankakuSettings,
'behoimi': getBehoimiSettings,
'youhate': getGelbooruSettings,
'safebooru': getSafebooruSettings,
'uberbooru': getUberbooruSettings,
'bronibooru': getBronibooruSettings,
'mspabooru': getMspabooruSettings,
'e926.net': getE926netSettings,
'e621.net': getE621netSettings,
'.booru.org': getBooruorgSettings,
'atfbooru': getAtfbooruSettings,
'lolibooru': getLolibooruSettings,
'hypnohub': getHypnohubSettings,
'tbib': getTbibSettings,
'konachan': getKonachanSettings,
'paheal.net': getPahealSettings,
},
data: null,
get: function( type, prop1, prop2 ){
var obj;
if( !type )
obj = this.currentObj;
else{
this.data[type].init();
obj = this.data[type];
}
return nodeWalk.call( obj, prop1, prop2 );
},
style: function(type){
return this.get( type, 'style' );
},
val: function(type){
return this.get( type, 'val' );
},
needXHR: function(type){
return this.get( type, 'needXHR' );
},
init: function(type, prefix){
if( !this.data )
{
this.data = {};
for( var key in this.settings )
this.data[key] = getSiteObject( key, this.settings[key], prefix );
}
if( !type )
this.initCurrent();
else if( this.data[type] )
this.data[type].init();
},
getSiteType: function(url){
url = url || window.location.href;
for( var key in this.data )
{
if( this.data[key].regexp.test(url) )
return key;
}
console.error("no site object found for this host");
return null;
},
initCurrent: function(){
if( !this.currentObj )
{
var type = this.getSiteType();
if( !type )
return;
this.currentObj = this.data[type];
}
this.currentObj.init();
},
};
retVal.init();
clog("siteList.current: ", retVal.val());
return retVal;
}
//------------------------------------- SITE LIST ------------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------- SITE OBJECT ----------------------------------//
function getSiteObject( siteName, getSiteSettings, prefix )
{
return {
val: null,
name: siteName,
regexp: new RegExp( siteName ),
get needXHR(){return this.val.needXHR;},
get style(){return this.val.style;},
get settings(){
var s = ( typeof getSiteSettings === 'function' ? getSiteSettings(prefix) : null);
Object.defineProperty( this, 'settings', {
get: function(){return s;},
enumerable: true,
configurable: true,
});
return s;
},
init: function(){
this.val = this.val || initSiteObject( this.settings );
},
};
}
function initSiteObject( siteSettings )
{
var retVal = {
data: null,
get name(){ return this.data.name; },
get prefixedName(){
var prefix = this.prefix,
name = this.shortName;
if( prefix )
name = prefix + name;
Object.defineProperty( this, 'prefixedName', {
get: function(){return name;},
enumerable: true,
configurable: true,
});
return name;
},
get prefix(){return this.data.prefix; },
get shortName(){
var name = this.name.replace(/^\./, '');
Object.defineProperty( this, 'shortName', {
get: function(){return name;},
enumerable: true,
configurable: true,
});
return name;
},
get hostname(){return this.data.hostname; },
get imageHostname(){return this.data.imageHostname;},
get imageDir(){return this.data.imageDir; },
get style(){return this.data.style;},
get postDivInsertionPlace(){return this.data.postDivInsertionPlace;},
get listDivInsertionPlace(){return this.data.listDivInsertionPlace;},
get viewDivInsertionPlace(){return this.data.viewDivInsertionPlace;},
get methodsMap(){return this.data.methodsMap;},
get needXHR(){return (typeof this.data.needXHR === 'boolean' ? this.data.needXHR : true);},
init: function( settings ){
this.data = this.data || settings;
if( !this.data )
{
console.error("[initSiteObject] can't init siteObject, invalid data: ", this.data);
return;
}
for( var i = 0, len = methodsObject.list.length, name, type, map = this.methodsMap || {}; i < len; ++i )
{
name = methodsObject.list[i];
type = map[name] || 'booru';
if( typeof methodsObject.method(type, name) === 'function' )
this[name] = methodsObject.method(type, name);
}
},
};
retVal.init( siteSettings );
return retVal;
}
//------------------------------------------------------------------------------------//
//-------------------------------------- GELBOORU ------------------------------------//
function getGelbooruSettings()
{
return {
name: 'gelbooru',
hostname: 'gelbooru.com',
imageDir: '/images',
imageHostname: 'simg3.gelbooru.com',
postDivInsertionPlace: '#image',
listDivInsertionPlace: '.contain-push',
viewDivInsertionPlace: '.padding15',
style: {
color: '#fff',
width: '180px',
background: '#0773fb',
backgroundHover: '#fbb307',
colorHover: '#fff',
backgroundView: '#fff',
},
methodsMap: {
isPost: 'gelbooru',
getPostId: 'gelbooru',
getPostUrl: 'gelbooru',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//--------------------------------------- RULE34 -------------------------------------//
function getRule34Settings()
{
return {
name: 'rule34.xxx',
hostname: 'rule34.xxx',
imageDir: '/images',
imageHostname: 'img.rule34.xxx',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#fff',
width: '180px',
background: '#84AE83',
backgroundHover: '#A4CEA3',
colorHover: '#fff',
},
methodsMap: {
isPost: 'gelbooru',
getPostId: 'gelbooru',
getPostUrl: 'gelbooru',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//------------------------------------- YANDE.RE -------------------------------------//
function getYandereSettings()
{
return {
name: 'yande.re',
hostname: 'yande.re',
imageDir: 'image',
imageHostname: 'files.yande.re',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#ee8887',
width: '180px',
background: '#222',
backgroundHover: '#444',
colorHover: '#ee8887',
},
methodsMap: null,
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//-------------------------------------- DONMAI --------------------------------------//
function getDonmaiSettings( prefix )
{
var prefixList = ['safebooru.', 'danbooru.', 'sonohara.', 'hijiribe.'],
hostnameSuffix = 'donmai.us';
prefix = getHostnamePrefix( hostnameSuffix, prefixList, prefix );
var hostname = prefix + hostnameSuffix;
return {
name: 'donmai',
prefix: prefix,
hostname: hostname,
imageHostname: hostname,
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: '#posts',
viewDivInsertionPlace: '#page', //'#c-posts',
style: {
color: '#0073ff',
width: '180px',
background: '#f5f5ff',
backgroundHover: '#f5f5ff',
colorHover: '#80b9ff',
},
methodsMap: {
isPost: 'donmai',
getPostId: 'donmai',
getPostUrl: 'donmai',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//-------------------------------------- SANKAKU -------------------------------------//
function getSankakuSettings(prefix)
{
var prefixList = ['chan.', 'idol.'],
hostnameSuffix = 'sankakucomplex.com';
prefix = getHostnamePrefix( hostnameSuffix, prefixList, prefix );
var hostname = prefix + hostnameSuffix,
imageHostnamePrefix = (prefix ? prefix[0] + 's.' : '');
return {
name: 'sankaku',
prefix: prefix,
hostname: hostname,
imageHostname: imageHostnamePrefix + hostnameSuffix,
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: '#content',
viewDivInsertionPlace: '#content',
style: {
color: '#ff761c',
width: '180px',
background: '',
backgroundHover: '',
colorHover: '#666',
},
methodsMap: null,
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//-------------------------------------- BEHOIMI -------------------------------------//
function getBehoimiSettings()
{
return {
name: 'behoimi',
hostname: 'behoimi.org',
imageHostname: 'behoimi.org',
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#43333f',
width: '180px',
background: '',
backgroundHover: '',
colorHover: '#354d99',
},
methodsMap: null,
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//-------------------------------------- SAFEBOORU -----------------------------------//
function getSafebooruSettings()
{
return {
name: 'safebooru',
hostname: 'safebooru.org',
imageHostname: 'safebooru.org',
imageDir: '/images',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#fff',
width: '180px',
background: '#006ffa',
backgroundHover: '#006ffa',
colorHover: '#33cfff',
},
methodsMap: {
isPost: 'gelbooru',
getPostId: 'gelbooru',
getPostUrl: 'gelbooru',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//-------------------------------------- UBERBOORU -----------------------------------//
function getUberbooruSettings()
{
return {
name: 'uberbooru',
hostname: 'uberbooru.com',
imageHostname: 'uberbooru.com',
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: '#posts',
viewDivInsertionPlace: 'div#page', // 'div#c-posts',
style: {
color: '#000',
width: '180px',
background: '#e6e6e6',
backgroundHover: '#e6e6e6',
colorHover: '#008',
},
methodsMap: {
isPost: 'donmai',
getPostId: 'donmai',
getPostUrl: 'donmai',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//------------------------------------- BRONIBOORU -----------------------------------//
function getBronibooruSettings()
{
return {
name: 'bronibooru',
hostname: 'bronibooru.com',
imageHostname: 's3.amazonaws.com',
imageDir: 'bronibooru',
postDivInsertionPlace: '#image',
listDivInsertionPlace: '#posts',
viewDivInsertionPlace: 'div#page', // 'div#c-posts',
style: {
color: '#0073ff',
width: '180px',
background: '#f7f7ff',
backgroundHover: '#f7f7ff',
colorHover: '#80b9ff',
},
methodsMap: {
isPost: 'donmai',
getPostId: 'donmai',
getPostUrl: 'donmai',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//-------------------------------------- MSPABOORU -----------------------------------//
function getMspabooruSettings()
{
return {
name: 'mspabooru',
hostname: 'mspabooru.com',
imageHostname: 'mspabooru.com',
imageDir: '/images',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list', // 'div#content',
style: {
color: '#fff',
width: '180px',
background: '#006ffa',
backgroundHover: '#006ffa',
colorHover: '#33cfff',
},
methodsMap: {
isPost: 'gelbooru',
getPostId: 'gelbooru',
getPostUrl: 'gelbooru',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//--------------------------------------- E926NET ------------------------------------//
function getE926netSettings()
{
return {
name: 'e926.net',
hostname: 'e926.net',
imageHostname: 'static1.e926.net',
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content-post',
viewDivInsertionPlace: 'div#content', // 'div#post-list',
style: {
color: '#fff',
width: '180px',
background: '#152f56',
backgroundHover: '#152f56',
colorHover: '#2e76b4',
},
methodsMap: null,
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//--------------------------------------- E621NET ------------------------------------//
function getE621netSettings()
{
return {
name: 'e621.net',
hostname: 'e621.net',
imageHostname: 'static1.e621.net',
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content-post',
viewDivInsertionPlace: 'div#content', // 'div#post-list',
style: {
color: '#fff',
width: '180px',
background: '#152f56',
backgroundHover: '#152f56',
colorHover: '#2e76b4',
},
methodsMap: null,
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//--------------------------------------- *.BOORU ------------------------------------//
function getBooruorgSettings(prefix)
{
var prefixList = [], hostnameSuffix = 'booru.org';
prefix = getHostnamePrefix( hostnameSuffix, prefixList, prefix );
var hostname = prefix + hostnameSuffix;
return {
name: '.booru.org',
prefix: prefix,
hostname: hostname,
imageHostname: 'img.booru.org',
imageDir: prefix + '//images',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#content', // 'div#post-list',
style: {
color: '#fff',
width: '180px',
background: '#0773fb',
backgroundHover: '#fbb307',
colorHover: '#fff',
},
methodsMap: {
isPost: 'gelbooru',
getPostId: 'gelbooru',
getPostUrl: 'gelbooru',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//--------------------------------------- ATFBOORU -----------------------------------//
function getAtfbooruSettings()
{
return {
name: 'atfbooru',
hostname: 'atfbooru.ninja',
imageHostname: 'atfbooru.ninja',
imageDir: 'data',
postDivInsertionPlace: '#image',
listDivInsertionPlace: '#posts',
viewDivInsertionPlace: '#page', //'#c-posts',
style: {
color: '#0073ff',
width: '180px',
background: '#f5f5ff',
backgroundHover: '#f5f5ff',
colorHover: '#80b9ff',
},
methodsMap: {
isPost: 'donmai',
getPostId: 'donmai',
getPostUrl: 'donmai',
},
needXHR: true,
};// donmai like
}
//------------------------------------------------------------------------------------//
//------------------------------------- LOLIBOORU ------------------------------------//
function getLolibooruSettings()
{
return {
name: 'lolibooru',
hostname: 'lolibooru.moe',
imageDir: 'image',
imageHostname: 'lolibooru.moe',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#ee8887',
width: '180px',
background: '#222',
backgroundHover: '#444',
colorHover: '#ee8887',
},
methodsMap: null,
needXHR: true,
};// yande.re like
}
//------------------------------------------------------------------------------------//
//------------------------------------- HYPNOHUB -------------------------------------//
function getHypnohubSettings()
{
return {
name: 'hypnohub',
hostname: 'hypnohub.net',
imageDir: '/data/image',
imageHostname: 'hypnohub.net',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#ee8887',
width: '180px',
background: '#222',
backgroundHover: '#444',
colorHover: '#ee8887',
},
methodsMap: null,
needXHR: true,
};// yande.re like
}
//------------------------------------------------------------------------------------//
//---------------------------------------- TBIB --------------------------------------//
function getTbibSettings()
{
return {
name: 'tbib',
hostname: 'tbib.org',
imageDir: '/images',
imageHostname: 'tbib.org',
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#fff',
width: '180px',
background: '#0773fb',
backgroundHover: '#fbb307',
colorHover: '#fff',
},
methodsMap: {
isPost: 'gelbooru',
getPostId: 'gelbooru',
getPostUrl: 'gelbooru',
},
needXHR: true,
};// gelbooru like
}
//------------------------------------------------------------------------------------//
//------------------------------------- KONACHAN -------------------------------------//
function getKonachanSettings()
{
var hostname = window.location.hostname;
return {
name: 'konachan',
hostname: hostname,
imageDir: 'image',
imageHostname: hostname,
postDivInsertionPlace: '#image',
listDivInsertionPlace: 'div.content',
viewDivInsertionPlace: 'div#post-list',
style: {
color: '#ee8887',
width: '180px',
background: '#222',
backgroundHover: '#444',
colorHover: '#ee8887',
},
methodsMap: null,
needXHR: true,
};// yande.re like
}
//------------------------------------------------------------------------------------//
//--------------------------------------- PAHEAL -------------------------------------//
function getPahealSettings()
{
return {
name: 'paheal.net',
prefix: 'rule34.',
hostname: 'rule34.paheal.net',
imageDir: '_images',
imageHostname: '.paheal.net',
postDivInsertionPlace: '#main_image',
listDivInsertionPlace: '#imagelist',
viewDivInsertionPlace: '#imagelist',
style: {
color: '#fff',
width: '180px',
background: '#84AE83',
backgroundHover: '#A4CEA3',
colorHover: '#fff',
},
methodsMap: {
isPost: 'paheal',
getPostId: 'paheal',
getPostUrl: 'paheal',
},
needXHR: true,
};
}
//------------------------------------------------------------------------------------//
//------------------------------------- HOST PREFIX ----------------------------------//
function getHostnamePrefix( hostnameSuffix, prefixList, prefix )
{
var hostname,
errorMessage = "[getHostnamePrefix](hostnameSuffix='" + hostnameSuffix +
"', prefixList=[" + prefixList.join(',') + "]" + (prefix ? ", prefix='" + prefix + "'" : "") + ") ",
regExp;
if( prefix )
{
if( prefixList.indexOf(prefix) == -1 )
{
console.error(errorMessage + "\nnot supported prefix");
return '';
}
}else{
hostname = window.location.hostname;
if( hostname.indexOf(hostnameSuffix) == -1 )
{
console.error(errorMessage + "\ninvalid hostname: " + hostname );
return '';
}
for( var i = 0, len = prefixList.length; i < len; ++i )
{
if( hostname.indexOf(prefixList[i]) == -1 )
continue;
prefix = prefixList[i];
break;
}
}
if( !prefix )
{
try{
regExp = new RegExp('([^\\.]+\\.)(' + hostnameSuffix + ')' );
prefix = hostname.match(regExp)[1];
}catch(e){
console.error(e);
console.error(errorMessage + "\nno valid prefix for hostname: " + hostname );
}
}
return prefix || '';
}
//------------------------------------------------------------------------------------//
//----------------------------------- METHODS OBJECT ---------------------------------//
function initMethodsObject()
{
var retVal = {
get list(){return this.map.list;},
map: {
list: [
'isPost',
'getPostId',// get post id from href
'getPostUrl',// get post url by postId
// method of thumbnail data grabbing
'getImageThumbs',
'setImageDataThumb',
// methods of image data getting from image post page
'getPostImage',
'setImageOriginalResolution',
'setImageDataSize',
'setImageDataSourceLowres',
'setImageDataSourceHighres',
'setImageDataTags',
'setImageDataName',
'setImageDataExtension',
'setImageDataBytes',
'setImageDataDoc',
// create place for buttons insertion
'getPostDivInsertionPlace',
'getListDivInsertionPlace',
'createDiv',
],
},
data: {
'booru': {
val: null,
init: function(){
this.val = this.val || getBooruMethodsObject();
},
},
'gelbooru': {
val: null,
init: function(){
this.val = this.val || getGelbooruMethodsObject();
},
},
'donmai': {
val: null,
init: function(){
this.val = this.val || getDonmaiMethodsObject();
},
},
'paheal': {
val: null,
init: function(){
this.val = this.val || getPahealMethodsObject();
},
},
},
init: function(){
for( var type in this.data )
this.data[type].init();
},
method: function( type, name ){
if( this.data[type] )
{
if( name )
return this.data[type].val[name];
return this.data[type].val;
}
return null;
},
};
retVal.init();
return retVal;
}
//----------------------------------- METHODS OBJECT ---------------------------------//
//------------------------------------------------------------------------------------//
//-------------------------------- BOORU METHODS OBJECT ------------------------------//
function getBooruMethodsObject()
{
var retVal = {
isPost: function(url){
url = url || window.location.pathname || window.location.href;
return /\/post\/show\/\d+/.test(url);
},
getPostId: function(url){
url = url || window.location.href;
if( this.isPost(url) )
return getLocation(url, 'pathname').match(/\d+/)[0];
return null;
},
getPostUrl: function(postId){
return window.location.protocol + '//' + this.hostname + '/post/show/' + postId;
},
getPostDivInsertionPlace: function(doc){
doc = doc || document;
var insertPlace = doc.querySelector( this.postDivInsertionPlace );
if( insertPlace )
{
var parent = insertPlace.parentNode;
if( parent.tagName === 'A' )
return parent.nextSibling || parent;
return insertPlace.nextSibling || insertPlace;
}
return null;
},
getListDivInsertionPlace: function(doc){
doc = doc || document;
var insertPlace = doc.querySelector(this.listDivInsertionPlace);
if( insertPlace )
return insertPlace.firstChild || insertPlace;
return null;
},
getPostImage: function(doc){
doc = doc || document;
return doc.querySelector('#image') || doc.querySelector('#main_image');//paheal
},
getImageThumbs: function( doc ){
doc = doc || document;
var thumbs = doc.querySelectorAll('img.preview');
if( thumbs && thumbs.length === 0 )
thumbs = doc.querySelectorAll('article > a > img');// donmai, uberbooru
if( thumbs && thumbs.length === 0 )
thumbs = doc.querySelectorAll('img[itemprop="thumbnailUrl"]');// donmai
if( thumbs && thumbs.length === 0 )
thumbs = doc.querySelectorAll('span.thumb > a > img');// *.booru.org
if( thumbs && thumbs.length === 0 )
thumbs = doc.querySelectorAll('a > img[id*="thumb_"]');// rule34.paheal.net
return thumbs;
},
setImageDataThumb: function( imgD, thumb ){
if( thumb && imgD )
{
if( thumb.dataset && thumb.dataset.original )
imgD.thumbSource = thumb.dataset.original;
else
imgD.thumbSource = thumb.src;
imgD.postUrl = thumb.parentNode.href;
if( thumb.parentNode.id && /\d+/.test(thumb.parentNode.id) )
imgD.postId = thumb.parentNode.id.match(/\d+/)[0];
else
imgD.postId = this.getPostId( imgD.postUrl );
if( thumb.title )
imgD.thumbTitle = thumb.title;
}
},
setImageDataSourceLowres: function( imgD, doc ){
var img = this.getPostImage(doc);
if( img )
imgD.lowresSource = img.src;
else
return 1;
return 0;
},
setImageOriginalResolution: function( imgD, img ){
if( !img )
return false;
var width, height;
width = img.getAttribute('large_width');
height = img.getAttribute('large_height');
if( !width || !height )
{
width = img.getAttribute('data-original-width');
height = img.getAttribute('data-original-height');
}
if( !width || !height )
{
// sankaku
width = img.getAttribute('orig_width');
height = img.getAttribute('orig_height');
}
if( !width || !height )
{
// e926.net, e621.net
width = img.getAttribute('data-orig_width');
height = img.getAttribute('data-orig_height');
}
if( (!width || !height) && this.name === 'paheal.net' )
{
// paheal.net
width = img.getAttribute('data-width');
height = img.getAttribute('data-height');
}
if( width && height )
{
imgD.width = width;
imgD.height = height;
return true;
}
return false;
},
setImageDataSize: function( imgD, doc ){
doc = doc || document;
var img = this.getPostImage(doc), res;
if( this.setImageOriginalResolution )
res = this.setImageOriginalResolution( imgD, img );
if( res )
return;
var lis = doc.querySelectorAll('li'), i, li, len = lis.length;
for( i = 0; i < len; ++i )
{
li = lis[i];
if( li.innerHTML.indexOf('Size:') != -1 )
break;
}
var match = li.innerHTML.match(/(\d+)x(\d+)/);
if( i < len && match )
{
imgD.width = match[1];
imgD.height = match[2];
}else
console.error("[setImageDataSize] can't find image size (width x height)");
},
setImageDataSourceHighres: function( imgD, doc ){
doc = doc || document;
var imgHost = this.imageHostname || this.hostname,
i, l, href,
link = doc.querySelectorAll('li > a[href*="' + imgHost + '/' + this.imageDir + '/"]');
if( link.length === 0 )// same origin imageboards
link = doc.querySelectorAll('li > a[href*="/' + this.imageDir + '/"]');
if( link.length > 0 )
{
for( i = 0, href = null; i < link.length; ++i )
{
l = link[i];
if( l.href.indexOf('sample') == -1 )
{
href = l.href;
break;
}
}
imgD.source = href ? href : last(link).href;
}
else if( imgD.lowresSource )
imgD.source = imgD.lowresSource;
else{
console.error("[setImageDataSourceHighres] no image source found");
return 1;
}
// jpeg image for yande.re like imageboards
var jpeg = doc.querySelector('li > a[href*="' + imgHost + '/jpeg/"]');
if( jpeg )
imgD.jpegSource = jpeg.href;
clog("imgD.source: " + imgD.source);
this.setType = this.setType || function( _type, _source, _imgD )
{
_imgD[_type + '-source'] = _source;
_imgD[_type + '-extension'] = getFileExt(_source);
};
this.setType( 'thumnail', imgD.thumbSource, imgD );
if( /mp4|webm|ogv|ogg/.test(getFileExt(imgD.source)) )
{
this.setType( 'vid_file', imgD.source, imgD );
imgD.viewType = 'vid_file';
}else{
this.setType( 'orig_img', imgD.source, imgD );
imgD.viewType = 'orig_img';
if( imgD.jpegSource )
{
this.setType( 'jpeg_img', imgD.jpegSource, imgD );
imgD.viewType = 'jpeg_img';
}
if( !isSameLink(imgD.source, imgD.lowresSource) )
{
this.setType( 'samp_img', imgD.lowresSource, imgD );
imgD.viewType = 'samp_img';
}
}
return 0;
},
setImageDataTags: function( imgD, doc ){
doc = doc || document;
this.getTagName = this.getTagName || function( tagElm, fl)
{
if( tagElm.querySelectorAll('a').length === 0 )
return '';
if( fl )
return tagElm.querySelectorAll('a')[0].innerText.trim().replace(/\s+/g, '_');// sankaku, safebooru.org
return last(tagElm.querySelectorAll('a')).innerText.trim().replace(/\s+/g, '_');
};
this.tagsId = this.tagsId || {
'general' : '0',
'artist' : '1',
'copyright': '3',
'character': '4',
'metadata' : '5',
// 3dbooru tags
'species' : '-1',
'model' : '-1',
'idol' : '-1',
'photo_set': '-1',
'circle' : '-1',
'medium' : '-1',
'faults' : '-1',
};
this.createTagObj = this.createTagObj || function( tagElm, tagsClass, fl )
{
try{
var links = tagElm.querySelectorAll('a'),
post_count = tagElm.querySelector('span.post-count') || tagElm.querySelector('span[style]'),
searchLink = null,
obj = {};
if( tagsClass === 'tag-type' )
{
if( fl )
{
obj.href = links[0].href;
obj.wiki = links[1].href;
searchLink = links[0];
}else{
obj.href = last(links).href;
obj.wiki = (links.length == 1 ? null : links[0].href);
}
obj.category = tagElm.className.match(/tag-type-([^\s]+)/)[1];
}
else if( tagsClass === 'category' )
{
obj.href = last(links).href;
obj.wiki = (links.length == 1 ? null : links[0].href);
obj.category = tagElm.className.match(/category-([^\s]+)/)[1];
}
else if( tagsClass === 'tag_name_cell' )
{
obj.href = links[1].href;
obj.wiki = links[0].href;
post_count = tagElm.querySelector('span.tag_count');
obj.category = null;
}else{
obj.href = links[0].href;
obj.wiki = null;
post_count = links[0].nextSibling;
obj.category = null;
}
if( obj.category )
obj.class = tagsClass;
searchLink = searchLink || last(links);
obj.count = parseInt(post_count.textContent, 10);
obj.name = searchLink.textContent;
return obj;
}catch(e){console.error(e);}
};
/*
--------------> n1 [ n2 ]
n1 - number of links in tag element
n2 - index 0..(n1-1) of search tag link (here, tag link)
--------------> 2 1
--> gelbooru:
ul#tag-list
div
div[style] -- tag category name
li.tag-type-
a[href] -- wiki link
a[href] -- tag link
span[style]
--------------> 1
--> rule34.xxx:
ul#tag-sidebar
li -- tag category name
li.tag-type- [class="tag"]
a[href] -- tag
span[style]
--------------> 2 1
--> yande.re,
--> 3dbooru (behoimi):
ul#tag-sidebar
li.tag-type-
a[href] -- wiki link
a[href] -- tag link
span.post-count
--------------> 2 0
--> sankaku:
ul#tag-sidebar
li.tag-type-
a[href, title] -- tag link
span.tag-extra-info
a[href] -- wiki link
span.post-count
--------------> 1
--> safebooru,
--> mspabooru,
--> tbib:
ul#tag-sidebar
li.tag-type-
a[href] -- tag link
span[style]
--------------> 2 1
--> e621.net,
--> e926.net:
ul#tag-sidebar
li.category- -- tag category name
li.tag-type-
a[style, href] -- wiki
a[href] -- tag
span.post-count
--------------> 1
--> booru.org:
div#tag_list
ul
li
span[style]
?
a[href] -- tag
number -- post count
--------------> 2 1
--> uberbooru,
--> bronibooru:
section#tag-list
h2 -- tag category name
ul
li.category-
a.wiki-link [href]
a.search-tag [href]
span.post-count
--------------> 2 1
--> donmai,
--> atfbooru:
section#tag-list
h2.copyright-tag-list -- tag category name
ul.copyright-tag-list
li.category-
a.wiki-link [href]
a.search-tag [href]
span.post-count
--------------> 2 1
--> lolibooru,
--> hypnohub,
--> konachan:
ul#tag-sidebar
li.tag-type- [class="tag-link", data-name="name of tag", data-type="tag category"]
a[href] -- wiki
a[href] -- tag
span.post-count
--------------> 2 1
--> rule34.paheal.net:
table.tag_list
tbody tr
td.tag_info_link_cell
a.tag_info_link [href] -- wiki
td.tag_name_cell
a.tag_name [href] -- tag
td.tag_count_cell
span.tag_count
*/
var getTagName = this.getTagName,
tagsId = this.tagsId,
createTagObj = this.createTagObj,
tagsClass = '';
var nameList = ['sankaku'],
tagsOrder = userOptions.val('tagsOrder'),
iter = 0, _fl = null, i, k, tags, tagType;
imgD.tags = imgD.tags || [];
imgD.tags.length = 0;
if( doc.querySelectorAll('li[class*="tag-type-"]').length > 0 )
tagsClass = 'tag-type';
else if( doc.querySelectorAll('li[class*="category-"]').length > 0 )
tagsClass = 'category';
for( i = 0, _fl = (nameList.indexOf(this.name) != -1); i < tagsOrder.length; ++i )
{
tagType = tagsOrder[i];
if( tagsClass === 'tag-type' )
tags = doc.querySelectorAll('li.tag-type-' + tagType);
else if( tagsClass === 'category' )
tags = doc.querySelectorAll('li.category-' + tagsId[tagType]);// donmai like
for( k = 0; tags && k < tags.length; ++k, ++iter )
{
imgD.tags.push( getTagName(tags[k], _fl) );
}
}
if( iter === 0 )
{
// not categorized tags
tagsClass = '';
tags = doc.querySelectorAll('div#tag_list li');// *.booru.org
if( !tags || tags.length === 0 )
{
tags = doc.querySelectorAll('.tag_name_cell');// paheal.net
tagsClass = 'tag_name_cell';
}
for( k = 0, _fl = (nameList.indexOf(this.name) != -1); tags && k < tags.length; ++k )
{
imgD.tags.push( getTagName(tags[k], _fl) );
}
}
// tag object
imgD.tagList = imgD.tagList || [];
imgD.tagList.length = 0;
if( tagsClass === 'tag-type' )
tags = doc.querySelectorAll('li[class*="tag-type-"]');
else if( tagsClass === 'category' )
tags = doc.querySelectorAll('ul li[class*="category-"]');
else if( tagsClass === 'tag_name_cell' )
tags = doc.querySelectorAll('table.tag_list > tbody tr');// paheal.net
else
tags = doc.querySelectorAll('div#tag_list ul li span');// .booru.org
for( i = 0; i < tags.length; ++i )
{
imgD.tagList.push( createTagObj(tags[i], tagsClass, _fl) );
}
},
createDiv: function(id, doc){
doc = doc || document;
var div = doc.querySelector('#' + id);
clog("[createDiv] div#" + id + ": ", div);
if( div )
return div;
div = document.createElement('div');
var insertPlace;
if( this.isPost() )
insertPlace = this.getPostDivInsertionPlace(doc);
else
insertPlace = this.getListDivInsertionPlace(doc) || doc.querySelector(".thumb") || doc.body.firstChild;
if( !insertPlace )
return null;
div.setAttribute('id', id);
if( insertPlace.tagName !== 'IMG' )
div = insertPlace.parentNode.insertBefore( div, insertPlace.nextSibling);// check_it_out
else
div = insertPlace.parentNode.appendChild(div);
if( typeof this.keyboardDiv === 'function' )
this.keyboardDiv( id, doc );
return div;
},
setImageDataDoc: function( imgD, doc ){
if( !imgD || imgD.state === 'ready' )
return 1;
doc = doc || document;
// size
this.setImageDataSize( imgD, doc );
// lowres
var errN = this.setImageDataSourceLowres( imgD, doc );
// highres
errN += this.setImageDataSourceHighres( imgD, doc );
if( errN > 1 )
return errN;
if( !imgD.lowresSource )
imgD.lowresSource = (imgD.jpegSource || imgD.source);
// tags
this.setImageDataTags( imgD, doc );
// name
this.setImageDataName( imgD );
// extension
this.setImageDataExtension( imgD );
imgD.state = 'ready';
return 0;
},
setImageDataName: function( imgD ){
if( !imgD || !imgD.tags )
return;
var tagsLen = imgD.tags.length,
uLen = userOptions.val('maxTagsInName'),
tagsDelim = userOptions.val('tagsDelim'),
imageId = imgD.postId,
boardName = '', name = '',
ignoredTags = userOptions.val('ignoredTags'),
tagName;
if( userOptions.val('addImgBrdName') )
{
boardName = (userOptions.val('prefixedName') ? this.prefixedName : this.shortName);
imageId = boardName + tagsDelim + imgD.postId;
}
for( var i = 0; i < tagsLen && i < uLen; ++i )
{
tagName = imgD.tags[i];
if( tagName.length > 0 && ignoredTags.indexOf(tagName) == -1 )
name += tagName + tagsDelim;
}
if( userOptions.val('imgIdAtNameEnd') )
imgD.name = name + imageId;
else
imgD.name = imageId + tagsDelim + name.slice(0, -tagsDelim.length);
},
setImageDataExtension: function( imgD ){
imgD.extension = getFileExt( imgD.source );
if( imgD.jpegSource )
imgD.jpegExtension = getFileExt( imgD.jpegSource );
},
};
return retVal;
}
//-------------------------------- BOORU METHODS OBJECT ------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------- GELBOORU METHODS OBJECT ----------------------------//
function getGelbooruMethodsObject()
{
var retVal = {
isPost: function( url ){
url = url || window.location.href;
if( this.getPostId(url) )
return true;
return false;
},
getPostId: function( postUrl ){
postUrl = postUrl || window.location.href;
var srch = getLocation( postUrl, 'search' ),
keys = getSearchObject( srch );
if( keys.s === 'view' && keys.page === 'post' )
return keys.id;
else
return null;
},
getPostUrl: function( postId ){
return window.location.protocol + this.hostname + '/index.php?page=post&s=view&id=' + postId;
},
};
return retVal;
}
//------------------------------------------------------------------------------------//
//-------------------------------- DONMAI METHODS OBJECT -----------------------------//
function getDonmaiMethodsObject()
{
var retVal = {
isPost: function(url){
url = url || window.location.href;
return /\/posts\/\d+/.test(url);
},
getPostId: function(url){
url = url || window.location.href;
if( this.isPost(url) )
return getLocation(url, 'pathname').match(/(\/posts\/)?(\d+)?/)[2];
return null;
},
getPostUrl: function(postId){
return window.location.protocol + '//' + this.hostname + '/posts/' + postId;
},
};
return retVal;
}
//------------------------------------------------------------------------------------//
//-------------------------------- PAHEAL METHODS OBJECT -----------------------------//
function getPahealMethodsObject()
{
var retVal = {
isPost: function(url){
url = url || window.location.href;
return /\/post\/view\/\d+/.test(url);
},
getPostId: function(url){
if( this.isPost(url) )
return getLocation(url, 'pathname').match(/(\/post\/view\/)?(\d+)?/)[2];
return null;
},
getPostUrl: function(postId){
return window.location.protocol + '//' + this.hostname + '/post/view/' + postId;
},
};
return retVal;
}
//------------------------------------------------------------------------------------//
//-------------------------------------- DATASET -------------------------------------//
function initImageBoardDataset(d)
{
var retVal = {
data: {
source: 'data-image-board-source',
index: 'data-image-board-index',
extension: 'data-image-board-extension',
bytes: 'data-image-board-bytes',
},
val: function(elm, propName, v){
if( this.data[propName] && elm )
{
if( v !== undefined )
elm.setAttribute(this.data[propName], v);
else
return elm.getAttribute(this.data[propName]);
}
return null;
},
init: function(doc){
this.doc = doc || document;
},
getSelector: function(propName, v){
var sel = this.data[propName];
if( sel )
{
if( v !== undefined )
{
var pos = v.indexOf('=');// &=, *=, ^=
if( pos > -1 && pos < 2 )
sel += v;
else
sel += '="' + v + '"';
}
return '[' + sel + ']';
}
return null;
},
query: function(propName, v){
var sel = this.getSelector(propName, v);
if( sel )
return this.doc.querySelector(sel);
return null;
},
queryAll: function(propName, v){
var sel = this.getSelector(propName, v);
if( sel )
return this.doc.querySelectorAll(sel);
return null;
},
};
retVal.init(d);
return retVal;
}
//-------------------------------------- DATASET -------------------------------------//
//------------------------------------------------------------------------------------//
//-------------------------------------- CLASSES -------------------------------------//
function initImageBoardClasses(d)
{
var retVal = {
get counted(){return this.data.counted;},
get viewActive(){return this.data.viewActive;},
get viewAttach(){return this.data.viewAttach;},
get ready(){return this.data.ready;},
get downloaded(){return this.data.downloaded;},
get downloadAttach(){ return this.data.downloadAttach;},
get downloadActive(){ return this.data.downloadActive;},
data: {
counted: 'image-board-counted',
viewAttach: 'image-board-attach-view-event',
viewActive: 'image-board-active-for-view',
ready: 'image-board-has-original-source',
downloaded: 'image-board-downloaded-original',
downloadAttach: 'image-board-attach-download-event',
downloadActive: 'image-board-active-for-download',
},
hasClass: function(elm, propName){
if( this.data[propName] )
return hasClass(elm, this.data[propName]);
return false;
},
addClass: function(elm, propName){
if( this.data[propName] )
addClass(elm, this.data[propName]);
},
removeClass: function(elm, propName){
if( this.data[propName] )
removeClass(elm, this.data[propName]);
},
toggleClass: function(elm, newPropName, oldPropName){
if( oldPropName && !this.data[oldPropName] )
return;
else if( !newPropName || !this.data[newPropName] )
return;
toggleClass( elm, this.data[newPropName], this.data[oldPropName] );
},
queryAll: function(propName){
if( this.data[propName] )
return this.doc.querySelectorAll('.' + this.data[propName]);
return null;
},
init: function(doc){this.doc = doc || document;},
};
retVal.init(d);
return retVal;
}
//-------------------------------------- CLASSES -------------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------- DOWNLOADER -----------------------------------//
function initImageBoardDownloader(d)
{
var iter = {
total : 0,
done: 0,
};
var retVal = {
get total(){return iter.total;},
set total(n){
iter.total = n;
this.downloadAllHtml(iter.total, iter.done);
},
get done(){return iter.done;},
set done(n){
iter.done = n;
this.downloadAllHtml(iter.total, iter.done);
},
data: {
downloaderId: 'image-board-downloader-' + RANDOM,
downloadAllId: 'image-board-download-all-' + RANDOM,
downloadSwitchId: 'image-board-download-switch-' + RANDOM,
classBtn: 'image-board-downloader-button',
classOn: 'image-board-downloader-on',
classOff: 'image-board-downloader-off',
classActive: 'image-board-downloader-active',
},
get downloaderId(){return this.data.downloaderId;},
get downloadAllId(){return this.data.downloadAllId;},
get downloadSwitchId(){return this.data.downloadSwitchId;},
get classBtn(){return this.data.classBtn;},
get classOn(){return this.data.classOn;},
get classOff(){return this.data.classOff;},
get classActive(){return this.data.classActive;},
init: function(id, doc){
doc = doc || document;
clog("[initImageBoardDownloader] init, doc: ", doc);
var div = doc.querySelector('div#' + id), html = '', btn;
clog("div: ", div, id);
if( !div )
{
console.error("[initImageBoardDownloader] can't find div#" + id);
return;
}
var downloadSwitch = doc.querySelector('#' + this.downloadSwitchId);
if( !downloadSwitch )
{
btn = document.createElement('button');
btn.setAttribute('id', this.downloadSwitchId);
btn.setAttribute('class', this.classOff + ' ' + this.classBtn );
btn.setAttribute('title', 'Press \'Shift+D\' to switch download mode on/off');
btn.appendChild(document.createTextNode('Donwload Mode'));
downloadSwitch = div.appendChild( btn );
}
var downloadAll = doc.querySelector('#' + this.downloadAllId);
if( !downloadAll )
{
btn = document.createElement('button');
btn.setAttribute('id', this.downloadAllId );
btn.setAttribute('class', this.classBtn );
btn.appendChild(document.createTextNode('Donwload All (0)'));
downloadAll = div.appendChild( btn );
}
return div;
},
downloadAllHtml: function( total, loaded, elm ){
if( !elm ) elm = document.querySelector('#' + this.downloadAllId );
elm.textContent = 'Download All (' + (loaded ? loaded + ' / ': '') + (total ? total : 0) + ')';
},
downloadOn: function(elm){
if( !elm ) elm = document.querySelector('#' + this.downloadSwitchId);
if( elm )
toggleClass( elm, this.classOn, this.classOff );
else
console.error("[downloadOn] empty elm: ", elm );
},
downloadOff: function(elm){
if( !elm ) elm = document.querySelector('#' + this.downloadSwitchId);
if( elm )
toggleClass( elm, this.classOff, this.classOn );
else
console.error("[downloadOff] empty elm: ", elm );
},
isActive: function(elm){
if( !elm ) elm = document.querySelector('#' + this.downloadSwitchId);
return hasClass(elm, this.classOn);
},
};
return retVal;
}
//------------------------------------- DOWNLOADER -----------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------ DOWNLOADER-2 ----------------------------------//
function handleDownloadEvent(event)
{
if( !imageBoard.imgBrdCl.hasClass( this, 'downloadActive' ) )
return;
event.preventDefault();
var thumb = event.target,
index = imageBoard.imgBrdDt.val( thumb, 'index' ),
imgD = imageBoard.images.list[index];
downloadFile( imgD );
}
function downloadFile( imgD )
{
if( imgD.state !== 'ready' || imgD.downloadState === 'downloaded' || imgD.downloadState === 'inProgress' )
return;
imgD.downloadState = 'inProgress';
var hostname = getLocation(imgD.source, 'hostname'), source, ext, stripe;
if( userOptions.val('downloadJPEG') && imgD.jpegSource )
source = imgD.jpegSource;
else
source = imgD.source;
ext = getFileExt(source);
stripe = document.querySelectorAll('#progress-stripe-' + imgD.index);
addClass( stripe, 'download-in-progress' );
if( userOptions.val('animateProgress') )
addClass( stripe, 'progress-animated' );
attr( stripe, 'style', 'width:0%;' );
if( hostname === window.location.hostname )
{
imageBoardDownloader( imgD, source, ext );
return;
}
GM.xmlHttpRequest({
url: source,
method: 'GET',
context: {
'index': imgD.index,
'url': source,
'ext': ext,
'stripe': stripe,
},
responseType: 'blob',
onload: blibBlobDownloader,
onprogress: downloadProgress,
});
}
function downloadProgress( xhr )
{
try{
if( !xhr.lengthComputable )
return;
var stripe = xhr.context.stripe || document.querySelectorAll('#progress-stripe-' + xhr.context.index),
width = Math.floor(xhr.loaded/xhr.total*100);
attr(stripe, 'style', 'width:' + width + '%');
}catch(e){console.error(e);}
}
function blibBlobDownloader( xhr )
{
var imgD = imageBoard.images.list[xhr.context.index];
if( xhr.status !== 200 )
{
console.error("xhr.status: ", xhr.status, xhr.statusText);
console.error("url: " + xhr.context.url);
if( imgD && imgD.downloadState === 'inProgress' )
imgD.downloadState = '';
return;
}
var wndURL = window.webkitURL || window.URL,
resource = wndURL.createObjectURL(xhr.response);
imageBoardDownloader( imgD, resource, xhr.context.ext );
wndURL.revokeObjectURL( resource );
}
function imageBoardDownloader( imgD, resource, extension )
{
var name = imgD.name + '.' + (extension || imgD.extension);
fileDownloader( name, resource );
var thumb = imageBoard.imgBrdDt.query('index', imgD.index + ''),
stripe = document.querySelectorAll('#progress-stripe-' + imgD.index);
imageBoard.imgBrdCl.addClass( thumb, 'downloaded' );
if( imgD.downloadState !== 'downloaded' )
imageBoard.imgBrdDw.done += 1;
imgD.downloadState = 'downloaded';
attr(stripe, 'style', 'width:100%');
//setTimeout(function(){
removeClass( stripe, 'download-in-progress' );
removeClass( stripe, 'progress-animated' );
addClass( stripe, 'progress-complete' );
//}, 50 );
}
function fileDownloader( name, resource )
{
var a = document.createElement('a'),
body = document.body || document.getElementsByTagName('body')[0];
a.setAttribute('download', name);
a.href = resource;
body.appendChild(a);
a.click();
body.removeChild(a);
}
function handleDownloadAllEvent(event)
{
var list = imageBoard.images.list;
for( var i = 0, len = list.length, imgD; i < len; ++i )
{
imgD = list[i];
downloadFile( imgD );
}
}
function handleDownloadSwitchEvent(event)
{
if( imageBoard.imgBrdDw.isActive() )
{
imageBoard.downloader.deactivate();
}else{
imageBoard.downloader.activate();
}
}
//------------------------------------ DOWNLOADER-2 ----------------------------------//
//------------------------------------------------------------------------------------//
//-------------------------------------- KEYBOARD ------------------------------------//
function activateKeyboard()
{
window.addEventListener('keydown', handleKeyboardEvent, false);
clog("--------> keyboard activated");
}
function deactivateKeyboard()
{
window.removeEventListener('keydown', handleKeyboardEvent, false);
clog("--------> keyboard deactivated");
}
function handleKeyboardEvent(event)
{
var charCode = event.keyCode || event.which,
str = String.fromCharCode(charCode).toLowerCase();
if( !event.shiftKey || event.ctrlKey || event.altKey )
return;
else if( str === 'a' )
{
handleDownloadAllEvent();
}
else if( str === 'd' )
{
handleDownloadSwitchEvent();
}
else if( str === 'i' )
{
if( imageBoard )
{
imageBoard.init();
imageBoard.fix();
}
}
else if( str === 'm' )
{
handleUserMenuEvent();
}
else if( str === 'v' )
{
handleViewerSwitchEvent();
}
}
//-------------------------------------- KEYBOARD ------------------------------------//
//------------------------------------------------------------------------------------//
//--------------------------------------- VIEWER -------------------------------------//
function initImageBoardViewer(d)
{
var iter = {
curr: 0,
total: 0,
};
var retVal = {
get curr(){return iter.curr;},
set curr(n){
n = parseInt(n, 10);
var elm = (this.doc || document).querySelector('#' + this.currentId);
if( elm )
elm.textContent = '' + (n + 1);
iter.curr = n;
},
set total(n){iter.total = parseInt(n, 10);},
get total(){return iter.total;},
data: {
buttonId: 'image-board-viewer-button-' + RANDOM,
containerId: 'image-board-viewer-container-' + RANDOM,
tagsId: 'image-board-viewer-tags-' + RANDOM,
listId: 'image-board-viewer-list-' + RANDOM,
bottomId: 'image-board-viewer-bottom-' + RANDOM,
thumbsId: 'image-board-viewer-thumbs-' + RANDOM,
// bottom div panel
prevId: 'image-board-viewer-show-prev-' + RANDOM,
nextId: 'image-board-viewer-show-next-' + RANDOM,
downloadId: 'image-board-viewer-downlaod-' + RANDOM,
closeId: 'image-board-viewer-close-' + RANDOM,
sourceId: 'image-board-viewer-source-' + RANDOM,
currentId: 'image-board-viewer-current-' + RANDOM,
// classes
classActive: 'image-board-viewer-active',
classOn: 'image-board-viewer-on',
classOff: 'image-board-viewer-off',
classBtn: 'image-board-viewer-btn',
classBottom: 'image-board-viewer-bottom-class',
},
get buttonId(){return this.data.buttonId;},
get containerId(){return this.data.containerId;},
get tagsId(){return this.data.tagsId;},
get listId(){return this.data.listId;},
get bottomId(){return this.data.bottomId;},
get thumbsId(){return this.data.thumbsId;},
get prevId(){return this.data.prevId;},
get nextId(){return this.data.nextId;},
get downloadId(){return this.data.downloadId;},
get closeId(){return this.data.closeId;},
get sourceId(){return this.data.sourceId;},
get currentId(){return this.data.currentId;},
get classActive(){return this.data.classActive;},
get classOn(){return this.data.classOn;},
get classOff(){return this.data.classOff;},
get classBtn(){return this.data.classBtn;},
get classBottom(){return this.data.classBottom;},
init: function(id, doc, selector){
if( !userOptions.val('createViewer') )
return;
doc = doc || document;
this.doc = doc;
var div = doc.querySelector('#' + id), viewDiv, html;
if( !div )
{
console.error("[initImageBoardViewer] imageBoard div not found, id: " + id);
return;
}
var btn = doc.querySelector('#' + this.buttonId);
if( !btn )
{
btn = document.createElement('button');
btn.setAttribute('id', this.buttonId);
btn.setAttribute('class', this.classOff);
btn.appendChild(document.createTextNode('Viewer'));
btn = div.insertBefore( btn, div.firstChild );
}
var cont = doc.querySelector('#' + this.containerId);
if( !cont )
{
cont = document.createElement('div');
var obj = {
'id': this.containerId,
'class': this.classOff + ' image-board-viewer-container',
'data-class-button': this.classBtn,
'data-prev-id': this.prevId,
'data-next-id': this.nextId,
'data-download-id': this.downloadId,
'data-current-id': this.currentId,
'data-source-id': this.sourceId,
'data-close-id': this.closeId,
'data-list-id': this.listId,
};
for( var key in obj )
cont.setAttribute( key, obj[key] );
html = '' +
'<div id="' + this.tagsId + '" class="viewer-tag-list" tabindex="1000"></div>' +
'<div id="' + this.thumbsId + '" class="viewer-thumb-list" tabindex="1001"></div>' +
'<div id="' + this.listId + '" class="viewer-img-list" style="text-align:center;"></div>' +
'<div id="' + this.bottomId + '" class="' + this.classBottom + ' viewer-bottom">' +
'<button id="' + this.prevId + '" class="' + this.classBtn + ' viewer-navigation-bar">Prev</button>' +
'<button id="' + this.sourceId + '" class="' + this.classBtn + ' viewer-navigation-bar">Source</button>' +
'<button id="' + this.closeId + '" class="' + this.classBtn + ' viewer-navigation-bar">Close</button>' +
'<button id="' + this.currentId + '" class="viewer-navigation-bar" style="width:40px;">' + '-' + '</button>' +
'<button id="' + this.downloadId + '" class="' + this.classBtn + ' viewer-navigation-bar">Download</button>' +
'<button id="' + this.nextId + '" class="' + this.classBtn + ' viewer-navigation-bar">Next</button>' +
'</div>';
cont.insertAdjacentHTML('beforeend', html);
if( userOptions.val('fixedThumbs') )
addClass( cont, 'viewer-thumb-list-fixed' );
if( userOptions.val('fixedTags') )
addClass( cont, 'viewer-tag-list-fixed' );
doc.body.appendChild(cont);
}
if( !cont.classList.contains(this.classActive) )
{
cont.addEventListener('click', handleViewerNavigationEvent, false);
cont.classList.add(this.classActive);
activateViewerKeyboard();
var activateSidebar = function(elm)
{
if( elm )
{
elm.addEventListener('mouseover', function(){this.focus();}, false);
elm.addEventListener('mouseout', function(){this.blur();}, false);
}
};
activateSidebar(doc.querySelector('#' + this.tagsId ));
activateSidebar(doc.querySelector('#' + this.thumbsId ));
}
},
showNext: function(){
if( !this.isActive() )
return;
try{
var idx = (this.curr + this.total + 1)%this.total;
viewImage(idx);
}catch(e){console.error(e);}
},
showPrev: function(){
if( !this.isActive() )
return;
try{
var idx = (this.curr + this.total - 1)%this.total;
viewImage(idx);
}catch(e){console.error(e);}
},
isActive: function(){
if( !this.btn ) this.btn = document.querySelector('#' + this.buttonId);
return hasClass( this.btn, this.classOn );
},
viewerOn: function(){
if( !this.btn ) this.btn = document.querySelector('#' + this.buttonId);
if( !this.cont ) this.cont = document.querySelector('#' + this.containerId);
toggleClass(this.btn, this.classOn, this.classOff);
toggleClass(this.cont, this.classOn, this.classOff);
this.setOverflow = this.setOverflow || function(elm, val){if(elm) elm.style.overflow = val;};
try{
var html = this.cont.querySelector('#' + this.currentId).textContent;
if( userOptions.val('viewFirst') && html === '-' )
viewImage(0);
else
viewImage('-');
this.setOverflow( document.body || document.getElementsByTagName('body')[0], 'hidden' );
this.setOverflow( document.getElementsByTagName('html')[0], 'hidden' );
}catch(e){console.error(e);}
resumeVideo(this.curr);
},
viewerOff: function(){
this.setOverflow = this.setOverflow || function(elm, val){if(elm) elm.style.overflow = val;};
this.setOverflow( document.body || document.getElementsByTagName('body')[0], 'auto' );
this.setOverflow( document.getElementsByTagName('html')[0], 'auto' );
toggleClass(this.btn, this.classOff, this.classOn);
toggleClass(this.cont, this.classOff, this.classOn);
historyChange( null );
pauseVideo(this.curr);
},
};
return retVal;
}
//--------------------------------------- VIEWER -------------------------------------//
//------------------------------------------------------------------------------------//
//-------------------------------------- VIEWER-2 ------------------------------------//
function activateViewerKeyboard()
{
window.addEventListener('keydown', handleViewerKeyboardEvent, false);
}
function deactivateViewerKeyboard()
{
window.removeEventListener('keydown', handleViewerKeyboardEvent, false);
}
function handleViewerKeyboardEvent(event)
{
var charCode = event.keyCode || event.which,
useCtrl = userOptions.val('holdCtrl') || window.location.hostname.indexOf('donmai.us') != -1,
condition1 = event.shiftKey || !event.ctrlKey || event.altKey,
condition2 = event.shiftKey || event.ctrlKey || event.altKey;
if( (useCtrl && condition1) || (!useCtrl && condition2) )
return;
var viewer = imageBoard.imgBrdVw;
if( charCode == 37 )
viewer.showPrev();
else if( charCode == 39 )
viewer.showNext();
}
function handleViewerEvent(event)
{
if( !imageBoard.imgBrdCl.hasClass( this, 'viewActive' ) )
return;
event.preventDefault();
var t = event.target;
if( t.tagName !== 'IMG' )
t = t.firstChild;
if( t.tagName !== 'IMG' )
return;
var idx = imageBoard.imgBrdDt.val(t, 'index');
clog("[handleViewerEvent] index: " + idx);
if( idx !== null && idx !== undefined )
viewImage( idx );
else
console.error("image index not found, img: ", t.src);
}
function viewImage( idx )
{
if( !imageBoard )
return;
var viewer = imageBoard.imgBrdVw,
hostname = window.location.hostname,
div, imgD, dwSource;
if( !viewer || !viewer.isActive() )
return;
idx = parseInt(idx, 10);
idx = (viewer.total + idx)%viewer.total;
makeThumbListHTML();
makeImageListHTML();
imgD = imageBoard.images.list[idx];
if( !imgD || imgD.state !== 'ready' )
return;
setImageList(true);// loop over all .viewer-img-list > div
if( hostname.indexOf('sankakucomplex') != -1 )
historyChange( imgD.postUrl );
div = document.querySelector('div[data-image-index="' + imgD.index + '"]');
loadImage(imgD.index, div);
if( userOptions.val('downloadJPEG') && imgD.jpegSource )
dwSource = imgD.jpegSource;
else
dwSource = imgD.source;
removeClass( document.querySelectorAll('div.viewer-img-div'), 'img-show' );
addClass( div, 'img-show' );
makeTagListHTML(idx);
setViewerBottom( viewer, dwSource, imgD.name );
pauseVideo(viewer.curr);
viewer.curr = idx;
}
//------------------------------------------------------------------------------------//
//--------------------------------- VIEWER-THUMB-LIST --------------------------------//
function makeThumbListHTML()
{
var thumbListDiv = document.querySelector('.viewer-thumb-list');
if( !thumbListDiv )
{
console.error("[makeThumbListHTML] can't find div");
return;
}
var imageList = imageBoard.images.list,
thumbTitle = '', html = '',
oldLen = thumbListDiv.getAttribute('data-viewer-thumb-length') || 0,
animate = userOptions.val('animateProgress'),
showProgress = userOptions.val('showProgress'),
i, len, imgD, img;
oldLen = parseInt(oldLen, 10);
for( i = oldLen, len = imageList.length; i < len; ++i )
{
imgD = imageList[i];
if( imgD.tags )
thumbTitle = imgD.tags.join(' ');
else if( imgD.thumbTitle )
thumbTitle = imgD.thumbTitle;
else
thumbTitle = '';
html += '<div class="viewer-thumb-div" data-viewer-thumb-div-index="' + imgD.index + '">';
html += '<span class="viewer-thumb-span">';
html += '<img data-viewer-thumb-index="' + imgD.index + '" ' +
'class="viewer-thumb" src="' + (imgD.thumbSource || imgD.lowresSource) + '" ' +
'title="' + thumbTitle + '"/>';
html += '</span>';
html += (showProgress ? makeProgressBarHTML( imgD, animate) : '');
html += '</div>';
}
thumbListDiv.setAttribute('data-viewer-thumb-length', len);
thumbListDiv.insertAdjacentHTML('beforeend', html);
if( hasClass(thumbListDiv, 'viewer-thumb-list-activated') )
return;
thumbListDiv.addEventListener('click', handleViewerThumbEvent, false);
addClass(thumbListDiv, 'viewer-thumb-list-activated');
}
function makeProgressBarHTML( imgD, animate )
{
return '<div class="progress-bar">' +
'<div id="progress-stripe-' + imgD.index + '" class="progress-stripe progress-counted' +
(imgD.state === 'ready' ? ' image-ready' : '') + '' +
(imgD.state === 'busy' && !!animate ? ' animate-progess' : '') + '' +
(imgD.downloadState === 'inProgress' ? ' download-in-progress' : '') + '' +
(imgD.downloadState === 'downloaded' ? ' progress-complete': '') + '' +
'"></div></div>';
}
function showViewerProgressBar( showProgress )
{
var animate = userOptions.val('animateProgress'),
divList = document.querySelectorAll('.viewer-thumb-div'),
imageList = imageBoard.images.list,
animate = userOptions.val('animateProgress'),
html, i, len, div, index, imgD, stripe;
for( i = 0, len = divList.length; i < len; ++i )
{
div = divList[i];
index = div.getAttribute('data-viewer-thumb-div-index');
stripe = div.querySelector('#progress-stripe-' + index);
if( !showProgress )
hide(stripe);
else if( !stripe )
{
imgD = imageList[index];
html = makeProgressBarHTML(imgD, animate);
div.insertAdjacentHTML('beforeend', html);
}else
show(stripe);
}
}
function handleViewerThumbEvent(event)
{
var t = event.target;
if( t.tagName !== 'IMG' )
return;
var idx = t.getAttribute('data-viewer-thumb-index');
viewImage(idx);
}
//--------------------------------- VIEWER-THUMB-LIST --------------------------------//
//------------------------------------------------------------------------------------//
//--------------------------------- VIEWER-IMAGE-LIST --------------------------------//
function makeImageListHTML()
{
var imgListDiv = document.querySelector('.viewer-img-list');
if( !imgListDiv )
return;
var imageList = imageBoard.images.list,
imgLen = imgListDiv.getAttribute('data-image-list-length') || 0,
html = '', i, len;
imgLen = parseInt(imgLen, 10);
for( i = imgLen, len = imageList.length; i < len; ++i )
{
html += '<div data-image-index="' + i + '" class="viewer-img-div">';
html += '<video class="vid_file" controls loop></video>';
html += '<img class="orig_img"></img>';
html += '<img class="jpeg_img"></img>';
html += '<img class="samp_img"></img>';
html += '</div>';
}
imgListDiv.setAttribute('data-image-list-length', len);
imgListDiv.insertAdjacentHTML('beforeend', html);
}
function setImageList( reset )
{
var imgListDiv = document.querySelector('.viewer-img-list');
if( !imgListDiv )
return;
var imageDivs = imgListDiv.querySelectorAll('.viewer-img-div'),
imageList = imageBoard.images.list,
imgLen = imgListDiv.getAttribute('data-image-list-length') || 0,
viewOriginal = userOptions.val('viewOriginal'),
viewJPEG = userOptions.val('viewJPEG'),
i, imgD, imgDiv, viewType;
imgLen = parseInt(imgLen, 10);
for( i = 0; i < imgLen; ++i )
{
imgD = imageList[i];
imgDiv = imageDivs[i];
viewType = imgDiv.getAttribute('data-image-view-type');
if( reset || !viewType || viewType === 'none_src' )
imgDiv.setAttribute('data-image-view-type', getImageViewType(imgD, viewOriginal, viewJPEG) );
}
}
function getImageViewType( imgD, viewOriginal, viewJPEG )
{
if( imgD['vid_file-source'] )
imgD.viewType = 'vid_file';
else if( imgD['jpeg_img-source'] && viewJPEG && viewOriginal )
imgD.viewType = 'jpeg_img';
else if( imgD['orig_img-source'] && (viewOriginal || !imgD['samp_img-source']) )
imgD.viewType = 'orig_img';
else if( imgD['samp_img-source'] )
imgD.viewType = 'samp_img';
else
imgD.viewType = 'none_src';
return imgD.viewType;
}
//--------------------------------- VIEWER-IMAGE-LIST --------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------ LOAD-IMAGE ------------------------------------//
function loadImage(idx, div, isNext)
{
idx = parseInt(idx, 10);
div = div || document.querySelector('div[data-image-index="' + idx + '"]');
var viewType = div.getAttribute('data-image-view-type');
if( !viewType || viewType === 'none_src' )
return;
var img = div.querySelector('.' + viewType),
//complete = (img.tagName === 'IMG' && img.complete),// || (img.tagName === 'VIDEO' && img.readyState > 2),
imgD = imageBoard.images.list[idx],
total = imageBoard.imgBrdVw.total,
curr = imageBoard.imgBrdVw.curr,
diff = (total + idx - curr)%total,
next = (total + idx + diff)%total;
if( !!isNext && img.tagName === 'VIDEO' )
return;
else if( !img.src )
img.src = imgD[viewType + '-source'];
if( (diff == 1 || diff == (total-1)) && total > 1 )
{
if( (img.tagName === 'IMG' && img.complete) || img.tagName === 'VIDEO' )
loadImage( next, diff == 1 ? div.nextSibling : div.previousSibling, true );
else{
img.setAttribute('data-index-diff', (diff == 1 ? 1 : -1) );
img.setAttribute('data-index-next', next );
img.addEventListener('load', preloadImageEvent, false);
}
}
if( img.tagName === 'VIDEO' && img.src )
resumeVideo( idx, div, img );
}
function preloadImageEvent(event)
{
var t = this,
p = t.parentNode,
diff = t.getAttribute('data-index-diff'),
next = t.getAttribute('data-index-next');
if( diff == 1 )
loadImage(next, p.nextSibling, true);
else if( diff == -1 )
loadImage(next, p.previousSibling, true);
else
loadImage(next);
t.removeAttribute('data-index-diff');
t.removeAttribute('data-index-next');
setTimeout(function(){
t.removeEventListener('load', preloadImageEvent, false);
}, 100);
}
function getVideoElm( idx, div, img )
{
if( !img || img.tagName === 'VIDEO' )
{
div = div || document.querySelector('[data-image-index="' + idx + '"]');
if( div.getAttribute('data-image-view-type') != 'vid_file' )
return;
img = div.querySelector('.vid_file');
}
return img;
}
function pauseVideo( idx, div, img )
{
img = getVideoElm( idx, div, img );
if( !img )
return;
else if( img.paused )
addClass( img, 'video-paused');
else
removeClass( img, 'video-paused');
img.pause();
}
function resumeVideo( idx, div, img, forcePause, playPromise )
{
img = getVideoElm( idx, div, img );
var playPromise = img.play();
if( !img )
return;
else if( forcePause || hasClass(img, 'video-paused') )
img.pause();
else if (playPromise !== undefined) {
playPromise.then(_ => {
// Automatic playback started!
// Show playing UI.
})
.catch(error => {
// Auto-play was prevented
// Show paused UI.
});
}
else
img.play()
}
//------------------------------------------------------------------------------------//
//---------------------------------- VIEWER-TAG-LIST ---------------------------------//
function makeTagListHTML( idx )
{
var tagListDiv = document.querySelector('.viewer-tag-list');
if( !tagListDiv )
{
console.error("[makeTagListHTML] can't find div");
return;
}
var imgD = imageBoard.images.list[idx],
tagList = imgD.tagList,
tagClass = (tagList && tagList[0] || {'class': ''}).class,
templ, html = '';
if( tagClass === 'tag-type' )
templ = '<li class="tag-type-';
else if( tagClass === 'category' )
templ = '<li class="category-';
else
templ = '<li class="empty-category';
html = '<h4 style="color:#a0a0a0;">Tags</h4>';
for( var i = 0, tagObj; i < tagList.length; ++i )
{
tagObj = tagList[i];
html += templ + (tagObj.category !== null ? tagObj.category : '') + '">';
html += makeTagHTML( tagObj );
html += '</li>';
}
tagListDiv.innerHTML = html;
}
function makeTagHTML( tagObj )
{
var html = (tagObj.wiki ? '<a href="' + tagObj.wiki + '"> ? </a>' : '') + '' +
'<a href="' + tagObj.href + '">' + tagObj.name + '</a>' +
'<span class="post-count" style="color:#a0a0a0;"> ' + (!!tagObj.count ? tagObj.count : '') + ' </span>';
return html;
}
//---------------------------------- VIEWER-TAG-LIST ---------------------------------//
//------------------------------------------------------------------------------------//
function historyChange( postURL )
{
this.href = this.href || window.location.href;
if( postURL )
window.history.replaceState(null, null, postURL);
else
window.history.replaceState(null, null, this.href);
}
function setViewerBottom( viewer, source, name )
{
var doc = document,
prevElm = doc.querySelector('#' + viewer.prevId ),
nextElm = doc.querySelector('#' + viewer.nextId ),
sourceElm = doc.querySelector('#' + viewer.sourceId ),
downloadElm = doc.querySelector('#' + viewer.downloadId ),
useCtrl = userOptions.val('holdCtrl') || window.location.hostname.indexOf('donmai.us') != -1;
prevElm.setAttribute('title', (useCtrl ? 'Ctrl+' : '') + 'Left');
nextElm.setAttribute('title', (useCtrl ? 'Ctrl+' : '') + 'Right');
sourceElm.setAttribute('title', source );
downloadElm.setAttribute('title', name );
}
function handleViewerSwitchEvent(event)
{
if( imageBoard.viewer.isActive() )
{
imageBoard.viewer.deactivate();
}else{
imageBoard.viewer.activate();
}
}
function handleViewerNavigationEvent(event)
{
var t = event.target,
viewer, idx, total, imgD;
if( !hasClass(t, this.getAttribute('data-class-button')) )
return;
viewer = imageBoard.imgBrdVw;
if( !viewer )
return;
idx = viewer.curr;
total = viewer.total;
clog("[navigation] index: " + idx);
clog("[navigation] total: " + total);
idx = parseInt(idx, 10);
total = parseInt(total, 10);
if( t.id == this.getAttribute('data-prev-id') )
{
viewImage( (idx + total - 1)%total );
}
else if( t.id == this.getAttribute('data-next-id') )
{
viewImage( (idx + total + 1)%total );
}
else if( t.id == this.getAttribute('data-download-id') )
{
imgD = imageBoard.images.list[idx];
downloadFile( imgD );
}
else if( t.id == this.getAttribute('data-source-id') )
{
imgD = imageBoard.images.list[idx];
window.open( imgD.source );
}
else if( t.id == this.getAttribute('data-close-id') )
{
handleViewerSwitchEvent();
}
}
//-------------------------------------- VIEWER-2 ------------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------- USER MENU ------------------------------------//
function initUserMenu()
{
var retVal = {
data: {
'container-id': 'image-board-user-menu-container-' + RANDOM,
'container-class': 'image-board-user-menu-container',
'title-class': 'image-board-user-menu-title',
'content-class': 'image-board-user-menu-content',
'bottom-class': 'image-board-user-menu-bottom',
'open-class': 'image-board-user-menu-open',
'close-class': 'image-board-user-menu-close',
},
get containerId(){return this.data['container-id'];},
get containerClass(){return this.data['container-class'];},
get titleClass(){return this.data['title-class'];},
get contentClass(){return this.data['content-class'];},
get bottomClass(){return this.data['bottom-class'];},
get openClass(){return this.data['open-class'];},
get closeClass(){return this.data['close-class'];},
init: function(id, doc){
doc = doc || document;
var div = doc.querySelector('div#' + id), btn;
clog("div: ", div, id);
if( !div )
{
console.error("[initUserMenu] can't find div#" + id);
return;
}
var userMenuId = 'image-board-user-menu-id-' + RANDOM,
userMenuActive = 'image-board-user-menu-button-active',
userMenuBtn = div.querySelector('#' + userMenuId );
if( !userMenuBtn )
{
btn = document.createElement('button');
btn.setAttribute('id', userMenuId );
for( var key in this.data )
btn.setAttribute('data-' + key, this.data[key] );
btn.setAttribute('title', 'Press \'Shift+M\' to open/close User Menu');
btn.appendChild(document.createTextNode('User Menu'));
userMenuBtn = div.insertBefore( btn, div.firstChild );
}
return div;
},
};
return retVal;
}
function handleUserMenuEvent(event)
{
var dataSet = (this.dataset && this.dataset.containerId ? this.dataset : imageBoard.userMenu ),
div = document.querySelector('#' + dataSet.containerId ),
body = document.body, contentHtml, bottomHtml,
html = '';
if( !div )
{
contentHtml = makeUserMenuContentHtml();
bottomHtml = makeUserMenuBottomHtml();
div = document.createElement('div');
div.setAttribute('id', dataSet.containerId);
div.setAttribute('class', dataSet.containerClass);
html = '' +
'<div class="' + dataSet.titleClass + '">' +
'<span>' + GM.info.script.name + ' v' + GM.info.script.version + '</span>' +
'<span class="image-board-user-menu-x"></span>' +
'</div>' +
'<div class="' + dataSet.contentClass + '">' + contentHtml + '</div>' +
'<div class="' + dataSet.bottomClass + '">' + bottomHtml + '</div>';
div.insertAdjacentHTML('beforeend', html);
div = body.appendChild(div);
addClass( div, dataSet.closeClass );
activateUserMenu();
}
if( hasClass(div, dataSet.openClass) )
{
closeUserMenu.call(this);
imageBoard.imgBrdSt.userMenu = false;
}
else if( hasClass(div, dataSet.closeClass) )
{
openUserMenu.call(this);
imageBoard.imgBrdSt.userMenu = true;
}
}
function makeUserMenuContentHtml()
{
var typeList = ['checkbox', 'number', 'text'],
longOptions = ['tagsOrder', 'ignoredTags'],
html = '', key, dt, inp, labl, inputWidth, i, k, catName, keyList;
html += '<div class="image-board-user-menu-tabs-navigation">';
html += '<ul class="image-board-user-menu-tabs-list">';
for( i = 0; i < userOptions.categoryList.length; ++i )
{
catName = userOptions.categoryList[i];
html += '<li id="image-board-user-menu-tab-nav-' + catName.replace(/\s+/g, '-') + '" ' +
'class="' + (catName === 'General' ? 'tab-nav-active' : '') + '"><span>' + catName + '</span></li>';
}
html += '</ul></div>';
html += '<div class="image-board-user-menu-tabs-content">';
for( i = 0; i < userOptions.categoryList.length; ++i )
{
catName = userOptions.categoryList[i];
keyList = userOptions.category(catName);
html += '<div id="image-board-user-menu-tab-' + catName.replace(/\s+/g, '-') + '" ' +
'tab-selected="' + (catName === 'General' ? 'true' : 'false') + '">';
for( k = 0; k < keyList.length; ++k )
{
key = keyList[k];
dt = userOptions.data[key];
if( typeList.indexOf(dt.type) == -1 )
continue;
inputWidth = (longOptions.indexOf(key) != -1 ? '200px' : '70px');
inp = '<input id="image-board-user-menu-' + key + '-val" type="' + dt.type + '" ' +
'style="' + (dt.type!=='checkbox' ? 'text-align: center; width: ' + inputWidth: '') + '"/>';
labl = '<label id="image-board-user-menu-' + key + '-caption" ' + (key == 'holdCtrl' ? 'title="Hodor" ': '') + '' +
'for="image-board-user-menu-' + key + '-val" style="cursor: pointer;">' + dt.getDesc() + '</label>';
html += '<section class="image-board-user-menu-section">' +
(dt.type === 'checkbox' ? inp + labl : labl + inp ) + '</section>';
}
html += '</div>';
}
html += '</div>';
return html;
}
function getUserOptionsListOf( prop )
{
var propList = [], key, dt;
for( key in userOptions.data )
{
dt = userOptions.data[key];
if( propList.indexOf(dt[prop]) == -1 )
propList.push(dt[prop]);
}
return propList;
}
function makeUserMenuBottomHtml()
{
this.btnList = this.btnList || {
'reset': {
html: 'Reset',
title: 'Reset all options to default ones',
},
'remove': {
html: 'Remove',
title: 'Remove all saved options',
},
'save': {
html: 'Save Settings',
title: '',
},
};
var key, val, html = '';
for( key in this.btnList )
{
val = this.btnList[key];
html += '<button id="image-board-user-menu-' + key + '-button" class="user-menu-button" ' +
'title="' + val.title + '">' + val.html + '</button>';
}
return html;
}
function activateUserMenu()
{
var doc = document,
active = 'image-board-user-menu-button-active',
btn, key;
var userMenuMethodsObj = {
'save': saveUserMenu,
'remove': removeUserMenu,
'reset': resetUserMenu,
'x': closeUserMenu,
'tabs-navigation': tabsUserMenu,
};
for( key in userMenuMethodsObj )
{
btn = doc.querySelector('#image-board-user-menu-' + key + '-button');
if( !btn )
btn = doc.querySelector('.image-board-user-menu-' + key );
if( btn && !btn.classList.contains(active) )
{
btn.addEventListener('click', userMenuMethodsObj[key], false );
btn.classList.add(active);
}
}
}
function setUserMenu()
{
var doc = document,
key, dt, valueElm, captionElm;
for( key in userOptions.data )
{
dt = userOptions.data[key];
valueElm = doc.querySelector('#image-board-user-menu-' + key + '-val');
if( !valueElm )
continue;
else if( dt.type === 'checkbox' )
valueElm.checked = dt.val;
else if( dt.type === 'number' || dt.type === 'text' )
valueElm.value = _toString_( dt.val, ', ' );
captionElm = doc.querySelector('#image-board-user-menu-' + key + '-caption');
if( captionElm )
captionElm.textContent = dt.getDesc();
}
}
function saveUserMenu()
{
var doc = document,
key, dt, valueElm;
for( key in userOptions.data )
{
dt = userOptions.data[key];
valueElm = doc.querySelector('#image-board-user-menu-' + key + '-val');
if( !valueElm )
continue;
else if( dt.type === 'checkbox' )
userOptions.val(key, valueElm.checked );
else if( dt.type === 'number' || dt.type === 'text' )
userOptions.val( key, valueElm.value );
}
userOptions.saveData();
closeUserMenu();
renameImages();
resetViewerSettings();
showViewerProgressBar( userOptions.val('showProgress') );
}
function renameImages()
{
if( !imageBoard )
return;
try{
var list = imageBoard.images.list,
site = imageBoard.siteList.val();
for( var i = 0, len = list.length; i < len; ++i )
site.setImageDataName( list[i] );
}catch(error){
console.error(error);
}
}
function resetViewerSettings()
{
if( !imageBoard )
return;
var viewer = imageBoard.imgBrdVw,
container = document.querySelector('#' + viewer.containerId);
if( userOptions.val('fixedThumbs') )
addClass( container, 'viewer-thumb-list-fixed' );
else
removeClass( container, 'viewer-thumb-list-fixed' );
if( userOptions.val('fixedTags') )
addClass( container, 'viewer-tag-list-fixed' );
else
removeClass( container, 'viewer-tag-list-fixed' );
}
function removeUserMenu()
{
userOptions.removeData();
}
function resetUserMenu()
{
userOptions.setDefs();
userOptions.saveData();
setUserMenu();
renameImages();
}
function closeUserMenu()
{
var dataSet = imageBoard.userMenu.data,
userMenu = document.querySelector('#' + dataSet['container-id']);
toggleClass( userMenu, dataSet['close-class'], dataSet['open-class'] );
imageBoard.imgBrdSt.userMenu = false;
}
function openUserMenu()
{
var dataSet = imageBoard.userMenu.data,
userMenu = document.querySelector('#' + dataSet['container-id']);
toggleClass( userMenu, dataSet['open-class'], dataSet['close-class'] );
setUserMenu();
imageBoard.imgBrdSt.userMenu = true;
}
function tabsUserMenu(event)
{
var t = event.target, categoryName, tabsNav, tabs, activeTab, i;
if( t.tagName === 'SPAN' )
t = t.parentNode;
if( t.tagName !== 'LI' )
return;
tabsNav = document.querySelectorAll('.image-board-user-menu-tabs-navigation li');
tabs = document.querySelectorAll('div[tab-selected]');
for( i = 0; i < tabs.length; ++i )
{
tabs[i].setAttribute('tab-selected', 'false');
removeClass( tabsNav[i], 'tab-nav-active' );
}
categoryName = t.id.replace('image-board-user-menu-tab-nav-', '');
activeTab = document.querySelector('#image-board-user-menu-tab-' + categoryName);
if( !activeTab )
return;
activeTab.setAttribute('tab-selected', 'true');
addClass(t, 'tab-nav-active');
}
//------------------------------------- USER MENU ------------------------------------//
//------------------------------------------------------------------------------------//
//------------------------------------ USER OPTIONS ----------------------------------//
async function initOptions()
{
function _setDef(){this.val = this.def;}
var tagsType = ['character', 'copyright', 'artist', 'species', 'model', 'idol', 'photo_set', 'circle', 'medium', 'metadata', 'general', 'faults'];
var retVal = {
data: {
'autoRun': {
val: null,
def: true,
type: 'checkbox',
category: 'General',
setDef: _setDef,
getDesc: function(){return 'Initialize the Script on start';},
},
'createViewer': {
val: null,
def: true,
type: 'checkbox',
category: 'General',
setDef: _setDef,
getDesc: function(){return 'Add Image Viewer to ImageBoard';},
},
'downloadJPEG': {
val: null,
def: false,
type: 'checkbox',
category: 'General',
setDef: _setDef,
getDesc: function(){return 'Donwload jpeg instead of png (yande.re option)';},
},
'animateProgress': {
val: null,
def: true,
type: 'checkbox',
category: 'General',
setDef: _setDef,
getDesc: function(){return 'Animate initialization/downloading progress'; },
},
'maxTagsInName': {
val: null,
def: 10,
type: 'number',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Maximum tags in file name:';},
validator: function( v ){
return v > 3 && v < 100;
},
},
'tagsOrder': {
_val: null,
set val(s){
if( !this._val )
this._val = [];
if( typeof s === 'string' )
s = s.split(/\s?\,\s?/i);
else if( !s )
s = tagsType;
//copy( this._val, s );
this._val.length = 0;
for( var i = 0; i < s.length; ++i )
this._val.push(s[i].trim());
},
get val(){return this._val;},
def: tagsType.join(','),
type: 'text',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Tags order in file name:';},
validator: function(s){
if( !s )
return false;
else if( typeof s === 'string' )
s = s.trim().split(/\s?\,\s?/i);
for( var i = 0, len = s.length; i < len; ++i )
{
if( tagsType.indexOf(s[i]) == -1 )
return false;
}
return true;
},
},
'ignoredTags': {
_val: null,
set val(s){
if( !this._val )
this._val = [];
if( !s )
s = [];
else if( typeof s === 'string' )
s = s.trim().split(',');
//s.forEach(function(item){item = item.trim();});
this._val.length = 0;
for( var i = 0; i < s.length; ++i )
this._val.push(s[i].trim());
},
get val(){return this._val;},
def: '',
type: 'text',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Ignored tag names:';},
},
'tagsDelim': {
val: null,
def: '-',
type: 'text',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Tags delimeter:';},
validator: function(v){
v = v.toString();
return v.length > 0 && v.length < 5;
},
},
'addImgBrdName': {
val: null,
def: true,
type: 'checkbox',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Add ImageBoard name to file name';},
},
'prefixedName': {
val: null,
def: false,
type: 'checkbox',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Prefixed ImageBoard name';},
},
'imgIdAtNameEnd': {
val: null,
def: true,
type: 'checkbox',
category: 'Filename',
setDef: _setDef,
getDesc: function(){return 'Image ID, and ImageBoard name at file name end';},
},
'viewOriginal': {
val: null,
def: false,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'View original images';},
},
'viewJPEG': {
val: null,
def: false,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'View jpeg image (yande.re option)';},
},
'viewFirst': {
val: null,
def: true,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'Load 1st image on viewer activation';},
},
'holdCtrl': {
val: null,
def: false,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'Hold Ctrl key to left/right navigate when viewing';},
},
'fixedTags': {
val: null,
def: true,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'Fix tag list';},
},
'fixedThumbs': {
val: null,
def: false,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'Fix thumb list';},
},
'showProgress': {
val: null,
def: true,
type: 'checkbox',
category: 'Viewer',
setDef: _setDef,
getDesc: function(){return 'Show progress/status bar';},
},
},
get storageKey(){ return 'user-options-storage-key';},
get categoryList()
{
var catList = [], key, opt;
for( key in this.data )
{
opt = this.data[key];
if( catList.indexOf(opt.category) == -1 )
catList.push(opt.category);
}
Object.defineProperty(this, 'categoryList', {
get: function(){return catList;},
enumerable: true,
configurable: true,
});
return catList;
},
category: function( categoryName )
{
if( this.keyList === undefined )
{
this.keyList = {};
var opt, key, list, i, catName;
for( i = 0; i < this.categoryList.length; ++i )
{
catName = this.categoryList[i];
list = this.keyList[catName] = [];
for( key in this.data )
{
opt = this.data[key];
if( opt.category === catName )
list.push(key);
}
}
Object.defineProperty(this, 'category', {
value: function(name)
{
if( this.categoryList.indexOf(name) == -1 )
return null;
return this.keyList[name];
},
enumerable: true,
configurable: true,
});
}
return this.keyList[categoryName];
},
val: function( opt, v )
{
if( this.data[opt] )
{
if( v !== undefined )
{
if( typeof this.data[opt].validator !== 'function' && v !== null )
this.data[opt].val = v;
else if( this.data[opt].validator(v) )
this.data[opt].val = v;
}
return this.data[opt].val;
}else
return null;
},
fixStorage: async function(){
// backward compatibility with v0.2.0 and older
var oldKey = 'user-options-storage-key-1681238',
objStr = await GM.getValue( oldKey, '' );
if( objStr )
{
GM.deleteValue(oldKey);
GM.setValue( this.storageKey, objStr );
}
// backward compatibility with v0.7.0 and older
var obj = null;
objStr = await GM.getValue(this.storageKey, '');
if( objStr )
obj = JSON.parse(objStr);
if( obj && obj.viewSample !== undefined )
{
obj.viewOriginal = !obj.viewSample;
delete obj.viewSample;
GM.deleteValue(this.storageKey);
GM.setValue(this.storageKey, JSON.stringify(obj));
}
},
saveData: function(){
var storageObj = {};
for( var key in this.data )
storageObj[key] = this.data[key].val;
GM.setValue( this.storageKey, JSON.stringify(storageObj) );
},
removeData: function(){
GM.deleteValue(this.storageKey);
},
loadData: async function(){
var storageObj = await GM.getValue(this.storageKey, null), v;
if( storageObj )
storageObj = JSON.parse(storageObj);
else
storageObj = {};
clog('storage [loadData]:', storageObj);
for( var key in this.data )
{
v = storageObj[key];
if( v !== undefined )
{
clog('storage[' + key + ']: ', v);
this.val( key, v );
}else{
clog('storage[' + key + ']: ', '[default]');
this.data[key].setDef();
}
}
this.saveData();
},
setDefs: function(){
for( var key in this.data )
this.data[key].setDef();
this.saveData();
},
init: async function(){
await this.fixStorage();
await this.loadData();
},
};
await retVal.init();
clog('end [initOptions]');
return retVal;
}
//------------------------------------ USER OPTIONS ----------------------------------//
//------------------------------------------------------------------------------------//
function newCssClasses()
{
generalCssClass();
userMenuCssClass();
imageViewerCssClass();
progressBarCssClass();
}
function generalCssClass()
{
var id = 'general-css-' + RANDOM;
addCssClass(`
#image-board-div-${RANDOM} {
text-align: right;
position: relative;
}
#image-board-user-menu-container-${RANDOM} button,
#image-board-div-${RANDOM} button {
margin: 3px 10px;
color: ${imageBoard.siteList.style().color};
font-weight: bold;
width: 180px;
border: 0px;
padding: 5px;
background: ${imageBoard.siteList.style().background};
cursor: pointer;
}
.image-board-viewer-bottom-class button:hover ,
#image-board-user-menu-container-${RANDOM} button:hover ,
#image-board-div-${RANDOM} button:hover {
background: ${imageBoard.siteList.style().backgroundHover};
color: ${imageBoard.siteList.style().colorHover};
}
div.image-board-viewer-on ,
.image-board-user-menu-open {
display: initial;
}
div.image-board-viewer-off ,
.image-board-user-menu-close {
display: none;
}
.image-board-downloader-off::after {
content: " [off]";
}
.image-board-downloader-on::after {
content: " [on]";
}
button.image-board-viewer-off::after {
content: " [+]";
}
button.image-board-viewer-on::after {
content: " [\u2013]";
}
.image-board-active-for-view,
.image-board-active-for-download {
cursor: default;
}
`, id);
}
function userMenuCssClass()
{
var id = 'user-menu-css-' + RANDOM;
addCssClass(`
.image-board-user-menu-title,
#image-board-user-menu-container-${RANDOM} {
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.image-board-user-menu-bottom,
#image-board-user-menu-container-${RANDOM} {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
#image-board-user-menu-container-${RANDOM} {
position: fixed;
bottom: 10px;
right: 10px;
z-index: 100200;
background-color: #e7e7e7;
width: 40%;
height: 40%;
}
div.image-board-user-menu-title {
font-weight: bold;
line-height: 30px;
color: ${imageBoard.siteList.style().color};
background-color: ${imageBoard.siteList.style().background};
position: absolute;
width: 100%;
height: 30px;
}
div.image-board-user-menu-title > span {
padding-left: 8px;
}
.image-board-user-menu-x::after,
.image-board-user-menu-x::before {
content: "";
position: absolute;
width: 2px;
height: 1.5em;
background: ${imageBoard.siteList.style().color} !important;
display: block;
transform: rotate(45deg);
left: 50%;
margin: -1px 0 0 -1px;
top: 0;
}
.image-board-user-menu-x::before {
transform: rotate(-45deg);
}
.image-board-user-menu-x:hover::after,
.image-board-user-menu-x:hover::before {
background: ${imageBoard.siteList.style().colorHover} !important;
}
.image-board-user-menu-x:hover {
background: ${imageBoard.siteList.style().backgroundHover};
}
.image-board-user-menu-x {
position: absolute;
width: 1.3em;
height: 1.3em;
cursor: pointer;
top: 8px;
right: 1px;
}
div.image-board-user-menu-content {
background-color: #eeeeee;
overflow-x: auto;
overflow-y: hidden;
position: absolute;
top: 30px;
right: 0px;
bottom: 30px;
left: 0px;
}
div.image-board-user-menu-content label {
font-family: verdana, sans-serif;
font-weight: initial;
font-size: 12px;
color: #7d7d7d !important;
line-height: 30px;
display: initial !important;
white-space: initial !important;
}
.image-board-user-menu-content label {
padding: 0 3px;
}
.image-board-user-menu-tabs-navigation {
position: absolute;
left: 0;
width: 100px;
height: 100%;
overflow-y: auto;
background-color: #e0e0e0;
}
.image-board-user-menu-tabs-navigation ul {
padding: 0;
}
.image-board-user-menu-tabs-navigation li {
list-style-type: none;
color: #7d7d7d !important;
height: 30px;
line-height: 30px;
padding-left: 10px;
margin: 0;
cursor: pointer;
}
.image-board-user-menu-tabs-navigation li:hover ,
.image-board-user-menu-tabs-navigation li.tab-nav-active {
background-color: #d0d0d0;
}
.image-board-user-menu-tabs-navigation li.tab-nav-active {
font-weight: bold;
}
div.image-board-user-menu-tabs-content {
position: absolute;
left: 100px;
right: 0;
padding-left: 10px;
overflow-y: auto;
min-width: 240px;
height: 100%;
}
div.image-board-user-menu-tabs-content div {
display: none;
}
div.image-board-user-menu-tabs-content div[tab-selected="true"] {
display: initial;
}
.image-board-user-menu-bottom {
/*text-align: right;*/
background-color: ${imageBoard.siteList.style().background};
position: absolute;
bottom: 0px;
width: 100%;
height: 30px;
}
#image-board-user-menu-reset-button {
left: 10px;
}
#image-board-user-menu-save-button {
position: absolute;
right: 10px;
}
#image-board-user-menu-container-${RANDOM} button {
width: initial;
margin: 1px 2px;
padding: 4px 6px;
`, id);
}
function imageViewerCssClass()
{
var id = 'image-viewer-css-' + RANDOM,
col = '#000';
addCssClass(`
.image-board-viewer-container {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100100;
background-color: ${col};
}
button.image-board-viewer-btn {
cursor: pointer;
}
.viewer-tag-list li {
list-style-type: none;
line-height: 1.8em;
display: block;
padding-left: 4px;
}
.viewer-thumb-div,
.viewer-tag-list * {
background-color: #303030;
}
.viewer-tag-list li.category-0 a,
.viewer-tag-list li.tag-type-general a,
.viewer-tag-list li.empty-category a {
color: #337ab7;
}
div.viewer-tag-list-fixed > div.viewer-tag-list {
opacity: 1;
left: 0;
}
div.viewer-tag-list-fixed > div.viewer-img-list {
left: 200px;
}
.viewer-tag-list:hover {
opacity: 1;
left: 0;
}
.viewer-tag-list:hover + * + .viewer-img-list {
left: 200px;
}
.viewer-tag-list {
position: absolute;
width: 200px;
min-width: 50px;
top: 0;
left: -170px;
overflow-y: auto;
height: 100%;
/*padding: 3px 10px;*/
background-color: #303030;
opacity: 0.2;
transition: all 0.75s;
}
div.viewer-thumb-list-fixed > div.viewer-thumb-list {
opacity: 1;
right: 0;
}
div.viewer-thumb-list-fixed > div.viewer-img-list {
right: 200px;
}
.viewer-thumb-list:hover {
opacity: 1;
right: 0;
}
.viewer-thumb-list {
position: absolute;
width: 200px;
min-width: 50px;
top: 0;
right: -170px;
opacity: 0.2;
overflow-y: auto;
height: 100%;
background-color: #303030;
text-align: right;
transition: all 0.75s;
}
.viewer-thumb-div {
max-width: 200px;
padding: 2px 1px 2px 0;
}
.viewer-thumb {
max-width: 180px;
/*max-height: 240px;*/
}
.viewer-thumb-list:hover + .viewer-img-list {
right: 200px;
}
.viewer-img-list {
position: absolute;
top: 0;
left: 30px;
right: 30px;
bottom: 5px;
transition: all 0.75s;
background-color: ${col};
}
.viewer-img-list > .viewer-img-div:not(.img-show) ,
div[data-image-view-type="none_src"],
div[data-image-view-type="vid_file"] > *:not(.vid_file) ,
div[data-image-view-type="orig_img"] > *:not(.orig_img) ,
div[data-image-view-type="jpeg_img"] > *:not(.jpeg_img) ,
div[data-image-view-type="samp_img"] > *:not(.samp_img) {
display: none;
}
.viewer-img-div.img-show {
width: 100%;
height: 100%;
text-align: center;
background-color: ${col};
}
/*USER SELF-FIX: Altered from Original Code to Fit to Screen*/
.viewer-img-div > * {
width: 100%;
object-fit: contain;
height: 100%;
}
.viewer-img-div:before {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
}
.viewer-bottom:hover {
opacity: 1;
}
.viewer-bottom {
transition: all 0.5s;
opacity: 0.2;
background-color: ${col};
}
.image-board-viewer-bottom-class {
position: absolute;
left: 200px;
right: 200px;
bottom: 0px;
text-align: center;
}
.image-board-viewer-bottom-class button {
color: ${imageBoard.siteList.style().color};
background-color: #303030;/*${imageBoard.siteList.style().background};*/
cursor: initial;
margin: 1px 1px 3px 1px;
padding: 1px 5px;
border: 0;
font-weight: bold;
}
`, id);
}
function progressBarCssClass()
{
var id = 'progress-bar-css-' + RANDOM;
addCssClass(`
@-webkit-keyframes progression{
from{background-position:0px 0px;}
to{background-position:50px 0px;}
}
@-o-keyframes progression{
from{background-position:0px 0px;}
to{background-position:50px 0px;}
}
@keyframes progression{
from{background-position:0px 0px;}
to{background-position:50px 0px;}
}
.progress-bar div.progress-counted {
background-color: #da504e;
width: 100%;
}
.progress-bar div.image-ready {
background-color: #fda02e;
}
div.progress-bar > div.progress-complete {
background-color: #5db75d;
}
div.progress-stripe {
background-image:-webkit-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-image:-o-linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-image:linear-gradient(-45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);
background-size: 50px 50px;
height: 12px;
}
div.progress-bar{
margin: 2px 2px 0px 0px;
}
.progress-bar > div.download-in-progress {
background-color: #0773fb;
}
div.progress-animated {
animation: progression 2s linear infinite;
}
`, id);
}
function addCssClass(cssClass, id)
{
var style = document.createElement('style'),
head = document.querySelector('head');
style.type = 'text/css';
if( id )
style.setAttribute('id', id);
if( style.styleSheets )
style.styleSheets.cssText = cssClass;
else
style.appendChild(document.createTextNode(cssClass));
return head.appendChild(style);
}
function resetCssClass(cssClass, id)
{
var style = document.getElementById(id);
if( style && style.type === 'text/css' )
remove( style );
addCssClass(cssClass, id);
}
function attr( elm, name, val )
{
var cond = (val === null || val === undefined);
if( !elm || !name )
return;
if( cond )
{
if( elm.length === undefined )
return elm.getAttribute(name);
return null;
}
else if( elm.length > 0 )
[].forEach.call(elm, function(it){it.setAttribute(name, val);});
else
elm.setAttribute(name, val);
}
function addClass( elm, name )
{
if( elm && name )
{
if( elm.length > 0 )
[].forEach.call(elm, function(it){it.classList.add(name);});
else
elm.classList.add(name);
}
}
function removeClass( elm, name )
{
if( elm && name )
{
if( elm.length > 0 )
[].forEach.call(elm, function(it){it.classList.remove(name);});
else
elm.classList.remove(name);
}
}
function hasClass( elm, name )
{
if( elm && name )
return elm.classList.contains(name);
return false;
}
function toggleClass( elm, newClass, oldClass )
{
if( !elm || !newClass )
return;
if( oldClass )
{
elm.classList.remove(oldClass);
elm.classList.add(newClass);
}
else if( elm.classList.contains(newClass) )
elm.classList.remove(newClass);
else
elm.classList.add(newClass);
}
function getLocation( url, attr )
{
if( !url || !attr )
return null;
this.link = this.link || document.createElement('a');
this.link.href = url;
return this.link[attr];
}
function getFileExt( source )
{
var ext = source ? getLocation( source, 'pathname' ) : null;
ext = ext ? ext.match(/\.([^\.]+)$/) : null;
ext = ext ? ext[1] : null;
return ext;
}
function getSearchObject( search )
{
var keys = {};
if( search )
{
search = search.replace(/^\?/, '');
search.split('&').forEach(function(item){
item = item.split('=');
keys[item[0]] = item[1];
});
}
return keys;
}
function isSameLink( lhs, rhs )
{
lhs = getLocation(lhs, 'href');
rhs = getLocation(rhs, 'href');
return lhs === rhs;
}
function last( arr )
{
if( arr && arr.length > 0 )
return arr[arr.length-1];
return null;
}
function copy( arr, v )
{
arr = arr || [];
if( v && v.length !== undefined )
{
arr.length = 0;
for( var i = 0, len = v.length; i < len; ++i )
arr.push(v[i]);
}
return arr;
}
function _toString_( obj, delim )
{
if( typeof obj === 'string' )
return obj;
else if( obj && obj.length !== undefined )
try{
return obj.join( delim || ', ' );
}catch(e){
return obj.toString();
}
else if( obj )
return obj.toString();
return '';
}
function nodeWalk()
{
var len = arguments.length, obj = this, i, arg;
for( i = 0; i < len; ++i )
{
arg = arguments[i];
if( arg === undefined )
return obj;
else if( obj[arg] === undefined )
return null;
obj = obj[arg];
}
return obj;
}
function hide( elm )
{
if( !elm )
return;
else if( elm.length === undefined )
elm.style.display = 'none';
else{
try{
for( var i = 0, len = elm.length; i < len; ++i )
elm[i].style.display = 'none';
}catch(e){console.error(e);}
}
}
function show( elm )
{
if( elm )
elm.style.display = '';
}
function remove( elm )
{
if( elm && elm.parentNode )
return elm.parentNode.removeChild(elm);
return null;
}
function parent( elm, n )
{
if( !elm || n === null || n === undefined )
return elm;
else if( /^\d+$/.test(n.toString()) )
{
n = parseInt(n, 10);
for( var i = 0; i < n && elm; ++i )
elm = elm.parentNode;
}
else if( typeof n === 'string' )
{
n = n.toUpperCase();
while( elm && elm.tagName !== n )
elm = elm.parentNode;
}
return elm;
}
})();