// ==UserScript==
// @name Unlock Hath Perks
// @name:zh-TW 解鎖 Hath Perks
// @name:zh-CN 解锁 Hath Perks
// @description Unlock Hath Perks and add other helpers
// @description:zh-TW 解鎖 Hath Perks 及增加一些小工具
// @description:zh-CN 解锁 Hath Perks 及增加一些小工具
// @namespace https://github.com/FlandreDaisuki
// @version 1.0.3
// @match *://e-hentai.org/*
// @match *://exhentai.org/*
// @icon https://i.imgur.com/JsU0vTd.png
// @run-at document-start
// @grant GM_setValue
// @grant GM_getValue
// @noframes
//
// Addition metas
//
// @supportURL https://github.com/FlandreDaisuki/My-Browser-Extensions/issues
// @homepageURL https://github.com/FlandreDaisuki/My-Browser-Extensions/blob/master/userscripts/UnlockHathPerks.md
// @author FlandreDaisuki
// @license MPLv2
// @compatible firefox 52+
// @compatible chrome 55+
// @incompatible any not support async/await, CSS-grid browsers
// ==/UserScript==
'use strict';
/************************************/
/***** Before DOM Ready *****/
/************************************/
Set.prototype.difference = function(setB) {
const difference = new Set(this);
for(const elem of setB) {
difference.delete(elem);
}
return difference;
};
function $find(el, selector, cb = () => {}) {
const found = el.querySelector(selector);
cb(found);
return found;
}
function $$find(el, selector, cb = () => {}) {
const found = Array.from(el.querySelectorAll(selector));
cb(found);
return found;
}
function $(selector) {
return $find(document, selector);
}
function $$(selector) {
return $$find(document, selector);
}
function $el(name, attr = {}, cb = () => {}) {
const el = document.createElement(name);
Object.assign(el, attr);
cb(el);
return el;
}
function $style(textContent) {
$el('style', {textContent}, el => document.head.appendChild(el));
}
// sessionStorage namespace:
// in tab && in domain
function $scrollYTo(n) {
n = parseFloat(n | 0);
const id = setInterval(() => {
scrollTo(scrollX, n);
if (scrollY >= n) {
clearInterval(id);
}
}, 100);
}
class API {
// ref: https://github.com/tommy351/ehreader-android/wiki/E-Hentai-JSON-API
static gInfo(href) {
// pathname = '/g/{gallery_id}/{gallery_token}/'
const a = $el('a', {href});
const path = a.pathname.split('/').filter(x => x);
if (path[0] !== 'g') {
return null;
}
// [{gallery_id}, {gallery_token}]
return path.slice(1);
}
static async gData(gInfos) {
const queue = [];
const result = [];
while(gInfos.length) {
const toQ = gInfos.slice(0, 25);
gInfos.splice(0, 25);
queue.push(toQ);
}
for(const glist of queue) {
const r = await fetch('/api.php', {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({
method: 'gdata',
gidlist: glist,
}),
});
const json = await r.json();
if (json.error) {
console.error('API.gdata(): glist', glist);
throw new Error(json.error);
} else {
result.push(...json.gmetadata);
}
}
return result;
}
static async sPage(href) {
const r = await fetch(href, {credentials: 'same-origin'});
const html = await r.text();
const imgsrc = html.replace(/[\s\S]*id="img" src="([^"]+)"[\s\S]*/g, '$1');
return {
imgsrc,
};
}
}
const uhpConfig = {
abg: true,
mt: true,
tf: false,
pe: true,
mpv: false,
fw: false,
rth: false,
sr: false,
pi: false,
tpf: false,
flaggingTags: {
red: {
hide: false,
tags:[],
},
green: {
hide: false,
tags:[],
},
brown: {
hide: false,
tags:[],
},
blue: {
hide: false,
tags:[],
},
yellow: {
hide: false,
tags:[],
},
purple: {
hide: false,
tags:[],
},
},
};
function uhpSaveConfig() {
GM_setValue('uhp', uhpConfig);
}
function uhpLoadConfig() {
return GM_getValue('uhp', uhpConfig);
}
Object.assign(uhpConfig, uhpLoadConfig());
uhpSaveConfig();
if (uhpConfig.abg) {
Object.defineProperty(window, 'adsbyjuicy', {
enumerable: false,
configurable: false,
writable: false,
value: null,
});
}
document.onreadystatechange = function() {
if (document.readyState === 'interactive') {
main();
$style(cssText);
$style(materialCSS);
$el('link', {
href: 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
rel: 'stylesheet',
integrity: 'sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN',
crossOrigin: 'anonymous',
}, el => document.head.appendChild(el));
}
};
/*****************************/
/***** DOM Ready *****/
/*****************************/
function main() {
if (!location.pathname.startsWith('/s/')) {
/* Make nav button */
const nb = $('#nb');
const mr = $el('img', {src: '//ehgt.org/g/mr.gif'});
const uhpBtnEl =
$el('a', {
textContent: 'Unlock Hath Perks',
id: 'uhp-btn',
}, el => {
el.addEventListener('click', () => {
$('#uhp-panel-container').classList.remove('hidden');
});
});
nb.appendChild(mr);
nb.appendChild(document.createTextNode(' '));
nb.appendChild(uhpBtnEl);
/* Setup UHP Panel */
const uhpPanelContainerEl = $el('div', {
className: 'hidden',
id: 'uhp-panel-container',
}, el => {
el.addEventListener('click', () => {
if($$('#uhp-panel input[pattern]').every(el=>el.validity.valid)) {
el.classList.add('hidden');
}
});
});
document.body.appendChild(uhpPanelContainerEl);
const uhpPanelEl = $el('div', {
id: 'uhp-panel',
}, el => {
if(location.host === 'exhentai.org') {
el.classList.add('dark');
}
el.addEventListener('click', event => { event.stopPropagation(); });
});
uhpPanelContainerEl.appendChild(uhpPanelEl);
/* Setup UHP Configs */
uhpPanelEl.innerHTML = uhpPanelElHTML + uhpTagFlaggingHTML;
$$('#uhp-panel input[id^="uhp-conf-"]').forEach(el => {
const abbr = el.id.replace('uhp-conf-', '');
el.checked = uhpConfig[abbr];
el.addEventListener('change', () => {
uhpConfig[abbr] = el.checked;
uhpSaveConfig();
});
});
$$('#uhp-panel input[pattern]').forEach(el => {
// tag color
const tc = el.id.replace('uhp-tf-', '');
el.addEventListener('change', () => {
const newTags = el.value.split(',').map(x => x.trim()).filter(x => x);
const oldTags = uhpConfig.flaggingTags[tc].tags;
const allTags = Object.values(uhpConfig.flaggingTags).reduce((acc, val) => acc.concat(val.tags), []);
const newAllSet = new Set(allTags).difference(oldTags);
const newSet = new Set(newTags).difference(newAllSet);
el.value = [...newSet].join(', ');
uhpConfig.flaggingTags[tc].tags = [...newSet];
uhpSaveConfig();
});
});
$$('.uhp-tf-options input[type="checkbox"]').forEach(el => {
// tag color
const tc = el.id.replace(/uhp-tf-(\w+)-hide/, '$1');
el.checked = uhpConfig.flaggingTags[tc].hide;
el.addEventListener('change', () => {
uhpConfig.flaggingTags[tc].hide = el.checked;
uhpSaveConfig();
});
});
/* Setup Reactable UHP Configs */
$('#uhp-conf-fw').addEventListener('change', event => {
const fwc = $('#uhp-full-width-container');
if(fwc) {
if(event.target.checked) {
fwc.classList.add('fullwidth');
} else {
fwc.classList.remove('fullwidth');
}
}
});
$('#uhp-conf-tf').addEventListener('change', event => {
const tfops = $$('.uhp-tf-options');
if(event.target.checked) {
tfops.forEach(el => el.classList.remove('hidden'));
} else {
tfops.forEach(el => el.classList.add('hidden'));
}
});
}
if ($('#searchbox')) {
const ido = $('div.ido');
ido.id = 'uhp-full-width-container';
if (uhpConfig.fw) {
ido.classList.add('fullwidth');
}
}
/* Main Functions by Configs */
/**************/
/* Ad-Be-Gone */
/**************/
if (uhpConfig.abg) {
// if "No hits found", there is no mode
if ($('#searchbox') && $('#dmi>span')) {
const mode = $('#dmi>span').textContent === 'Thumbnails' ? 't' : 'l';
if (mode === 'l') {
$$('table.itg tr:nth-of-type(n+2)')
.forEach(el => {
if (!el.className) {
el.remove();
}
});
}
}
$$('script[async]').forEach(el => el.remove());
$$('iframe').forEach(el => el.remove());
}
/**********************/
/* Paging Enlargement */
/**********************/
async function getNextPage(nextURL, mode) {
const selector = mode === 't' ? 'div.id1' : 'table.itg tr:nth-of-type(n+2)';
const result = {
mode,
elements: [],
nextURL: null,
};
if (!nextURL) {
return result;
}
const response = await fetch(nextURL, {
credentials: 'same-origin',
});
if (response.ok) {
const html = await response.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
result.elements = Array.from($$find(doc, selector));
if (uhpConfig.abg) {
result.elements = result.elements.filter(el => el.className);
}
result.elements =
result.elements
.filter(el => {
if(uhpConfig.rth) {
if (mode === 't') {
return !$find(el, '.id3 img').src.endsWith('blank.gif');
} else {
return $find(el, '.it5 > a').getAttribute('onmouseover');
}
}
return true;
})
.map(el => {
el.removeAttribute('style');
return el;
});
const nextEl = $find(doc, '.ptb td:last-child > a');
result.nextURL = nextEl ? nextEl.href : null;
}
console.log(result);
return result;
}
async function addTagFlags(page) {
const selector = page.mode === 't' ? '.id3 > a' : '.it5 > a';
const gLinks = page.elements.map(el => $find(el, selector).href);
const gInfos = gLinks.map(a => API.gInfo(a));
const gData = await API.gData(gInfos);
const tagsMap = {};
for(const i in gLinks) {
const gLink = gLinks[i];
// tag1;tag2;tag3
tagsMap[gLink] = gData[i].tags.join(';');
}
for(const pageEl of page.elements) {
const parent = (page.mode === 't') ?
$find(pageEl, '.id44') :
$el('div', {className: 'it4t'}, el => {
if($find(pageEl, '.it4t')) {
$find(pageEl, '.it4t').replaceWith(el);
} else {
$find(pageEl, '.it4').appendChild(el);
}
});
const aLink = $find(pageEl, selector);
// remove exists
$$find(parent, `.tf${page.mode}`).forEach(el => el.remove());
for (const c in uhpConfig.flaggingTags) {
const tags = uhpConfig.flaggingTags[c].tags;
const matchs = tags.filter(t => tagsMap[aLink.href].includes(t));
if (matchs.length) {
const flagEl = $el('div', {title: matchs.join(', '), className:`tf${page.mode} ${c}`});
parent.appendChild(flagEl);
if (uhpConfig.flaggingTags[c].hide) {
if (page.mode === 't') {
$find(aLink, 'img').src = '//ehgt.org/g/blank.gif';
} else {
aLink.removeAttribute('onmouseover');
aLink.removeAttribute('onmouseout');
}
}
}
}
}
page.elements = page.elements.filter(el => {
if(uhpConfig.rth) {
if (page.mode === 't') {
return !$find(el, '.id3 img').src.endsWith('blank.gif');
} else {
return $find(el, '.it5 > a').getAttribute('onmouseover');
}
}
return true;
});
}
// if "No hits found", there is no mode
if ($('#searchbox') && $('#dmi>span')) {
(async() => {
const nextEl = $('.ptb td:last-child > a');
let nextURL = nextEl ? nextEl.href : null;
const mode = $('#dmi>span').textContent === 'Thumbnails' ? 't' : 'l';
const parent = mode === 't' ? $('div.itg') : $('table.itg tbody');
const status = $el('h1', {
textContent: 'Loading...',
id: 'uhp-status',
});
const urlSet = new Set();
if (mode === 'l') {
if (location.hostname.startsWith('exh')) {
parent.classList.add('uhp-list-parent-exh');
} else {
parent.classList.add('uhp-list-parent-eh');
}
} else {
parent.style.borderBottom = 'none';
$$('div.id1').forEach(el => el.removeAttribute('style'));
}
// this page
const thisPage = await getNextPage(location.href, mode);
if(uhpConfig.tf) {
await addTagFlags(thisPage);
}
while (parent.firstChild) {
parent.firstChild.remove();
}
thisPage.elements.forEach(el => parent.appendChild(el));
nextURL = thisPage.nextURL;
if (!nextURL) {
status.textContent = 'End';
}
// next page
if (uhpConfig.pe) {
$('table.ptb').replaceWith(status);
// remove popular section
$$('div.c, #pt, #pp').forEach(el => el.remove());
document.addEventListener('scroll', async() => {
const anchorTop = status.getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (anchorTop < windowHeight * 2 && nextURL && !urlSet.has(nextURL)) {
urlSet.add(nextURL);
const nextPage = await getNextPage(nextURL, mode);
if(uhpConfig.tf) {
await addTagFlags(nextPage);
}
//// work around first ////
if(uhpConfig.pi) {
if (mode === 'l') {
parent.appendChild($el('tr', {
className: 'uhp-open-in-new-page',
}, el => {
el.innerHTML = `<td colspan="4" style="font-size: 4rem;">
<a href="${nextURL}" style="text-decoration: none; display: inline-flex; align-items: flex-end;">
P${~~nextURL.replace(/.*(?:page=(\d+)|\/(\d+)$).*/g, '$1$2') + 1}
</a>
</td>`;
}));
} else {
parent.appendChild($el('div', {
className: 'uhp-open-in-new-page',
style: 'grid-column: 1; display: flex; align-items: center; justify-content: center;',
}, el => {
el.innerHTML = `<div style="position: sticky;top: 0;font-size: 4rem;">
<a href="${nextURL}" style="text-decoration: none; display: inline-flex; align-items: flex-end;">
P${~~nextURL.replace(/.*(?:page=(\d+)|\/(\d+)$).*/g, '$1$2') + 1}
</a>
</div>`;
}));
}
}
if(uhpConfig.tpf) {
parent.classList.add('uhp-tpf-dense');
}
//// work around first ////
nextPage.elements.forEach(el => parent.appendChild(el));
nextURL = nextPage.nextURL;
if (!nextURL) {
status.textContent = 'End';
}
}
});
}
})();
}
/***************/
/* More Thumbs */
/***************/
async function getNextGallaryPage(nextURL) {
const result = {
elements: [],
nextURL: null,
};
if (!nextURL) {
return result;
}
const response = await fetch(nextURL, {
credentials: 'same-origin',
});
if (response.ok) {
const html = await response.text();
const doc = new DOMParser().parseFromString(html, 'text/html');
result.elements = $$find(doc, '#gdt > div');
const nextEl = $find(doc, '.ptb td:last-child > a');
result.nextURL = nextEl ? nextEl.href : null;
}
console.log(result);
return result;
}
if (uhpConfig.mt && location.pathname.startsWith('/g/')) {
(async() => {
$('#gdo1').style.display = 'none';
const nextEl = $('.ptb td:last-child > a');
let nextURL = nextEl ? nextEl.href : null;
const parent = $('#gdt');
parent.classList.add('uhp-page-parent');
const urlSet = new Set();
// this page
const thisPage = await getNextGallaryPage(location.href);
while (parent.firstChild) {
parent.firstChild.remove();
}
thisPage.elements.forEach(el => parent.appendChild(el));
// next page
document.addEventListener('scroll', async() => {
const anchorTop = $('#cdiv').getBoundingClientRect().top;
const windowHeight = window.innerHeight;
if (anchorTop < windowHeight * 2 && !urlSet.has(nextURL)) {
urlSet.add(nextURL);
const nextPage = await getNextGallaryPage(nextURL);
nextPage.elements.forEach(el => parent.appendChild(el));
nextURL = nextPage.nextURL;
}
});
})();
}
/**********************/
/* Scroll Restoration */
/**********************/
if(uhpConfig.sr) {
history.scrollRestoration = 'manual';
window.addEventListener('beforeunload', () => {
history.replaceState(scrollY, null);
});
window.addEventListener('load', () => {
if (history.state) {
$scrollYTo(history.state);
}
});
}
}
var uhpPanelElHTML = `
<h1>Hath Perks</h1>
<div class="option-grid">
<div class="material-switch">
<input id="uhp-conf-abg" type="checkbox">
<label for="uhp-conf-abg"></label>
</div>
<span id="uhp-conf-abg-title">Ads-Be-Gone</span>
<span id="uhp-conf-abg-desc">Make ad scripts won't work before request.</span>
<div class="material-switch">
<input id="uhp-conf-tf" type="checkbox">
<label for="uhp-conf-tf"></label>
</div>
<span id="uhp-conf-tf-title">Tag Flagging</span>
<span id="uhp-conf-tf-desc">Can flag 6 color for tags.<br/>
Hide thumbnail of search results when the switch turn on.<br/>
Conflict with official "Tag Flagging".
</span>
<div class="material-switch">
<input id="uhp-conf-mpv" type="checkbox" disabled>
<label for="uhp-conf-mpv"></label>
</div>
<span id="uhp-conf-mpv-title">Multi-Page Viewer</span>
<span id="uhp-conf-mpv-desc">Work in Progress</span>
<div class="material-switch">
<input id="uhp-conf-mt" type="checkbox">
<label for="uhp-conf-mt"></label>
</div>
<span id="uhp-conf-mt-title">More Thumbs</span>
<span id="uhp-conf-mt-desc">Make thumbnails in book page infinitely scroll.</span>
<div class="material-switch">
<input id="uhp-conf-pe" type="checkbox">
<label for="uhp-conf-pe"></label>
</div>
<span id="uhp-conf-pe-title">Paging Enlargement</span>
<span id="uhp-conf-pe-desc">Make search results page infinitely scroll.<br/>Popular section will be removed.</span>
</div>
<h1>Others</h1>
<div class="option-grid">
<div class="material-switch">
<input id="uhp-conf-fw" type="checkbox">
<label for="uhp-conf-fw"></label>
</div>
<span id="uhp-conf-fw-title">Full Width</span>
<span id="uhp-conf-fw-desc">Make search results fitting browser width.<br/>Only affect on thumb display mode.</span>
<div class="material-switch">
<input id="uhp-conf-rth" type="checkbox">
<label for="uhp-conf-rth"></label>
</div>
<span id="uhp-conf-rth-title">Remove Tag Hidden</span>
<span id="uhp-conf-rth-desc">Remove search results which tagged with hidden when "Tag Flagging" work.</span>
<div class="material-switch">
<input id="uhp-conf-sr" type="checkbox">
<label for="uhp-conf-sr"></label>
</div>
<span id="uhp-conf-sr-title">Scroll Restoration</span>
<span id="uhp-conf-sr-desc">Scroll last position you seen in last page when "Paging Enlargement" work.</span>
<div class="material-switch">
<input id="uhp-conf-pi" type="checkbox">
<label for="uhp-conf-pi"></label>
</div>
<span id="uhp-conf-pi-title">Page Indicator</span>
<span id="uhp-conf-pi-desc">Add page indicator link to prevent "Scroll Restoration" work too hard.</span>
<div class="material-switch">
<input id="uhp-conf-tpf" type="checkbox">
<label for="uhp-conf-tpf"></label>
</div>
<span id="uhp-conf-tpf-title">Thumb Page Flow</span>
<span id="uhp-conf-tpf-desc">Make dense flow when "Page Indicator" work.<br/>Only affect on thumb display mode.</span>
</div>
`;
var uhpTagFlaggingHTML = `
<h1 class="uhp-tf-options ${uhpConfig.tf ? '' : 'hidden'}">Tag Flagging</h1>
<div class="uhp-tf-options tf-option-grid ${uhpConfig.tf ? '' : 'hidden'}">
<div class="tfl red"></div>
<input id="uhp-tf-red" pattern="(\\w(?:[^:]|[\\w\\s])+)(?:,\\s*\\1)*" value="${uhpConfig.flaggingTags.red.tags.join(', ')}" placeholder="e.g. touhou, flandre scarlet"/>
<div class="material-switch">
<input id="uhp-tf-red-hide" type="checkbox">
<label for="uhp-tf-red-hide"></label>
</div>
<div class="tfl green"></div>
<input id="uhp-tf-green" pattern="(\\w(?:[^:]|[\\w\\s])+)(?:,\\s*\\1)*" value="${uhpConfig.flaggingTags.green.tags.join(', ')}"/>
<div class="material-switch">
<input id="uhp-tf-green-hide" type="checkbox">
<label for="uhp-tf-green-hide"></label>
</div>
<div class="tfl brown"></div>
<input id="uhp-tf-brown" pattern="(\\w(?:[^:]|[\\w\\s])+)(?:,\\s*\\1)*" value="${uhpConfig.flaggingTags.brown.tags.join(', ')}"/>
<div class="material-switch">
<input id="uhp-tf-brown-hide" type="checkbox">
<label for="uhp-tf-brown-hide"></label>
</div>
<div class="tfl blue"></div>
<input id="uhp-tf-blue" pattern="(\\w(?:[^:]|[\\w\\s])+)(?:,\\s*\\1)*" value="${uhpConfig.flaggingTags.blue.tags.join(', ')}"/>
<div class="material-switch">
<input id="uhp-tf-blue-hide" type="checkbox">
<label for="uhp-tf-blue-hide"></label>
</div>
<div class="tfl yellow"></div>
<input id="uhp-tf-yellow" pattern="(\\w(?:[^:]|[\\w\\s])+)(?:,\\s*\\1)*" value="${uhpConfig.flaggingTags.yellow.tags.join(', ')}"/>
<div class="material-switch">
<input id="uhp-tf-yellow-hide" type="checkbox">
<label for="uhp-tf-yellow-hide"></label>
</div>
<div class="tfl purple"></div>
<input id="uhp-tf-purple" pattern="(\\w(?:[^:]|[\\w\\s])+)(?:,\\s*\\1)*" value="${uhpConfig.flaggingTags.purple.tags.join(', ')}"/>
<div class="material-switch">
<input id="uhp-tf-purple-hide" type="checkbox">
<label for="uhp-tf-purple-hide"></label>
</div>
</div>
`;
var cssText = `
#uhp-btn {
cursor: pointer;
}
#uhp-panel-container {
position:fixed;
top: 0;
height: 100vh;
width: 100vw;
background-color: rgba(200, 200, 200, 0.7);
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
}
#uhp-panel-container.hidden {
visibility: hidden;
opacity: 0;
}
#uhp-panel {
padding: 1.2rem;
background-color: floralwhite;
border-radius: 1rem;
font-size: 1rem;
color: darkred;
max-width: 650px;
}
#uhp-panel.dark {
background-color: dimgray;
color: ghostwhite;
}
#uhp-panel > .option-grid {
display: grid;
grid-template-columns: max-content max-content 1fr;
grid-gap: 0.5rem 1rem;
}
#uhp-panel > .tf-option-grid {
display: grid;
grid-template-columns: 20px 1fr max-content;
grid-gap: 0.5rem 1rem;
}
#uhp-panel > .option-grid > *,
#uhp-panel > .tf-option-grid > * {
display: flex;
justify-content: center;
align-items: center;
}
#uhp-panel > .tf-option-grid > .tfl {
margin: auto;
}
#uhp-panel > .uhp-tf-options.hidden {
display: none;
}
#uhp-full-width-container.fullwidth,
#uhp-full-width-container.fullwidth div.itg {
max-width: none;
}
#uhp-full-width-container div.itg {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
grid-gap: 2px;
}
#uhp-full-width-container div.itg.uhp-tpf-dense {
grid-auto-flow: dense;
}
#uhp-full-width-container div.id1 {
height: 345px;
float: none;
display: flex;
flex-direction: column;
margin: 3px auto;
padding: 4px 0;
}
#uhp-full-width-container div.id2 {
overflow: visible;
height: initial;
margin: 4px auto;
}
#uhp-full-width-container div.id3 {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.uhp-list-parent-eh tr:nth-of-type(2n+1){
background-color: #EDEBDF;
}
.uhp-list-parent-eh tr:nth-of-type(2n+2){
background-color: #F2F0E4;
}
.uhp-list-parent-exh tr:nth-of-type(2n+1) {
background-color: #363940;
}
.uhp-list-parent-exh tr:nth-of-type(2n+2){
background-color: #4F535B;
}
#uhp-status {
text-align: center;
font-size: 3rem;
clear: both;
padding: 2rem 0;
}
/* replace */
div#pp,
div#gdt.uhp-page-parent {
display: flex;
flex-wrap: wrap;
}
div#gdt.uhp-page-parent>div{
float: initial;
}
div.it4t {
width: 102px;
}
div.tfl.red,
div.tft.red {
background-position: 0 -1px;
}
div.tfl.green,
div.tft.green {
background-position: 0px -52px;
}
div.tfl.brown,
div.tft.brown {
background-position: 0px -18px;
}
div.tfl.blue,
div.tft.blue {
background-position: 0px -69px;
}
div.tfl.yellow,
div.tft.yellow {
background-position: 0px -35px;
}
div.tfl.purple,
div.tft.purple {
background-position: 0px -86px;
}`;
/* https://bootsnipp.com/snippets/featured/material-design-switch */
var materialCSS = `
.material-switch {
display: inline-block;
}
.material-switch > input[type="checkbox"] {
display: none;
}
.material-switch > input[type="checkbox"] + label {
display: inline-block;
position: relative;
margin: 6px;
border-radius: 8px;
width: 40px;
height: 16px;
opacity: 0.3;
background-color: rgb(0, 0, 0);
box-shadow: inset 0px 0px 10px rgba(0, 0, 0, 0.5);
transition: all 0.4s ease-in-out;
}
.material-switch > input[type="checkbox"] + label::after {
position: absolute;
top: -4px;
left: -4px;
border-radius: 16px;
width: 24px;
height: 24px;
content: "";
background-color: rgb(255, 255, 255);
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease-in-out;
}
.material-switch > input[type="checkbox"]:checked + label {
background-color: #0e0;
opacity: 0.7;
}
.material-switch > input[type="checkbox"]:checked + label::after {
background-color: inherit;
left: 20px;
}
.material-switch > input[type="checkbox"]:disabled + label::after {
content: "\\f023";
line-height: 24px;
font-size: 0.8em;
font-family: FontAwesome;
color: initial;
}`;