NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
"use strict";
// ==UserScript==
// @name Tsumino Enhanced
// @namespace http://codingtoby.com
// @version 2.0.3.11
// @description Adds a collection of customizable tweaks, enhancements, and new features to Tsumino.com.
// @author Toby
// @include /((http)(s)?(\:\/\/)(www\.)?(tsumino\.com)(\/)?([\s\S]*))/
// @exclude /((http)(s)?(\:\/\/)(www\.)?(tsumino\.com)(\/)?(Read\/AuthProcess)/
// @exclude http://www.tsumino.com/bhome/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require http://js.codingtoby.com/semantic.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/bean/1.0.15/bean.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant unsafeWindow
// @grant GM_openInTab
// @grant GM_xmlhttpRequest
// @run-at document-start
// ==/UserScript==
/*************************************************************************************
* Open source libraries.
*************************************************************************************/
/*************************************************************************************
* This one adds arrayBuffer support to jQuery's ajax method.
* -------------------------
* jquery.binarytransport.js
* @description. jQuery ajax transport for making binary data type requests.
* @version 1.0
* @author Henry Algus <henryalgus@gmail.com>
*************************************************************************************/
// use this transport for "binary" data type
$.ajaxTransport( "+binary", function (options, originalOptions, jqXHR)
{
if ( window.FormData &&
(options.dataType && options.dataType == "binary"
|| options.data && (window.ArrayBuffer && options.data instanceof ArrayBuffer
|| window.Blob && options.data instanceof Blob)) )
{
return {
send : function (headers, callback)
{
var xhr = new XMLHttpRequest,
url = options.url,
type = options.type,
async = options.async || true,
dataType = options.responseType || "blob", data = options.data || null,
username = options.username || null,
password = options.password || null;
xhr.addEventListener( "load", function ()
{
var data = {};
data[ options.dataType ] = xhr.response;
callback( xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders() )
} );
xhr.open( type, url, async, username, password );
for (var i in headers)
{
xhr.setRequestHeader( i, headers[ i ] );
}
xhr.responseType = dataType;
xhr.send( data )
},
abort : function () {jqXHR.abort()}
}
}
} );
/*************************************************************************************
* Tsumino Enhanced
*************************************************************************************/
// Establish Tsumino Enhanced
(function (w, $)
{
// Main object - Metadata
var TE = {
name : GM_info[ "script" ][ "name" ],
version : GM_info[ "script" ][ "version" ],
status : {},
updateLocation : "https://openuserjs.org/scripts/Tobias.Kelmandia/Tsumino_Enhanced",
installLocation: "https://openuserjs.org/install/Tobias.Kelmandia/Tsumino_Enhanced.user.js"
};
TE.status.pagesLoaded = {};
// Tsumino Enhanced Configuration
TE.config = {
debug : true,
verboseDebug : true,
pfRange : 5,
preload : true
};
// User's current location.
TE.myLocation = w.location.href;
/*************************************************************************************
* User Configuration
*************************************************************************************/
TE.User = {};
(function ()
{
if ( GM_getValue( "TE_settings" ) )
{
var TE_settings = GM_getValue( "TE_settings" );
TE.User = JSON.parse( TE_settings );
}
})();
/*************************************************************************************
* Detect which features the user's browser has.
*************************************************************************************/
TE.ft = {};
TE.ft.logGroups = typeof w.console.group === 'function';
/*************************************************************************************
* Tsumino Site Configuration.
*************************************************************************************/
// Define prefixes for all major site pages.
TE.site = {
account : {
prefix : "/Account/Home",
regex : "(\/Account\/Home*)"
},
auth : {
prefix : "/Read/Auth/",
regex : "(\/Read\/Auth\/[\\s\\S]*)"
},
baseURL : {root : TE.myLocation.split( ".com" )[ 0 ] + ".com"},
book : {
prefix : "/Book/Info/",
regex : "(\/Book\/Info\/[\\s\\S]*)"
},
browse : {
prefix : "/Browse/Index/",
regex : "(\/Browse\/[\\s\\S]*)||(\/*)"
},
error : {
prefix : "/Error/Index/",
regex : "(\/Error\/Index\/[\\s\\S]*)"
},
image : {
prefix : "/Image/Object/?data=",
regex : "(\/Image\/Object\/\\?data=[\\s\\S]*)"
},
login : {
prefix : "/Account/Login",
regex : "(\/Account\/Login[\\s\\S]*)"
},
manageTags : {
prefix : "/Account/ManageTags",
regex : "(\/Account\/ManageTags[\\s\\S]*)"
},
reader : {
prefix : "/Read/View/",
regex : "(\/Read\/View\/[\\s\\S]*)"
},
search : {
prefix : "/Search",
regex : "(\/Search[\\s\\S]*)"
},
forum : {
prefix : "/Forum",
regex : "(\/Forum[\\s\\S]*)"
}
};
var temp = TE.site.baseURL.root;
TE.site.baseURL.regex = temp.replace( /([.*+?\^=!:${}()\|\[\]\/\\])/g, "\\$1" );
// Location Checking object.
TE.on = {};
// Create full URLs and do location checking.
for (var key in TE.site)
{
if ( TE.site.hasOwnProperty( key ) )
{
var obj = TE.site[ key ];
if ( obj.hasOwnProperty( "prefix" ) )
{
// Create Full URLs.
obj[ "url" ] = TE.site.baseURL.root + obj[ "prefix" ];
// Perform location checking.
if ( obj[ "prefix" ] )
{
TE.on[ key ] = !!RegExp( "^(" + TE.site.baseURL.regex + ")" + obj[ "regex" ] + "$" ).test( TE.myLocation );
}
}
}
}
TE.on[ "tsumino" ] = !!RegExp( "^(" + TE.site.baseURL.regex + ")([\\s\\S]*)$" ).test( TE.myLocation );
// Prepare prefetch.
TE.status.prefetch = {};
TE.status.numRandStrings = 0;
/*************************************************************************************
* Utility Functions
*************************************************************************************/
TE.fn = TE.prototype = {
// Logging to console with Timestamps.
randomString : function()
{
TE.status.numRandStrings++;
var text = TE.status.numRandStrings + "_";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
var baseline = 9;
var ceiling = (Math.floor(Math.random() * 10));
var numChars = baseline + ceiling;
for( var i=0; i < numChars; i++ )
{
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
},
log : function ()
{
if ( (arguments.length > 0) && (TE.config.debug) )
{
var date = new Date(), hr, min, sec, mil, timeStamp;
hr = date.getHours();
min = date.getMinutes();
sec = date.getSeconds();
mil = date.getMilliseconds();
timeStamp = hr + ":" + min + ":" + sec + ":" + mil;
if ( TE.ft.logGroups )
{
if ( arguments[ 0 ] == "gname" )
{
console.group( arguments[ 1 ] + " - [" + timeStamp + "]" );
for (var i = 2 ; i < arguments.length ; ++i)
{
console.log( arguments[ i ] );
}
}
else
{
console.group( timeStamp );
for (var i = 0 ; i < arguments.length ; ++i)
{
console.log( arguments[ i ] );
}
}
console.groupEnd();
}
else
{
if ( arguments[ 0 ] == "gname" )
{
console.log( "----- " + arguments[ 1 ] + " -----" );
for (var i = 0 ; i < arguments.length ; ++i)
{
console.log( arguments[ i ] );
}
}
else
{
console.log( "[" + timeStamp + "]" );
for (var i = 0 ; i < arguments.length ; ++i)
{
console.log( arguments[ i ] );
}
}
console.log();
}
}
},
vbLog : function ()
{
if ( TE.config.verboseDebug )
{
if ( arguments.length > 0 )
{
this.log.apply( this.log, arguments );
}
}
},
errorMsg : function (code, situation, error)
{
this.log( "gname", TE.name, "An error was detected while:", situation, "Error Code: " + code, error );
},
replaceAll : function (str, find, replace)
{
// Escape regex.
find = find.replace( /([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1" );
return str.replace( new RegExp( find, 'g' ), replace );
},
load : function (pageNumber, imageUrl)
{
var dfd = jQuery.Deferred(), authUrl = TE.site.baseURL.root + TE.site.auth.prefix + TE.book.id + "/" + pageNumber;
if ( (TE.status.pagesLoaded[ pageNumber ] != "working") && (TE.status.pagesLoaded[ pageNumber ] != "done") )
{
TE.status.pagesLoaded[ pageNumber ] = "working";
$( "#te_readerMessageDisplay" ).append( `
<div id="te_loading_message" class="ui segment"><br />
<div class="ui active dimmer"><div class="ui text loader">Loading...</div></div><br /></div>
` );
this.vbLog( "gname", "TE.load", "Loading Image: " + pageNumber + "...", TE.site.baseURL.root + imageUrl );
var downloadStart = new Date();
// Make an ajax request expecting a binary (arraybuffer) datatype.
var loadImage = $.ajax( {
method : "GET",
url : imageUrl,
dataType : "binary",
processData : false,
responseType : 'arraybuffer',
success : $.proxy( function (data, textStatus, request)
{
// Put the response headers into an array.
var rh = loadImage.getAllResponseHeaders(), rha = rh.split( "\r\n" );
// Create a proper object from the response header array.
var responseHeader = {};
for (var i = 0 ; i < rha.length ; i++)
{
var thisRH = rha[ i ];
thisRH = thisRH.split( ": " );
if ( thisRH[ 0 ] != "" )
{
responseHeader[ thisRH[ 0 ] ] = thisRH[ 1 ];
}
}
// Local logging to examine response headers.
TE.vbLog("gname","TE.fn.load","Response Headers",responseHeader);
// Content-Type is undefined if Tsumino requires us to solve a captcha.
if ( typeof responseHeader[ "Content-Type" ] === "undefined" )
{
// Redirect to the auth page.
w.location.href = authUrl;
}
else
{
var downloadComplete = new Date();
TE.vbLog( "gname", "TE.load", "Content Type: " + responseHeader[ "Content-Type" ] );
// If we're dealing with a JPEG image.
// (Why is it 'images/jpeg' instead of 'image/jpeg'? Typo by Tsumino devs?)
if ( responseHeader[ "Content-Type" ] == "images/jpeg" )
{
TE.vbLog( "gname", "TE.load", "Image data loaded.", "Running conversions..." );
var startTime = new Date();
// Use Uint8Array to view the arrayBuffer response data.
var typedArray = new Uint8Array( data );
// Determine number of bytes for the assembly loop.
var numBytes = typedArray.length;
var binaryString = "";
// Convert it into a useable binary string.
for (i = 0 ; i < numBytes ; i++)
{
binaryString = binaryString + String.fromCharCode( typedArray[ i ] );
}
// And finally encode the binary string as base64.
var encodedBS = btoa( binaryString );
var endTime = new Date();
var dlTime = downloadComplete - downloadStart;
var runTime = endTime - startTime;
var dlTimeDISP = dlTime + "ms";
var runTimeDISP = runTime + "ms";
if ( dlTime >= 1000 )
{ dlTimeDISP = (dlTime / 1000) + "s"; }
if ( runTime >= 1000 )
{ runTimeDISP = (runTime / 1000) + "s"; }
TE.vbLog( "gname", "TE.load", "Conversions completed.",
"Image downloaded in: " + dlTimeDISP + ".", "Total time spent on conversion: " + runTimeDISP + "." );
// Take the base64 string and prepend it so it can be used as a dataURI.
var dataURI = "data:image/jpeg;base64," + encodedBS;
// Add a hidden image to the page so the dataURI can be harvested from its source later.
$( "body" ).append( "<img id='te_loadImage_" + pageNumber + "' src='" + dataURI + "' style='display:none;'>" );
// And we're done.
this.vbLog( "gname", "TE.load", "Image " + pageNumber + " loaded." );
$( "#te_loading_message" ).remove();
TE.status.pagesLoaded[ pageNumber ] = "done";
dfd.resolve();
}
}
}, this ), error : $.proxy( function (request, status, error)
{
this.log( "gname", "TE.load", "Error retrieving image.", request, status, error );
}, this )
} );
}
else if ( TE.status.pagesLoaded[ pageNumber ] == "working" )
{
var checkAgain = function ()
{
setTimeout( function ()
{
if ( TE.status.pagesLoaded[ pageNumber ] == "done" )
{
dfd.resolve();
}
else
{
checkAgain();
}
}, 500 );
};
checkAgain();
}
else if ( TE.status.pagesLoaded[ pageNumber ] == "done" )
{
dfd.resolve();
}
return dfd.promise();
},
prefetch : {
init : function (pageNumber)
{
var dfd = jQuery.Deferred();
TE.vbLog( "gname", "Prefetch Init", "Initializing..." );
var pfRange = TE.config.pfRange, pfStart = (pageNumber - pfRange), pfEnd = (pageNumber + pfRange);
if ( pfStart < 1 )
{
pfStart = 1;
}
if ( pfEnd > TE.book.totalPages )
{
pfEnd = TE.book.totalPages;
}
var thisRange = pfEnd - pfStart;
if ( thisRange == 1 )
{
thisRange++;
}
var timestamp = new Date().getTime();
TE.status.prefetch[ timestamp ] = 0;
TE.vbLog( "gname", "Prefetch Init", "Base: " + pageNumber, "Start: " + pfStart, "End: " + pfEnd );
for (var i = pfStart ; i <= pfEnd ; i++)
{
if ( TE.status.prefetch[ TE.book.id ][ i ] == "" )
{
$.when( this.get( i ) ).then( function ()
{
TE.status.prefetch[ timestamp ]++;
if ( TE.status.prefetch[ timestamp ] == thisRange )
{
dfd.resolve();
}
} );
}
}
return dfd.promise();
},
get : function (pageNumber)
{
TE.status.prefetch[ TE.book.id ][ pageNumber ] = "working";
var dfd = jQuery.Deferred();
var url = TE.site.baseURL.root + "/Image/Image/" + TE.book.id + "/" + pageNumber;
TE.status.prefetch[ TE.book.id ][ pageNumber ] = url;
if ( TE.config.preload )
{
$.when( TE.fn.load( pageNumber, url ) ).then( function ()
{
setTimeout(function()
{
dfd.resolve();
},500);
} );
//TE.fn.load(pageNumber, pfImgSrc);
}
else
{
dfd.resolve();
}
//TE.vbLog( "gname", "Prefetch", url );
/*
$.get( url ).done( function (data)
{
data = TE.fn.scrubAjaxData( data );
var pfImg = $( data ).find( "img.reader-img" );
var pfImgSrc = $( pfImg ).attr( "data-content" );
TE.status.prefetch[ TE.book.id ][ pageNumber ] = pfImgSrc;
TE.vbLog( "gname", "Prefetch", "Prefetched image src for page " + pageNumber, TE.site.baseURL.root + pfImgSrc );
if ( TE.config.preload )
{
$.when( TE.fn.load( pageNumber, pfImgSrc ) ).then( function ()
{
dfd.resolve();
} );
//TE.fn.load(pageNumber, pfImgSrc);
}
else
{
dfd.resolve();
}
} );
*/
return dfd.promise();
}
},
camelize : function (str)
{
return str.replace( /(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index)
{
if ( +match === 0 )
{
return "";
}
return index == 0 ? match.toLowerCase() : match.toUpperCase();
} );
},
updateSettings : function ()
{
GM_setValue( "TE_settings", JSON.stringify( TE.User ) );
},
scrubAjaxData : function (data)
{
data = TE.fn.replaceAll( data, "src=", "data-content=" );
data = TE.fn.replaceAll( data, "script", "div" );
data = TE.fn.replaceAll( data, "type=", "data-notype=" );
data = TE.fn.replaceAll( data, "link", "div" );
return data;
},
checkForUpdates : function ()
{
var dfd = jQuery.Deferred();
if ( typeof TE.User.tsuminoEnhanced === "undefined" )
{
TE.User.tsuminoEnhanced = {};
TE.User.tsuminoEnhanced.lastUpdateCheck = parseInt( new Date().getTime() );
TE.User.tsuminoEnhanced.upToDate = true;
this.updateSettings();
}
if ( TE.User.tsuminoEnhanced.latestVersion != TE.version )
{
TE.User.tsuminoEnhanced.upToDate = false;
this.updateSettings();
}
else
{
TE.User.tsuminoEnhanced.upToDate = true;
this.updateSettings();
}
var now = parseInt( new Date().getTime() );
var oneMinute = 60000;
var oneHour = oneMinute * 60;
var oneDay = oneHour * 24;
if ( now >= (parseInt( TE.User.tsuminoEnhanced.lastUpdateCheck ) + oneMinute) )
{
GM_xmlhttpRequest({
method: "GET",
url: TE.updateLocation,
onload: $.proxy(function(response) {
var scrubbed = this.scrubAjaxData(response.responseText);
TE.log( "gname", TE.name, "Checking for updates..." );
TE.User.tsuminoEnhanced.lastUpdateCheck = parseInt( new Date().getTime() );
var latestVersion = $(scrubbed).find( "code" )[ 0 ];
latestVersion = $( latestVersion ).text();
TE.User.tsuminoEnhanced.latestVersion = latestVersion;
if ( TE.User.tsuminoEnhanced.latestVersion != TE.version )
{
TE.log( "gname", TE.name, "An update is available!" );
TE.User.tsuminoEnhanced.upToDate = false;
this.updateSettings();
dfd.resolve();
}
else
{
TE.log( "gname", TE.name, TE.name + " is up to date!" );
TE.User.tsuminoEnhanced.upToDate = true;
this.updateSettings();
dfd.resolve();
}
},this)
});
}
else
{
dfd.resolve();
}
return dfd.promise();
}
};
// Alias specific commonly used utility functions to the main namespace.
TE.log = TE.fn.log;
TE.vbLog = TE.fn.vbLog;
TE.errorMsg = TE.fn.errorMsg;
TE.replaceAll = TE.fn.replaceAll;
TE.prefetch = TE.fn.prefetch;
TE.load = TE.fn.load;
TE.updateSettings = TE.fn.updateSettings;
TE.randomString = TE.fn.randomString;
TE.config.internalIDs = {};
TE.config.internalIDs.teConfigLink = TE.randomString();
TE.status.idPrefix = TE.randomString() + "_";
/*************************************************************************************
* Tsumino Enhanced User Interface settings.
* Stylesheets, data URI, etc.
*************************************************************************************/
// User Interface object.
TE.ui = {};
/* Tsumino Enhanced CSS.
** Minified so it's easier to include in the script.
**
** Beautify here: http://www.cleancss.com/css-beautify/
** Minify here: http://cssminifier.com/
*/
TE.ui.css = {};
TE.ui.css.master = `
.te_mainColor { color:#22a7f0; }
.te_en_incompatible{background-color:rgba(255,0,0,.1);border:2px solid rgba(255,0,0,0);border-radius:5px;padding:.5em}
@font-face { font-family: "Icons";
src: url("https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/themes/default/assets/fonts/icons.eot");
src: url("https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/themes/default/assets/fonts/icons.eot?#iefix")
format("embedded-opentype"),
url("https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/themes/default/assets/fonts/icons.woff2") format("woff2"),
url("https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/themes/default/assets/fonts/icons.woff") format("woff"),
url("https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/themes/default/assets/fonts/icons.ttf") format("truetype"),
url("https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.1.8/themes/default/assets/fonts/icons.svg#icons") format("svg");
font-style: normal; font-weight: normal; font-variant: normal; text-decoration: inherit; text-transform: none; }
#te_readerCurrentImage { margin: 0 auto; cursor: pointer; }
`;
// Browsing Tweaks CSS.
TE.ui.css.browsingTweaks = {};
// Browsing tweaks - Master CSS.
TE.ui.css.browsingTweaks.master = `
.te_browsetweak_infobutton,.te_browsetweak_readbutton {
position:absolute;border:3px solid #fff;bottom:10px;
padding:10px;margin-left:5%;margin-right:5%;
font-size:17px;color:#fff;width:42.5%;display:inline-block;
text-decoration:none;box-sizing:border-box }
.te_browsetweak_readbutton{right:0;box-sizing:border-box}
.te_browsetweak_infobutton:focus,.te_browsetweak_readbutton:focus{text-decoration:none}
.te_browsetweak_infobutton:hover,.te_browsetweak_infobutton:visited,
.te_browsetweak_readbutton:hover,.te_browsetweak_readbutton:visited
{background-color:#22a7f0;color:#fff;text-decoration:none}
.te_browsetweak_infobutton{left:0}
`;
// More Books CSS.
TE.ui.css.browsingTweaks.moreBooks =
`
@media(min-width:768px) { .overlay-title { font-size:.8em; } .col-sm-4 { width: 25% } }
@media(min-width:992px) { .col-md-3 { width: 20% } }
`;
// Record Keeper CSS.
TE.ui.css.recordKeeper = `
.te_recordKeeper_finished:hover { border: 3px solid rgba(0,125,0,.8) !important; }
.te_recordKeeper_started:hover { border: 3px solid rgba(190,190,90,.8) !important; }
`;
// Tsumino Enhanced Favicon Data URI
TE.ui.favicon
= `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADNQTFRFGhoaIqfwHD1QHmGFIIS7H3KgGyw1GyMnIY3JHU9rIZbWHlh4Ip/jHDVCIHuuH2qTHUZdxJ5l4gAAAcBJREFUeNrsmdFygyAQRbcCAiqY///apm2qqIBkdDFN73lkBA4O7K5IBAAAAADwssiPMxEQgAAEIHBEQDUB85h90KqmVhM+bM4RWHSeBZqgVUytsmAMCEDgz50CBCIIQAACFwkAAMDL0kSqwqoRDwL/RsDJBCYr0KW62QMLTRIROK8ggAAEIHC5wOWBCLkAAkS6idJnBVS8U3twqYePoYAABCDwpgKaV6AVUYLru8hfDBnv1JwYocdpsoEuSUYy8nemnkDb2GFOPBUF7hMLJbvlzuorCNyzr/DriX9x5ZvwwVMLHuXOdfW8B0uq4uWx2Zm+bDTPJlB4U2/5BGzJYIPmE9BDwWCK+ARIFQzWcgq4517A+QJkNn2NFCb1AhgE7GLm/ruuX2zNkXgF9FdQ84+ZfxDhSJ1OCJwWit3ayMdroVrJ6DZkCxxuASczJ4BfwK2jgqGKAtpuEoPR1QRa67dHSW7nZxHQVpndCMj6BuJlUE/VBGLfJV2ivufZA9uyxGuqKbCuzLpb8lGmUzAuo5+m2gJhYaSy91xccWBKgN7lH2SLhF3B6lkFbvfiVxRcMh68kcztQ0sAAAAAeHc+BRgAUX8aiFKipZUAAAAASUVORK5CYII=`;
TE.ui.mainColor = "#22a7f0";
/*************************************************************************************
* Classes
*************************************************************************************/
/*************************************************************************************
* Class: Enhancement
*************************************************************************************/
TE.Enhancement = {
/*************************************************************************************
* Class: Enhancement
* Subclass: main
*
* Master Enhancement class.
*
* name: Name of the Enhancement. String only.
* description: Description of the Enhancement.
* Can be a string or boolean false.
* False will indicate no description.
* options: Should be an object collection of option subclasses, or boolean false.
* False will indicate no options.
* If no options are provided:
* - The Enhancement will not appear on the configuration page.
* - The Enhancement will be activated automatically.
* If options is not false, one option must use the "enable" key.
* section: The section of the config page the Enhancement will appear in.
* incompatible: Array of Enhancements that this one is incompatible with.
* fn: Object containing all actual Enhancement functionality.
* "init" key should be used for activation.
* "upgradeHandling" key should be used for upgrade handling.
*
* TE.Enhancement.main(name,displayName,description,options,section,incompatible,fn)
*************************************************************************************/
main : function (name, description, options, section, incompatible, fn)
{
try
{
if ( typeof name !== "string" )
{
throw new Error( "Enhancement name must be defined as a string." );
}
if ( (typeof description !== "string") && (description != false) )
{
throw new Error( "Enhancement description must be defined as a string." );
}
if ( (typeof options !== "object") && (options != false) )
{
throw new Error( "Enhancement options must be defined as an object." );
}
if ( (options != false) || (section != false) )
{
if ( typeof section !== "string" )
{
throw new Error( "Enhancement section must be defined as a string." );
}
}
if ( (typeof incompatible !== "object") && (incompatible != false) )
{
throw new Error( "Enhancement incompatibilities must be defined as 'false', or an array." );
}
if ( typeof fn !== "object" )
{
throw new Error( "Enhancement functionality must be defined as an object." );
}
this.name = name;
this.shortName = TE.fn.camelize( name );
this.description = description;
this.options = options;
this.section = section;
this.incompatible = incompatible;
this.fn = fn;
}
catch (error)
{
TE.errorMsg( "CD01", "Creating an Enhancement class object.", error );
}
}, /*************************************************************************************
* Class: Enhancment
* Subclass: option
*************************************************************************************/
option : {
/*************************************************************************************
* Master Class: Enhancment
* Parent Class: option
* Subclass: main
*
* Primary Option class.
*
* type: The type of the Option.
* Must be a string of one of the following:
* "enable" - Switch that enables the Enhancement.
* Options of this type require no further parameters.
* "toggle" - Renders a checkbox.
* "radio" - Radio buttons. Requires arguments.
* "dropdown" - Dropdown menu. Requires arguments.
* name: Name of the option.
* description: Description of the Option.
* Can be a string or boolean false.
* False will indicate no description.
* defaultValue: The default value of the option.
* params: Must be an object of the "params" subclass, or boolean false.
* False indicates no params.
*
* TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
*************************************************************************************/
main : function (type, name, description, defaultValue, params)
{
try
{
if ( typeof type !== "string" )
{
throw new Error( "Option type must be defined as a string." );
}
if ( type != "enable" )
{
if ( typeof name !== "string" )
{
throw new Error( "Option name must be defined as a string." );
}
if ( typeof description === "undefined" )
{
description = false;
}
if ( type == "toggle" )
{
}
if ( (type == "dropdown") && (typeof v !== "object") )
{
throw new Error( "You must define params for option type dropdown." );
}
if ( (type == "radio") && (typeof params !== "object") )
{
if ( typeof params === "undefined" )
{
throw new Error( "You must define params for option type radio." );
}
}
if ( type == "dropdown" )
{
if ( typeof params === "undefined" )
{
throw new Error( "Option params must be defined with type dropdown." );
}
}
this.type = type;
this.name = name;
this.shortName = TE.fn.camelize( name );
this.description = description;
this.defaultValue = defaultValue;
this.params = params;
}
else
{
this.type = type;
this.name = "enable";
this.shortName = "enable";
this.description = false;
this.defaultValue = false;
this.params = false;
}
}
catch (error)
{
TE.errorMsg( "CD02", "Creating an Enhancement.option.main class object.", error );
}
}, /*************************************************************************************
* Master Class: Enhancment
* Parent Class: option
* Subclass: params
*
* params for radio and dropdown type options.
*
* type: The type of the param.
* Must be a string of one of the following:
* "range" - For generating a
*************************************************************************************/
params : function (type, options)
{
try
{
if ( typeof type !== "string" )
{
throw new Error( "Type must be defined as a string." );
}
if ( type == "range" )
{
if ( typeof options !== "object" )
{
throw new Error( "options must be defined as an object." );
}
else
{
if ( typeof options.rangeStart !== "number" )
{
throw new Error( "Range start must be defined as a number." );
}
if ( typeof options.rangeEnd !== "number" )
{
throw new Error( "Range end must be defined as a number." );
}
}
}
else
{
throw new Error( "Range is the only acceptable type right now." );
}
this.type = type;
this.params = params;
}
catch (error)
{
TE.log( "gname", "ERROR", "Error defining params class:", error );
TE.errorMsg( "CD03", "Creating an Enhancement.option.params class object.", error );
}
}
}
};
//Fix the navbar.
TE.fixNavbar = function ()
{
$( "nav .tsumino-nav-items li" ).click( function ()
{
var thisLink = $( this ).find( "a:first" )[ 0 ];
if ( $( thisLink ).text() == "Browse " )
{
$( "#te_navMenu" ).toggleClass( "tsumino-nav-visible" );
}
else if ( $( thisLink ).prop( "id" ) == TE.config.internalIDs.teConfigLink )
{
$( "#te_config_modal" )
.modal( "show" )
.modal( "refresh" );
}
else
{
window.location.href = $( thisLink ).prop( "href" );
}
} );
};
/*************************************************************************************
* Enhance Page - Core Functionality
* + Creates IDs for important elements.
* + Gathers data for storage in the TE object.
*************************************************************************************/
TE.enhancePage = function ()
{
var dfd = jQuery.Deferred();
TE.vbLog( "gname", "TE.enhancePage", "Started working..." );
$( document ).ready( function ()
{
/*************************************************************************************
* All pages.
*************************************************************************************/
if ( !TE.on.forum )
{
// Prepare config modal
$( "body" ).append( `<div id="te_config_modal" class="ui fullscreen basic modal"></div>` );
TE.settings.render();
// The navigation bar at the top.
$( "nav" ).attr( "id", "te_siteNavbar" );
// Replace favicon.
$( "link[rel='icon']" ).attr( "href", TE.ui.favicon );
// Add Tsumino Enhanced config link to navbar.
var navbar = $( ".tsumino-nav-left" )[ 0 ];
$( navbar ).attr( "id", "te_navbarMain" );
// Add ID to browse button link and swap href from # to javascript:;.
var browseButton = $( "#te_navbarMain" ).find( "a[href=#]" )[ 0 ];
$( browseButton ).prop( "id", "te_navBrowse" );
$( "#te_navBrowse" ).prop( "href", "javascript:;" );
// Add ID to nav menu.
var navMenu = $( "#te_navBrowse" ).siblings()[ 0 ];
$( navMenu ).prop( "id", "te_navMenu" );
TE.fixNavbar();
if ( !TE.User.tsuminoEnhanced.upToDate )
{
$( w ).load( function ()
{
$( "#"+TE.config.internalIDs.teConfigLink ).append( " <i class='ui red icon upload'></i>" );
$(".search-wrapper input").css("margin-left","8em");
$( "#"+TE.config.internalIDs.teConfigLink ).parent().popup( {
title : 'An update is available!'
} );
$( "#"+TE.config.internalIDs.teConfigLink ).parent().css( "background-color", "#333333" );
setTimeout( function ()
{
$( "#"+TE.config.internalIDs.teConfigLink ).parent()
.velocity( {backgroundColor : "#2D5467"} )
.velocity( {backgroundColor : "#333333"} )
.velocity( {backgroundColor : "#2D5467"} )
.velocity( {backgroundColor : "#333333"} );
}, 1 );
} );
}
// ID the primary content area.
var pageContent = $( "div.container-fluid" )[ 0 ];
$( pageContent ).attr( "id", "te_pageContent" );
var footer = $( "div.nav-footer" )[ 0 ];
$( footer ).attr( "id", "te_page_footer" );
var teNLC_className = TE.fn.randomString();
var teNLC_height = $("nav.tsumino-nav").height();
var teNLC_left = $("#te_navbarMain").offset().left;
teNLC_left += $("#te_navbarMain").width();
var teNLC_top = 0;
TE.ui.css.master = TE.ui.css.master + `
.a`+teNLC_className+`
{
float: left;
position: absolute;
top:`+teNLC_top+`px;
left:`+teNLC_left+`px;
height: `+teNLC_height+`px;
z-index:999;
border-left: 1px solid #282828;
}
.a`+TE.config.internalIDs.teConfigLink+`
{
margin:10px 5px;
}
`;
$( "body" ).append( `
<div class="a`+teNLC_className+`">
<div id="`+TE.config.internalIDs.teConfigLink+`" class="a`+TE.config.internalIDs.teConfigLink+`">
<a href='javascript:;' style='color:` + TE.ui.mainColor + ` !important;'>ENHANCED</a>
</div>
</div>
` );
$( "#"+TE.config.internalIDs.teConfigLink ).click( function ()
{
$( "#te_config_modal" ).modal( "show" );
$( "#te_config_modal" ).modal( "refresh" );
} );
$(".search-wrapper input").css("margin-left","6.5em");
$( "head" )
// Include Semantic CSS
.prepend( "<link rel='stylesheet' href='http://js.codingtoby.com/semantic.min.css' />" )
// Apply Tsumino Enhanced CSS.
.append( "<style>" + TE.ui.css.master + "</style>" );
}
/*************************************************************************************
* Book & Reader
*************************************************************************************/
if ( TE.on.reader || TE.on.book )
{
// Create the book object.
TE.book = {};
// Reader only.
if ( TE.on.reader && false )
{
// Create IDs.
$( ".reader-page" ).attr( "id", "te_readerPageMain" );
var imageBlock = $( "#te_readerPageMain" ).children()[ 0 ];
$( imageBlock ).attr( "id", "te_imageBlock" );
$( ".reader-btn" ).attr( "id", "te_readerButtonContainer" );
//$( "img.reader-img" ).attr( "id", "te_readerCurrentImage" );
var readInfo = TE.myLocation;
readInfo = readInfo.replace( TE.site.reader.url, "" );
readInfo = readInfo.split( "/" );
// Book ID.
TE.book.id = parseInt( readInfo[ 0 ] );
TE.log(readInfo[ 1 ]);
// Book title.
var bookTitle = $( "title" ).text();
TE.book.title = bookTitle.replace( "Tsumino - Free Premium Doujinshi and Hentai Manga: Read ", "" );
// Pagination setup.
var pagination = $( "#te_readerButtonContainer" ).find( "h1" )[ 0 ];
$( pagination ).attr( "id", "te_readerPagination" );
var pagesInfo = $( "#te_readerPagination" ).text();
pagesInfo = pagesInfo.split( " Page " )[ 1 ];
pagesInfo = pagesInfo.split( " of " );
// Current Page.
if(readInfo.length > 1)
{
TE.book.currentPage = parseInt( readInfo[ 1 ] );
}
else
{
TE.book.currentPage = 1;
}
TE.book.currentPageURL = TE.site.reader.prefix + TE.book.id + "/" + TE.book.currentPage;
$( "title" ).text( "Tsumino - " + TE.book.title + " - Page " + TE.book.currentPage );
// Origin page.
TE.book.originPage = TE.book.currentPage;
// Total Pages.
TE.book.totalPages = parseInt( pagesInfo[ 1 ] );
// Next page.
TE.book.nextPage = TE.book.currentPage + 1;
if ( TE.book.nextPage > TE.book.totalPages )
{
TE.book.nextPage = false;
}
else
{
TE.book.nextPageURL = TE.site.reader.prefix + TE.book.id + "/" + TE.book.nextPage;
}
// Previous page.
TE.book.prevPage = TE.book.currentPage - 1;
if ( TE.book.prevPage == 0 )
{
TE.book.prevPage = false;
}
else
{
TE.book.prevPageURL = TE.site.reader.prefix + TE.book.id + "/" + TE.book.prevPage;
}
// Rewrite pagination section.
$( "#te_readerPagination" ).before( `<div id="te_readerMessageDisplay" style="margin-bottom: 25px;"></div>` );
$( "#te_readerPagination" ).html( "Page <span id='te_currentPage'></span> of <span id='te_totalPages'></span>" );
$( "#te_currentPage" ).html( TE.book.currentPage );
$( "#te_totalPages" ).html( TE.book.totalPages );
// Rename Return button to 'book info' and give it an ID.
var bookInfoButton = $( "a[href*='" + TE.site.book.prefix + "']:contains('RETURN')" );
$( bookInfoButton ).attr( "id", "te_bookInfoButton" );
$( "#te_bookInfoButton" )
.attr("href",TE.site.book.prefix + TE.book.id)
.text( "BOOK INFO" );
// Add a return button that takes you to the index.
$( "#te_bookInfoButton" ).after( `
<a class='book-read-button button-stack' id='te_returnToIndexButton'><i class='fa fa-home'></i> BACK TO INDEX</a>
` );
var returnToIndexLink = sessionStorage.getItem( "te_returnLink" );
if ( typeof returnToIndexLink === "object" )
{
$( "#te_returnToIndexButton" ).attr( "href", TE.site.baseURL.root );
}
else
{
$( "#te_returnToIndexButton" ).attr( "href", returnToIndexLink );
}
// Enhance Previous Page button.
if ( $( "a:contains(' PREV')" ).length )
{
$( "a:contains(' PREV')" ).attr( "id", "te_prevButton" );
}
else
{
$( "#jump-page" ).before( `
<a id="te_prevButton" class="book-read-button button-stack" style="margin-right: 10px;"><i class="fa fa-arrow-left"></i> PREV</a>
` );
$( "#te_prevButton" ).css( "display", "none" );
}
$( "#te_prevButton" ).attr( "href", TE.book.prevPageURL );
if ( TE.book.currentPage > 1 )
{
$( "#te_prevButton" ).css( "display", "inline" );
}
else
{
$( "#te_prevButton" ).css( "display", "none" );
}
// Enhance Next Page Button
if ( $( "a:contains('NEXT ')" ).length )
{
$( "a:contains('NEXT ')" ).attr( "id", "te_nextButton" );
}
else
{
$( "#jump-page" ).after( `
<a id="te_nextButton" class="book-read-button button-stack">NEXT <i class="fa fa-arrow-right"></i></a>
` );
$( "#te_nextButton" ).css( "display", "none" );
}
$( "#te_nextButton" ).attr( "href", TE.book.nextPageURL );
if ( TE.book.currentPage < TE.book.totalPages )
{
$( "#te_nextButton" ).css( "display", "inline" );
}
else
{
$( "#te_nextButton" ).css( "display", "none" );
}
$("#image-container")
.after(`
<div id="te_imageContainer">
<img class="img-responsive" src="/Image/Image/`+TE.book.id+`/`+TE.book.currentPage+`" id="te_readerCurrentImage">
</div>
`)
.remove();
// Enhance Image link.
/*
var imageLink = $( "#te_readerCurrentImage" ).parent();
$( imageLink ).attr( "id", "te_imageLink" );
$( "#te_imageLink" ).attr( "href", TE.book.nextPageURL );
*/
TE.vbLog( "gname", "TE.book", TE.book );
}
else if ( TE.on.book )
{
var readInfo = TE.myLocation;
readInfo = readInfo.replace( TE.site.book.url, "" );
readInfo = readInfo.split( "/" );
// Book ID.
TE.book.id = parseInt( readInfo[ 0 ] );
// Fix tag display bug causing unwanted line breaks.
$( ".book-tag:contains(' ')" ).css( "white-space", "nowrap" );
$( ".book-tag:contains('-')" ).css( "white-space", "nowrap" );
// Read Online button.
$( "a.book-read-button:contains(' READ ONLINE')" ).attr( "id", "te_readOnlineButton" );
var indexButton = $( "a:contains(' BACK TO INDEX')" );
$( indexButton ).attr( "id", "te_backToIndexButton" );
var returnToIndexLink = sessionStorage.getItem( "te_returnLink" );
if ( typeof returnToIndexLink !== "object" )
{
$( "#te_backToIndexButton" ).attr( "href", returnToIndexLink );
}
}
}
if ( TE.on.browse )
{
sessionStorage.setItem( "te_returnLink", TE.myLocation );
var browsePage = $( "div.browse-page" );
var ctProper = $( "div.row.push-in" );
var bookshelfContainer = $( ctProper ).children()[ 0 ];
$( bookshelfContainer ).attr( "id", "te_bookshelfContainer" );
var sidebarContainer = $( ctProper ).children()[ 1 ];
$( sidebarContainer ).attr( "id", "te_sidebarContainer" );
var bookshelf = $( browsePage ).find( "div.row.row-no-padding" );
$( bookshelf ).attr( "id", "te_bookshelf" );
$( bookshelf ).children().each( function ()
{
var thisLinkUrl = $( this ).find( "a.overlay-button" ).attr( "href" );
var temp = thisLinkUrl.replace( TE.site.book.prefix, "" );
temp = temp.split( "/" );
var thisBookID = temp[ 0 ];
$( this ).attr( "id", "te_book_" + thisBookID + "_masterContainer" );
$( this ).find( "div.book-grid-item" ).attr( "id", "te_book_" + thisBookID + "_container" );
var thisOverlay = $( "#te_book_" + thisBookID + "_container" ).find( "div.overlay" );
$( thisOverlay ).attr( "id", "te_book_" + thisBookID + "_overlay" );
var thisData = $( "#te_book_" + thisBookID + "_overlay" ).find( "div.overlay-data" );
$( thisData ).attr( "id", "te_book_" + thisBookID + "_data" );
var thisPages = $( "#te_book_" + thisBookID + "_data" ).find( "div.overlay-sub" );
$( thisPages ).attr( "id", "te_book_" + thisBookID + "_pagesContainer" );
var bottomTitle = $( this ).find( "a.title" );
$( bottomTitle ).attr( "id", "te_book_" + thisBookID + "_bottomTitle" );
$( "#te_book_" + thisBookID + "_bottomTitle" ).attr( "href", "javascript:;" );
} );
//TE.vbLog( "gname", "TE.enhancePage", bookshelf );
}
TE.vbLog( "gname", "TE.enhancePage", "Finished working." );
dfd.resolve();
} );
return dfd.promise();
};
/*************************************************************************************
* Enhancement Code
* Create all Enhancements within anonymous functions.
*************************************************************************************/
// Object for storing all Enhancements
TE.Enhancements = {};
/*******************************************************
* Hidden Enhancements
* Important functionality.
* Customization either not yet written or not required.
*******************************************************/
/*******************************************************
* General Enhancements
*******************************************************/
(function ()
{
/*******************************************************
* Unstickied Header - General Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Unstickied Header";
shortName = TE.fn.camelize( name );
description = "The Tsumino navigation bar will no longer follow you as you scroll down.";
options = [];
section = "General";
incompatible = false;
main = {
init : function ()
{
this.run();
},
run : function ()
{
$( "#te_siteNavbar" ).css( "position", "absolute" );
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
(function ()
{
/*******************************************************
* Record Keeper - General Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Record Keeper";
shortName = TE.fn.camelize( name );
description = `
The aptly named Record Keeper Enhancement keeps a record of what Doujin you've read.<br />
This record includes Doujin IDs, the last page you read, and whether or not you finished reading a Doujin.<br />
While browsing, unread Doujin will retain the normal blue border.<br />
Doujin you have started but haven't finished will have a yellow border.<br />
Doujin you've finished reading will have a green border.<br />
Additionally, the information overlay will now contain this information.<br />
There will also be a 'Continue Reading' button on the book info page if you have previously read a Doujin past the first page.
`;
options = [];
section = "General";
incompatible = false;
main = {};
main = {
init : function ()
{
if ( typeof TE.User.recordKeeper.data !== "object" )
{
TE.User.recordKeeper.data = {};
TE.updateSettings();
}
if ( TE.on.browse )
{
$.when( TE.status.enhancePage ).done( function ()
{
$( "style" ).append( TE.ui.css.recordKeeper );
TE.log( "gname", name, "Initializining..." );
for (var key in TE.User.recordKeeper.data)
{
if ( TE.User.recordKeeper.data.hasOwnProperty( key ) )
{
var obj = TE.User.recordKeeper.data[ key ];
if ( obj[ "finished" ] )
{
$( "#te_book_" + key + "_pagesContainer" ).append( "<br />Finished!" );
$( "#te_book_" + key + "_container" ).addClass( "te_recordKeeper_finished" );
$( "#te_book_" + key + "_bottomTitle" ).css( "border-top", "3px solid rgba(0,125,0,.8)" );
}
if ( (obj[ "lastSeen" ] > 1) && (!obj[ "finished" ]) )
{
$( "#te_book_" + key + "_pagesContainer" ).text( "Read " + obj[ "lastSeen" ] + " / " + obj[ "totalPages" ]
+ " pages." );
$( "#te_book_" + key + "_container" ).addClass( "te_recordKeeper_started" );
$( "#te_book_" + key + "_bottomTitle" ).css( "border-top", "3px solid rgba(190,190,90,.8)" );
}
}
}
} );
}
if ( TE.on.book )
{
$.when( TE.status.enhancePage ).done( $.proxy( function ()
{
TE.log( "gname", name, "Initializining..." );
if ( typeof TE.User.recordKeeper.data[ TE.book.id ] === "object" )
{
if ( TE.User.recordKeeper.data[ TE.book.id ][ "lastSeen" ] > 1 )
{
//TE.User.recordKeeper.data[ TE.book.id ][ "lastSeen" ];
var oldButton = $( "#te_readOnlineButton" ).html();
var starOver = oldButton.replace( " READ ONLINE", " START OVER" );
var continueReading = oldButton.replace( " READ ONLINE", " CONTINUE READING" );
$( "#te_readOnlineButton" ).html( starOver );
var resumeUrl = TE.site.reader.url + TE.book.id + "/" + TE.User.recordKeeper.data[ TE.book.id ][ "lastSeen" ];
$( "#te_readOnlineButton" ).before( `
<a id='te_resumeButton' class='book-read-button button-stack' href='` + resumeUrl + `'></a>
` );
$( "#te_resumeButton" ).html( continueReading );
}
}
}, this ) );
}
if ( TE.on.reader && false )
{
$.when( TE.status.enhancePage ).done( $.proxy( function ()
{
TE.log( "gname", name, "Initializining..." );
if ( typeof TE.User[ shortName ].data !== "object" )
{
TE.User[ shortName ].data = {};
}
if ( typeof TE.User[ shortName ].data[ TE.book.id ] !== "object" )
{
TE.User[ shortName ].data[ TE.book.id ] = {
totalPages : TE.book.totalPages, lastSeen : TE.book.currentPage, finished : false
};
}
this.update();
}, this ) );
}
},
update : function ()
{
TE.User[ shortName ].data[ TE.book.id ].lastSeen = TE.book.currentPage;
if ( !TE.User[ shortName ].data[ TE.book.id ][ "finished" ] )
{
if ( TE.book.totalPages == TE.book.currentPage )
{
TE.User[ shortName ].data[ TE.book.id ][ "finished" ] = true;
}
}
TE.updateSettings();
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
var opt2 = {
type : "toggle",
name : "Show Messages",
description : "Displays loading messages while preparing images for display.",
defaultValue : true,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
//options.push(new TE.Enhancement.option.main(opt2.type,opt2.name,opt2.description,opt2.defaultValue,opt2.arguments));
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
(function ()
{
/*******************************************************
* Browse Tags Lite - Browsing Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Browse Tags Lite";
shortName = TE.fn.camelize( name );
description = `Adds a new link to Tsumino's browse menu. "ALL TAGS (LITE)"<br />
Clicking it launches the Browse Tags Lite modal window.<br />
Tags are sorted alphabetically and categorized by the first letter.<br />
Click a letter at the top to view tags that start with that letter.<br />
The tag list is downloaded automatically the first time you open BTL.`;
options = [];
section = "Browsing";
incompatible = false;
main = {
init : function ()
{
var firstLoad = true;
$.when( TE.status.enhancePage ).then( $.proxy( function ()
{
$( "ul#te_navMenu" ).append( `<li id="te_browseTagsLite_menuLink"><a>All Tags (Lite)</a></li>` );
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
alphabet = alphabet.split( "" );
$( "body" ).append( `
<div id="te_browseTagsLite_modal" class="ui fullscreen long basic modal">
<button id="te_btl_closeModal" class="ui icon button right floated"><i class="remove icon"></i></button>
<div class="header"><h1><span class="te_mainColor">Browse Tags Lite</span></h1></div>
<div id="te_browseTagsLite_modalBody" class="content" style="font-size: 1.4em;">
<div id="te_btl_mainDisplay">
</div>
</div>
</div>
` );
$( "#te_browseTagsLite_modalBody" ).prepend( `<div id="te_browseTagsLite_tabs" class="ui top attached inverted large tabular menu"></div>` );
/*
$( "#te_browseTagsLite_tabs" ).prepend( `<a class="item te_btl_tabButton" data-tab="te_btl_tab_all">*</a>` );
$( "#te_btl_mainDisplay" ).append( `
<div id="te_btl_tab_all" class="ui bottom attached inverted tab segment" data-tab="te_btl_tab_all">
<h1>ALL</h1>
Tags go here
</div>
` );
*/
// Populate button list
for (var i = 0 ; i < alphabet.length ; i++)
{
$( "#te_browseTagsLite_tabs" ).append(
`<a id="te_btl_btn_` + alphabet[ i ] + `" class="item te_btl_tabButton" data-tab="te_btl_tab_` + alphabet[ i ] + `">`
+ alphabet[ i ] + `</a>` );
$( "#te_btl_mainDisplay" ).append( `
<div id="te_btl_tab_` + alphabet[ i ] + `" class="ui bottom attached inverted tab segment" data-tab="te_btl_tab_`
+ alphabet[ i ] + `">
</div>
` );
}
$( "#te_browseTagsLite_tabs" ).append( `
<a id="te_btl_btn_reload" class="icon item"><i class="icon refresh" style="color:` + TE.ui.mainColor + `"></i></a>
` );
// Check to see if the user has a copy of the tag list.
if ( TE.User.tsuminoEnhanced.tagList )
{
this.renderTagList();
$( "#te_btl_btn_B" ).addClass( "active" );
$( "#te_btl_tab_B" ).addClass( "active" );
}
$( "#te_btl_btn_reload" ).click( $.proxy( function ()
{
$( "#te_btl_btn_reload" ).addClass( "disabled" );
$( "#te_browseTagsLite_modal" ).append( `
<div id="te_btl_downloadingTagListLoader">
<div class="ui active dimmer">
<div class="ui text loader">Downloading Tag List...</div>
</div>
</div>
` );
$.when( this.updateTagList() ).then( $.proxy( function ()
{
$( "#te_btl_downloadingTagListLoader" ).remove();
$( "#te_btl_btn_reload" ).removeClass( "disabled" );
this.renderTagList();
$( "#te_btl_btn_A" ).addClass( "active" );
$( "#te_btl_tab_A" ).addClass( "active" );
}, this ) );
}, this ) );
// Initialize tabs.
$( ".tabular.menu .item" ).tab();
// Initialize modal.
$( "#te_browseTagsLite_modal" ).modal(
{
onVisible : function ()
{
if ( !TE.User.tsuminoEnhanced.tagList )
{
$( "#te_btl_btn_reload" ).click();
}
if ( firstLoad )
{
$( "#te_browseTagsLite_modal" ).modal( "refresh" );
$( "#te_btl_btn_A" ).click();
firstLoad = false;
}
},
observeChanges : true
} );
$( "#te_browseTagsLite_menuLink" ).click( function ()
{
$( "#te_browseTagsLite_modal" ).modal( "show" );
} );
$( ".te_btl_tabButton" ).click( function ()
{
$( "#te_browseTagsLite_modal" ).modal( "refresh" );
} );
// Close button
$( "#te_btl_closeModal" ).click( function ()
{
$( "#te_browseTagsLite_modal" ).modal( "hide" );
} );
$( "#te_btl_btn_reload" ).popup( {
title : 'Reload the Tag List from the Server'
} );
}, this ) );
}, updateTagList : function ()
{
var dfd = jQuery.Deferred();
TE.status.tagListDownloaded = false;
TE.status.tagListPage = 1;
TE.status.tagList = [];
bean.on( TE.status.tagList, "completed", function ()
{
TE.log( "gname", "Browse Tags Lite", "Tag list downloaded." );
TE.status.tagListDownloaded = true;
TE.User.tsuminoEnhanced.tagList = TE.status.tagList;
TE.updateSettings();
dfd.resolve();
} );
this.loadTagPages( TE.status.tagListPage );
TE.log( "gname", "Browse Tags Lite", "Retrieving tags..." );
return dfd.promise();
},
loadTagPages : function (pageNum)
{
if ( !TE.status.tagListDownloaded )
{
$.ajax( {
method : "GET",
url : TE.site.baseURL.root + TE.site.browseTags.prefix,
data : {infpage : pageNum},
success : $.proxy( function (data)
{
if ( data.trim() != "" )
{
data = TE.fn.scrubAjaxData( data );
$( data ).find( "a" ).each( function ()
{
TE.status.tagList.push( $( this ).text() );
} );
TE.status.tagListPage++;
this.loadTagPages( TE.status.tagListPage );
}
else
{
bean.fire( TE.status.tagList, "completed" );
}
}, this ),
error : function () {}
} );
}
},
renderTagList : function ()
{
var numTags = 0;
var tagCounter = [];
var thisTag = "";
var urlFormattedTag = "";
var thisTagUrl = "";
var idFormattedTag = "";
var thisTagFL = "";
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
alphabet = alphabet.split( "" );
for (var i = 0 ; i < alphabet.length ; i++)
{
tagCounter[ alphabet[ i ] ] = 0;
$( "#te_btl_tab_" + alphabet[ i ] ).html( `
<h1>` + alphabet[ i ] + `</h1>
<hr />` );
$( "#te_btl_tab_" + alphabet[ i ] ).removeClass( "active" );
$( "#te_btl_btn_" + alphabet[ i ] ).removeClass( "active" );
}
for (var i = 0 ; i < TE.User.tsuminoEnhanced.tagList.length ; i++)
{
numTags++;
thisTag = TE.User.tsuminoEnhanced.tagList[ i ];
thisTag = thisTag.trim();
thisTag = thisTag.replace( /\w\S*/g, function (txt)
{
return txt.charAt( 0 ).toUpperCase() + txt.substr( 1 ).toLowerCase();
} );
urlFormattedTag = TE.fn.replaceAll( thisTag, " ", "+" );
thisTagUrl = TE.site.baseURL.root + "/Browse/Tag?name=" + urlFormattedTag;
idFormattedTag = TE.fn.replaceAll( thisTag, " ", "_" );
thisTagFL = thisTag.charAt( 0 );
tagCounter[ thisTagFL ]++;
$( "#te_btl_tab_" + thisTagFL ).append( `
<a class="book-tag" id="te_btl_tagLink_"` + idFormattedTag + `" href="` + thisTagUrl + `">` + thisTag + `</a>
<br />
` );
}
// Remove links from empty sections and add tag counters to populated sections.
for (i = 0 ; i < alphabet.length ; i++)
{
if ( tagCounter[ alphabet[ i ] ] == 0 )
{
$( "#te_btl_tab_" + alphabet[ i ] ).append( "<br />No tags that start with '" + alphabet[ i ] + "'.<br />" );
}
}
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
(function ()
{
/*******************************************************
* Browsing Tweaks - Browsing Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = 'Browsing Tweaks';
shortName = TE.fn.camelize( name );
description = "A collection of customizations to browsing.";
options = [];
section = "Browsing";
incompatible = false;
main = {};
main = {
init : function ()
{
if ( TE.on.browse )
{
$.when( TE.status.enhancePage ).done( function ()
{
if ( typeof TE.User[ shortName ] !== "undefined" )
{
if ( TE.User[ shortName ].removeSidebar )
{
$( "#te_sidebarContainer" ).remove();
$( "#te_bookshelfContainer" ).css( "width", "100%" );
}
if ( TE.User[ shortName ].moreBooks )
{
$( "style" ).append( "@media(min-width:768px) { .overlay-title { font-size:.8em; } .col-sm-4 { width: 25% } }" );
$( "style" ).append( "@media(min-width:992px) { .col-md-3 { width: 20% } }" );
}
if ( TE.User[ shortName ].skipInfo )
{
// Apply new CSS.
$( "style" ).append( TE.ui.css.browsingTweaks.master );
$( "div.overlay" ).each( function ()
{
// Get Book ID
var bookID = $( this ).attr( "id" );
bookID = bookID.replace( "te_book_", "" );
bookID = bookID.replace( "_overlay", "" );
bookID = parseInt( bookID );
// Replace old class on view button.
var viewInfoButton = $( this ).find( "a.overlay-button" );
var linkURL = TE.site.reader.prefix + bookID + "/1";
$( viewInfoButton ).after(`<a href="`+linkURL+`" class="te_browsetweak_readbutton">READ</a>`);
var readButton = $( this ).next();
$( viewInfoButton ).text( "INFO" );
$( viewInfoButton ).removeClass( "overlay-button" );
$( viewInfoButton ).addClass( "te_browsetweak_infobutton" );
// Add new read button.
if ( TE.User.recordKeeper )
{
if ( TE.User.recordKeeper.data[ bookID ] )
{
linkURL = TE.site.reader.prefix + bookID + "/" + TE.User.recordKeeper.data[ bookID ][ 'lastSeen' ];
$( readButton ).attr( "href", linkURL );
}
}
} );
}
if ( TE.User[ shortName ].fitTitles )
{
$( "div.overlay" ).each( function ()
{
var thisTitle = $( this ).find( ".overlay-title" ).text();
var titleLength = thisTitle.split( "" );
titleLength = titleLength.length;
if ( (titleLength > 40) && (titleLength < 50) )
{
$( this ).find( ".overlay-title" ).css( "font-size", ".75em" );
}
else if ( (titleLength > 50) )
{
$( this ).find( ".overlay-title" ).css( "font-size", ".65em" );
}
} );
}
if ( TE.User[ shortName ].thumbnailLinks )
{
$( "div.overlay" ).each( function ()
{
$( this ).on( "mousedown", $.proxy( function (e)
{
// Only fire if the div itself is clicked, ignoring children.
if ( e.target == this )
{
// Left Mouse
if ( (e.which == 1) )
{
TE.vbLog( "Left mouse button clicked." );
if ( TE.User[ shortName ].skipInfo )
{
var thisLink = $( this ).find( "a.te_browsetweak_readbutton" ).attr( "href" );
}
else
{
var thisLink = $( this ).find( "a.overlay-button" ).attr( "href" );
}
thisLink = TE.site.baseURL.root + thisLink;
if ( e.ctrlKey )
{
w.open( thisLink );
}
else
{
w.location.href = thisLink;
}
}
// Middle Mouse
if ( (e.which == 2) )
{
TE.vbLog( "Middle mouse button clicked." );
if ( TE.User[ shortName ].skipInfo )
{
var thisLink = $( this ).find( "a.te_browsetweak_readbutton" ).attr( "href" );
}
else
{
var thisLink = $( this ).find( "a.overlay-button" ).attr( "href" );
}
thisLink = TE.site.baseURL.root + thisLink;
w.open( thisLink );
}
}
e.preventDefault();
}, this ) );
} );
}
}
} );
}
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "toggle",
name : "Remove Sidebar",
description : "Removes the "random picks" sidebar.",
defaultValue : false,
arguments : false
};
var opt2 = {
type : "toggle",
name : "More Books",
description : "Displays one extra book per row.",
defaultValue : false,
arguments : false
};
var opt3 = {
type : "toggle",
name : "Thumbnail Links",
description : "Clicking anywhere on the thumbnail image will load the Doujin.",
defaultValue : false,
arguments : false
};
var opt4 = {
type : "toggle",
name : "Skip Info",
description : "Skips the book info page and takes you directly to the reader.",
defaultValue : false,
arguments : false
};
var opt5 = {
type : "toggle",
name : "Fit Titles",
description : "Makes sure doujin titles fit appropriately.",
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
options.push( new TE.Enhancement.option.main( opt2.type, opt2.name, opt2.description, opt2.defaultValue, opt2.arguments ) );
options.push( new TE.Enhancement.option.main( opt3.type, opt3.name, opt3.description, opt3.defaultValue, opt3.arguments ) );
options.push( new TE.Enhancement.option.main( opt4.type, opt4.name, opt4.description, opt4.defaultValue, opt4.arguments ) );
options.push( new TE.Enhancement.option.main( opt5.type, opt5.name, opt5.description, opt5.defaultValue, opt5.arguments ) );
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
/*******************************************************
* Reader Enhancements
*******************************************************/
(function ()
{
/*******************************************************
* Automatic Repositioning - Reader Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Automatic Repositioning";
shortName = TE.fn.camelize( name );
description = "Automatically scrolls you to the top of the image.";
options = [];
section = "Reader";
incompatible = false;
main = {
init : function ()
{
if ( TE.on.reader )
{
$.when( TE.status.enhancePage ).done( $.proxy( function ()
{
TE.Enhancements.unstickiedHeader.fn.run();
this.run();
}, this ) );
}
}, run : function ()
{
var imgPos = $( "#te_imageBlock" ).offset().top;
$( "html, body" ).animate( {scrollTop : imgPos}, 300 );
}
};
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
(function ()
{
/*******************************************************
* Seamless Viewing - Reader Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Seamless Viewing";
shortName = TE.fn.camelize( name );
description = `Negates the need to load the entire Tsumino webpage again every time you flip through a Doujin.<br />
This means faster load times, and not losing sight of the previous page until the instant the new page is loaded.<br />
Seamless Viewing leaves the previous image in place until the new one is ready.<br />
Once the new image is ready, you are automatically scrolled up to the top of the image.`;
options = [];
section = "Reader";
incompatible = [ "Infinity Scrolling" ];
main = {};
main = {
replaceKeybinds : function ()
{
// Disable default Tsumino Reader Keybinds.
unsafeWindow.$( document ).off( "keydown" );
// Use Classic Seamless Viewing keybinds instead.
$( document ).keydown( $.proxy( function (e)
{
var bk = $.proxy( function ()
{
this.changePage( TE.book.prevPage );
}, this );
var fwd = $.proxy( function ()
{
this.changePage( TE.book.nextPage );
}, this );
if ( (!e.ctrlKey) && (!e.altKey) )
{
switch (e.which)
{
case 87: // w
w.scrollBy( 0, -100 );
break;
case 83: // s
w.scrollBy( 0, 100 );
break;
case 8: //back
case 37: //left
case 65: //a
bk();
break;
case 32: //space
case 13: //enter
case 39: //right
case 68: //d
fwd();
break;
default:
return;
}
e.preventDefault();
}
}, this ) );
}, changePage : function (pageNumber)
{
var dfd = jQuery.Deferred();
function changePageCommon(pageNumber)
{
pageNumber = parseInt( pageNumber );
// Update page and location variables.
TE.book.currentPage = pageNumber;
TE.book.prevPage = pageNumber - 1;
TE.book.nextPage = pageNumber + 1;
if ( TE.book.nextPage > TE.book.totalPages )
{
TE.book.nextPage = false;
}
if ( TE.book.prevPage <= 0 )
{
TE.book.prevPage = false;
}
TE.book.currentPageURL = TE.site.reader.prefix + TE.book.id + "/" + TE.book.currentPage;
// Get the dataURI from the source of loader's hidden image.
var newImageSrc = $( "#te_loadImage_" + pageNumber ).attr( "src" );
//var newImageSrc = TE.site.image.prefix + TE.book.id + "/" + TE.book.currentPage;
// Remove the loader's hidden image.
$( "#te_readerCurrentImage" ).attr( "src", newImageSrc );
// Reposition.
TE.Enhancements.automaticRepositioning.fn.run();
// If Record Keeper is Enabled.
if ( TE.User.recordKeeper.enable )
{
TE.Enhancements.recordKeeper.fn.update();
}
// If Page Jumper is Enabled.
if ( TE.User.pageJumper.enable )
{
$( "#te_pageJumper" ).val( pageNumber );
$( "#te_pageJumper" ).dropdown( "set selected", pageNumber );
}
// Prefetch new pages.
// TE.fn.prefetch.init( TE.book.currentPage );
// Update title.
$( "title" ).text( "Tsumino - " + TE.book.title + " - Page " + TE.book.currentPage );
// Update links.
this.updateLinks();
// Update history and window location.
if ( (!history.state) || (history.state && history.state.pageNumber != TE.book.currentPage) )
{
w.history.pushState( {pageNumber : TE.book.currentPage}, $( "title" ).text(), TE.book.currentPageURL );
}
TE.log( "gname", name, "Image " + pageNumber + " has been placed in the reader." );
unsafeWindow.ga('send', 'pageview', TE.site.reader.prefix + TE.book.id + '/' + TE.book.currentPage);
dfd.resolve();
}
var cpc = changePageCommon.bind( this );
// Make sure the page is in range first.
if ( (pageNumber <= TE.book.totalPages) && (pageNumber > 0) )
{
if ( TE.status.pagesLoaded[ pageNumber ] == "done" )
{
cpc( pageNumber );
}
else
{
if ( (TE.status.prefetch[ TE.book.id ][ pageNumber ] != "") && (TE.status.prefetch[ TE.book.id ][ pageNumber ] != "working") )
{
//TE.status.load = TE.load( pageNumber, TE.status.prefetch[ TE.book.id ][ pageNumber ] );
// Once the requested page is loaded, continue.
/*
$.when( TE.status.load ).then( $.proxy( function ()
{
if ( TE.status.pagesLoaded[ pageNumber ] == "done" )
{
TE.log( "CPC going" );
cpc( pageNumber );
}
else
{
TE.log( "CPC ERROR" );
}
}, this ) );
*/
$( "body" ).append( `
<img id="te_loadImage_"` + pageNumber + `" src="/Image/Image/"` + TE.book.id + `/` + pageNumber + `" style="display:none;">
` );
$("#te_loadImage_" + pageNumber).load(function ()
{
cpc ( pageNumber );
});
if ($("#te_loadImage_" + pageNumber).complete)
{
$(this).load();
}
}
else
{
TE.log( "gname", name, "Prefetch is still initializing..." )
}
}
}
// If the user requested a page that was less than 1 or greater than the total number of pages, stop.
else
{
if ( pageNumber == false )
{
w.location.href = TE.site.book.url + TE.book.id;
}
TE.log( "gname", "Seamless Viewing", "Image " + pageNumber + " is out of range and will not be loaded." );
dfd.resolve();
}
return dfd.promise();
}, updateLinks : function ()
{
TE.vbLog( "gname", name, "Updating links... " );
// Remove old click binds from links.
$( "#te_prevButton" ).off( "click" );
$( "#te_nextButton" ).off( "click" );
$( "#te_readerCurrentImage" ).off( "click" );
// Establish updated click binds.
if ( TE.book.currentPage <= TE.book.totalPages )
{
$( "#te_nextButton" ).css( "display", "inline" );
$( "#te_nextButton" ).click( $.proxy( function ()
{
this.changePage( TE.book.nextPage );
}, this ) );
$( "#te_readerCurrentImage" ).click( $.proxy( function ()
{
this.changePage( TE.book.nextPage );
}, this ) );
}
if ( TE.book.currentPage == TE.book.totalPages )
{
$( "#te_nextButton" ).css( "display", "none" );
$( "#te_readerCurrentImage" ).click( $.proxy( function ()
{
this.changePage( TE.book.nextPage );
}, this ) );
}
if ( TE.book.currentPage > 1 )
{
$( "#te_prevButton" ).css( "display", "inline" );
$( "#te_prevButton" ).click( $.proxy( function ()
{
this.changePage( TE.book.prevPage );
}, this ) );
}
else
{
$( "#te_prevButton" ).css( "display", "none" );
}
$( "#te_currentPage" ).html( "<a href='" + TE.book.currentPageURL + "'>" + TE.book.currentPage + "</a>" );
}, init : function ()
{
if ( TE.on.reader )
{
$.when( TE.status.enhancePage ).done( $.proxy( function ()
{
w.history.replaceState( {pageNumber : TE.book.currentPage}, $( "title" ).text(), TE.book.currentPageURL );
// Allow history navigation to work with Seamless Viewing.
$( w ).on( "popstate", $.proxy( function ()
{
if ( history.state )
{
this.changePage( history.state.pageNumber );
}
}, this ) );
TE.log( "gname", name, "Initializining..." );
// Replace default Tsumino reader keybinds with Enhanced Seamless Viewing keybinds.
this.replaceKeybinds();
// Automatic Repositioning.
TE.Enhancements.automaticRepositioning.fn.run();
// Unstickied Header.
TE.Enhancements.unstickiedHeader.fn.run();
// Remove default Tsumino doujin navigation links.
$( "#te_prevButton" ).attr( "href", "javascript:;" );
$( "#te_nextButton" ).attr( "href", "javascript:;" );
$( "#te_imageLink" ).attr( "href", "javascript:;" );
// Prepare Prefetch
TE.status.prefetch[ TE.book.id ] = {};
for (var i = 1 ; i <= TE.book.totalPages ; i++)
{
TE.status.prefetch[ TE.book.id ][ i ] = "";
}
TE.fn.prefetch.init( TE.book.currentPage );
// Update doujin navigation links.
this.updateLinks();
$( "body" ).append( "<img id='te_loadImage_" + TE.book.currentPage + "' style='display:none;'>" );
// "Cache" the first image that loads for later.
var originImage = new Image();
originImage.onload = function ()
{
var canvas = document.createElement( "canvas" );
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
canvas.getContext( "2d" ).drawImage( this, 0, 0 );
var newSrc = canvas.toDataURL( "image/jpeg" );
$( "#te_loadImage_" + TE.book.currentPage ).attr( "src", newSrc );
};
originImage.src = $( "#te_readerCurrentImage" ).attr( "src" );
}, this ) );
}
else if ( TE.on.auth )
{
// Reserved
}
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
var opt2 = {
type : "toggle",
name : "Show Messages",
description : "Displays loading messages while preparing images for display.",
defaultValue : true,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
//options.push(new TE.Enhancement.option.main(opt2.type,opt2.name,opt2.description,opt2.defaultValue,opt2.arguments));
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
(function ()
{
/*******************************************************
* Page Jumper - Reader Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Page Jumper";
shortName = TE.fn.camelize( name );
description = "Adds a dropdown box to the Reader that lets you skip directly to a page.";
options = [];
section = "Reader";
incompatible = false;
main = {};
main = {
init : function ()
{
if ( TE.on.reader )
{
$.when( TE.status.enhancePage ).done( $.proxy( function ()
{
$( "#te_readerPagination" ).after( `
<h1 style='display:inline;'>Jump to page: </h1>
<select class='ui compact search dropdown' id='te_pageJumper'></select><br />
` );
for (var i = 1 ; i <= TE.book.totalPages ; i++)
{
$( "#te_pageJumper" ).append( "<option value='" + i + "'>" + i + "</option>" );
}
$( "#te_pageJumper" ).val( TE.book.currentPage );
$( "#te_pageJumper" ).change( $.proxy( function ()
{
// Seamless Viewing Compatibility
if ( TE.User.seamlessViewing.enable )
{
var pageNumber = parseInt( $( "#te_pageJumper" ).val() );
$.when( TE.fn.prefetch.init( pageNumber ) ).then( function ()
{
TE.Enhancements.seamlessViewing.fn.changePage( pageNumber );
} );
}
// Vanilla Tsumino
else
{
w.location.href = TE.site.reader.url + TE.book.id + "/" + pageNumber;
}
}, this ) );
$( "#te_pageJumper" ).dropdown();
}, this ) );
}
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
(function ()
{
/*******************************************************
* Infinity Scrolling - Reader Enhancement
*******************************************************/
var name = "Infinity Scrolling",
shortName = TE.fn.camelize( name ),
description = "Scroll down to load images.",
options = [], section = "Reader",
incompatible = [ "Seamless Viewing" ],
main = {};
main = {
init : function ()
{
if ( TE.on.reader )
{
$.when( TE.status.enhancePage ).done( function ()
{
} );
}
}
};
//TE.Enhancement.option.main(type,name,description,defaultValue,arguments)
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
//TE.Enhancements[shortName] = new TE.Enhancement.main(name,description,options,section,incompatible,main);
})();
(function ()
{
/*******************************************************
* Automatic Repositioning - Reader Enhancement
*******************************************************/
var name, shortName, description, options, section, incompatible, main;
name = "Automatic Update Installation";
shortName = TE.fn.camelize( name );
description = `If your version of Tsumino Enhanced is out of date, it will automatically attempt to update.<br />
You must still accept your browser's prompt to install the update.`;
options = [];
section = "TsuminoEnhanced";
incompatible = false;
main = {
init : function ()
{
// No INIT.
}
};
var opt1 = {
type : "enable",
name : false,
description : false,
defaultValue : false,
arguments : false
};
options.push( new TE.Enhancement.option.main( opt1.type, opt1.name, opt1.description, opt1.defaultValue, opt1.arguments ) );
TE.Enhancements[ shortName ] = new TE.Enhancement.main( name, description, options, section, incompatible, main );
})();
/*************************************************************************************
* Tsumino Enhanced Settings Page
*************************************************************************************/
TE.settings = {
render : function ()
{
if ( !TE.User.readNews )
{
TE.User.readNews = {};
}
$( "#te_config_modal" ).html( "<div id='te_settings' style='font-size: 1.4em;'></div>" );
$( "#te_config_modal" ).prepend( `
<div class="header"><h1><span class="te_mainColor">Tsumino Enhanced</span>
<span id="te_version" class="small">` + TE.version + `</span></h1></div>
` );
if ( !TE.User.tsuminoEnhanced.upToDate )
{
var installLocation = TE.updateLocation;
installLocation = installLocation.replace( "/scripts/", "/install/" );
installLocation = installLocation + ".user.js";
//$("#te_version").append(" - <a style='color:#ff0000; text-decoration:none;' href='" + installLocation + "'>Update!</a>");
$( "#te_version" ).after( `
<div id="te_updateButton" class="ui big labeled button" tabindex="0">
<div class="ui red button"><i class="upload icon"></i>Update!</div><a class="ui basic red left pointing label">`
+ TE.User.tsuminoEnhanced.latestVersion + `</a></div>` );
//$("#te_config_modal").append("<a id='te_secretUpdateLink' style='display:none;' href='" + installLocation + "'>Update!</a>")
$( "#te_updateButton" ).click( function ()
{
$( "body" ).append( `
<div id="te_refresh_modal" class="ui basic modal">
<div class="header" style="font-size: 3em;"><i class="upload icon"></i> Updating Tsumino Enhanced...</div>
<div class="image content"><div class="image"><i class="refresh icon"></i></div>
<div class="description" style="font-size: 1.5em;">
<p>You will be prompted to install the update in just a moment.</p>
<p>In order for the update to take effect, you must refresh this page after it has finished installing.</p>
<p>Do you wish to refresh the page now?</p>
</div></div>
<div class="actions">
<button id="te_refreshPageButton" class="massive fluid gray ui button disabled">Refresh</button>
</div></span>` );
$( "#te_config_modal" ).modal( "hide" );
$( "#te_refresh_modal" ).modal();
$( "#te_refresh_modal" ).modal( "show" );
$( "#te_refresh_modal" ).modal( "refresh" );
setTimeout( function ()
{
w.location.href = installLocation;
$("#te_refreshPageButton").removeClass("disabled");
$("#te_refreshPageButton").removeClass("gray");
$("#te_refreshPageButton").addClass("green");
}, 5000 );
$( "#te_refreshPageButton" ).click( function ()
{
if(!$("#te_refreshPageButton").hasClass("disabled"))
{
w.location.reload();
}
} );
} );
$( "#te_updateButton" ).popup( {
title : 'Click here to begin the update process.'
} );
}
// Settings page navigation structure.
//$("#te_settings").prepend("<div id='te_tabContainer' class='te_configTab'><nav><ul><li id='tab_generalEnhancements'><a href='javascript:;'>General</a></li><li id='tab_browsingEnhancements'><a href='javascript:;'>Browsing</a></li><li id='tab_readerEnhancements'><a href='javascript:;'>Reader</a></li><li id='tab_teNews'><a href='javascript:;'>TE News</a></li><li id='tab_searchEnhancements'><a href='javascript:;'>Search</a></li></ul></nav></div>");
$( "#te_settings" ).prepend( `<div id="te_settings_tabs" class="ui top attached inverted large tabular menu"></div>` );
$( "#te_settings_tabs" ).append( `<a class="item active" data-tab="generalEnhancements">General Enhancements</a>` );
$( "#te_settings_tabs" ).append( `<a class="item" data-tab="browsingEnhancements">Browsing Enhancements</a>` );
$( "#te_settings_tabs" ).append( `<a class="item" data-tab="readerEnhancements">Reader Enhancements</a>` );
$( "#te_settings_tabs" ).append( `<a class="item" data-tab="TE_options">TE Configuration</a>` );
$( "#te_settings_tabs" ).append( `<a class="item" data-tab="teAbout">About</a>` );
$( "#te_settings" ).append( `
<div id="te_settings_tab_generalEnhancements" class="ui bottom attached inverted tab segment active" data-tab="generalEnhancements"></div>
` );
$( "#te_settings" ).append( `
<div id="te_settings_tab_browsingEnhancements" class="ui bottom attached inverted tab segment" data-tab="browsingEnhancements"></div>
` );
$( "#te_settings" ).append( `
<div id="te_settings_tab_readerEnhancements" class="ui bottom attached inverted tab segment" data-tab="readerEnhancements"></div>
` );
$( "#te_settings" ).append( `
<div id="te_settings_tab_TE_options" class="ui bottom attached inverted tab segment" data-tab="TE_options"></div>
` );
$( "#te_settings" ).append( `
<div id="te_settings_tab_teAbout" class="ui bottom attached tab segment inverted" data-tab="teAbout"></div>
` );
var tobyAvatar = "data:image/jpeg;base64,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";
var tobyCard = `
<div class="ui centered grid">
<div class="seven wide column">
<div class="ui fluid blue card">
<div class="content">
<h1 class="header">Tsumino Enhanced</h1>
<div class="description">
<table class="ui single line definition blue left aligned table">
<tbody>
<tr>
<td class="left aligned">Current Version</td>
<td>` + TE.version + `</td>
</tr>
<tr>
<td class="left aligned">Latest Version</td>
<td>` + TE.User.tsuminoEnhanced.latestVersion + `</td>
</tr>
<tr>
<td>Changelog</td>
<td><button id="te_tobyLinks_teChangelog" data-content="Check out the list of changes."
class="ui large circular inverted blue icon button teTooltip"><i class="external icon"></i></button></td>
</tr>
<tr>
<td>Requests & Feedback</td>
<td><button id="te_tobyLinks_teRaF" data-content="Influence TE's development."
class="ui large circular inverted blue icon button teTooltip"><i class="external icon"></i></button></td>
</tr>
<tr class="top aligned">
<td class="left aligned collapsing">Library Dependencies</td>
<td>
<ul class="ui list">
<li class="teTooltip" data-content="General purpose library.">jQuery 2.1.14</li>
<li class="teTooltip" data-content="Gives TE a nicer UI.">SemanticUI 2.1.8</li>
<li class="teTooltip" data-content="Event management library.">Bean 1.0.15</li>
<li class="teTooltip" data-content="A more versatile animation library.">Velocity 1.2.3</li>
</ul>
</td>
</tr>
<tr class="top aligned">
<td class="left aligned">@grant Permissions</td>
<td>
<ul class="ui list">
<li class="teTooltip" data-content="Used for saving your settings.">GM_setValue</li>
<li class="teTooltip" data-content="Used for reading your settings.">GM_getValue</li>
<li class="teTooltip" data-content="Used for cleaning up depreciated values.">GM_deleteValue</li>
<li class="teTooltip" data-content="Used for opening links in new tabs.">GM_openInTab</li>
<li class="teTooltip" data-content="Used for overriding Tsumino hotkeys.">unsafeWindow</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="five wide column">
<div class="ui purple card" style="width:280px;">
<div class="image">
<img src="` + tobyAvatar + `" />
</div>
<div class="content">
<div class="header">Toby</div>
<div class="meta">
<a>Web Developer</a>
</div>
<div class="description">
<p>I'm the guy that made this.</p>
</div>
</div>
<div class="extra content">
<button id="te_tobylinks_home" data-content="Visit my project blog!" class="ui large circular inverted violet icon button teTooltip">
<i class="large home icon"></i>
</button>
<button id="te_tobylinks_github" data-content="Take a look at my GitHub profile!" class="ui large circular black icon button teTooltip">
<i class="large github alternate icon"></i>
</button>
<button id="te_tobylinks_openuserjs" data-content="Check out my other userscripts!" class="ui large circular grey icon button teTooltip">
<i class="large code icon"></i>
</button>
<button id="te_tobylinks_skype" data-content="Chat with me on Skype!" class="ui large circular inverted blue icon button teTooltip">
<i class="large skype icon"></i>
</button>
<button id="te_tobylinks_tsuminopm" data-content="Send me a PM on the Tsumino Forums!" class="ui large circular inverted pink icon button teTooltip">
<i class="large mail outline icon"></i>
</button>
</div>
</div>
</div>
</div>
`;
$( "#te_settings_tab_teAbout" ).append( tobyCard );
$( "#te_tobyLinks_teChangelog" ).click( function ()
{
GM_openInTab( "http://codingtoby.com/category/userscripts/tsumino-enhanced/te-updates/" );
} );
$( "#te_tobyLinks_teRaF" ).click( function ()
{
GM_openInTab( "http://codingtoby.com/userscripts/tsumino-enhanced/requests-and-feedback/#respond" );
} );
$( "#te_tobylinks_home" ).click( function ()
{
GM_openInTab( "http://codingtoby.com/" );
} );
$( "#te_tobylinks_github" ).click( function ()
{
GM_openInTab( "https://github.com/tobiaskelmandia" );
} );
$( "#te_tobylinks_openuserjs" ).click( function ()
{
GM_openInTab( "https://openuserjs.org/users/Tobias.Kelmandia/scripts" );
} );
$( "#te_tobylinks_skype" ).click( function ()
{
w.location.href = "skype:tobias_kelmandia?chat";
} );
$( "#te_tobylinks_tsuminopm" ).click( function ()
{
GM_openInTab( "http://www.tsumino.com/Forum/ucp.php?i=pm&mode=compose&u=191" );
} );
// Initialize Tooltips
$( ".teTooltip" ).popup();
// Populate Sections.
for (var key in TE.Enhancements)
{
if ( TE.Enhancements.hasOwnProperty( key ) )
{
var obj = TE.Enhancements[ key ];
if ( obj[ "section" ] != false )
{
// Determine which section to append to.
var sectionID = "";
if ( obj[ "section" ] == "General" )
{
sectionID = "#te_settings_tab_generalEnhancements";
}
else if ( obj[ "section" ] == "Browsing" )
{
sectionID = "#te_settings_tab_browsingEnhancements";
}
else if ( obj[ "section" ] == "Reader" )
{
sectionID = "#te_settings_tab_readerEnhancements";
}
else if ( obj[ "section" ] == "TsuminoEnhanced" )
{
sectionID = "#te_settings_tab_TE_options";
}
// Append the Enhancement's options group to the section.
$( sectionID ).append( "<div id='" + obj[ "shortName" ] + "_group' class='te_optionGroup'></div>" );
// Add the description.
if ( obj[ "description" ] != false )
{
$( "#" + obj[ "shortName" ] + "_group" ).append( "<div class='te_optionDescription'>" + obj[ "description" ] + "</div>" );
}
// Add the primary options area.
$( "#" + obj[ "shortName" ] + "_group" ).append( "<div id='te_options_" + obj[ "shortName" ] + "'></div>" );
var noEnable = true;
if ( obj[ "options" ] != false )
{
// Display all options.
for (var oKey in obj[ "options" ])
{
if ( obj[ "options" ].hasOwnProperty( oKey ) )
{
var option = obj[ "options" ][ oKey ];
// Write the Enable option.
if ( option[ "type" ] == "enable" )
{
$( "#" + obj[ "shortName" ] + "_group" ).prepend( `
<div class="ui middle aligned toggle checkbox">
<input id="tes_` + obj[ "shortName" ] + `_enable" name="tes_` + obj[ "shortName" ] + `_enable" type="checkbox">
<label for="tes_` + obj[ "shortName" ] + `_enable">
<a class="ui huge blue label">` + obj[ "name" ] + `</a>
</label>
</div>` );
$( "#enhancement_header_" + obj[ "shortName" ] ).click( {obj : obj}, function (event)
{
TE.log( $( "#tes_" + event.data.obj[ "shortName" ] + "_enable" ) );
if ( $( "#tes_" + event.data.obj[ "shortName" ] + "_enable" ).prop( "checked" ) == true )
{
$( "#tes_" + event.data.obj[ "shortName" ] + "_enable" ).prop( "checked", false );
}
else
{
$( "#tes_" + event.data.obj[ "shortName" ] + "_enable" ).prop( "checked", true );
}
} );
noEnable = false;
}
// Write Toggle Options.
if ( option[ "type" ] == "toggle" )
{
$( "#te_options_" + obj[ "shortName" ] ).append( `<br />
<div class="ui middle aligned toggle checkbox">
<input id="tes_` + obj[ "shortName" ] + `_` + option[ "shortName" ] + `"
name="tes_` + obj[ "shortName" ] + `_` + option[ "shortName" ] + `" type="checkbox" class="te_subOption" />
<label for="tes_` + obj[ "shortName" ] + `_` + option[ "shortName" ] + `">
<a class="ui huge blue label">` + option[ "name" ] + `</a>
</label>
</div>` );
// Write the option's description.
if ( option[ "description" ] != false )
{
$( "#" + obj[ "shortName" ] + "_optionContainer_" + option[ 'shortName' ] ).append( "<br />"
+ option[ "description" ] );
}
}
}
}
}
// If there was no "enable" option found:
if ( noEnable )
{
// Display the title without a switch.
$( "#" + obj[ "shortName" ] + "_group" ).prepend( "<h2 class='te_enhancementName'>" + obj[ "name" ] + "</h2>" );
// Apply user settings.
if ( typeof TE.User[ obj[ "shortName" ] ] !== "undefined" )
{
for (var oKey in obj[ "options" ])
{
var option = obj[ "options" ][ oKey ];
if ( option[ "type" ] == "toggle" )
{
$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] )
.prop( "checked", TE.User[ obj[ "shortName" ] ][ option[ 'shortName' ] ] );
}
}
}
}
else
{
// Apply user settings.
if ( typeof TE.User[ obj[ "shortName" ] ] !== "undefined" )
{
for (var oKey in obj[ "options" ])
{
var option = obj[ "options" ][ oKey ];
if ( TE.User[ obj[ "shortName" ] ][ "enable" ] == true )
{
if ( option[ "type" ] == "enable" )
{
$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] ).prop( "checked", true );
}
else
{
if ( option[ "type" ] == "toggle" )
{
$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] )
.prop( "checked", TE.User[ obj[ "shortName" ] ][ option[ 'shortName' ] ] );
}
}
}
else
{
if ( option[ "type" ] != "enable" )
{
$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] ).prop( "checked", false );
$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] ).prop( "disabled", "disabled" );
}
}
}
}
else
{
for (var oKey in obj[ "options" ])
{
if ( obj[ "options" ].hasOwnProperty( oKey ) )
{
var option = obj[ "options" ][ oKey ];
if ( option[ "type" ] != "enable" )
{
if ( !$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] ).prop( "disabled" ) )
{
$( "#tes_" + obj[ "shortName" ] + "_" + option[ 'shortName' ] ).prop( "disabled", "disabled" );
}
}
}
}
}
// Apply default values to options when enabling an Enhancement.
$( "#tes_" + obj[ "shortName" ] + "_enable" ).change( {obj : obj}, function (event)
{
if ( $( "#tes_" + event.data.obj[ "shortName" ] + "_enable" ).prop( "checked" ) == true )
{
for (var oKey in event.data.obj[ "options" ])
{
if ( event.data.obj[ "options" ].hasOwnProperty( oKey ) )
{
var option = event.data.obj[ "options" ][ oKey ];
if ( option[ "type" ] == "toggle" )
{
$( "#tes_" + event.data.obj[ "shortName" ] + "_" + option[ 'shortName' ] ).removeProp( "disabled" );
$( "#tes_" + event.data.obj[ "shortName" ] + "_"
+ option[ 'shortName' ] ).prop( "checked", option[ 'defaultValue' ] );
}
}
}
}
// Disable Enhancement options unless the enhancement is enabled.
else
{
for (var oKey in event.data.obj[ "options" ])
{
if ( event.data.obj[ "options" ].hasOwnProperty( oKey ) )
{
var option = event.data.obj[ "options" ][ oKey ];
if ( option[ "type" ] != "enable" )
{
if ( option[ "type" ] == "toggle" )
{
$( "#tes_" + event.data.obj[ "shortName" ] + "_"
+ option[ 'shortName' ] ).prop( "checked", false );
}
$( "#tes_" + event.data.obj[ "shortName" ] + "_"
+ option[ 'shortName' ] ).prop( "disabled", "disabled" );
}
}
}
}
} );
}
// List any incompatibilities.
if ( obj[ "incompatible" ] != false )
{
$( "#" + obj[ "shortName" ] + "_group" ).append( "<br /><div class='te_en_incompatible' id='" + obj[ "shortName" ]
+ "_incompatible'>This Enhancement is incompatible with: </div>" );
var punct = "";
for (var i = 0 ; i < obj[ "incompatible" ].length ; i++)
{
$( "#" + obj[ "shortName" ] + "_incompatible" ).append( "<span class='te_enhancementColor'>"
+ obj[ "incompatible" ][ i ] + "</span>" );
if ( i + 1 == obj[ "incompatible" ].length )
{
punct = ".";
}
else
{
punct = ", ";
}
$( "#" + obj[ "shortName" ] + "_incompatible" ).append( punct );
$( "#" + obj[ "shortName" ] + "_group" ).append( "<br /><br />" );
}
}
else
{
$( "#" + obj[ "shortName" ] + "_group" ).append( "<br /><br />" );
}
}
}
}
//$("#te_settingsBody").append("<div id='searchEnhancements' class='te_options'></div>");
//$("#te_settings").append("<div id='forumEnhancements' class='te_options'></div>");
// Create Buttons
$( "#te_settings" ).append( "<br /><br /><div id='te_buttonContainer'></div><br /><br />" );
$( "#te_buttonContainer" ).append( "<a id='te_saveAndCloseButton' class='book-read-button'>Save & Reload</a> " );
$( "#te_saveAndCloseButton" ).click( $.proxy( function ()
{
this.save();
location.reload();
}, this ) );
$( "#te_buttonContainer" ).append( "<a id='te_applySettingsButton' class='book-read-button'>Apply</a> " );
$( "#te_applySettingsButton" ).click( $.proxy( function ()
{
this.save();
}, this ) );
$( "#te_buttonContainer" ).append( "<a id='te_cancelSettingsButton' class='book-read-button'>Cancel</a>" );
$( "#te_cancelSettingsButton" ).click( $.proxy( function ()
{
this.remove();
}, this ) );
$( ".menu .item" ).tab();
$( "#te_config_modal" ).modal(
{
onVisible : function ()
{
$( "#te_config_modal" ).modal( "refresh" );
},
observeChanges : true
} );
$( ".menu .item" ).click( function ()
{
$( "#te_config_modal" ).modal( "refresh" );
} );
}, remove : function ()
{
$( "#te_config_modal" ).modal( "hide" );
}, save : function ()
{
// Find all enhancement groups within the main settings area.
var enhancementGroups = $( "#te_settings" ).find( "div[id*='_group']" ), enhancementSettings = {};
// Loop through the groups to get all the individual settings.
for (var i = 0 ; i < enhancementGroups.length ; i++)
{
var thisEnhancement = enhancementGroups[ i ];
var thisEnhName = $( thisEnhancement ).attr( "id" );
thisEnhName = thisEnhName.replace( "_group", "" );
var thisEnhSettings = $( thisEnhancement ).find( "*[id*='tes_" + thisEnhName + "_']" );
enhancementSettings[ thisEnhName ] = {};
for (var es = 0 ; es < thisEnhSettings.length ; es++)
{
var thisEnSetting = $( thisEnhSettings )[ es ], thisEnSettingName = $( thisEnSetting ).attr( "id" );
thisEnSettingName = thisEnSettingName.replace( "tes_" + thisEnhName + "_", "" );
if ( $( thisEnSetting ).prop( "tagName" ) == "INPUT" )
{
if ( $( thisEnSetting ).prop( "type" ) == "checkbox" )
{
enhancementSettings[ thisEnhName ][ thisEnSettingName ] = $( thisEnSetting ).prop( "checked" );
}
}
}
}
$.extend( true, TE.User, enhancementSettings );
TE.updateSettings();
}
};
/*************************************************************************************
* Tsumino Enhanced Initialization Code
*************************************************************************************/
// Initialization.
TE.init = function ()
{
TE.User.seamlessViewing.enable = false;
TE.User.automaticRepositioning.enable = false;
TE.User.pageJumper.enable = false;
TE.User.unstickiedHeader.enable = false;
$.when(TE.fn.checkForUpdates()).then(function()
{
if(!TE.User.tsuminoEnhanced.upToDate)
{
if(TE.User.automaticUpdateInstallation)
{
if(TE.User.automaticUpdateInstallation.enable)
{
GM_openInTab(TE.installLocation);
}
}
}
});
// Output initializating messages to the console.
var debugState = "Disabled";
if ( TE.config.debug )
{
if ( TE.config.verboseDebug )
{
debugState = "Verbose";
}
else
{
debugState = "Standard";
}
}
// Check which Enhancements the user has enabled.
var enabledEnhancements = [], eeLongNames = "", autoOn = [];
for (var key in TE.User)
{
if ( TE.User.hasOwnProperty( key ) )
{
var obj = TE.User[ key ];
for (var prop in obj)
{
if ( obj.hasOwnProperty( prop ) )
{
if ( (prop == "enable") && (obj[ prop ] == true) )
{
// Add enabled Enhancements to the appropriate array.
if ( typeof TE.Enhancements[ key ] !== "undefined" )
{
enabledEnhancements.push( key );
eeLongNames = eeLongNames + "[X] " + TE.Enhancements[ key ].name + "\r\n";
}
}
else if ( (prop == "enable") && (obj[ prop ] == false) )
{
eeLongNames = eeLongNames + "[ ] " + TE.Enhancements[ key ].name + "\r\n";
}
}
}
}
}
// Check for automatic enhancements.
for (var key in TE.Enhancements)
{
if ( TE.Enhancements.hasOwnProperty( key ) )
{
var obj = TE.Enhancements[ key ];
autoOn.push( obj[ 'shortName' ] );
for (var prop in obj)
{
if ( obj.hasOwnProperty( prop ) )
{
if ( prop == "options" )
{
for (var optNum in obj[ 'options' ])
{
for (var opt in obj[ 'options' ][ optNum ])
{
if ( (opt == "type") && (obj[ 'options' ][ optNum ][ opt ] == "enable") )
{
var thisIndex = autoOn.indexOf( obj[ 'shortName' ] );
if ( thisIndex > -1 )
{
autoOn.splice( thisIndex, 1 );
}
}
}
}
}
}
}
}
}
// Enable automatic enhancements.
for (i = 0 ; i < autoOn.length ; i++)
{
enabledEnhancements.push( autoOn[ i ] );
eeLongNames = eeLongNames + "[X] " + TE.Enhancements[ autoOn[ i ] ].name + "\r\n";
}
// Output initialization messages.
TE.log( "gname", TE.name,
"Version: " + TE.version,
"Latest: " + TE.User.tsuminoEnhanced.latestVersion,
"Up2Date: " + TE.User.tsuminoEnhanced.upToDate,
"Debugging: " + debugState,
"Enhancements:", eeLongNames );
TE.vbLog( "gname", TE.name, "Current Settings:", TE.User );
//TE.vbLog( "gname", "TE.site", TE.site );
TE.vbLog( "gname", "TE.on", TE.on );
TE.vbLog( "gname", "TE.Enhancements", TE.Enhancements );
// Set up TE.status.enhancePage for Enhancements that require it to run.
TE.status.enhancePage = TE.enhancePage();
// Initialize all enabled Enhancements.
for (var i = 0 ; i < enabledEnhancements.length ; i++)
{
if ( typeof TE.Enhancements[ enabledEnhancements[ i ] ] !== "undefined" )
{
TE.Enhancements[ enabledEnhancements[ i ] ].fn.init();
}
}
};
var tempMyLocation = TE.myLocation;
// Initialize Tsumino Enhanced.
if ( !TE.on.forum )
{
TE.init();
}
else if((TE.on.forum) && ( (tempMyLocation.indexOf("#p1472") != -1) || (tempMyLocation.indexOf("p=1472") != -1) ) )
{
var messageID = TE.randomString();
$(document).ready(function()
{
$("#p1472").after(`
<div class="post" id="`+messageID+`">
<div class="inner">
<div class="postbody">
<span style="font-size: 2em">A message from <span style="color:`+TE.ui.mainColor+`">Tsumino Enhanced</span></span>
<hr />
<div class="content">
If you're here, that means that Tsumino has updated their detection script again.<br />
The good news is that you don't appear to be banned yet.<br />
If you wish to continue using Tsumino Enhanced, make sure you're using the latest version.<br />
You can still manually update over on
<a href="https://openuserjs.org/scripts/Tobias.Kelmandia/Tsumino_Enhanced">OpenUserJS</a>
by clicking the install button on that page.<br />
If you're using the latest version and still get taken here, you can disable TE until the new
detection method has been defeated.<br />
You can find the latest news for Tsumino Enhanced over on the
<a href="http://codingtoby.com/" target="_blank">development blog</a>.<br />
If you're interested, you can also read
<a href="http://codingtoby.com/userscripts/tsumino-enhanced/regarding-tsuminos-stance-on-userscripts/"
target="_blank">my response to Tweety's post</a>.<br />
</div>
</div>
</div>
</div>
`);
});
$(w).load(function()
{
var scrollTo = $( "#" + messageID ).offset().top;
scrollTo -= ($(".nav-tabs").height() + 10);
$( "html, body" ).animate( {scrollTop : scrollTo}, 300 );
});
}
})( typeof window === "undefined" ? this : window, this.jQuery );