// ==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