// started on main page load function init() { init_board(); } class AdjustingTimer { constructor() { this.update_counter = 0; // counting seconds since last counter reset } fast_mode() { if (this.update_counter < 5*60) return true; return false; } // private method // returns current interval in ms current_update_interval() { if (this.fast_mode()) return 2000; // 2s else return 5 * 60000; // 5min }; // private count() { this.update_counter += this.current_update_interval() / 1000; }; // private on_timeout() { this.m_fn(); this.count(); var _this = this; this.update_timer = setTimeout(function(){_this.on_timeout();}, this.current_update_interval()); }; // to be called once on startup start(fn) { this.m_fn = fn; var _this = this; this.update_timer = setTimeout(function(){_this.on_timeout();}, this.current_update_interval()); }; // to be called on activity: // * changes from remote // * changes by ourselves // * local activity, e.g. mouse move, or key presses reset() { if (!this.fast_mode()) { this.update_counter = 0; clearTimeout(this.update_timer); var _this = this; this.update_timer = setTimeout(function(){_this.on_timeout();}, this.current_update_interval()); } else { this.update_counter = 0; } }; } var timer = new AdjustingTimer(); function showQRWindow() { document.getElementById("qrwindow").style.display = 'block'; } function hideQRWindow() { document.getElementById("qrwindow").style.display = 'none'; } function init_board() { var xhr = new XMLHttpRequest(); const searchParams = (new URL(document.location)).searchParams; if (!searchParams.has('id')) { redirect_to_new_page(); return; } // run on data received back xhr.onreadystatechange = function() { if (this.readyState == 3) { //set_status("Please wait while downloading " + filename + " ..."); return; } if (this.readyState != 4) { return; } if (this.status != 200) { //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText); return; } var file = new Blob([this.response]); reader = new FileReader(); reader.onload = function() { var board = document.getElementById("board"); var pos = reader.result.indexOf('\x01'); if (pos == -1) { // not found board.value = reader.result; } else { board.value = reader.result.substr(0, pos) + reader.result.substr(pos + 1); } textAreaSetPos("board", pos); // Initialization done. Now we can start modifying. board.addEventListener("input", function() {on_modify(); }); board.addEventListener("selectionchange", function() {on_modify(); }); // Initialization done. Now we can start modifying. document.addEventListener("mousemove", function() {timer.reset(); }); timer.start(checkupdate); } reader.readAsBinaryString(file); //set_status(""); // OK } var parser = new DOMParser(); var xmlDocument = parser.parseFromString("", "text/xml"); var requestElement = xmlDocument.getElementsByTagName("request")[0]; var commandElement = xmlDocument.createElement("command"); commandElement.appendChild(document.createTextNode("getfile")); requestElement.appendChild(commandElement); var idElement = xmlDocument.createElement("id"); idElement.appendChild(document.createTextNode(get_id())); requestElement.appendChild(idElement); xhr.open("POST", "whiteboard.fcgi", true); xhr.setRequestHeader("Content-type", "text/xml"); xhr.responseType = 'blob'; xhr.send(xmlDocument); //set_status("Please wait while server prepares " + filename + " ..."); document.getElementById("qrwindow").onclick = function() { hideQRWindow(); } document.onkeydown = function(evt) { if (evt.key == "Escape") { hideQRWindow(); } } document.getElementById("board").focus(); } function get_id() { const searchParams = (new URL(document.location)).searchParams; return searchParams.get('id'); } function on_new_page() { redirect_to_new_page(); } function redirect_to_new_page() { var xhr = new XMLHttpRequest(); // run on data received back xhr.onreadystatechange = function() { if (this.readyState == 3) { //set_status("Please wait while downloading " + filename + " ..."); return; } if (this.readyState != 4) { return; } if (this.status != 200) { //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText); return; } var id = this.responseText; //alert("location=" + document.location.href); var new_location = document.location.href; var pos = new_location.search("\\?"); if (pos >= 0) new_location = new_location.substring(0, pos); new_location += '?id=' + id; window.location.href = new_location; //set_status(""); // OK } var parser = new DOMParser(); var xmlDocument = parser.parseFromString("", "text/xml"); var requestElement = xmlDocument.getElementsByTagName("request")[0]; var commandElement = xmlDocument.createElement("command"); commandElement.appendChild(document.createTextNode("newid")); requestElement.appendChild(commandElement); xhr.open("POST", "whiteboard.fcgi", true); xhr.setRequestHeader("Content-type", "text/xml"); xhr.send(xmlDocument); //set_status("Please wait while server prepares " + filename + " ..."); } // local change done function on_modify() { timer.reset(); var xhr = new XMLHttpRequest(); // run on data received back xhr.onreadystatechange = function() { if (this.readyState == 3) { //set_status("Please wait while downloading " + filename + " ..."); return; } if (this.readyState != 4) { return; } if (this.status != 200) { //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText); return; } //set_status(""); // OK } var parser = new DOMParser(); var xmlDocument = parser.parseFromString("", "text/xml"); var requestElement = xmlDocument.getElementsByTagName("request")[0]; var commandElement = xmlDocument.createElement("command"); commandElement.appendChild(document.createTextNode("modify")); requestElement.appendChild(commandElement); var idElement = xmlDocument.createElement("id"); idElement.appendChild(document.createTextNode(get_id())); requestElement.appendChild(idElement); var dataElement = xmlDocument.createElement("data"); dataElement.appendChild(document.createTextNode(addPos(document.getElementById("board").value, document.getElementById("board").selectionStart))); requestElement.appendChild(dataElement); xhr.open("POST", "whiteboard.fcgi", true); xhr.setRequestHeader("Content-type", "text/xml"); xhr.responseType = 'blob'; xhr.send(xmlDocument); //set_status("Please wait while server prepares " + filename + " ..."); } // checksum of string function checksum32(s) { var result = 0; for (var i = 0; i < s.length; i++) { result = ((((result >>> 1) | ((result & 1) << 31)) | 0) ^ (s.charCodeAt(i) & 0xFF)) | 0; } return (result & 0x7FFFFFFF) | 0; } function textAreaSetPos(id, pos) { document.getElementById(id).selectionStart = pos; document.getElementById(id).selectionEnd = pos; } function addPos(s, pos) { return s.substr(0, pos) + '\x01' + s.substr(pos); } // gets called by regular polling function checkupdate() { var xhr = new XMLHttpRequest(); // run on data received back xhr.onreadystatechange = function() { if (this.readyState == 3) { //set_status("Please wait while downloading " + filename + " ..."); return; } if (this.readyState != 4) { return; } if (this.status != 200) { //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText); return; } // no change if response is text/plain if (this.getResponseHeader("Content-Type") == "application/octet-stream") { timer.reset(); var file = new Blob([this.response]); reader = new FileReader(); reader.onload = function() { var board = document.getElementById("board"); var pos = reader.result.indexOf('\x01'); if (pos == -1) { // not found board.value = reader.result; } else { board.value = reader.result.substr(0, pos) + reader.result.substr(pos + 1); } textAreaSetPos("board", pos); } reader.readAsBinaryString(file); } //set_status(""); // OK } var parser = new DOMParser(); var xmlDocument = parser.parseFromString("", "text/xml"); var requestElement = xmlDocument.getElementsByTagName("request")[0]; var commandElement = xmlDocument.createElement("command"); commandElement.appendChild(document.createTextNode("checkupdate")); requestElement.appendChild(commandElement); var idElement = xmlDocument.createElement("id"); idElement.appendChild(document.createTextNode(get_id())); requestElement.appendChild(idElement); var checksumElement = xmlDocument.createElement("checksum"); checksumElement.appendChild(document.createTextNode(checksum32(addPos(document.getElementById("board").value, document.getElementById("board").selectionStart)))); requestElement.appendChild(checksumElement); xhr.open("POST", "whiteboard.fcgi", true); xhr.setRequestHeader("Content-type", "text/xml"); xhr.responseType = 'blob'; xhr.send(xmlDocument); //set_status("Please wait while server prepares " + filename + " ..."); } function on_qrcode() { var xhr = new XMLHttpRequest(); // run on data received back xhr.onreadystatechange = function() { if (this.readyState == 3) { //set_status("Please wait while downloading " + filename + " ..."); return; } if (this.readyState != 4) { return; } if (this.status != 200) { //set_status("Server Error while retrieving " + filename + ", status: " + this.status + " " + this.statusText); return; } if (this.getResponseHeader("Content-Type") == "image/png") { var blob = new Blob([this.response], {type: 'image/png'}); var url = URL.createObjectURL(blob); var img = document.getElementById("qrcode"); img.src = url; showQRWindow(); } //set_status(""); // OK } var parser = new DOMParser(); var xmlDocument = parser.parseFromString("", "text/xml"); var requestElement = xmlDocument.getElementsByTagName("request")[0]; var commandElement = xmlDocument.createElement("command"); commandElement.appendChild(document.createTextNode("qrcode")); requestElement.appendChild(commandElement); var idElement = xmlDocument.createElement("url"); idElement.appendChild(document.createTextNode(document.location)); requestElement.appendChild(idElement); xhr.open("POST", "whiteboard.fcgi", true); xhr.setRequestHeader("Content-type", "text/xml"); xhr.responseType = 'blob'; xhr.send(xmlDocument); }