// ==UserScript== // @name True Underground (by MixesDB) // @author User:Martin@MixesDB (Subfader@GitHub) // @version 2026.09.04.2 // @description Change the look and behaviour of certain DJ culture related websites to help contributing to MixesDB, e.g. add copy-paste ready tracklists in wiki syntax. // @homepageURL https://www.mixesdb.com/w/Help:MixesDB_userscripts // @supportURL https://discord.com/channels/1258107262833262603/1261652394799005858 // @updateURL https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/Tracklist_box_userscripts/True_Underground/script.user.js // @downloadURL https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/Tracklist_box_userscripts/True_Underground/script.user.js // @require https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/shared/jquery-3.7.1.min.js // @require https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/shared/waitForKeyElements.js // @require https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/shared/global.js?v-True_Underground_1 // @require https://raw.githubusercontent.com/mixesdb/userscripts/refs/heads/main/shared/tracklist_editor/funcs.js?v-True_Underground_1 // @match https://www.trueunderground.one/* // @match https://trueunderground.one/* // @icon https://www.google.com/s2/favicons?sz=64&domain=trueunderground.one // @noframes // @grant none // @run-at document-end // ==/UserScript== (function() { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Load @ressource files with variables * global.js URL needs to be changed manually * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ var cacheVersion = 1, scriptName = "True_Underground"; window.scriptName = scriptName; // toolkit.js reads this global directly window.cacheVersion = cacheVersion; // same reason: the @require'd shared files cache-bust their own CSS with it loadRawCss( githubPath_raw + "shared/global.css?v-" + scriptName + "_" + cacheVersion ); loadRawCss( githubPath_raw + "Tracklist_box_userscripts/" + scriptName + "/script.css?v-" + cacheVersion ); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Right click, copying and text selection * * The site ships a "protection" script (hmwp) that registers contextmenu, copy, cut, paste, * selectstart and drag listeners on document and calls preventDefault() in each of them, plus * a keydown listener that swallows the shortcuts behind them (cmd/ctrl+C, cmd/ctrl+S, * cmd/ctrl+U, F12 and the devtools combos). Left alone, that makes the tracklist box below * pointless - a box you can neither select in nor copy out of. * * Those listeners cannot be removed (anonymous functions in the page's own world), but they * can be starved: a CAPTURING listener on window runs before anything registered on document, * and stopImmediatePropagation() there means the event never reaches theirs. The browser then * does its default - opens the menu, copies the selection - because nobody called * preventDefault(). Nothing of ours listens to these event types (the box binds input, keyup, * click and blur on its textarea), so nothing of ours is starved along with them. * * keydown is the exception: starving every keydown would take the site's own keyboard handling * down too, so only the combinations the site swallows are cut off. Every other key passes * through untouched. * * The "user-select: none" the site puts on every element is CSS and is answered in script.css. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ var tuFreedEvents = [ "contextmenu", "copy", "cut", "paste", "selectstart", "dragstart" ]; tuFreedEvents.forEach(function( type ) { window.addEventListener( type, function( e ) { e.stopImmediatePropagation(); }, true ); }); window.addEventListener( "keydown", function( e ) { var mod = e.ctrlKey || e.metaKey, key = e.key ? e.key.toUpperCase() : ""; if( e.key === "F12" || ( mod && e.shiftKey && ( key === "C" || key === "I" || key === "J" || key === "K" ) ) || ( mod && !e.shiftKey && ( key === "C" || key === "S" || key === "U" ) ) ) { e.stopImmediatePropagation(); } }, true ); log( "Right click, copying and text selection re-enabled (" + tuFreedEvents.join(", ") + " + keydown combos)" ); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Tracklist box * * A mix page that publishes its tracklist does so as plain post content: a heading ending in * "Tracklist" ("Ilona Maras True Techno 105 Tracklist"), then ONE paragraph with one track per * line, separated by
, numbered by hand: * * 1. Ben Sims – Snapshot 99 (ANNE Remix V1) [Hardgroove]
* 2. Sola Contagio – Mutatio 02 (Original Mix) [Ketra Records]
* * Only a few episodes carry one at all (True Techno 105 was the first at the time of writing), * so on most pages there is simply nothing to find - which is logged, not an error. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ // tuTextLines // The lines of a block the way the reader sees them: a
ends a line, a list item or a // nested paragraph is a line of its own, everything else just contributes its text. Walks the // nodes instead of re-parsing innerHTML through jQuery, which would EVALUATE a stray