2024-09-03 10:37:30 -04:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta name = "viewport" content = "width=device-width, initial-scale=1" / >
< title > NETV MAM< / title >
< style >
body {
background-color: black;
color: white;
height: 100vh;
width: 100vw;
}
header, footer {
display: flex;
width: 100vw;
}
header {
height: 16vh;
justify-content: space-between;
}
main {
width: 100%;
height: 74vh;
border-top: 3px solid white;
border-bottom: 3px solid white;
}
footer {
height: 2vh;
}
#cue_video, #live_video {
width: 100% !important;
height: auto !important;
}
#tabs {
width: 100%;
}
#tabs ul {
list-style: none;
padding: 0;
margin: 0;
}
#tabs li {
display: inline-block;
border: solid;
border-width: 1px 1px 0 1px;
border-color: white;
margin: 0 5px 0 0;
background-color: white;
color: black;
}
#tabs li a {
padding: 0 10px;
color: black;
text-decoration: none;
}
#tab_content {
display: flex;
border-top: 1px solid white;
width: 100%;
}
.active_tab {
padding-bottom: 1px;
background-color: green;
}
#upload_target {
position: absolute;
bottom: 3vh;
left: 0;
width: 25vw;
height: 10vh;
border: 1px solid white;
}
2024-09-03 15:39:18 -04:00
.readyToDrop {
background-color: green;
}
2024-09-03 10:37:30 -04:00
::backdrop {
background-image: linear-gradient(
45deg,
magenta,
rebeccapurple,
dodgerblue,
green
);
opacity: 0.75;
}
#media_library_contents {
width: 25vw;
height: 52vh;
overflow-y: scroll;
}
#media_item_details {
position: absolute;
right: 0;
bottom: 3vh;
width: 70vw;
height: 45vh;
border: 1px solid white;
display: flex;
}
#media_item_preview {
width: 720px;
height: 480px;
}
#media_item_form {
float: right;
}
.media_item_list_element {
width: 24vw;
border: 1px solid white;
}
#media_item_editor_tags {
width: 30vw;
}
.tag {
display: inline-block;
border: 1px solid white;
padding: 2px;
margin: 4px;
}
.tag.enabled {
background-color: green;
}
.tag.disabled {
background-color: red;
}
#media_item_edl_list {
margin: 0;
}
#media_item_edl_list label {
display: inline-block;
width: 5vw;
}
#media_item_edl_list_container {
display: inline-flex;
align-items: flex-start;
}
#media_item_edl_list_add_insert_button {
width: 3vw;
}
< / style >
< / head >
< body >
< header >
< section id = "infobox" >
< h1 > NETV MAM< / h1 >
< p id = "current_date" > < / p >
< p id = "current_time" > < / p >
< / section >
<!-- we don't need these yet
< section id = "cue_deck" >
< video id = "cue_video" controls > < / video >
< p > Preview Deck< / p >
< / section >
< section id = "live_deck" >
< video id = "live_video" controls = "off" > < / video >
< p > Live on NETV< / p >
< / section >
-->
< / header >
< main >
< nav id = "tabs" >
< ul >
< li class = "active_tab" > < a href = "#" id = "library_tab" > Media Library< / a > < / li >
< li > < a href = "#" id = "tags_tab" > Tag Editor< / a > < / li >
< li > < a href = "#" id = "schedules_tab" > Schedules< / a > < / li >
< li > < a href = "#" id = "epg_tab" > EPG< / a > < / li >
< / ul >
< / nav >
< section id = "tab_content" >
< / section >
< / main >
< footer >
< section id = "colophon" >
Copyright © 2024 Mountaintown Technology - Version 0.8.0
< / section >
< / footer >
< template id = "media_library_template" >
< section id = "media_library_browser" >
< h2 > Media Files< / h2 >
< section id = "upload_target" > < p > Drag files here to upload< / p > < / section >
< p > Filter: < input type = "text" id = "media_library_filter" / > < / p >
< section id = "media_library_contents" >
< / section >
< dialog id = "media_details_dialog" >
< form id = "media_details_dialog_form" method = "dialog" / >
< h2 id = "media_details_dialog_header" > Metadata< / h2 >
< p > < label for = "media_details_dialog_id" > ID: < / label > < input name = "id" id = "media_details_dialog_id" type = "text" disabled / > < / p >
< p > < label for = "media_details_dialog_title" > Title: < / label > < input name = "title" id = "media_details_dialog_title" type = "text" / > < / p >
< p > < label for = "media_details_dialog_description" > Description: < / label > < input name = "description" id = "media_details_dialog_description" type = "text" / > < / p >
< p > < label for = "media_details_dialog_season" > Season number: < / label > < input name = "season" id = "media_details_dialog_season" type = "number" / > < / p >
< p > < label for = "media_details_dialog_episode_number" > Episode number: < / label > < input name = "episode_number" id = "media_details_dialog_episode_number" type = "number" / > < / p >
< p > Duration: < span id = "media_details_dialog_duration_secs" > < / span > < / p >
< button id = "media_details_dialog_save_button" value = "save" > Save Metadata< / button > < button id = "media_details_dialog_close_button" value = "close" > Close Without Saving< / button >
< / form >
< / dialog >
< / section >
< section id = "media_library_details" >
< / section >
< / template >
< template id = "media_item_template" >
< div class = "media_item_list_element" >
< img class = "media_item_thumbnail" >
< strong class = "media_item_title" > < / strong >
< p class = "media_item_info" > < / p >
< / template >
< template id = "tag_editor_template" >
< section id = "tag_editor" >
< h2 > Tags< / h2 >
< p > Current tags:< / p >
< p class = "tags_list" > < / p >
< form id = "add_tag_form" action = "tags.php" method = "POST" >
< p > < label for = "new_tag" > New tag: < / label > < input type = "text" name = "new_tag" id = "new_tag_input" / > < / p >
< button id = "save_tag_button" value = "save" > Save Tag< / button >
< / form >
< / section >
< / template >
< template id = "media_item_editor" >
< div class = "media_item_details" >
< video id = "media_item_preview" controls > < / video >
< div id = "media_item_form" >
< form id = "media_item_form_data" >
< p > < label for = "id" > ID: < / label > < input type = "text" name = "id" disabled / > < / p >
< p > < label for = "title" > Title: < / label > < input type = "text" name = "title" / > < / p >
< p > < label for = "description" > Description: < / label > < input type = "text" name = "description" / > < / p >
< p > < label for = "season" > Season number: < / label > < input type = "number" name = "season" / > < / p >
< p > < label for = "episode_number" > Episode number: < / label > < input type = "number" name = "episode_number" / > < / p >
< p > Tags:< / p >
< div id = "media_item_editor_tags" > < / div >
< button id = "media_item_editor_save_button" value = "save" > Save Metadata< / button > < button id = "media_item_editor_cancel_button" value = "cancel" > Cancel< / button >
< / form >
< / div >
< / div >
< div id = "media_item_edl" >
< h2 > EDL< / h2 >
< div id = "media_item_edl_list_container" >
< div id = "media_item_edl_add_insert_container" >
< input type = "button" id = "media_item_edl_add_insert_button" name = "media_item_edl_add_insert_button" value = "Add Insert Point" > < / button >
< / div >
< ul id = "media_item_edl_list" >
< li id = "media_item_edl_inpoint_listitem" > < label for = "media_item_edl_inpoint" > In: < / label > < input type = "text" name = "media_item_edl_inpoint" value = "00:00:00" / > < / li >
< li id = "media_item_edl_outpoint_listitem" > < label for = "media_item_edl_outpoint" > Out: < / label > < input type = "text" name = "media_item_edl_outpoint" value = "" / > < / li >
< / ul >
< / div >
< button id = "media_item_edl_save_button" value = "Save EDL" > Save EDL< / button >
< / div >
< / template >
< template id = "media_item_edl_insert_listitem" >
< li class = "media_item_edl_insert" >
< label for = "media_item_edl_insert" > Insert: < / label >
< input type = "text" name = "media_item_edl_insert" / >
< button class = "media_item_edl_remove_insert" > X< / button >
< button class = "media_item_edl_jump_insert" > -> < / button >
< / li >
< / template >
2024-09-03 15:39:18 -04:00
< template id = "schedules_template" >
2024-09-03 16:43:10 -04:00
< section id = "schedule_pane" >
< h2 > Schedules< / h2 >
< section id = "schedules_time_range_section" >
2024-09-09 10:02:10 -04:00
< button id = "previous_day_button" > < < / button >
< span id = "current_day_indicator" > < / span >
< button id = "next_day_button" > > < / button >
2024-09-03 16:43:10 -04:00
< / section >
< table id = "schedule_grid" >
< / table >
2024-09-03 15:39:18 -04:00
< / section >
2024-09-03 16:26:01 -04:00
< / template >
< template id = "schedule_header_row_template" >
< th >
< td > < / td >
< td id = "header_1" > < / td >
< td id = "header_2" > < / td >
< td id = "header_3" > < / td >
< td id = "header_4" > < / td >
< td id = "header_5" > < / td >
< td id = "header_6" > < / td >
< td id = "header_0" > < / td >
< / th >
2024-09-03 15:39:18 -04:00
< / template >
2024-09-03 10:37:30 -04:00
< script >
const init_library = () => {
2024-09-03 11:43:42 -04:00
// set up handler for library filtering
const filter_box = document.querySelector("#media_library_filter");
filter_box.addEventListener("keypress", (e) => {
if (e.key === 'Enter') {
fetch_filtered_media_items();
}
});
2024-09-03 10:37:30 -04:00
// set up drag'n'drop file upload handling
const upload_target = document.getElementById("upload_target");
upload_target.addEventListener("dragenter", (event) => {
event.target.classList.add("readyToDrop");
});
upload_target.addEventListener("dragleave", (event) => {
event.target.classList.remove("readyToDrop");
});
upload_target.addEventListener("dragover", (event) => {
event.preventDefault();
event.stopPropagation();
});
upload_target.addEventListener("drop", (event) => {
console.log("Objects dropped on upload target.");
event.preventDefault();
event.target.classList.remove("readyToDrop");
if (event.dataTransfer.items) {
console.log(event.dataTransfer.items.length + " items dropped.");
[...event.dataTransfer.items].forEach((item, i) => {
// If dropped items aren't files, reject them
if (item.kind === "file") {
const file = item.getAsFile();
console.log(`… file[${i}].name = ${file.name}`);
const uploadURL = "upload.php";
let data = new FormData();
data.append('file', file);
fetch(uploadURL, {
method: "POST",
body: data
})
.then((response) => {
if (response.ok) {
response.json().then((json) => {
// show dialog to get media metadata for new upload
console.log("Upload response: ");
console.dir(json);
const metadata_dialog = document.getElementById("media_details_dialog");
const metadata_header = document.getElementById("media_details_dialog_header");
metadata_header.innerHTML = "Metadata for " + json.source_path;
const metadata_title = document.getElementById("media_details_dialog_title");
metadata_title.value = json.title === "unknown" ? json.source_path : json.title;
const dur_span = document.getElementById("media_details_dialog_duration_secs");
dur_span.innerHTML = new Date(json.duration_secs * 1000).toISOString().substring(11, 19);
const id_input = document.getElementById("media_details_dialog_id");
id_input.value = json.id;
const metadata_close_button = document.getElementById("media_details_dialog_close_button");
metadata_close_button.addEventListener("click", (event) => {
event.preventDefault();
metadata_dialog.close();
});
const metadata_save_button = document.getElementById("media_details_dialog_save_button");
metadata_save_button.addEventListener("click", (event) => {
event.preventDefault();
const updateURL = "update.php?id=" + json.id;
let data = new FormData(document.getElementById("media_details_dialog_form"));
console.dir(data);
fetch(updateURL, {
method: "POST",
body: data
})
.then((response) => {
if (response.ok) {
response.json().then((json) => {
metadata_dialog.close();
fetch_media_items();
});
}
});
});
metadata_dialog.showModal();
});
}
})
.catch(() => {});
}
});
} else {
// Use DataTransfer interface to access the file(s)
[...event.dataTransfer.files].forEach((file, i) => {
console.log(`… file[${i}].name = ${file.name}`);
});
}
});
}
const fetch_media_items = () => {
const api_url = "media.php";
fetch(api_url).then((response) => {
if (response.ok) {
2024-09-03 11:34:40 -04:00
response.json().then(populate_media_library);
2024-09-03 10:37:30 -04:00
}
});
}
2024-09-03 11:34:40 -04:00
const fetch_filtered_media_items = () => {
const filter_term = document.querySelector("#media_library_filter").value;
const api_url = "media.php?filter=" + filter_term;
fetch(api_url).then((response) => {
if (response.ok) {
response.json().then(populate_media_library);
}
});
}
2024-09-03 12:09:42 -04:00
const secs_to_human_duration = (secs) => {
// date and time munging using substring? it's more likely than you think
return new Date(secs * 1000).toISOString().substring(11, 19);
}
2024-09-03 11:34:40 -04:00
const populate_media_library = (rows) => {
const media_library_target = document.querySelector("#media_library_contents");
media_library_target.innerHTML = "";
rows.forEach((row) => {
2024-09-03 12:09:42 -04:00
const duration = secs_to_human_duration(row['duration_secs']);
2024-09-03 11:34:40 -04:00
const template = document.getElementById("media_item_template");
const template_clone = template.content.firstElementChild.cloneNode(true);
template_clone.querySelector(".media_item_title").innerHTML = row['title'];
template_clone.querySelector(".media_item_info").innerHTML = `< p > ${row['description']}< / p > < p > Season ${row['season']} Episode ${row['episode_number']} - (Duration ${duration})< / p > `;
const thisRow = media_library_target.appendChild(template_clone);
thisRow.setAttribute("data-id", row['id']);
thisRow.setAttribute("data-title", row['title']);
thisRow.setAttribute("data-description", row['description']);
thisRow.setAttribute("data-season", row['season']);
thisRow.setAttribute("data-episode-number", row['episode_number']);
thisRow.setAttribute("data-duration", row['duration_secs']);
});
const nodes = document.querySelectorAll(".media_item_list_element");
nodes.forEach((node) => {
node.addEventListener("click", (event) => {
// open media item details editing pane
const media_item = event.target.classList.contains(".media_item_list_element") ? event.target : event.target.closest(".media_item_list_element");
edit_media_item(media_item);
});
});
}
2024-09-03 10:37:30 -04:00
const edit_media_item = (media_item_element) => {
const template = document.getElementById("media_item_editor");
const template_clone = template.content.cloneNode(true);
const target = document.querySelector("#media_library_details");
const item_id = media_item_element.getAttribute("data-id");
template_clone.querySelector('#media_item_preview').setAttribute('src', 'play.php?id=' + item_id);
template_clone.querySelector('input[name="id"]').value = item_id;
template_clone.querySelector('input[name="title"]').value = media_item_element.getAttribute("data-title");
template_clone.querySelector('input[name="description"]').value = media_item_element.getAttribute("data-description");
template_clone.querySelector('input[name="season"]').value = media_item_element.getAttribute("data-season");
template_clone.querySelector('input[name="episode_number"]').value = media_item_element.getAttribute("data-episode-number");
const formatted_duration = new Date(media_item_element.getAttribute("data-duration") * 1000).toISOString().substring(11, 19);
template_clone.querySelector('input[name="media_item_edl_outpoint"]').value = formatted_duration;
template_clone.firstElementChild.setAttribute("data-duration", media_item_element.getAttribute("data-duration"));
target.innerHTML = "";
target.appendChild(template_clone);
fetch_item_tags(item_id);
const save_button = document.getElementById("media_item_editor_save_button");
save_button.addEventListener("click", (e) => {
e.preventDefault();
const api_url = "update.php?id=" + item_id;
const item_data = new FormData(document.getElementById("media_item_form_data"));
fetch(api_url, {
method: "POST",
body: item_data
})
.then((response) => {
if (response.ok) {
response.json().then((json) => {
fetch_media_items();
});
}
})
});
const edl_insert_button = document.getElementById("media_item_edl_add_insert_button");
edl_insert_button.addEventListener("click", (e) => {
e.preventDefault();
const current_timecode = document.getElementById("media_item_preview").currentTime;
const template = document.getElementById("media_item_edl_insert_listitem");
const template_clone = template.content.cloneNode(true);
2024-09-03 12:09:42 -04:00
const formatted_timestamp = secs_to_human_duration(current_timecode);
2024-09-03 10:37:30 -04:00
template_clone.querySelector('input[name="media_item_edl_insert"]').value = formatted_timestamp;
template_clone.firstElementChild.setAttribute('data-timestamp', current_timecode);
const target = document.querySelector("#media_item_edl_outpoint_listitem");
const parent = document.querySelector("#media_item_edl_list");
parent.insertBefore(template_clone, target);
sort_inserts();
const remove_buttons = document.querySelectorAll("button.media_item_edl_remove_insert");
remove_buttons.forEach((btn) => {
btn.addEventListener("click", (e) => {
e.preventDefault();
e.target.parentNode.remove();
});
});
const jump_buttons = document.querySelectorAll("button.media_item_edl_jump_insert");
jump_buttons.forEach((btn) => {
btn.removeEventListener("click", jumpHandler);
btn.addEventListener("click", jumpHandler);
});
});
}
const jumpHandler = (e) => {
e.preventDefault();
const player = document.getElementById("media_item_preview");
if (!player.paused) player.pause();
player.currentTime = e.target.parentNode.getAttribute("data-timestamp");
}
const sort_inserts = () => {
const list_parent = document.querySelector("#media_item_edl_list");
let new_parent = list_parent.cloneNode(false);
const current_items = list_parent.querySelectorAll("li");
let items = [];
current_items.forEach((item) => {
items.push(item);
});
items.sort((a, b) => {
return a.querySelector("input[type='text']").value > b.querySelector("input[type='text']").value
});
items.forEach((item) => {
new_parent.appendChild(item);
});
list_parent.parentNode.replaceChild(new_parent, list_parent);
}
const fetch_tags = async () => {
const api_url = "tags.php";
fetch(api_url).then((response) => {
if (response.ok) {
response.json().then((tags) => {
const container = document.querySelector(".tags_list");
container.innerHTML = "";
for (var i = 0; i < tags.length ; i + + ) {
container.innerHTML += "< span class = 'tag' data-id = " + tags[i]['id'] + " > " + tags[i]['tag'] + "< / span > ";
}
tag_editor_handlers();
});
} else {
console.log('err fetching tags:');
console.dir(response);
}
});
}
const fetch_item_tags = async (media_id) => {
const api_url = "tags.php?media_id=" + media_id;
fetch(api_url).then((response) => {
if (response.ok) {
response.json().then((tags) => {
const container = document.querySelector("#media_item_editor_tags");
container.innerHTML = "";
for (var i = 0; i < tags.length ; i + + ) {
const enabled_class = tags[i]['enabled'] === 'true' ? 'enabled' : 'disabled';
const new_span = `< span class = 'tag ${enabled_class}' data-id = '${tags[i][' id ' ] } ' data-media-id = ${media_id} > ${tags[i]['tag']}< / span > `;
container.innerHTML += new_span;
}
item_tag_editor_handlers();
});
}
});
}
// tab handlers
document.getElementById("library_tab").addEventListener("click", (e) => {
setActiveTab(e);
const tab_content = document.getElementById("tab_content");
tab_content.innerHTML = "";
const template = document.getElementById("media_library_template");
const template_clone = template.content.cloneNode(true);
tab_content.appendChild(template_clone);
// fetch media and populate
fetch_media_items();
// attach event handlers
init_library();
});
document.getElementById("tags_tab").addEventListener("click", (e) => {
setActiveTab(e);
const tab_content = document.getElementById("tab_content");
tab_content.innerHTML = "";
const template = document.getElementById("tag_editor_template");
const template_clone = template.content.cloneNode(true);
tab_content.appendChild(template_clone);
fetch_tags();
});
2024-09-03 15:39:18 -04:00
document.getElementById("schedules_tab").addEventListener("click", (e) => {
setActiveTab(e);
const tab_content = document.querySelector("#tab_content");
tab_content.innerHTML = "";
const template = document.querySelector("#schedules_template");
2024-09-03 16:44:46 -04:00
const template_clone = template.content.cloneNode(true);
2024-09-03 15:39:18 -04:00
tab_content.appendChild(template_clone);
2024-09-03 16:46:40 -04:00
init_schedules();
2024-09-03 15:39:18 -04:00
});
2024-09-09 10:02:10 -04:00
const init_schedules = (day) => {
2024-09-09 11:51:24 -04:00
const day_of_schedule = day ? new Date(Date.parse(day)) : new Date();
2024-09-09 11:50:16 -04:00
console.dir(day);
2024-09-09 11:21:42 -04:00
console.dir(day_of_schedule);
2024-09-03 16:26:01 -04:00
for (let i = 0; i < 8 ; i + + ) {
if (i === 0) {
// first column of schedule, set up the grid
2024-09-09 10:02:10 -04:00
const current_day_indicator = document.querySelector("#current_day_indicator");
2024-09-09 10:05:19 -04:00
current_day_indicator.innerHTML = `Schedule for < strong > ${day_of_schedule.toDateString()}< / strong > `;
2024-09-09 11:48:50 -04:00
current_day_indicator.setAttribute("data-day", day_of_schedule.toISOString());
2024-09-03 16:26:01 -04:00
// set up previous/next buttons
2024-09-09 10:02:10 -04:00
document.querySelector('#previous_day_button').addEventListener("click", previous_day_schedule);
document.querySelector('#next_day_button').addEventListener("click", next_day_schedule);
2024-09-03 16:26:01 -04:00
// init grid structure itself
2024-09-03 16:32:55 -04:00
const template = document.querySelector("#schedule_header_row_template");
2024-09-03 16:26:01 -04:00
const template_clone = template.content.cloneNode(true);
2024-09-03 16:28:43 -04:00
const grid = document.querySelector("#schedule_grid");
2024-09-03 16:26:01 -04:00
grid.appendChild(template_clone);
}
}
}
2024-09-09 10:02:10 -04:00
const previous_day_schedule = (e) => {
2024-09-03 16:26:01 -04:00
e.preventDefault();
2024-09-09 11:59:44 -04:00
const day = Date.parse(document.querySelector("#current_day_indicator").getAttribute("data-day"));
2024-09-09 11:53:58 -04:00
console.dir(day);
2024-09-09 11:48:50 -04:00
day.setDate(day.getDate() - 1);
2024-09-09 10:28:55 -04:00
init_schedules(day);
2024-09-03 16:26:01 -04:00
}
2024-09-09 10:02:10 -04:00
const next_day_schedule = (e) => {
2024-09-03 16:26:01 -04:00
e.preventDefault();
2024-09-09 11:56:21 -04:00
console.dir(e.target);
2024-09-09 12:01:02 -04:00
const day = new Date(Date.parse(document.querySelector("#current_day_indicator").getAttribute("data-day")));
2024-09-09 11:53:58 -04:00
console.dir(day);
2024-09-09 11:48:50 -04:00
day.setDate(day.getDate() + 1);
2024-09-09 10:28:55 -04:00
init_schedules(day);
2024-09-03 15:39:18 -04:00
}
2024-09-03 10:37:30 -04:00
const tag_editor_handlers = () => {
document.querySelector("#save_tag_button").addEventListener("click", (e) => {
e.preventDefault();
const api_url = "tags.php";
fetch(api_url, {
method: "POST",
body: new FormData(document.getElementById("add_tag_form"))
})
.then((response) => {
if (response.ok) {
response.json().then((row) => {
document.querySelector("#new_tag_input").value = "";
fetch_tags();
});
}
});
});
}
const item_tag_editor_handlers = () => {
const enabled_tags = document.querySelectorAll(".tag.enabled");
const disabled_tags = document.querySelectorAll(".tag.disabled");
enabled_tags.forEach((tag) => {
tag.addEventListener("click", (e) => {
e.preventDefault();
const tag_id = e.target.getAttribute("data-id");
const media_id = e.target.getAttribute("data-media-id");
const api_url = "tags.php?enable=false& media_id=" + media_id + "& tag_id=" + tag_id;
fetch(api_url).then((response) => {
if (response.ok) {
response.json().then((json) => {
if (json.ok) {
fetch_item_tags(media_id);
} else {
console.dir(json);
}
});
}
});
});
});
disabled_tags.forEach((tag) => {
tag.addEventListener("click", (e) => {
e.preventDefault();
const tag_id = e.target.getAttribute("data-id");
const media_id = e.target.getAttribute("data-media-id");
const api_url = "tags.php?enable=true& media_id=" + media_id + "& tag_id=" + tag_id;
fetch(api_url).then((response) => {
if (response.ok) {
response.json().then((json) => {
if (json.ok) {
fetch_item_tags(media_id);
} else {
console.dir(json);
}
});
}
});
});
});
}
document.getElementById("epg_tab").addEventListener("click", (e) => {
setActiveTab(e);
document.querySelector("#tab_content").innerHTML = "< h2 > EPG< / h2 > "
+ "< p > EPG goes here.< / p > ";
});
const setActiveTab = (e) => {
e.preventDefault();
document.querySelector(".active_tab").classList.remove("active_tab");
e.target.classList.add("active_tab");
}
const updateDate = () => {
document.getElementById("current_date").innerHTML = new Date().toDateString();
}
const updateTime = () => {
document.getElementById("current_time").innerHTML = new Date().toTimeString();
}
window.addEventListener("load", () => {
document.getElementById("library_tab").click();
updateDate();
updateTime();
const dateTimer = setInterval(updateDate, 1000 * 60);
const timeTimer = setInterval(updateTime, 1000);
window.addEventListener("dragenter", () => {
});
});
< / script >
< / body >
< / html >