// 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);
}