534 lines
23 KiB
JavaScript
534 lines
23 KiB
JavaScript
|
// Global socketio variable for socketio operations
|
|||
|
var socket = "";
|
|||
|
// Notification count
|
|||
|
var notifications = 0;
|
|||
|
// Web page title, used to help with notifications
|
|||
|
var title = "yt-dlp web ui";
|
|||
|
// Global videojs player variable for videojs operations
|
|||
|
var player = "";
|
|||
|
// Sources for video being clipped
|
|||
|
var vsources = "";
|
|||
|
// All the easy id3 fields to add to the easy id3 form
|
|||
|
var eid3fields = ["album", "bpm", "compilation", "composer", "encodedby", "lyricist", "length", "media", "mood", "title", "version", "artist", "albumartist", "conductor", "arranger", "discnumber", "tracknumber", "author", "albumartistsort", "albumsort", "composersort", "artistsort", "titlesort", "language", "genre", "date", "performer", "asin", "performer", "catalognumber"];
|
|||
|
// Info for current video
|
|||
|
var ginfo = [];
|
|||
|
// Limits of the server
|
|||
|
var glimits = [];
|
|||
|
// Predefined themes
|
|||
|
var themes = {
|
|||
|
"white": {
|
|||
|
"bgcolor": "#ffffff",
|
|||
|
"textcolor": "#000000"
|
|||
|
},
|
|||
|
"yellow": {
|
|||
|
"bgcolor": "#ffff00",
|
|||
|
"textcolor": "#000000"
|
|||
|
},
|
|||
|
"purple": {
|
|||
|
"bgcolor": "#b642f5",
|
|||
|
"textcolor": "#ffffff"
|
|||
|
},
|
|||
|
"grayblue": {
|
|||
|
"bgcolor": "#636987",
|
|||
|
"textcolor": "#ffffff"
|
|||
|
},
|
|||
|
"sky": {
|
|||
|
"bgcolor": "#aaaaff",
|
|||
|
"textcolor": "#ffffff"
|
|||
|
},
|
|||
|
"black": {
|
|||
|
"bgcolor": "#000000",
|
|||
|
"textcolor": "#ffffff"
|
|||
|
}
|
|||
|
};
|
|||
|
// Do when the document has loaded
|
|||
|
document.addEventListener("DOMContentLoaded", function () {
|
|||
|
// Check url for youtube link and if there's a youtube link prefill the Video/Playlist URL field with it
|
|||
|
var here = window.location.href;
|
|||
|
if (here.includes("?h")) {
|
|||
|
document.querySelector("#url").value = "https://youtube.com/watch?v=" + here.split("?v=")[1];
|
|||
|
}
|
|||
|
// Load theme from last theme settings
|
|||
|
// If a gradient is set up load the gradient
|
|||
|
if (document.querySelector("#gradienttop").value != "none"){
|
|||
|
if (document.querySelector("#gradientbottom").value != "none"){
|
|||
|
setTheme(true);
|
|||
|
} else {
|
|||
|
setTheme();
|
|||
|
}
|
|||
|
} else {
|
|||
|
setTheme();
|
|||
|
}
|
|||
|
// Connect to the socketio server
|
|||
|
socket = io("localhost:8888");
|
|||
|
// Emit signal to query the limits of the server
|
|||
|
socket.emit("limits", {});
|
|||
|
// Preset the step variable to 1, this makes sure step based functionality won't break
|
|||
|
document.querySelector("#step").value = 1;
|
|||
|
// Hide all warnings, in some cases this is unncessary, but better safe than sorry
|
|||
|
document.querySelector("#clipperWarning").style.display = "none";
|
|||
|
document.querySelector("#clipperWarning2").style.display = "none";
|
|||
|
document.querySelector("#clipperWarning3").style.display = "none";
|
|||
|
document.querySelector("#clipperWarning4").style.display = "none";
|
|||
|
document.querySelector('#urlWarning').style.display = 'none';
|
|||
|
// Populate easy id3 configuration form with fields from the eid3fields variable
|
|||
|
for (it in eid3fields) {
|
|||
|
document.querySelector("#id3").innerHTML += '<label for="' + eid3fields[it] + '">' + eid3fields[it] + '</label>';
|
|||
|
document.querySelector("#id3").innerHTML += '<input type="text" id="' + eid3fields[it] + '" />';
|
|||
|
}
|
|||
|
// Adds submit button to easy id3 configuration form
|
|||
|
document.querySelector("#id3").innerHTML += '<br><button onclick="return id3submit()">Submit</button><br>';
|
|||
|
// All emissions return here
|
|||
|
socket.on("done", (data) => {
|
|||
|
// If necessary add a notification to the tab of the site
|
|||
|
notify();
|
|||
|
// If there's an error in the result just make the row with the given data, logic for what to actually show is in makeRow
|
|||
|
if (data["error"]) {
|
|||
|
var tr = makeRow(data);
|
|||
|
document.querySelector("#results").prepend(tr);
|
|||
|
} else if (data["method"] == "limits") {
|
|||
|
// For the limits method we don't want to show snything in the results table, but the limits table at the top of the page
|
|||
|
var limits = data["limits"];
|
|||
|
glimits = limits;
|
|||
|
// Populate the limits table
|
|||
|
for (it in limits) {
|
|||
|
document.querySelector("#" + limits[it]["limitid"]).innerHTML = limits[it]["limitvalue"];
|
|||
|
}
|
|||
|
} else {
|
|||
|
// Get method
|
|||
|
var method = data["method"];
|
|||
|
// If done with step 1 of subtitles increment to step 2, otherwise reset to step 1
|
|||
|
if (method == "subtitles") {
|
|||
|
if ("step" in data) {
|
|||
|
document.querySelector("#step").value = 2;
|
|||
|
} else {
|
|||
|
document.querySelector("#step").value = 1;
|
|||
|
}
|
|||
|
}
|
|||
|
// When done with clipping reset to step 1
|
|||
|
if (method == "clip") {
|
|||
|
document.querySelector("#step").value = 1;
|
|||
|
}
|
|||
|
// When done wih getting streams set global video info
|
|||
|
if (method == "streams") {
|
|||
|
ginfo = data["info"];
|
|||
|
}
|
|||
|
// When done with step 1 of clipping (getting the tracks), populate the sources for the videojs player and reload it
|
|||
|
if (method == "getClipperTracks") {
|
|||
|
var sources = [];
|
|||
|
for (i in data["info"]["formats"]) {
|
|||
|
var cob = data["info"]["formats"][i];
|
|||
|
if (cob["vcodec"] != "none") {
|
|||
|
var codec = cob["video_ext"];
|
|||
|
sources.push({
|
|||
|
"type": "video/" + codec,
|
|||
|
"src": cob["url"],
|
|||
|
"resolution": cob["resolution"],
|
|||
|
"format_id": cob["format_id"]
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
loadVideo(sources);
|
|||
|
} else {
|
|||
|
// For any other case just make a row with the given data
|
|||
|
var tr = makeRow(data);
|
|||
|
document.querySelector("#results").prepend(tr);
|
|||
|
}
|
|||
|
}
|
|||
|
// Remove the loading spinner if it exists
|
|||
|
if (data["method"] != "limits") {
|
|||
|
document.querySelector("[id='" + data["spinnerid"] + "']").remove();
|
|||
|
}
|
|||
|
});
|
|||
|
// When the tab is viewed the notifications are reset to 0 and the notifications are removed from the tab title
|
|||
|
document.addEventListener("visibilitychange", () => {
|
|||
|
if (!document.hidden) {
|
|||
|
document.title = title;
|
|||
|
notifications = 0;
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
// When downloading a stream from the select the Get all download links for video method provides just open the link in a new tab
|
|||
|
function dlStream(selectID) {
|
|||
|
var index = document.getElementById(selectID).value;
|
|||
|
window.open(ginfo["formats"][Number(index)]["url"], "_blank");
|
|||
|
}
|
|||
|
|
|||
|
// Method for showing notifications in title
|
|||
|
function notify() {
|
|||
|
notifications += 1;
|
|||
|
document.title = "(" + notifications.toString() + ") " + title;
|
|||
|
}
|
|||
|
|
|||
|
// Very important, the logic here decides what is returned to the user via the results tables
|
|||
|
function makeRow(data) {
|
|||
|
// Create a table row element
|
|||
|
var tr = document.createElement("tr");
|
|||
|
// Create variable to store generated html for later adding to the tr element
|
|||
|
var lhtml = "";
|
|||
|
// Columns of the table
|
|||
|
const cols = ["status", "title", "link", "details"];
|
|||
|
// Iterate through each column of the table and construct and add the html for each column
|
|||
|
for (col in cols) {
|
|||
|
col = cols[col];
|
|||
|
// If the column is in the data or it's status then...
|
|||
|
if ((col in data) || (col == "status")) {
|
|||
|
// Starts the column html
|
|||
|
lhtml += "<td>";
|
|||
|
// If the column is status, display either an error indicator or a success indicator
|
|||
|
if (col == "status") {
|
|||
|
if (data["error"]) {
|
|||
|
lhtml += "<button class='button error'>Error</button>";
|
|||
|
} else {
|
|||
|
lhtml += "<button class='button success'>Success</button></td>";
|
|||
|
}
|
|||
|
} else if (col == "link") {
|
|||
|
// If the column is link then wrap the link in the requisite html to be clickable
|
|||
|
lhtml += "<a href='" + data[col] + "'>" + data[col] + "</a>";
|
|||
|
} else if (col == "details") {
|
|||
|
// If the column is details first display whatever data has been set for this column
|
|||
|
lhtml += "<p>" + data[col] + "</p>";
|
|||
|
// If select is in our data keys...
|
|||
|
if ("select" in data) {
|
|||
|
// Generate specially formatted selects based on whether this is for streams or another method (subtitles)
|
|||
|
if (data["method"] == "streams") {
|
|||
|
// Used to differentiate selects when there are multiple
|
|||
|
var iid = crypto.randomUUID();
|
|||
|
lhtml += "<br><select id='" + iid + "'>";
|
|||
|
for (it in ginfo["formats"]) {
|
|||
|
lhtml += "<option value='" + it.toString() + "'>" + ginfo["formats"][it]["format"] + " / " + ginfo["formats"][it]["ext"] + " / audio: " + ginfo["formats"][it]["audio_ext"] + "</option>";
|
|||
|
}
|
|||
|
lhtml += "</select>";
|
|||
|
lhtml += "<br><button onclick='dlStream(`" + iid + "`)'>Download selected</button>"
|
|||
|
} else {
|
|||
|
var iid = crypto.randomUUID();
|
|||
|
lhtml += "<select data-iid='" + iid + "' id='langSelect'>";
|
|||
|
for (it in data["select"]) {
|
|||
|
lhtml += "<option value='" + data["select"][it] + "'>" + data["select"][it] + "</option>";
|
|||
|
}
|
|||
|
lhtml += "</select>";
|
|||
|
lhtml += '<input id="autoSubs" data-iid="' + iid + '" type="checkbox" name="checkbox" />';
|
|||
|
lhtml += '<label for="autoSubs">Download automatic subs?</label>';
|
|||
|
lhtml += '<br><button data-iid="' + iid + '" onclick="process(this)">Get subtitles</button>';
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
// Anything else just display the data directly
|
|||
|
lhtml += "<p>" + data[col] + "</p>";
|
|||
|
}
|
|||
|
// Close out the column
|
|||
|
lhtml += "</td>";
|
|||
|
} else {
|
|||
|
// Otherwise write the row with nothing in it so all the other rows display properly
|
|||
|
lhtml += "<td></td>"
|
|||
|
}
|
|||
|
}
|
|||
|
// Add html to tr element and return it for display
|
|||
|
tr.innerHTML = lhtml;
|
|||
|
return tr;
|
|||
|
}
|
|||
|
|
|||
|
// Generate a turretcss spinner with a random id (so it can be removed later) and append it to the spinners div (just above the results table)
|
|||
|
function genSpinner() {
|
|||
|
var spinnerid = crypto.randomUUID();
|
|||
|
var sp = document.createElement("button");
|
|||
|
sp.setAttribute("id", spinnerid);
|
|||
|
sp.setAttribute("class", "spinner");
|
|||
|
sp.setAttribute("title", "Loading");
|
|||
|
document.querySelector("#spinners").append(sp);
|
|||
|
return spinnerid;
|
|||
|
}
|
|||
|
|
|||
|
// When an ID3 submission is made we use a submit button, this way we run the code we need and return false, meaning that the form won't actually post
|
|||
|
// Constructs an object with all the id3 form values to use with toMP3, which will in turn set these values to be added to the metadata of the MP3
|
|||
|
function id3submit() {
|
|||
|
var form = document.getElementById('id3');
|
|||
|
var values = {};
|
|||
|
for (var it in form.elements) {
|
|||
|
values[form.elements[it].id] = form.elements[it].value;
|
|||
|
}
|
|||
|
var url = document.querySelector('#url').value;
|
|||
|
toMP3(url, values);
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// Hide/show the ID3 form
|
|||
|
function toggleID3Form() {
|
|||
|
var form = document.querySelector("#id3");
|
|||
|
var button = document.querySelector("#id3toggle");
|
|||
|
if (form.style.display == "none") {
|
|||
|
form.style.display = "block";
|
|||
|
button.textContent = "Hide ID3 form";
|
|||
|
} else {
|
|||
|
form.style.display = "none";
|
|||
|
button.textContent = "Show ID3 form";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Generate spinner and emit toMP3 with the spinnerid (to remove the spinner later) to download mp3 and optionally add metadata to the file
|
|||
|
function toMP3(url, id3data = null) {
|
|||
|
var spinnerid = genSpinner();
|
|||
|
socket.emit("toMP3", { "url": url, "spinnerid": spinnerid, "id3": id3data });
|
|||
|
}
|
|||
|
|
|||
|
// Emit playlist to download a playlist as MP3s and zip them for download
|
|||
|
function playlist(url) {
|
|||
|
var spinnerid = genSpinner();
|
|||
|
socket.emit("playlist", { "url": url, "spinnerid": spinnerid });
|
|||
|
}
|
|||
|
|
|||
|
// Emit either step of subtitles
|
|||
|
// Step 1 will give us a list of the subtitle languages
|
|||
|
// Step 2 will give us a download of the selected subtitle
|
|||
|
function subtitles(url, iid = null) {
|
|||
|
var spinnerid = genSpinner();
|
|||
|
var step = Number(document.querySelector("#step").value);
|
|||
|
var data = { "url": url, "spinnerid": spinnerid, "step": step }
|
|||
|
if (step == 2) {
|
|||
|
data["languageCode"] = document.querySelector('[data-iid="' + iid + '"]#langSelect').value;
|
|||
|
data["autoSub"] = document.querySelector('[data-iid="' + iid + '"]#autoSubs').checked;
|
|||
|
}
|
|||
|
socket.emit("subtitles", data);
|
|||
|
}
|
|||
|
|
|||
|
// Generic getInfo method to get all the info for a given link
|
|||
|
function getInfo(url, method) {
|
|||
|
var spinnerid = genSpinner()
|
|||
|
var data = { "url": url, "spinnerid": spinnerid, "method": method };
|
|||
|
socket.emit("getInfoEvent", data);
|
|||
|
}
|
|||
|
|
|||
|
// Clip method, ultimately produces a clip of a video or a gif of that clip
|
|||
|
function clip(url, directURL = null, gif = null, format_id = null, resolution = null) {
|
|||
|
// Get the start and end times for the clip
|
|||
|
var timeA = document.querySelector("#timeA").value;
|
|||
|
var timeB = document.querySelector("#timeB").value;
|
|||
|
// If there is a set resolution, check to make sure that resolution is not larger then the servers limit
|
|||
|
// Otherwise display an error message
|
|||
|
if (resolution != null) {
|
|||
|
if (resolution > glimits[4]["limitvalue"]) {
|
|||
|
document.querySelector("#clipperWarning4").style.display = "block";
|
|||
|
}
|
|||
|
} else if (gif && ((timeB - timeA) > glimits[3]["limitvalue"])) {
|
|||
|
// If this is a gif check if the length of the clip is longer than the servers limit
|
|||
|
// If so, display an error message
|
|||
|
document.querySelector("#clipperWarning3").style.display = "block";
|
|||
|
} else {
|
|||
|
// If the start of the clip is after the end of the clip display an error message
|
|||
|
if (Number(timeA) > Number(timeB)) {
|
|||
|
document.querySelector("#clipperWarning2").style.display = "block";
|
|||
|
} else {
|
|||
|
var spinnerid = genSpinner();
|
|||
|
// Set up the required values for the clip method and then set up additional values if they are not null
|
|||
|
var data = { "url": url, "spinnerid": spinnerid, "timeA": timeA, "timeB": timeB };
|
|||
|
if (format_id != null) {
|
|||
|
data["format_id"] = format_id
|
|||
|
}
|
|||
|
if (directURL != null) {
|
|||
|
data["directURL"] = directURL
|
|||
|
}
|
|||
|
if (gif != null) {
|
|||
|
data["gif"] = true;
|
|||
|
}
|
|||
|
socket.emit("clip", data);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// This loads a video into videojs with multiple sources and generates a list of options in a select for the user to choose a stream
|
|||
|
// setSource handles actually setting the stream
|
|||
|
function loadVideo(sources) {
|
|||
|
// Clear the video container, to be populated later
|
|||
|
document.querySelector("#videoContainer").innerHTML = "";
|
|||
|
// Create video element to be used to create videojs player
|
|||
|
var video = document.createElement("video");
|
|||
|
// Sources object for videojs initialization
|
|||
|
var sdata = {
|
|||
|
"sources": sources
|
|||
|
};
|
|||
|
// Set global sources for the video
|
|||
|
vsources = sources;
|
|||
|
// Set some basic attributes for the videojs player
|
|||
|
video.setAttribute("id", "clipPlayer");
|
|||
|
video.setAttribute("class", "video-js vjs-default-skin");
|
|||
|
video.setAttribute("controls", "");
|
|||
|
video.setAttribute("width", "720");
|
|||
|
// Add the video to the DOM
|
|||
|
document.querySelector("#videoContainer").append(video);
|
|||
|
// Clear the source selector
|
|||
|
document.querySelector("#srcSelect").innerHTML = "";
|
|||
|
// Populate source selector
|
|||
|
for (it in sources) {
|
|||
|
document.querySelector("#srcSelect").innerHTML += "<option value='" + it + "'>" + sources[it]["resolution"] + " " + sources[it]["type"] + "</option>";
|
|||
|
}
|
|||
|
// If the videojs player object isn't empty then dispose it
|
|||
|
if (player != "") {
|
|||
|
player.dispose();
|
|||
|
}
|
|||
|
// Test if this is doing anything
|
|||
|
//document.querySelector("#clipPlayer").innerHTML = "";
|
|||
|
// Initialize the videojs player
|
|||
|
player = videojs('clipPlayer', sdata, function onPlayerReady() {
|
|||
|
this.on("loadedmetadata", function () {
|
|||
|
// If the video is too long display a warning
|
|||
|
if (this.duration() < glimits[0]["limitvalue"]) {
|
|||
|
document.querySelector("#timeA").max = this.duration();
|
|||
|
document.querySelector("#timeB").max = this.duration();
|
|||
|
document.querySelector("#clipper").style.display = "block";
|
|||
|
} else {
|
|||
|
document.querySelector("#clipper").innerHTML = "";
|
|||
|
document.querySelector("#clipperWarning").style.display = "block";
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
// Set the current source of the videojs player based on the selected source
|
|||
|
function setSource(srcIndex) {
|
|||
|
player.src(vsources[Number(srcIndex)]);
|
|||
|
}
|
|||
|
|
|||
|
// Set the current time of the video player when the time sliders change, format the time display to minutes and seconds
|
|||
|
function updatePlayerTime(range) {
|
|||
|
player.currentTime(range.value);
|
|||
|
if (range.id == "timeA") {
|
|||
|
document.querySelector("#timeStart").value = (Math.floor(range.value / 60)).toString().padStart(2, "0") + ":" + (range.value % 60).toString().padStart(2, "0");
|
|||
|
} else if (range.id == "timeB") {
|
|||
|
document.querySelector("#timeEnd").value = (Math.floor(range.value / 60)).toString().padStart(2, "0") + ":" + (range.value % 60).toString().padStart(2, "0");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Submit the clip to be cut
|
|||
|
function clipSecond() {
|
|||
|
document.querySelector("#step").value = 2;
|
|||
|
process();
|
|||
|
}
|
|||
|
|
|||
|
// Semi generic method tying buttons to emissions
|
|||
|
// This should be reworked and the extraSteps stuff should be removed completely
|
|||
|
// Recommendation: just a set of if elses for the exact method that was passed
|
|||
|
function process(button = null, extraSteps = null) {
|
|||
|
// Get method from method select
|
|||
|
var method = document.querySelector('#method').value;
|
|||
|
// Get the url from the Video/Playlist URL field
|
|||
|
var url = document.querySelector('#url').value;
|
|||
|
// Check if the url is valid
|
|||
|
if (isValidUrl(url)) {
|
|||
|
// Hide warnings until needed
|
|||
|
document.querySelector('#urlWarning').style.display = 'none';
|
|||
|
document.querySelector("#clipperWarning").style.display = "none";
|
|||
|
// Run toMP3 function
|
|||
|
if (method == "toMP3") {
|
|||
|
toMP3(url);
|
|||
|
} else if (method == "playlist") {
|
|||
|
// Run playlist function
|
|||
|
playlist(url);
|
|||
|
} else if (method == "subtitles") {
|
|||
|
// Run subtitles function
|
|||
|
// If a button has been passed (in order to identify the select) pass the iid to subtitles for selected subtitles retrieval
|
|||
|
if (button == null) {
|
|||
|
subtitles(url);
|
|||
|
} else {
|
|||
|
subtitles(url, iid = button.getAttribute('data-iid'));
|
|||
|
}
|
|||
|
} else if (method == "streams") {
|
|||
|
// Run getInfo function with the streams method
|
|||
|
getInfo(url, "streams");
|
|||
|
} else if (method == "clip") {
|
|||
|
// Get the step of clipping we're at
|
|||
|
var step = Number(document.querySelector("#step").value);
|
|||
|
// Add extraSteps if specified
|
|||
|
if (extraSteps != null) {
|
|||
|
extraSteps = Number(extraSteps);
|
|||
|
step += extraSteps;
|
|||
|
}
|
|||
|
// If we're at step 1 get a list of tracks for the video
|
|||
|
if (step == 1) {
|
|||
|
getInfo(url, "getClipperTracks");
|
|||
|
} else if (step == 2) {
|
|||
|
// If we're at step 2 get some info and clip the video
|
|||
|
var format = vsources[Number(document.querySelector("#srcSelect").value)]["format_id"]
|
|||
|
var format_id = format["format_id"];
|
|||
|
var resolution = format["width"]
|
|||
|
if (document.querySelector("#toGif").checked) {
|
|||
|
clip(url, null, true, format_id = format_id, resolution = resolution);
|
|||
|
} else {
|
|||
|
clip(url, null, null, format_id = format_id, resolution = resolution);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
} else {
|
|||
|
// If the url isn't valid display a warning
|
|||
|
document.querySelector('#urlWarning').style.display = 'block';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Checks if an entered url is valid
|
|||
|
// https://www.freecodecamp.org/news/check-if-a-javascript-string-is-a-url/
|
|||
|
const isValidUrl = urlString => {
|
|||
|
try {
|
|||
|
return Boolean(new URL(urlString));
|
|||
|
}
|
|||
|
catch (e) {
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Get a random integer in an inclusive range
|
|||
|
function randomInt(min, max) {
|
|||
|
return Math.floor(Math.random() * (max - min + 1) + min)
|
|||
|
}
|
|||
|
|
|||
|
// Set theme based on the selected theme or custom values
|
|||
|
function setTheme(customgradient=false) {
|
|||
|
var r = document.querySelector(':root');
|
|||
|
var theme = document.querySelector("#theme").value;
|
|||
|
if (theme == "custom") {
|
|||
|
theme = {
|
|||
|
"bgcolor": document.querySelector("#bgcolor").value,
|
|||
|
"textcolor": document.querySelector("#textcolor").value
|
|||
|
}
|
|||
|
if (customgradient){
|
|||
|
// Set the gradient with the custom values from Top/Bottom color of gradient
|
|||
|
theme["gradienttop"] = document.querySelector("#gradienttop").value;
|
|||
|
theme["gradientbottom"] = document.querySelector("#gradientbottom").value;
|
|||
|
} else {
|
|||
|
// Set the gradient to a solid color
|
|||
|
theme["gradienttop"] = theme["bgcolor"];
|
|||
|
theme["gradientbottom"] = theme["bgcolor"];
|
|||
|
}
|
|||
|
} else {
|
|||
|
var themename = theme;
|
|||
|
theme = themes[theme];
|
|||
|
if (themename != "sky") {
|
|||
|
theme["gradienttop"] = theme["bgcolor"];
|
|||
|
theme["gradientbottom"] = theme["bgcolor"];
|
|||
|
} else {
|
|||
|
// Generate two light blues colors in hex format and set the top and bottom of the gradient to these
|
|||
|
var top0 = randomInt(50, 99).toString().padStart(2, "0");
|
|||
|
top0 = "#" + top0 + top0 + "ff";
|
|||
|
var bottom0 = randomInt(50, 99).toString().padStart(2, "0");
|
|||
|
bottom0 = "#" + bottom0 + bottom0 + "ff";
|
|||
|
theme["gradienttop"] = top0;
|
|||
|
theme["gradientbottom"] = bottom0;
|
|||
|
}
|
|||
|
}
|
|||
|
for (it in theme) {
|
|||
|
r.style.setProperty('--' + it, theme[it]);
|
|||
|
}
|
|||
|
var rs = getComputedStyle(r);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Generic toggle method, requires an element with an id and a element that will be used for toggling the visibility of the main element with an id of the main element + _toggle
|
|||
|
function toggle(id) {
|
|||
|
if (document.querySelector(id).style.display == "none") {
|
|||
|
document.querySelector(id).style.display = "block";
|
|||
|
document.querySelector(id + "_toggle").innerHTML = "➖ Hide";
|
|||
|
} else {
|
|||
|
document.querySelector(id).style.display = "none";
|
|||
|
document.querySelector(id + "_toggle").innerHTML = "➕ Show";
|
|||
|
}
|
|||
|
}
|