您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
解锁 Hath Perks 及增加一些小工具
当前为
// ==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; }`;