// ==UserScript==
// @name DeviantArt Search Galleries and Favorites
// @namespace http://tampermonkey.net/
// @version 0.3.1
// @description Creates a search function that works on artist galleries and favorites collections. Search by deviation title and artist name. Numerous sorting options.
// @author corepower
// @match https://www.deviantart.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=deviantart.com
// @grant none
// @license MIT
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
/////////////////////////////////////////////////////////
////// Default User Preference Variables //////
// Maximum search return: Maximum number of results that will tile after a search. This can be set as large as you like, but the page may slow down.
// Possibly even consider lowering it if things are laggy while searching.
let maximumsearchreturn = 1000;
// Sort by: based on setting, search results will be sorted based on description in the value. The "sortoptions" array holds the possible options for sorting.
// The "Default" option is based on date added to the collection. Matches DeviantArt default.
const sortoptions = ["Default", "Post Date(Desc)", "Post Date(Asc)", "Title(Desc)", "Title(Asc)", "Artist(Desc)", "Artist(Asc)", "Favorites(Desc)", "Favorites(Asc)", "Views(Desc)", "Views(Asc)"];
let sortby = sortoptions[0];
// Pagination rate limit: the amount of time, in seconds to delay downloading gallery/collection page results during search indexing.
// Can be set to 0 but if lots of large galleries are indexed, DeviantArt may automatically temp ban your account/IP.
// Note: Multiple searches on a single page will not rerun the index, therefore unlimited searches may be run on a single page without consequence as long as the page is not navigated or refreshed.
let paginationratelimit = 1.0;
// Rate limit warning: If true, puts a warning message in the search output text while indexing about rate limits if the rate limit is under two seconds on a large search.
// Set to false to disable warning.
let ratelimitwarning = true;
// Pivotal row height: height, in pixels, that the dynamic deviation tiles will be based around. Tiles will be this tall or larger.
let pivotalrowheight = 280;
//// End Default User Preference Variables //////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
////// Desired Features List - NOT YET IMPLEMENTED //////
// These features may come in future versions.
// * Navigate within DeviantArt and reset search capability based on new page (partially implemented)
// * Dynamic tile visibility so that any number of search results can be efficiently displayed without slowing down the page.
// * Dynamic re-tiling on viewport size change
// * React framework tie-ins for things like element creation, event listeners, and deviation favoriting. This may not be feasible. I don't know React.
////// End Desired Features List //////
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
////// Other Constants //////
//Define constants for text style toggles
const regularblacktextcolor = "black";
const regularwhitetextcolor = "rgb(242, 242, 242)";
const warningtextcolor = "orange";
const errortextcolor = "red";
// Preferred size: this is a named identifier for a size of DeviantArt generated thumbnails. Larger numbers are generally higher quality.
// Set this variable based on your internet speed or personal preference.
// Choose a value from amongst the following list: ["92S", "150", "200H", "300W", "375W", "414W", "250T", "350T","400T", "preview", "social_preview"]
const preferredsize = "350T";
// Tile margin: margin between deviation tiles, in pixels. DeviantArt uses 4 pixels, don't know why you would change this but you can.
const tilemargin = 4;
// Debug: debug flag turns on various console outputs for debugging. Outputs may or may not make sense, they were added as needed.
const debug = false;
////// End Other Constants //////
/////////////////////////////////////////////////////////
//Special event listener for React-style internal navigation
//Credit to Raja Osama: https://rajaosama.me/blogs/detect-react-route-change-in-vanilla-js
//Re-run entire script after this type of navigation
let monitored_url = location.href;
document.body.addEventListener('click', ()=>{
requestAnimationFrame(()=>{
if(monitored_url!==location.href){
let old_url = monitored_url;
monitored_url = location.href
//DEBUG
if(debug && false) console.log('url changed to: ', monitored_url);
//Check for specific case where navigation is between Galleries and Favorites on a single artist
let old_url_parts = old_url.split("/");
let monitored_url_parts = monitored_url.split("/");
//Disregard these base path type of /artist
if(old_url_parts.length < 5 || monitored_url_parts.length < 5)
return;
//Check for if navigations from gallery -> favourite or favourite -> gallery
let is_old_url_favourite = old_url_parts[4] == "favourites";
let is_new_url_favourite = monitored_url_parts[4] == "favourites";
if( is_old_url_favourite ? !is_new_url_favourite : is_new_url_favourite ) {
return;
//TODO modify main script execution for this type of internal navigation then enable observer by uncommenting code below
//set observer on this element to load
// waitForElm('._3h7d3').then((elm) => {
// if(debug && true) console.log('Element ._3h7d3 is ready');
// //executeScript();
// });
}
else { //not a problem navigation, run script regularly
executeScript();
}
}
});
}, true);
//DEBUG
if(debug && false) console.log("Entrypoint ready state: ", document.readyState);
if(debug && false) document.addEventListener('readystatechange', () => console.log("Ready state change: ", document.readyState));
//Page needs to be fully loaded for script to work
if(document.readyState == "loading" || document.readyState == "interactive")
window.addEventListener('load',executeScript);
else
executeScript();
//Function wrapper for the entire script to enable document readiness check above, as well as for re-running after React-style navigation events
function executeScript() {
//DEBUG
if(debug && false) console.log("Entered script execution.");
if(debug && false) console.log("Internal entrypoint ready state: ", document.readyState);
//Pull in existing user preference settings to globals, re-store to confirm all of them exist in localStorage. Use global variables for get() actions, update globals and localStorage with set() actions.
getUserPreferencesFromLocalStorage();
//Only execute script on gallery pages, otherwise return immediately
let pathparts = window.location.pathname.split("/");
if(pathparts.length < 3) {
return;
}
if(pathparts[2] != "gallery" && pathparts[2] != "favourites") {
return;
}
//Creating and adding this search element structure to the DOM
//
//
//
//
//
//
//
//
let searchparentparent = document.getElementsByClassName("_3h7d3")[0];
let searchparent = document.createElement("div");
searchparent.className = "_1hkGk DRK5r _1bp4v";
searchparent.style.width = "350px";
let searchiconparentparent = document.createElement("span");
searchiconparentparent.className = "DLN_H";
let searchiconparent = document.createElement("span");
searchiconparent.className = "z8jNZ _1yoxj _2F1i2";
var searchicon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
searchicon.setAttribute("viewBox", "0 0 24 24");
var searchiconpath = document.createElementNS("http://www.w3.org/2000/svg", "path");
searchiconpath.setAttribute("d", "M19 11a8 8 0 10-3.095 6.32l3.63 3.63.095.083a1 1 0 001.32-1.498l-3.63-3.63A7.965 7.965 0 0019 11zM5 11a6 6 0 1112 0 6 6 0 01-12 0z");
let searchelement = document.createElement('input');
searchelement.type = 'text';
searchelement.className = 'aFKMF _3kAA3 _2KZ9p';
searchelement.setAttribute('aria-invalid', 'false');
searchelement.id = 'search-gallery';
searchelement.autocomplete = 'off';
searchelement.placeholder = 'Search Gallery';
searchelement.value = '';
searchicon.appendChild(searchiconpath);
searchiconparent.appendChild(searchicon);
searchiconparentparent.appendChild(searchiconparent);
searchparent.appendChild(searchiconparentparent);
searchparent.appendChild(searchelement);
//Create search results text output and put it next to the search box
let searchoutputtext = document.createElement("span");
searchoutputtext.style.margin = "8px";
searchoutputtext.style.maxWidth = "1000px";
searchparentparent.appendChild(searchoutputtext);
searchparentparent.appendChild(searchparent);
//Create Settings Cog. Minified because we don't need access to any of the inner elements.
let settingscogdiv = document.createElement("div");
settingscogdiv.style.position = "relative";
settingscogdiv.id = "settingscog";
settingscogdiv.innerHTML = ''
//Create toggleable settings menu
let settingsmenudiv = createSettingsMenu();
searchparentparent.appendChild(settingscogdiv);
//Add event listeners
searchelement.addEventListener("focus", buildIndex, {once : true});
searchelement.addEventListener("keyup", search);
settingscogdiv.firstChild.addEventListener("click", toggleSettingsElement);
document.addEventListener("click", hideSettingsElement);
//define persistent globals for search indexing
const csrftoken = window.__CSRF_TOKEN__;
const artist_friendly_id = pathparts[1];//from the early URL path deconstruction
let deviations;//this will be objects to index search with
let matchingdeviations;//this will be objects that match the search
let initialstatescripttext;
let initialstatematch;
let i = 0;
while(initialstatematch == null) {
initialstatescripttext = document.getElementsByTagName("script")[i].innerHTML;
initialstatematch = initialstatescripttext.match(/\.__INITIAL_STATE__\s*=\s*JSON\.parse\(("[^\n]+")/);
i++;
}
if(initialstatematch.length < 2) {
console.error("DeviantArt Gallery Search Userscript Error: Could not parse script tag for __INITIAL_STATE__")
searchelement.placeholder = 'Script Error';
return;
}
let initialstateJSON = JSON.parse(eval(initialstatematch[1]));
//console.log(initialstateJSON);
let isgallery = pathparts[2] == "gallery";
let collectionorgalleryfoldername = isgallery ? "galleryFolder" : "collectionFolder";
let foldersJSON = initialstateJSON["@@entities"][collectionorgalleryfoldername];
//console.log(foldersJSON);
let isallfolder = false;
if(pathparts.length >= 4 && pathparts[3] == "all") {
isallfolder = true;
}
let folderid = "-1";// folder id of "-1" corresponds to "All" gallery
let gallerysize = 0;
if(!isallfolder) {
//Get folder id from current url as most up-to-date source, else default assigned folder id from initial state
//TODO this is imperfect. Fails if internal React navigation between galleries and favorites is initiated.
if(pathparts.length >= 4) {
folderid = pathparts[3];
}
else {//Featured or imperfect navigation, for now just handle featured
for(let folder in foldersJSON) {
if(foldersJSON[folder].name == "Featured"){
folderid = folder;
break;
}
}
//folderid = initialstateJSON["gallectionSection"]["currentlyViewedFolderId"];
}
}
gallerysize = foldersJSON[folderid]["totalItemCount"];
//construct reuseable gallery pagination api string pieces
let paginationstring_base = "https://www.deviantart.com/_napi/da-user-profile/api/"
+ (isgallery ? "gallery" : "collection")
+ "/contents?username=" + artist_friendly_id
+ "&csrf_token=" + csrftoken
+ (isallfolder ? "&all_folder=true" : "&folderid=" + folderid)
+ "&limit=60&offset=";
//Get remaining UI elements needed for reference
let itemscontainer = document.getElementsByClassName("RMUi2")[0];
let searchitemscontainer = itemscontainer.cloneNode();
itemscontainer.parentNode.appendChild(searchitemscontainer);
//This function indexes the gallery to be searched on textbox focus
async function buildIndex() {
searchelement.placeholder = 'Building Search Index...';
searchelement.blur();
searchelement.setAttribute('readonly', 'readonly');
//DEBUG indexing performance
let starttime;
if(debug && true) {
starttime = performance.now();
}
let currentoffset = 0;
let paginated_urls = [];
while(currentoffset < gallerysize) {
let currentpaginationurl = paginationstring_base + currentoffset;
paginated_urls.push(currentpaginationurl);
currentoffset += 60;//60 is maximum pagination
}
//DEBUG
if(debug && false) console.log(paginated_urls);
await concatenateJson(paginated_urls)
.then(concatenatedJson => {
//prepare indexable items by making them lowercase
let lowercasejson = concatenatedJson;
for(let i=0; i< lowercasejson.length; i++) {
let deviation = lowercasejson[i].deviation;
deviation.gs_username = deviation.author.username.toLowerCase();
deviation.gs_title = deviation.title.toLowerCase().trim();
deviation.gs_default_order = i;
deviation.gs_favorites = deviation.stats.favourites;
deviation.gs_views = deviation.stats.views;
//Convert datetime to unix milliseconds and store
const pubDate = new Date(deviation.publishedTime )
deviation.gs_published_time = pubDate.getTime();
//Collect static thumbnail meta info. easier to do once
let thumbpathstring = "";
for(let i=0; i 30 && paginationratelimit < 2.0 && ratelimitwarning) {
searchoutputtext.style.color = warningtextcolor;
searchoutputtext.innerHTML = "This is a large gallery to search. If you run searches like this frequently, "
+ "consider raising the rate limit to 2 seconds or longer to avoid DeviantArt temp bans. "
+ "Rate limits can be changed and this warning can be turned off in the settings next to the search box.";
}
// Loop through the URLs
for (let i=0; i 0 && waitmilliseconds != 0) {
//DEBUG
if(debug && false) console.log("Waiting " + waitmilliseconds + " milliseconds.");
await new Promise(resolve => setTimeout(resolve, waitmilliseconds));
}
//DEBUG
if(debug && false) console.log("Download round " + i);
// Download the current URL and concatenate the JSON result
let response = await fetchJson(urls[i]);
let json = response.results;
results = results.concat(json);
}
//Reset output text after possible rate limit warning.
searchoutputtext.style.color = regularwhitetextcolor;
searchoutputtext.innerHTML = "";
return results;
}
//Searches through deviations array on keyup event
function search() {
let currentsearchtext = searchelement.value.toLowerCase();
//DEBUG
if(debug && true) console.log("Searching: " + currentsearchtext);
//Restore gallery visibility and abort if search goes inactive
if(currentsearchtext == "") {
itemscontainer.style.display = "";
searchitemscontainer.style.display = "none";
searchoutputtext.innerHTML = "";
return;
}
//hide original gallery, show custom search gallery
itemscontainer.style.display = "none";
searchitemscontainer.style.display = "";
//search
let resultsarray = [];
for(let i=0; i< deviations.length; i++) {
let deviation = deviations[i].deviation;
if(deviation.gs_username.includes(currentsearchtext)) {
resultsarray.push(deviation);
continue;
}
if(deviation.gs_title.includes(currentsearchtext)) {
resultsarray.push(deviation);
continue;
}
}
matchingdeviations = resultsarray;
//Now display based on results
if(resultsarray.length == 0) {
searchoutputtext.innerHTML = "No results.";
return;
}
//DEBUG
if(debug && true) console.log("Results:", matchingdeviations);
if(resultsarray.length > maximumsearchreturn) {
searchoutputtext.innerHTML = resultsarray.length + " results. Too many for display. (Maximum is set to " + maximumsearchreturn + " results)";
return;
}
searchoutputtext.innerHTML = resultsarray.length + " result" + (resultsarray.length == 1 ? "." : "s.");
assignTileDimensions();
tileElements();
//DEBUG sort order
if(debug && false) {
console.log("-----------")
for(let i=0; i= viewportwidth)
{
break;
}
else
{
rowitemcount++;
currentindex++;
}
//last row case detector
if(currentindex == matchingdeviations.length)
{
isfinalrow = true;
}
}
//now we have row item count, size to fill usable viewport width and then aspect for final row height
//console.log("Row item count: ", rowitemcount);
var useableviewportwidth = viewportwidth - (rowitemcount*tilemargin*2);
//console.log("Useable viewport width: ", useableviewportwidth);
currentindex = useditemscount;//reset to beginning of row index
if(!isfinalrow)//perform sizing calculations on every row but the last
{
//get width ratio denominator
var sumwidthratios = 0;
for(let i=0; i", deviation.media.prettyName);
let tokenstring = deviation.media.token == null ? "" : "?token=" + deviation.media.token[0];
let thumburl = deviation.media.baseUri + thumbpathstring + tokenstring;
//Create Image DONE
let imgelement = document.createElement("img");
imgelement.alt = deviation.title;
imgelement.src = thumburl;
imgelement.style.width = "100%";
imgelement.style.height = "100%";
imgelement.style.objectFit = "cover";
imgelement.style.objectPosition = "50% 100%";
//Image container Done
let imgcontainer = document.createElement("div")
imgcontainer.className = "_24Wda";
imgcontainer.style.width = "100%";
imgcontainer.style.height = "100%";
//Create link to deviation page Done
let artlinkelement = document.createElement("a")
artlinkelement.href = deviation.url;
//Draggable div container, also contains mouseover event assigned later
let draggablecontainer = document.createElement("div")
draggablecontainer.style.width = "100%";
draggablecontainer.style.height = "100%";
draggablecontainer.className = "_1xcj5 _1QdgI";
draggablecontainer.draggable = "true";
imgcontainer.appendChild(imgelement);
artlinkelement.appendChild(imgcontainer);
draggablecontainer.appendChild(artlinkelement);
outermostdiv.appendChild(draggablecontainer);
//////////////////////////////////
//Mouseover elements section
//////////////////////////////////
//Outer mouseover element
let outermousediv = document.createElement("div");
outermousediv.style.visibility = "hidden";
outermousediv.style.width = "100%";
outermousediv.style.height = "100%";
outermousediv.className = "_2jPGh _3Cax3";
//event attaches to its parent
draggablecontainer.addEventListener("mouseover", (event) => { outermousediv.style.visibility = ""; });
draggablecontainer.addEventListener("mouseout", (event) => { outermousediv.style.visibility = "hidden"; });
//Divs for slight black fade on hover
let blackfadeouterdiv = document.createElement("div");
blackfadeouterdiv.className = "_1mmGw";
let blackfadeinnerdiv = document.createElement("div");
blackfadeinnerdiv.className = "cjZ9o _2QZ8F _3b-i8";
blackfadeouterdiv.appendChild(blackfadeinnerdiv);
outermousediv.appendChild(blackfadeouterdiv);
//Next inward div
let innermousediv = document.createElement("div");
innermousediv.style.width = "100%";
innermousediv.style.height = "100%";
innermousediv.className = "_2ehf4 YpNhf";
//Div for all all meta elements container (title, artist, icon, comments)
let metaelementscontainer = document.createElement("div");
metaelementscontainer.className = "_5Xty_";
//Div to contain title and artist elements (title, artist, icon)
let titleartistcontainer = document.createElement("div");
titleartistcontainer.className = "_1mmGw _31MCr";
//Link with Title
let arttitlelinkelement = document.createElement("a")
arttitlelinkelement.href = deviation.url;
arttitlelinkelement.className = "KoW6A";
//Title
let arttitleelement = document.createElement("h2")
arttitleelement.className = "_1lmpZ";
arttitleelement.innerHTML = deviation.title;
arttitlelinkelement.appendChild(arttitleelement);
titleartistcontainer.appendChild(arttitlelinkelement);
metaelementscontainer.appendChild(titleartistcontainer);
innermousediv.appendChild(metaelementscontainer);
outermousediv.appendChild(innermousediv);
draggablecontainer.appendChild(outermousediv);
//Subsection for artist icon and name
//Divs to contain title and artist elements (title, artist, icon)
let iconartistcontainer = document.createElement("div");
iconartistcontainer.className = "_13y-9";
let iconartistcontainer2 = document.createElement("div");
iconartistcontainer2.className = "_2o1Q1";
//Div to contain artist icon
let iconcontainer = document.createElement("div");
iconcontainer.className = "_3CR67 _1I9Ar";
//Link for artist that surrounds icon
let artisticonlinkelement = document.createElement("a")
artisticonlinkelement.href = "https://www.deviantart.com/" + deviation.gs_username;
artisticonlinkelement.className = "user-link _2f0dA _23x0l";
//Artist icon element
let artisticonelement = document.createElement("img");
artisticonelement.alt = deviation.author.username + "'s avatar";
artisticonelement.src = deviation.author.usericon;
artisticonelement.style.width = "24px";
artisticonelement.style.height = "24px";
artisticonelement.loading = "lazy";
artisticonelement.className = "_1IDJa";
artisticonlinkelement.appendChild(artisticonelement);
iconcontainer.appendChild(artisticonlinkelement);
//Div to contain artist name
let artistnamecontainer = document.createElement("div");
artistnamecontainer.className = "_3CR67 k4CiA";
//Link for artist that surrounds icon
let artistnamelinkelement = document.createElement("a")
artistnamelinkelement.href = "https://www.deviantart.com/" + deviation.gs_username;
artistnamelinkelement.className = "user-link _2f0dA";
//Artist name text span
let artistnametextelement = document.createElement("span")
artistnametextelement.className = "_2UI2c";
artistnametextelement.innerHTML = deviation.author.username;
//Artist cursor span
let artistcursorelement = document.createElement("span")
artistcursorelement.className = "_3LUMH _1NhtS G0rcN";
artistcursorelement.style.cursor = "pointer";
artistcursorelement.role = "img";
artistnamelinkelement.appendChild(artistnametextelement);
artistnamelinkelement.appendChild(artistcursorelement);
artistnamecontainer.appendChild(artistnamelinkelement);
iconartistcontainer2.appendChild(iconcontainer);
iconartistcontainer2.appendChild(artistnamecontainer);
iconartistcontainer.appendChild(iconartistcontainer2);
titleartistcontainer.appendChild(iconartistcontainer);
//Subsection for comments icon and link elements
//Div to contain comment icon and link elements
let commenticonlinkcontainer = document.createElement("div");
commenticonlinkcontainer.className = "_1mmGw _2WpJA _6oiPd";
//Link for comment section
let commentlinkelement = document.createElement("a")
commentlinkelement.href = deviation.url + "#comments";
commentlinkelement.className = "_1-Wh7 x48yz";
//Comment icon span
let commenticonspan = document.createElement("span")
commenticonspan.className = "z8jNZ _1yoxj _38kc5";
//Comment icon (SVG)
let commenticonSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
commenticonSVG.setAttribute("viewBox", "0 0 24 24");
commenticonSVG.setAttribute("version", "1.1");
commenticonSVG.setAttribute("xlmns:xlink", "http://www.w3.org/1999/xlink");
//Comment icon (SVG PATH)
let commenticonpath = document.createElementNS("http://www.w3.org/2000/svg", "path");
commenticonpath.setAttribute("fill-rule", "evenodd");
commenticonpath.setAttribute("d", "M20 3a1 1 0 01.993.883L21 4v9.586a1 1 0 01-.206.608l-.087.099-2.414 2.414a1 1 0 01-.576.284l-.131.009H13l-2.7 3.6a1 1 0"
+ " 01-.683.393L9.5 21H8a1 1 0 01-.993-.883L7 20v-3H4a1 1 0 01-.993-.883L3 16V6.414a1 1 0 01.206-.608l.087-.099 2.414-2.414a1 1 0 01.576-.284L6.414"
+ " 3H20zm-1 2H6.828L5 6.828V15h4v4l3-4h5.17L19 13.17V5z");
//Comment count span
let commentcountspan = document.createElement("span")
commentcountspan.innerHTML = deviation.stats.comments;
commenticonSVG.appendChild(commenticonpath);
commenticonspan.appendChild(commenticonSVG);
commentlinkelement.appendChild(commenticonspan);
commentlinkelement.appendChild(commentcountspan);
commenticonlinkcontainer.appendChild(commentlinkelement);
metaelementscontainer.appendChild(commenticonlinkcontainer);
return outermostdiv;
}
function createLiteratureDeviationElement(deviation) {
let outermostdiv = document.createElement("div");
outermostdiv.style.width = deviation.gs_tile_width;
outermostdiv.style.height = deviation.gs_tile_height;
outermostdiv.style.display = "inline-block";
outermostdiv.style.float = "left";
outermostdiv.style.position = "relative";
outermostdiv.style.margin = tilemargin + "px";
//Draggable div container, also contains mouseover event assigned later
let draggablecontainer = document.createElement("div")
draggablecontainer.style.width = "100%";
draggablecontainer.style.height = "100%";
draggablecontainer.className = "_1xcj5 _1QdgI";
draggablecontainer.draggable = "true";
//Section element that contains literature display elements
let sectionelement = document.createElement("section");
sectionelement.className = "_33VtO _3rqZq";
sectionelement.style.width = "100%";
sectionelement.style.height = "100%";
//Fancy literature deviation background element container
let literaturebgcontainer = document.createElement("div")
literaturebgcontainer.className = "xPxyA LXVwg";
//Fancy literature deviation background (SVG)
let litbgSVG = document.createElement("svg");
litbgSVG.setAttribute("viewBox", "0 0 15 12");
litbgSVG.setAttribute("height", "100%");
litbgSVG.setAttribute("preserveAspectRatio", "xMidYMin slice");
litbgSVG.setAttribute("fill-rule", "evenodd");
//Fancy literature deviation background (lineargradient)
let litbglineargradient = document.createElement("linearGradient");
litbglineargradient.setAttribute("x1", "87.8481761%");
litbglineargradient.setAttribute("y1", "16.3690766%");
litbglineargradient.setAttribute("x2", "45.4107524%");
litbglineargradient.setAttribute("y2", "71.4898596%");
//Fancy literature deviation background (stop color)s
let litbgstop1 = document.createElement("stop");
litbgstop1.setAttribute("stop-color", "#00FF62");
litbgstop1.setAttribute("offset", "0%");
let litbgstop2 = document.createElement("stop");
litbgstop2.setAttribute("stop-color", "#3197EF");
litbgstop2.setAttribute("offset", "100%");
litbgstop2.setAttribute("stop-opacity", "0");
litbglineargradient.appendChild(litbgstop1);
litbglineargradient.appendChild(litbgstop2);
litbgSVG.appendChild(litbglineargradient);
literaturebgcontainer.appendChild(litbgSVG);
sectionelement.appendChild(literaturebgcontainer);
draggablecontainer.appendChild(sectionelement);
outermostdiv.appendChild(draggablecontainer);
//Literature Preview Subsection
//Fancy literature deviation background element container
let literaturecategorydiv = document.createElement("div")
literaturecategorydiv.className = "_3hLq8";
literaturecategorydiv.innerHTML = "Literature";
//Literature Preview Title
let literaturepreviewtitle = document.createElement("h2")
literaturepreviewtitle.className = "_2mwJN";
literaturepreviewtitle.innerHTML = deviation.title;
//Literature Preview Text
let literaturepreviewtext = document.createElement("h2")
literaturepreviewtext.className = "heXvc";
literaturepreviewtext.innerHTML = deviation.textContent.excerpt;
sectionelement.appendChild(literaturecategorydiv);
sectionelement.appendChild(literaturepreviewtitle);
sectionelement.appendChild(literaturepreviewtext);
//Deviation link section
let deviationlink = document.createElement("a");
deviationlink.href = deviation.url;
deviationlink.className = "_1vRyy";
draggablecontainer.appendChild(deviationlink);
//////////////////////////////////
//Mouseover elements section
//////////////////////////////////
//Outer mouseover element
let outermousediv = document.createElement("div");
outermousediv.style.visibility = "hidden";
outermousediv.style.width = "100%";
outermousediv.style.height = "100%";
outermousediv.className = "_2jPGh _3Cax3";
//event attaches to its parent
draggablecontainer.addEventListener("mouseover", (event) => { outermousediv.style.visibility = ""; });
draggablecontainer.addEventListener("mouseout", (event) => { outermousediv.style.visibility = "hidden"; });
//Divs for slight black fade on hover
let blackfadeouterdiv = document.createElement("div");
blackfadeouterdiv.className = "_1mmGw";
let blackfadeinnerdiv = document.createElement("div");
blackfadeinnerdiv.className = "cjZ9o _2QZ8F _3b-i8";
blackfadeouterdiv.appendChild(blackfadeinnerdiv);
outermousediv.appendChild(blackfadeouterdiv);
//Next inward div
let innermousediv = document.createElement("div");
innermousediv.style.width = "100%";
innermousediv.style.height = "100%";
innermousediv.className = "_2ehf4 YpNhf";
//Div for all all meta elements container (title, artist, icon, comments)
let metaelementscontainer = document.createElement("div");
metaelementscontainer.className = "_5Xty_";
//Div to contain title and artist elements (title, artist, icon)
let titleartistcontainer = document.createElement("div");
titleartistcontainer.className = "_1mmGw _31MCr";
// //Link with Title
// let arttitlelinkelement = document.createElement("a")
// arttitlelinkelement.href = deviation.url;
// arttitlelinkelement.className = "KoW6A";
// //Title
// let arttitleelement = document.createElement("h2")
// arttitleelement.className = "_1lmpZ";
// arttitleelement.innerHTML = deviation.title;
// arttitlelinkelement.appendChild(arttitleelement);
//titleartistcontainer.appendChild(arttitlelinkelement);
metaelementscontainer.appendChild(titleartistcontainer);
innermousediv.appendChild(metaelementscontainer);
outermousediv.appendChild(innermousediv);
draggablecontainer.appendChild(outermousediv);
//Subsection for artist icon and name
//Divs to contain title and artist elements (title, artist, icon)
let iconartistcontainer = document.createElement("div");
iconartistcontainer.className = "_13y-9";
let iconartistcontainer2 = document.createElement("div");
iconartistcontainer2.className = "_2o1Q1";
//Div to contain artist icon
let iconcontainer = document.createElement("div");
iconcontainer.className = "_3CR67 _1I9Ar";
//Link for artist that surrounds icon
let artisticonlinkelement = document.createElement("a")
artisticonlinkelement.href = "https://www.deviantart.com/" + deviation.gs_username;
artisticonlinkelement.className = "user-link _2f0dA _23x0l";
//Artist icon element
let artisticonelement = document.createElement("img");
artisticonelement.alt = deviation.author.username + "'s avatar";
artisticonelement.src = deviation.author.usericon;
artisticonelement.style.width = "24px";
artisticonelement.style.height = "24px";
artisticonelement.loading = "lazy";
artisticonelement.className = "_1IDJa";
artisticonlinkelement.appendChild(artisticonelement);
iconcontainer.appendChild(artisticonlinkelement);
//Div to contain artist name
let artistnamecontainer = document.createElement("div");
artistnamecontainer.className = "_3CR67 k4CiA";
//Link for artist that surrounds icon
let artistnamelinkelement = document.createElement("a")
artistnamelinkelement.href = "https://www.deviantart.com/" + deviation.gs_username;
artistnamelinkelement.className = "user-link _2f0dA";
//Artist name text span
let artistnametextelement = document.createElement("span")
artistnametextelement.className = "_2UI2c";
artistnametextelement.innerHTML = deviation.author.username;
//Artist cursor span
let artistcursorelement = document.createElement("span")
artistcursorelement.className = "_3LUMH _1NhtS G0rcN";
artistcursorelement.style.cursor = "pointer";
artistcursorelement.role = "img";
artistnamelinkelement.appendChild(artistnametextelement);
artistnamelinkelement.appendChild(artistcursorelement);
artistnamecontainer.appendChild(artistnamelinkelement);
iconartistcontainer2.appendChild(iconcontainer);
iconartistcontainer2.appendChild(artistnamecontainer);
iconartistcontainer.appendChild(iconartistcontainer2);
titleartistcontainer.appendChild(iconartistcontainer);
//Subsection for comments icon and link elements
//Div to contain comment icon and link elements
let commenticonlinkcontainer = document.createElement("div");
commenticonlinkcontainer.className = "_1mmGw _2WpJA _6oiPd";
//Link for comment section
let commentlinkelement = document.createElement("a")
commentlinkelement.href = deviation.url + "#comments";
commentlinkelement.className = "_1-Wh7 x48yz";
//Comment icon span
let commenticonspan = document.createElement("span")
commenticonspan.className = "z8jNZ _1yoxj _38kc5";
//Comment icon (SVG)
let commenticonSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
commenticonSVG.setAttribute("viewBox", "0 0 24 24");
commenticonSVG.setAttribute("version", "1.1");
commenticonSVG.setAttribute("xlmns:xlink", "http://www.w3.org/1999/xlink");
//Comment icon (SVG PATH)
let commenticonpath = document.createElementNS("http://www.w3.org/2000/svg", "path");
commenticonpath.setAttribute("fill-rule", "evenodd");
commenticonpath.setAttribute("d", "M20 3a1 1 0 01.993.883L21 4v9.586a1 1 0 01-.206.608l-.087.099-2.414 2.414a1 1 0 01-.576.284l-.131.009H13l-2.7 3.6a1 1 0"
+ " 01-.683.393L9.5 21H8a1 1 0 01-.993-.883L7 20v-3H4a1 1 0 01-.993-.883L3 16V6.414a1 1 0 01.206-.608l.087-.099 2.414-2.414a1 1 0 01.576-.284L6.414"
+ " 3H20zm-1 2H6.828L5 6.828V15h4v4l3-4h5.17L19 13.17V5z");
//Comment count span
let commentcountspan = document.createElement("span")
commentcountspan.innerHTML = deviation.stats.comments;
commenticonSVG.appendChild(commenticonpath);
commenticonspan.appendChild(commenticonSVG);
commentlinkelement.appendChild(commenticonspan);
commentlinkelement.appendChild(commentcountspan);
commenticonlinkcontainer.appendChild(commentlinkelement);
metaelementscontainer.appendChild(commenticonlinkcontainer);
return outermostdiv;
}
function createJournalDeviationElement(deviation) {
let outermostdiv = document.createElement("div");
outermostdiv.style.width = deviation.gs_tile_width;
outermostdiv.style.height = deviation.gs_tile_height;
outermostdiv.style.display = "inline-block";
outermostdiv.style.float = "left";
outermostdiv.style.position = "relative";
outermostdiv.style.margin = tilemargin + "px";
//Draggable div container, also contains mouseover event assigned later
let draggablecontainer = document.createElement("div")
draggablecontainer.style.width = "100%";
draggablecontainer.style.height = "100%";
draggablecontainer.className = "_1xcj5 _1QdgI";
draggablecontainer.draggable = "true";
//Section element that contains journal display elements
let sectionelement = document.createElement("section");
sectionelement.className = "_1C7DQ _1L6MH";
sectionelement.style.width = "100%";
sectionelement.style.height = "100%";
//Journal title container
let journaltitlecontainer = document.createElement("div")
journaltitlecontainer.className = "_1i4Yb";
//Journal Title
let journaltitle = document.createElement("h2")
journaltitle.className = "mhmhR";
journaltitle.innerHTML = deviation.title;
journaltitlecontainer.appendChild(journaltitle);
sectionelement.appendChild(journaltitlecontainer);
draggablecontainer.appendChild(sectionelement);
outermostdiv.appendChild(draggablecontainer);
//Journal Excerpt subsection
//Journal preview container
let journalpreviewcontainer = document.createElement("div")
journalpreviewcontainer.className = "_2Hfrr";
//Journal datetime container
let journaldtcontainer = document.createElement("div")
journaldtcontainer.className = "uBAbQ";
//Journal datetime, also get the correct date format. Example: Dec 14, 2015
let journaldt = document.createElement("time")
journaldt.dateTime = deviation.publishedTime;
let date = new Date(deviation.publishedTime);
let formattedDate = date.toLocaleDateString('en-US', {month: 'short', day: 'numeric', year: 'numeric'});
journaldt.innerHTML = formattedDate;
//Journal excerpt div
let journalexcerptdiv = document.createElement("div")
journalexcerptdiv.className = "legacy-journal _2HUtS";
journalexcerptdiv.innerHTML = deviation.textContent.excerpt;
journaldtcontainer.appendChild(journaldt);
journalpreviewcontainer.appendChild(journaldtcontainer);
journalpreviewcontainer.appendChild(journalexcerptdiv);
sectionelement.appendChild(journalpreviewcontainer);
//Deviation link section
let deviationlink = document.createElement("a");
deviationlink.href = deviation.url;
deviationlink.className = "_1vRyy";
draggablecontainer.appendChild(deviationlink);
//////////////////////////////////
//Mouseover elements section
//////////////////////////////////
//Outer mouseover element
let outermousediv = document.createElement("div");
outermousediv.style.visibility = "hidden";
outermousediv.style.width = "100%";
outermousediv.style.height = "100%";
outermousediv.className = "_2jPGh _3Cax3";
//event attaches to its parent
draggablecontainer.addEventListener("mouseover", (event) => { outermousediv.style.visibility = ""; });
draggablecontainer.addEventListener("mouseout", (event) => { outermousediv.style.visibility = "hidden"; });
//Divs for slight black fade on hover
let blackfadeouterdiv = document.createElement("div");
blackfadeouterdiv.className = "_1mmGw";
let blackfadeinnerdiv = document.createElement("div");
blackfadeinnerdiv.className = "cjZ9o _2QZ8F _3b-i8";
blackfadeouterdiv.appendChild(blackfadeinnerdiv);
outermousediv.appendChild(blackfadeouterdiv);
//Next inward div
let innermousediv = document.createElement("div");
innermousediv.style.width = "100%";
innermousediv.style.height = "100%";
innermousediv.className = "_2ehf4 YpNhf";
//Div for all all meta elements container (title, artist, icon, comments)
let metaelementscontainer = document.createElement("div");
metaelementscontainer.className = "_5Xty_";
//Div to contain title and artist elements (title, artist, icon)
let titleartistcontainer = document.createElement("div");
titleartistcontainer.className = "_1mmGw _31MCr";
// arttitlelinkelement.appendChild(arttitleelement);
//titleartistcontainer.appendChild(arttitlelinkelement);
metaelementscontainer.appendChild(titleartistcontainer);
innermousediv.appendChild(metaelementscontainer);
outermousediv.appendChild(innermousediv);
draggablecontainer.appendChild(outermousediv);
//Subsection for artist icon and name
//Divs to contain title and artist elements (title, artist, icon)
let iconartistcontainer = document.createElement("div");
iconartistcontainer.className = "_13y-9";
let iconartistcontainer2 = document.createElement("div");
iconartistcontainer2.className = "_2o1Q1";
//Div to contain artist icon
let iconcontainer = document.createElement("div");
iconcontainer.className = "_3CR67 _1I9Ar";
//Link for artist that surrounds icon
let artisticonlinkelement = document.createElement("a")
artisticonlinkelement.href = "https://www.deviantart.com/" + deviation.gs_username;
artisticonlinkelement.className = "user-link _2f0dA _23x0l";
//Artist icon element
let artisticonelement = document.createElement("img");
artisticonelement.alt = deviation.author.username + "'s avatar";
artisticonelement.src = deviation.author.usericon;
artisticonelement.style.width = "24px";
artisticonelement.style.height = "24px";
artisticonelement.loading = "lazy";
artisticonelement.className = "_1IDJa";
artisticonlinkelement.appendChild(artisticonelement);
iconcontainer.appendChild(artisticonlinkelement);
//Div to contain artist name
let artistnamecontainer = document.createElement("div");
artistnamecontainer.className = "_3CR67 k4CiA";
//Link for artist that surrounds icon
let artistnamelinkelement = document.createElement("a")
artistnamelinkelement.href = "https://www.deviantart.com/" + deviation.gs_username;
artistnamelinkelement.className = "user-link _2f0dA";
//Artist name text span
let artistnametextelement = document.createElement("span")
artistnametextelement.className = "_2UI2c";
artistnametextelement.innerHTML = deviation.author.username;
//Artist cursor span
let artistcursorelement = document.createElement("span")
artistcursorelement.className = "_3LUMH _1NhtS G0rcN";
artistcursorelement.style.cursor = "pointer";
artistcursorelement.role = "img";
artistnamelinkelement.appendChild(artistnametextelement);
artistnamelinkelement.appendChild(artistcursorelement);
artistnamecontainer.appendChild(artistnamelinkelement);
iconartistcontainer2.appendChild(iconcontainer);
iconartistcontainer2.appendChild(artistnamecontainer);
iconartistcontainer.appendChild(iconartistcontainer2);
titleartistcontainer.appendChild(iconartistcontainer);
//Subsection for comments icon and link elements
//Div to contain comment icon and link elements
let commenticonlinkcontainer = document.createElement("div");
commenticonlinkcontainer.className = "_1mmGw _2WpJA _6oiPd";
//Link for comment section
let commentlinkelement = document.createElement("a")
commentlinkelement.href = deviation.url + "#comments";
commentlinkelement.className = "_1-Wh7 x48yz";
//Comment icon span
let commenticonspan = document.createElement("span")
commenticonspan.className = "z8jNZ _1yoxj _38kc5";
//Comment icon (SVG)
let commenticonSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
commenticonSVG.setAttribute("viewBox", "0 0 24 24");
commenticonSVG.setAttribute("version", "1.1");
commenticonSVG.setAttribute("xlmns:xlink", "http://www.w3.org/1999/xlink");
//Comment icon (SVG PATH)
let commenticonpath = document.createElementNS("http://www.w3.org/2000/svg", "path");
commenticonpath.setAttribute("fill-rule", "evenodd");
commenticonpath.setAttribute("d", "M20 3a1 1 0 01.993.883L21 4v9.586a1 1 0 01-.206.608l-.087.099-2.414 2.414a1 1 0 01-.576.284l-.131.009H13l-2.7 3.6a1 1 0"
+ " 01-.683.393L9.5 21H8a1 1 0 01-.993-.883L7 20v-3H4a1 1 0 01-.993-.883L3 16V6.414a1 1 0 01.206-.608l.087-.099 2.414-2.414a1 1 0 01.576-.284L6.414"
+ " 3H20zm-1 2H6.828L5 6.828V15h4v4l3-4h5.17L19 13.17V5z");
//Comment count span
let commentcountspan = document.createElement("span")
commentcountspan.innerHTML = deviation.stats.comments;
commenticonSVG.appendChild(commenticonpath);
commenticonspan.appendChild(commenticonSVG);
commentlinkelement.appendChild(commenticonspan);
commentlinkelement.appendChild(commentcountspan);
commenticonlinkcontainer.appendChild(commentlinkelement);
metaelementscontainer.appendChild(commenticonlinkcontainer);
return outermostdiv;
}
function toggleSettingsElement() {
if(settingsmenudiv.style.display == "none") {
settingsmenudiv.style.display = "";
settingsmenudiv.focus();
}
else {
settingsmenudiv.style.display = "none";
}
}
function hideSettingsElement(event) {
if(!settingscogdiv.contains(event.target)){
settingsmenudiv.style.display = 'none';
}
}
function createSettingsMenu() {
let settingscontainer = document.createElement("div");
settingscontainer.id = "settingsmenu";
settingscontainer.style.background = "#06070d";
settingscontainer.style.border = "1px solid #262830";
settingscontainer.style.borderRadius = "5px solid #262830";
settingscontainer.style.position = "absolute";
settingscontainer.style.right = "-20px";
settingscontainer.style.top = "40px";
settingscontainer.style.zIndex = "1";
settingscontainer.style.zIndex = "1";
settingscontainer.style.display = "none";
let settingstable = document.createElement("table");
settingstable.style.width = "300px";
settingstable.style.margin = "5px";
let tr1 = document.createElement("tr");
let tr2 = document.createElement("tr");
let tr3 = document.createElement("tr");
let tr4 = document.createElement("tr");
let t2tr1 = document.createElement("tr");
let maxsearchresultsleft = document.createElement("td");
maxsearchresultsleft.style.textAlign = "left";
maxsearchresultsleft.style.padding = "5px";
maxsearchresultsleft.innerHTML = "Max Search Results:";
let maxsearchresultsright = document.createElement("td");
maxsearchresultsright.style.textAlign = "right";
maxsearchresultsright.style.padding = "5px";
let maxsearchresultsinput = document.createElement("input");
maxsearchresultsinput.id = "maximumsearchreturn";
maxsearchresultsinput.type = "text";
maxsearchresultsinput.style.width = "40px";
maxsearchresultsinput.value = maximumsearchreturn;
maxsearchresultsright.appendChild(maxsearchresultsinput);
tr1.appendChild(maxsearchresultsleft);
tr1.appendChild(maxsearchresultsright);
let paginationrateleft = document.createElement("td");
paginationrateleft.style.textAlign = "left";
paginationrateleft.style.padding = "5px";
paginationrateleft.innerHTML = "Index Rate Limit (in seconds):";
let paginationrateright = document.createElement("td");
paginationrateright.style.textAlign = "right";
paginationrateright.style.padding = "5px";
let paginationrateinput = document.createElement("input");
paginationrateinput.id = "paginationratelimit";
paginationrateinput.type = "text";
paginationrateinput.style.width = "40px";
paginationrateinput.value = paginationratelimit.toPrecision(2);
paginationrateright.appendChild(paginationrateinput);
tr2.appendChild(paginationrateleft);
tr2.appendChild(paginationrateright);
let ratewarningleft = document.createElement("td");
ratewarningleft.style.textAlign = "left";
ratewarningleft.style.padding = "5px";
ratewarningleft.innerHTML = "Rate Limit Warning:";
let ratewarningright = document.createElement("td");
ratewarningright.style.textAlign = "right";
ratewarningright.style.padding = "5px";
let ratewarningcheckbox = document.createElement("input");
ratewarningcheckbox.id = "ratelimitwarning";
ratewarningcheckbox.type = "checkbox";
ratewarningcheckbox.style.accentColor = "green";
ratewarningcheckbox.checked = ratelimitwarning;
ratewarningright.appendChild(ratewarningcheckbox);
tr3.appendChild(ratewarningleft);
tr3.appendChild(ratewarningright);
let tileheightleft = document.createElement("td");
tileheightleft.style.textAlign = "left";
tileheightleft.style.padding = "5px";
tileheightleft.innerHTML = "Rough Tiling Height (in pixels):";
let tileheightright = document.createElement("td");
tileheightright.style.textAlign = "right";
tileheightright.style.padding = "5px";
let tileheightinput = document.createElement("input");
tileheightinput.id = "pivotalrowheight";
tileheightinput.type = "text";
tileheightinput.style.width = "40px";
tileheightinput.value = pivotalrowheight;
tileheightright.appendChild(tileheightinput);
tr4.appendChild(tileheightleft);
tr4.appendChild(tileheightright);
settingstable.appendChild(tr1);
settingstable.appendChild(tr2);
settingstable.appendChild(tr3);
settingstable.appendChild(tr4);
settingscontainer.appendChild(settingstable);
let settingstable2 = document.createElement("table");
settingstable2.style.width = "300px";
settingstable2.style.margin = "5px";
let sortbyleft = document.createElement("td");
sortbyleft.style.textAlign = "left";
sortbyleft.style.padding = "5px";
sortbyleft.innerHTML = "Sort By:";
let sortbyright = document.createElement("td");
sortbyright.style.textAlign = "right";
let sortbycontainer = document.createElement("div");
sortbycontainer.style.width = "180px";
sortbycontainer.style.color = regularwhitetextcolor;
sortbycontainer.style.marginLeft = "auto";
sortbycontainer.style.display = "flex";
let leftsort = document.createElement("span");
leftsort.id = "leftsort";
leftsort.style.cursor = "pointer";
leftsort.style.margin = "5px auto";
leftsort.style.background = "green";
leftsort.style.padding = "5px";
leftsort.style.borderTopLeftRadius = "5px";
leftsort.style.borderBottomLeftRadius = "5px";
leftsort.innerHTML = "<";
let sorttext = document.createElement("span");
sorttext.id = "sortby";
sorttext.style.margin = "5px auto";
sorttext.style.background = "green";
sorttext.style.padding = "5px";
sorttext.style.width = "120px";
sorttext.style.textAlign = "center";
sorttext.innerHTML = sortby;
let rightsort = document.createElement("span");
rightsort.id = "rightsort";
rightsort.style.cursor = "pointer";
rightsort.style.margin = "5px auto";
rightsort.style.background = "green";
rightsort.style.padding = "5px";
rightsort.style.borderTopRightRadius = "5px";
rightsort.style.borderBottomRightRadius = "5px";
rightsort.innerHTML = ">";
sortbycontainer.appendChild(leftsort);
sortbycontainer.appendChild(sorttext);
sortbycontainer.appendChild(rightsort);
sortbyright.appendChild(sortbycontainer);
t2tr1.appendChild(sortbyleft);
t2tr1.appendChild(sortbyright);
settingstable2.appendChild(t2tr1);
settingscontainer.appendChild(settingstable2);
settingscogdiv.appendChild(settingscontainer);
//Add button animations
leftsort.addEventListener("mouseover", hoverFade);
leftsort.addEventListener("mouseout", hoverFadeCancel);
rightsort.addEventListener("mouseover", hoverFade);
rightsort.addEventListener("mouseout", hoverFadeCancel);
//Add user settings change listeners
maxsearchresultsinput.addEventListener("keyup", updateUserNumberPreference);
paginationrateinput.addEventListener("keyup", updateUserNumberPreference);
ratewarningcheckbox.addEventListener("click", updateUserCheckboxPreference);
tileheightinput.addEventListener("keyup", updateUserNumberPreference);
leftsort.addEventListener("click", updateSortByPreference);
rightsort.addEventListener("click", updateSortByPreference);
return settingscontainer;
function hoverFade(event) {
event.target.style.opacity = "50%";
}
function hoverFadeCancel(event) {
event.target.style.opacity = "100%";
}
function updateUserNumberPreference(event) {
let key = event.target.id;
let value = event.target.value;
let updatedCorrectly = setUserPreference(key, value);
if(updatedCorrectly) {
event.target.style.color = regularblacktextcolor;
searchoutputtext.style.color = regularwhitetextcolor;
if(searchoutputtext.innerHTML.includes("Error"))
searchoutputtext.innerHTML = "";
}
else {
event.target.style.color = errortextcolor;
searchoutputtext.style.color = errortextcolor;
let typeproblemstring = (key == "paginationratelimit" ? "decimal number. (Example: 2.5)" : "integer. (Example: 300)");
searchoutputtext.innerHTML = "Error: '" + value + "' is not a " + typeproblemstring;
}
//Add an immediate tiling update if the desired row height is changed, handled within the search function
if(key == "pivotalrowheight") {
search();
}
}
function updateUserCheckboxPreference(event) {
let key = event.target.id;
let value = event.target.checked;
setUserPreference(key, value);
}
function updateSortByPreference(event) {
let isleft = event.target.id == "leftsort";
//Get and set new sort option based on click direction
let sortoptionindex = 0;
for(let i=0; i "1000" ) and direct inputs (ex: maximumsearchreturn -> 1000 )
if(key == "maximumsearchreturn") {
let newmaximumsearchreturn = parseInt(value);
if(isNaN(newmaximumsearchreturn))
return false;
maximumsearchreturn = newmaximumsearchreturn;
localStorage.setItem("maximumsearchreturn", maximumsearchreturn);
}
else if(key == "paginationratelimit") {
let newpaginationratelimit = parseFloat(value);
if(isNaN(newpaginationratelimit))
return false;
paginationratelimit = newpaginationratelimit;
localStorage.setItem("paginationratelimit", paginationratelimit);
}
else if(key == "ratelimitwarning") {
let newratelimitwarning = (value == true || value == "true");
ratelimitwarning = newratelimitwarning;
localStorage.setItem("ratelimitwarning", ratelimitwarning);
}
else if(key == "pivotalrowheight") {
let newpivotalrowheight = parseInt(value);
if(isNaN(newpivotalrowheight))
return false;
pivotalrowheight = newpivotalrowheight;
localStorage.setItem("pivotalrowheight", pivotalrowheight);
}
else if(key == "sortby") {
sortby = value;
localStorage.setItem("sortby", sortby);
}
else {
return false;
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////
///// Other Helper Functions /////
/////////////////////////////////////////////////////////////////////////////////////
function getAspectedWidth(origwidth, origheight, sizedheight)
{
return (origwidth * sizedheight) / origheight;
}
function getAspectedHeight(origwidth, origheight, sizedwidth)
{
return (origheight * sizedwidth) / origwidth;
}
//Function to sort deviationJSON based on attribute provided
function sortDeviationsByKey(array, key, ascending) {
//spot check for the variable type
let spotcheck = typeof array[0].deviation[key];
if(spotcheck == "string")
{
return array.sort(function(a, b) {
let x = a.deviation[key]; let y = b.deviation[key];
if(ascending)
return x.localeCompare(y);
else
return y.localeCompare(x);
});
}
else if(spotcheck == "number") {
return array.sort(function(a, b) {
let x = a.deviation[key]; let y = b.deviation[key];
if(x == y)
return 0;
let returnval;
if(x > y)
returnval = 1;
else
returnval = -1;
if(ascending)
return returnval;
else
return -returnval;
});
}
else {
console.error("Unhandled type for sort key: " + key);
}
}
}
//DOM Element Mutation Observer
//Credit to Yong Wang on StackOverflow: https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists
function waitForElm(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
})();