var common = {}; common.INPUT_TYPES = new Set(["INPUT", "TEXTAREA"]); common._request = function _request(method, url, callback) { var request = new XMLHttpRequest(); request.onreadystatechange = function() { if (request.readyState == 4) { if (callback != null) { var response = { "data": JSON.parse(request.responseText), "meta": {} }; response["meta"]["request_url"] = url; response["meta"]["status"] = request.status; callback(response); } } }; var asynchronous = true; request.open(method, url, asynchronous); return request; } common.get = function get(url, callback) { request = common._request("GET", url, callback); request.send(); } common.post = function post(url, data, callback) { request = common._request("POST", url, callback); request.send(data); } common.bind_box_to_button = function bind_box_to_button(box, button, ctrl_enter) { // Thanks Yaroslav Yakovlev // http://stackoverflow.com/a/9343095 var bound_box_hook = function(event) { if (event.key !== "Enter") {return;} ctrl_success = !ctrl_enter || (event.ctrlKey) if (! ctrl_success) {return;} button.click(); } box.addEventListener("keyup", bound_box_hook); } 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); } } common.entry_with_history_hook = function entry_with_history_hook(event) { //console.log(event); var box = event.target; if (box.entry_history === undefined) {box.entry_history = [];} if (box.entry_history_pos === undefined) {box.entry_history_pos = -1;} if (event.key === "Enter") { box.entry_history.push(box.value); } else if (event.key === "ArrowUp") { if (box.entry_history.length == 0) {return} if (box.entry_history_pos == -1) {box.entry_history_pos = box.entry_history.length - 1;} else if (box.entry_history_pos > 0) {box.entry_history_pos -= 1;} box.value = box.entry_history[box.entry_history_pos]; setTimeout(function(){box.selectionStart = box.value.length;}, 0); } else if (event.key === "Escape") { box.value = ""; } else { box.entry_history_pos = -1; } } common.entry_with_tagname_replacements = function entry_with_tagname_replacements(event) { var cursor_position = event.target.selectionStart; event.target.value = common.tagname_replacements(event.target.value); event.target.selectionStart = cursor_position; event.target.selectionEnd = cursor_position; } common.html_to_element = function html_to_element(html) { var template = document.createElement("template"); template.innerHTML = html; return template.content.firstChild; } common.init_button_with_confirm = function init_button_with_confirm() { /* 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. Optional: data-prompt: Text that appears next to the confirm button. Default is "Are you sure?". data-prompt-class data-confirm: Text inside the confirm button. Default is to inherit the original button's text. data-confirm-class data-cancel: Text inside the cancel button. Default is "Cancel". data-cancel-class data-holder-class: CSS class for the new span that holds the menu. */ var buttons = Array.from(document.getElementsByClassName("button_with_confirm")); buttons.forEach(function(button) { button.classList.remove("button_with_confirm"); var holder = document.createElement("span"); holder.classList.add("confirm_holder"); holder.classList.add(button.dataset.holderClass || "confirm_holder"); 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); 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) delete button.dataset.prompt; delete button.dataset.promptClass; var button_confirm = document.createElement("button"); button_confirm.innerText = (button.dataset.confirm || button.innerText).trim(); button_confirm.className = button.dataset.confirmClass || ""; button_confirm.input_source = input_source; holder_stage2.appendChild(button_confirm); holder_stage2.appendChild(document.createTextNode(" ")); if (button.dataset.isInput) { common.bind_box_to_button(prompt, button_confirm); } delete button.dataset.confirm; delete button.dataset.confirmClass; delete button.dataset.isInput; 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"); ` button_confirm.onclick = Function(confirm_onclick); 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"); var input = holder.getElementsByTagName("input")[0]; if (input) { input.focus(); } } 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; }); } var spinner_button_count = 0; common.init_button_with_spinner = function init_button_with_spinner() { /* To create a button that has a spinner, and cannot be clicked again while the action is running, assign it the class "button_with_spinner". Required: data-onclick: The string that would normally be the button's onclick. Optional: data-spinner-id: If you want to use your own element as the spinner, give its ID here. Otherwise a new one will be created. data-holder-class: CSS class for the new span that holds the menu. */ var buttons = Array.from(document.getElementsByClassName("button_with_spinner")); buttons.forEach(function(button) { button.classList.remove("button_with_spinner"); button.innerHTML = button.innerHTML.trim(); var holder = document.createElement("span"); holder.classList.add("spinner_holder"); holder.classList.add(button.dataset.holderClass || "spinner_holder"); button.parentElement.insertBefore(holder, button); button.parentElement.removeChild(button); holder.appendChild(button); var spinner_element; if (button.dataset.spinnerId) { spinner_element = document.getElementById(button.dataset.spinnerId); } else { spinner_element = document.createElement("span"); spinner_element.innerText = "Working..."; spinner_element.classList.add("hidden"); holder.appendChild(spinner_element); } var spin = new spinner.Spinner(spinner_element); var spin_delay = parseFloat(button.dataset.spinnerDelay) || 0; var wrapped_onclick = Function(button.dataset.onclick); button.onclick = function() { spin.show(spin_delay); button.disabled = true; wrapped_onclick(); } delete button.dataset.onclick; var closer_id = "spinner_closer_" + spinner_button_count; spinner_button_count += 1; window[closer_id] = function spinner_closer() { spin.hide(); button.disabled = false; } button.dataset.spinnerCloser = closer_id; }); } common.normalize_tagname = function normalize_tagname(tagname) { tagname = tagname.trim(); tagname = tagname.toLocaleLowerCase(); tagname = tagname.split("."); tagname = tagname[tagname.length-1]; tagname = tagname.split("+")[0]; tagname = common.tagname_replacements(tagname); return tagname; } common.tagname_replacements = function tagname_replacements(tagname) { tagname = tagname.replace(new RegExp(" ", 'g'), "_"); tagname = tagname.replace(new RegExp("-", 'g'), "_"); return tagname; } common.refresh = function refresh() { window.location.reload(); } common.on_pageload = function on_pageload() { common.init_button_with_confirm(); common.init_button_with_spinner(); } document.addEventListener("DOMContentLoaded", common.on_pageload);