2018-07-23 02:12:08 +00:00
|
|
|
var common = {};
|
2018-04-18 01:35:32 +00:00
|
|
|
|
2018-07-23 02:12:08 +00:00
|
|
|
common.INPUT_TYPES = new Set(["INPUT", "TEXTAREA"]);
|
|
|
|
|
|
|
|
common._request =
|
2018-02-18 00:08:38 +00:00
|
|
|
function _request(method, url, callback)
|
2016-09-18 08:33:46 +00:00
|
|
|
{
|
|
|
|
var request = new XMLHttpRequest();
|
2020-06-28 23:52:08 +00:00
|
|
|
var response = {
|
|
|
|
"completed": false,
|
|
|
|
};
|
|
|
|
|
2016-09-18 08:33:46 +00:00
|
|
|
request.onreadystatechange = function()
|
|
|
|
{
|
2020-06-29 00:07:28 +00:00
|
|
|
if (request.readyState != 4)
|
2020-07-01 03:33:24 +00:00
|
|
|
{return;}
|
2020-06-29 00:07:28 +00:00
|
|
|
|
|
|
|
if (callback == null)
|
2020-07-01 03:33:24 +00:00
|
|
|
{return;}
|
2020-06-29 00:07:28 +00:00
|
|
|
|
|
|
|
if (request.status != 0)
|
|
|
|
{
|
|
|
|
response.completed = true;
|
|
|
|
response.data = JSON.parse(request.responseText);
|
|
|
|
}
|
|
|
|
response.meta = {
|
|
|
|
"request_url": url,
|
|
|
|
"status": request.status
|
|
|
|
}
|
|
|
|
callback(response);
|
2016-09-18 08:33:46 +00:00
|
|
|
};
|
|
|
|
var asynchronous = true;
|
2018-02-18 00:08:38 +00:00
|
|
|
request.open(method, url, asynchronous);
|
|
|
|
return request;
|
|
|
|
}
|
2018-07-23 02:12:08 +00:00
|
|
|
|
|
|
|
common.get =
|
2018-02-18 00:08:38 +00:00
|
|
|
function get(url, callback)
|
|
|
|
{
|
2018-07-23 02:12:08 +00:00
|
|
|
request = common._request("GET", url, callback);
|
2018-02-18 00:08:38 +00:00
|
|
|
request.send();
|
|
|
|
}
|
2018-07-23 02:12:08 +00:00
|
|
|
|
|
|
|
common.post =
|
2018-02-18 00:08:38 +00:00
|
|
|
function post(url, data, callback)
|
|
|
|
{
|
2018-07-23 02:12:08 +00:00
|
|
|
request = common._request("POST", url, callback);
|
2016-09-18 08:33:46 +00:00
|
|
|
request.send(data);
|
|
|
|
}
|
|
|
|
|
2018-07-23 02:12:08 +00:00
|
|
|
common.bind_box_to_button =
|
2018-02-24 08:24:38 +00:00
|
|
|
function bind_box_to_button(box, button, ctrl_enter)
|
|
|
|
{
|
2020-07-01 03:33:24 +00:00
|
|
|
/*
|
|
|
|
Bind a textbox to a button so that pressing Enter within the textbox is the
|
|
|
|
same as clicking the button.
|
|
|
|
|
|
|
|
If `ctrl_enter` is true, then you must press ctrl+Enter to trigger the
|
|
|
|
button, which is important for textareas.
|
|
|
|
|
|
|
|
Thanks Yaroslav Yakovlev
|
|
|
|
http://stackoverflow.com/a/9343095
|
|
|
|
*/
|
2018-02-24 08:24:38 +00:00
|
|
|
var bound_box_hook = function(event)
|
|
|
|
{
|
|
|
|
if (event.key !== "Enter")
|
|
|
|
{return;}
|
|
|
|
|
2020-07-01 03:33:24 +00:00
|
|
|
ctrl_success = !ctrl_enter || (event.ctrlKey);
|
2018-02-24 08:24:38 +00:00
|
|
|
|
|
|
|
if (! ctrl_success)
|
|
|
|
{return;}
|
|
|
|
|
|
|
|
button.click();
|
|
|
|
}
|
|
|
|
box.addEventListener("keyup", bound_box_hook);
|
|
|
|
}
|
|
|
|
|
2018-07-29 08:25:53 +00:00
|
|
|
common.create_message_bubble =
|
|
|
|
function create_message_bubble(message_area, message_positivity, message_text, lifespan)
|
|
|
|
{
|
|
|
|
if (lifespan === undefined)
|
|
|
|
{
|
|
|
|
lifespan = 8000;
|
|
|
|
}
|
|
|
|
var message = document.createElement("div");
|
|
|
|
message.className = "message_bubble " + message_positivity;
|
|
|
|
var span = document.createElement("span");
|
|
|
|
span.innerHTML = message_text;
|
|
|
|
message.appendChild(span);
|
|
|
|
message_area.appendChild(message);
|
|
|
|
setTimeout(function(){message_area.removeChild(message);}, lifespan);
|
|
|
|
}
|
|
|
|
|
|
|
|
common.delete_all_children =
|
|
|
|
function delete_all_children(element)
|
|
|
|
{
|
|
|
|
while (element.firstChild)
|
|
|
|
{
|
|
|
|
element.removeChild(element.firstChild);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-23 02:12:08 +00:00
|
|
|
common.entry_with_history_hook =
|
2018-02-24 08:24:38 +00:00
|
|
|
function entry_with_history_hook(event)
|
2016-10-10 03:50:13 +00:00
|
|
|
{
|
2018-02-24 08:24:38 +00:00
|
|
|
//console.log(event);
|
|
|
|
var box = event.target;
|
|
|
|
|
2016-10-10 03:50:13 +00:00
|
|
|
if (box.entry_history === undefined)
|
2018-02-24 08:24:38 +00:00
|
|
|
{box.entry_history = [];}
|
|
|
|
|
2016-10-10 03:50:13 +00:00
|
|
|
if (box.entry_history_pos === undefined)
|
2018-02-24 08:24:38 +00:00
|
|
|
{box.entry_history_pos = -1;}
|
|
|
|
|
|
|
|
if (event.key === "Enter")
|
2016-10-10 03:50:13 +00:00
|
|
|
{
|
|
|
|
box.entry_history.push(box.value);
|
|
|
|
}
|
2018-02-24 08:24:38 +00:00
|
|
|
else if (event.key === "ArrowUp")
|
2016-10-10 03:50:13 +00:00
|
|
|
{
|
|
|
|
if (box.entry_history.length == 0)
|
2018-02-24 08:24:38 +00:00
|
|
|
{return}
|
|
|
|
|
2016-10-10 03:50:13 +00:00
|
|
|
if (box.entry_history_pos == -1)
|
2018-02-24 08:24:38 +00:00
|
|
|
{box.entry_history_pos = box.entry_history.length - 1;}
|
2016-10-10 03:50:13 +00:00
|
|
|
else if (box.entry_history_pos > 0)
|
2018-02-24 08:24:38 +00:00
|
|
|
{box.entry_history_pos -= 1;}
|
|
|
|
|
2016-10-10 03:50:13 +00:00
|
|
|
box.value = box.entry_history[box.entry_history_pos];
|
2017-02-25 06:07:59 +00:00
|
|
|
setTimeout(function(){box.selectionStart = box.value.length;}, 0);
|
2016-10-10 03:50:13 +00:00
|
|
|
}
|
2018-02-24 08:24:38 +00:00
|
|
|
else if (event.key === "Escape")
|
2016-10-10 03:50:13 +00:00
|
|
|
{
|
|
|
|
box.value = "";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
box.entry_history_pos = -1;
|
|
|
|
}
|
2016-12-21 09:11:50 +00:00
|
|
|
}
|
2018-02-18 03:12:34 +00:00
|
|
|
|
2018-07-23 02:12:08 +00:00
|
|
|
common.html_to_element =
|
2018-02-18 03:12:34 +00:00
|
|
|
function html_to_element(html)
|
|
|
|
{
|
|
|
|
var template = document.createElement("template");
|
|
|
|
template.innerHTML = html;
|
|
|
|
return template.content.firstChild;
|
|
|
|
}
|
2018-07-23 01:17:39 +00:00
|
|
|
|
2020-06-17 20:38:20 +00:00
|
|
|
common.init_atag_merge_params =
|
|
|
|
function init_atag_merge_params()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
To create an <a> tag where the ?parameters written on the href are merged
|
|
|
|
with the parameters of the current page URL, give it the class
|
|
|
|
"merge_params". If the URL and href contain the same parameter, the href
|
|
|
|
takes priority.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
URL: ?filter=hello&orderby=score
|
|
|
|
href: "?orderby=date"
|
|
|
|
Result: "?filter=hello&orderby=date"
|
|
|
|
*/
|
|
|
|
var as = Array.from(document.getElementsByClassName("merge_params"));
|
|
|
|
page_params = new URLSearchParams(window.location.search);
|
|
|
|
as.forEach(function(a){
|
|
|
|
var a_params = new URLSearchParams(a.search);
|
|
|
|
var new_params = new URLSearchParams();
|
|
|
|
page_params.forEach(function(value, key) {new_params.set(key, value); });
|
|
|
|
a_params.forEach(function(value, key) {new_params.set(key, value); });
|
|
|
|
a.search = new_params.toString();
|
|
|
|
a.classList.remove("merge_params");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-07-29 03:21:20 +00:00
|
|
|
common.init_button_with_confirm =
|
|
|
|
function init_button_with_confirm()
|
|
|
|
{
|
|
|
|
/*
|
2019-06-15 09:15:31 +00:00
|
|
|
To create a button that requires a second confirmation step, assign it the
|
|
|
|
class "button_with_confirm".
|
|
|
|
|
|
|
|
Required:
|
|
|
|
data-onclick: String that would normally be the button's onclick.
|
2020-06-17 21:08:03 +00:00
|
|
|
This is done so that if the button_with_confirm fails to initialize,
|
|
|
|
the button will be non-operational as opposed to being operational
|
|
|
|
but with no confirmation. For dangerous actions I think this is a
|
|
|
|
worthwhile move though it could lead to feature downtime.
|
2018-07-29 03:21:20 +00:00
|
|
|
|
|
|
|
Optional:
|
2019-06-15 09:15:31 +00:00
|
|
|
data-prompt: Text that appears next to the confirm button. Default is
|
|
|
|
"Are you sure?".
|
2018-07-29 03:21:20 +00:00
|
|
|
data-prompt-class
|
|
|
|
|
2019-06-15 09:15:31 +00:00
|
|
|
data-confirm: Text inside the confirm button. Default is to inherit the
|
|
|
|
original button's text.
|
2020-08-08 04:14:27 +00:00
|
|
|
data-confirm-class: CSS class for the confirm button. Default is to
|
|
|
|
inheret all classes of the original button, except for
|
|
|
|
"button_with_confirm" of course.
|
2018-07-29 03:21:20 +00:00
|
|
|
|
2019-06-15 09:15:31 +00:00
|
|
|
data-cancel: Text inside the cancel button. Default is "Cancel".
|
2018-07-29 03:21:20 +00:00
|
|
|
data-cancel-class
|
2019-06-15 23:02:41 +00:00
|
|
|
|
|
|
|
data-holder-class: CSS class for the new span that holds the menu.
|
2018-07-29 03:21:20 +00:00
|
|
|
*/
|
2019-06-15 20:49:33 +00:00
|
|
|
var buttons = Array.from(document.getElementsByClassName("button_with_confirm"));
|
2019-06-15 23:02:41 +00:00
|
|
|
buttons.forEach(function(button)
|
2018-07-29 03:21:20 +00:00
|
|
|
{
|
2019-06-15 20:16:38 +00:00
|
|
|
button.classList.remove("button_with_confirm");
|
|
|
|
|
2018-07-29 03:21:20 +00:00
|
|
|
var holder = document.createElement("span");
|
|
|
|
holder.classList.add("confirm_holder");
|
2018-09-23 22:03:50 +00:00
|
|
|
holder.classList.add(button.dataset.holderClass || "confirm_holder");
|
2018-07-29 03:21:20 +00:00
|
|
|
button.parentElement.insertBefore(holder, button);
|
|
|
|
button.parentElement.removeChild(button);
|
|
|
|
|
|
|
|
var holder_stage1 = document.createElement("span");
|
|
|
|
holder_stage1.classList.add("confirm_holder_stage1");
|
|
|
|
holder_stage1.appendChild(button);
|
|
|
|
holder.appendChild(holder_stage1);
|
|
|
|
|
|
|
|
var holder_stage2 = document.createElement("span");
|
|
|
|
holder_stage2.classList.add("confirm_holder_stage2");
|
|
|
|
holder_stage2.classList.add("hidden");
|
|
|
|
holder.appendChild(holder_stage2);
|
|
|
|
|
2019-04-27 22:30:33 +00:00
|
|
|
var prompt;
|
|
|
|
var input_source;
|
|
|
|
if (button.dataset.isInput)
|
|
|
|
{
|
|
|
|
prompt = document.createElement("input");
|
|
|
|
prompt.placeholder = button.dataset.prompt || "";
|
|
|
|
input_source = prompt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
prompt = document.createElement("span");
|
|
|
|
prompt.innerText = (button.dataset.prompt || "Are you sure?") + " ";
|
|
|
|
input_source = undefined;
|
|
|
|
}
|
|
|
|
prompt.className = button.dataset.promptClass || "";
|
|
|
|
holder_stage2.appendChild(prompt)
|
2018-07-29 03:21:20 +00:00
|
|
|
delete button.dataset.prompt;
|
|
|
|
delete button.dataset.promptClass;
|
|
|
|
|
|
|
|
var button_confirm = document.createElement("button");
|
2018-09-23 21:57:25 +00:00
|
|
|
button_confirm.innerText = (button.dataset.confirm || button.innerText).trim();
|
2020-08-08 04:14:27 +00:00
|
|
|
if (button.dataset.confirmClass === undefined)
|
|
|
|
{
|
|
|
|
button_confirm.className = button.className;
|
|
|
|
button_confirm.classList.remove("button_with_confirm");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
button_confirm.className = button.dataset.confirmClass;
|
|
|
|
}
|
2019-04-27 22:30:33 +00:00
|
|
|
button_confirm.input_source = input_source;
|
2018-07-29 03:21:20 +00:00
|
|
|
holder_stage2.appendChild(button_confirm);
|
2018-08-17 02:46:33 +00:00
|
|
|
holder_stage2.appendChild(document.createTextNode(" "));
|
2019-04-27 22:30:33 +00:00
|
|
|
if (button.dataset.isInput)
|
|
|
|
{
|
|
|
|
common.bind_box_to_button(prompt, button_confirm);
|
|
|
|
}
|
2018-07-29 03:21:20 +00:00
|
|
|
delete button.dataset.confirm;
|
|
|
|
delete button.dataset.confirmClass;
|
2019-04-27 22:30:33 +00:00
|
|
|
delete button.dataset.isInput;
|
2018-07-29 03:21:20 +00:00
|
|
|
|
|
|
|
var button_cancel = document.createElement("button");
|
|
|
|
button_cancel.innerText = button.dataset.cancel || "Cancel";
|
|
|
|
button_cancel.className = button.dataset.cancelClass || "";
|
|
|
|
holder_stage2.appendChild(button_cancel);
|
|
|
|
delete button.dataset.cancel;
|
|
|
|
delete button.dataset.cancelClass;
|
|
|
|
|
|
|
|
// If this is stupid, let me know.
|
|
|
|
var confirm_onclick = button.dataset.onclick + `
|
|
|
|
;
|
|
|
|
var holder = event.target.parentElement.parentElement;
|
|
|
|
holder.getElementsByClassName("confirm_holder_stage1")[0].classList.remove("hidden");
|
|
|
|
holder.getElementsByClassName("confirm_holder_stage2")[0].classList.add("hidden");
|
|
|
|
`
|
2019-04-27 22:28:22 +00:00
|
|
|
button_confirm.onclick = Function(confirm_onclick);
|
2018-07-29 03:21:20 +00:00
|
|
|
button.removeAttribute("onclick");
|
|
|
|
button.onclick = function(event)
|
|
|
|
{
|
|
|
|
var holder = event.target.parentElement.parentElement;
|
|
|
|
holder.getElementsByClassName("confirm_holder_stage1")[0].classList.add("hidden");
|
|
|
|
holder.getElementsByClassName("confirm_holder_stage2")[0].classList.remove("hidden");
|
2019-06-15 20:04:33 +00:00
|
|
|
var input = holder.getElementsByTagName("input")[0];
|
2019-04-27 22:30:33 +00:00
|
|
|
if (input)
|
|
|
|
{
|
2019-06-15 20:04:33 +00:00
|
|
|
input.focus();
|
2019-04-27 22:30:33 +00:00
|
|
|
}
|
2018-07-29 03:21:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
button_cancel.onclick = function(event)
|
|
|
|
{
|
|
|
|
var holder = event.target.parentElement.parentElement;
|
|
|
|
holder.getElementsByClassName("confirm_holder_stage1")[0].classList.remove("hidden");
|
|
|
|
holder.getElementsByClassName("confirm_holder_stage2")[0].classList.add("hidden");
|
|
|
|
}
|
|
|
|
delete button.dataset.onclick;
|
2019-06-15 23:02:41 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-08-28 23:23:28 +00:00
|
|
|
common.init_enable_on_pageload =
|
|
|
|
function init_enable_on_pageload()
|
|
|
|
{
|
2020-09-03 18:46:26 +00:00
|
|
|
/*
|
|
|
|
To create an input element which is disabled at first, and is enabled when
|
|
|
|
the DOM has completed loading, give it the disabled attribute and the
|
|
|
|
class "enable_on_pageload".
|
|
|
|
*/
|
2020-08-28 23:23:28 +00:00
|
|
|
var elements = Array.from(document.getElementsByClassName("enable_on_pageload"));
|
|
|
|
elements.forEach(function(element)
|
|
|
|
{
|
|
|
|
element.disabled = false;
|
|
|
|
element.classList.remove("enable_on_pageload");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-09-03 18:54:14 +00:00
|
|
|
common.init_tabbed_container =
|
|
|
|
function init_tabbed_container()
|
|
|
|
{
|
|
|
|
var switch_tab =
|
|
|
|
function switch_tab(event)
|
|
|
|
{
|
|
|
|
var tab_button = event.target;
|
|
|
|
if (tab_button.classList.contains("tab_button_active"))
|
|
|
|
{ return; }
|
|
|
|
|
|
|
|
var tab_id = tab_button.dataset.tabId;
|
|
|
|
var tab_buttons = tab_button.parentElement.getElementsByClassName("tab_button");
|
|
|
|
var tabs = tab_button.parentElement.parentElement.getElementsByClassName("tab");
|
|
|
|
for (let tab_button of tab_buttons)
|
|
|
|
{
|
|
|
|
if (tab_button.dataset.tabId === tab_id)
|
|
|
|
{
|
|
|
|
tab_button.classList.remove("tab_button_inactive");
|
|
|
|
tab_button.classList.add("tab_button_active");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tab_button.classList.remove("tab_button_active");
|
|
|
|
tab_button.classList.add("tab_button_inactive");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (let tab of tabs)
|
|
|
|
{
|
|
|
|
if (tab.dataset.tabId === tab_id)
|
|
|
|
{ tab.classList.remove("hidden"); }
|
|
|
|
else
|
|
|
|
{ tab.classList.add("hidden"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var tabbed_containers = Array.from(document.getElementsByClassName("tabbed_container"));
|
|
|
|
tabbed_containers.forEach(function(tabbed_container)
|
|
|
|
{
|
|
|
|
var button_container = document.createElement("div");
|
|
|
|
button_container.className = "tab_buttons";
|
|
|
|
tabbed_container.prepend(button_container);
|
|
|
|
var tabs = Array.from(tabbed_container.getElementsByClassName("tab"));
|
|
|
|
tabs.forEach(function(tab)
|
|
|
|
{
|
|
|
|
tab.classList.add("hidden");
|
|
|
|
var tab_id = tab.dataset.tabId || tab.dataset.tabTitle;
|
|
|
|
tab.dataset.tabId = tab_id;
|
|
|
|
tab.style.borderTopColor = "transparent";
|
|
|
|
|
|
|
|
var button = document.createElement("button");
|
|
|
|
button.className = "tab_button tab_button_inactive";
|
|
|
|
button.onclick = switch_tab;
|
|
|
|
button.innerText = tab.dataset.tabTitle;
|
|
|
|
button.dataset.tabId = tab_id;
|
|
|
|
button_container.append(button);
|
|
|
|
});
|
|
|
|
tabs[0].classList.remove("hidden");
|
|
|
|
button_container.firstElementChild.classList.remove("tab_button_inactive");
|
|
|
|
button_container.firstElementChild.classList.add("tab_button_active");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-07-29 08:25:53 +00:00
|
|
|
common.refresh =
|
|
|
|
function refresh()
|
|
|
|
{
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
|
2018-07-29 03:21:20 +00:00
|
|
|
common.on_pageload =
|
|
|
|
function on_pageload()
|
|
|
|
{
|
2020-06-17 20:38:20 +00:00
|
|
|
common.init_atag_merge_params();
|
2018-07-29 03:21:20 +00:00
|
|
|
common.init_button_with_confirm();
|
2020-08-28 23:23:28 +00:00
|
|
|
common.init_enable_on_pageload();
|
2020-09-03 18:54:14 +00:00
|
|
|
common.init_tabbed_container();
|
2018-07-29 03:21:20 +00:00
|
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", common.on_pageload);
|