mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-06 05:24:46 +01:00
55f590ca0b
I'm running out of memory when using the largest English dicts. Something other than LocalStorage will have to be used for Polish, but for now limp along with smaller wordlists.
377 lines
10 KiB
JavaScript
377 lines
10 KiB
JavaScript
var state = {client: null,
|
|
closure: null,
|
|
connected: false,
|
|
};
|
|
|
|
function ccallString(proc, closure, str) {
|
|
Module.ccall('cbckString', null, ['number', 'number', 'string'],
|
|
[proc, closure, str]);
|
|
}
|
|
|
|
function registerOnce(devid, gitrev, now) {
|
|
let nextTimeKey = 'next_reg';
|
|
let gitKey = 'last_write';
|
|
let nextTime = parseInt(localStorage.getItem(nextTimeKey));
|
|
let prevGit = localStorage.getItem(gitKey);
|
|
if ( prevGit == gitrev && now < nextTime ) {
|
|
console.log('registerOnce(): next in ' + (nextTime - now) + ' secs');
|
|
} else {
|
|
let args = { devid: devid,
|
|
gitrev: gitrev,
|
|
loc: navigator.language,
|
|
os: navigator.appName,
|
|
vers: '0.0',
|
|
dbg: true,
|
|
myNow: now,
|
|
vrntName: 'wasm',
|
|
};
|
|
let body = JSON.stringify(args);
|
|
|
|
fetch('/xw4/api/v1/register', {
|
|
method: 'post',
|
|
body: body,
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
}).then(res => {
|
|
return res.json();
|
|
}).then(data => {
|
|
console.log('data: ' + JSON.stringify(data));
|
|
if ( data.success ) {
|
|
localStorage.setItem(nextTimeKey, data.atNext);
|
|
localStorage.setItem(gitKey, gitrev);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function getDict(closure) {
|
|
// set these later
|
|
let gots = {};
|
|
|
|
let langs = 'en'; // navigator.language;
|
|
if (langs) {
|
|
langs = [langs.split('-')[0]];
|
|
}
|
|
console.log('langs: ' + langs + '; langs[0]: ' + langs[0]);
|
|
if (langs[0] != 'en' ) {
|
|
langs.push('en');
|
|
}
|
|
let args = '?lc=' + langs.join('|');
|
|
console.log('args: ' + args);
|
|
fetch('/xw4/info.py/listDicts' + args, {
|
|
method: 'post',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
}).then(response => {
|
|
console.log(response);
|
|
if (response.ok) {
|
|
return response.json();
|
|
}
|
|
}).then(data => {
|
|
// console.log('data: ' + JSON.stringify(data));
|
|
for ( lang of data.langs ) {
|
|
let dict = null;
|
|
for ( one of lang.dicts ) {
|
|
if ( !dict || one.nBytes < dict.nBytes ) {
|
|
dict = one;
|
|
}
|
|
}
|
|
if ( dict ) {
|
|
gots.xwd = dict.xwd;
|
|
gots.langName = lang.lang;
|
|
gots.lc = lang.lc;
|
|
let path = '/' + ['android', gots.langName, gots.xwd].join('/');
|
|
return fetch(path);
|
|
}
|
|
}
|
|
}).then(response => {
|
|
// console.log('got here!!!' + response);
|
|
return response.arrayBuffer();
|
|
}).then(data=> {
|
|
let len = data.byteLength;
|
|
let dataPtr = Module._malloc(len);
|
|
// Copy data to Emscripten heap
|
|
var dataHeap = new Uint8Array(Module.HEAPU8.buffer, dataPtr, len);
|
|
dataHeap.set( new Uint8Array(data) );
|
|
// console.log('made array?: ' + dataHeap);
|
|
Module.ccall('gotDictBinary', null,
|
|
['number', 'string', 'string', 'string', 'number', 'array'],
|
|
[closure, gots.xwd, gots.langName, gots.lc, len, dataHeap]);
|
|
Module._free(dataPtr);
|
|
});
|
|
console.log('getDict() done');
|
|
}
|
|
|
|
// Called from main() asap after things are initialized etc.
|
|
function jssetup(closure, devid, gitrev, now, noTabProc, focusProc, msgProc) {
|
|
// Set a unique tag so we know if somebody comes along later
|
|
let tabID = Math.random();
|
|
localStorage.setItem('tabID', tabID);
|
|
let listener = function () {
|
|
newTabID = localStorage.getItem('tabID');
|
|
if ( newTabID != tabID ) {
|
|
state.client.disconnect();
|
|
ccallString(noTabProc, state.closure, '');
|
|
window.removeEventListener('storage', listener);
|
|
}
|
|
};
|
|
window.addEventListener('storage', listener);
|
|
|
|
window.onfocus = function () {
|
|
ccallString(focusProc, state.closure, '');
|
|
};
|
|
|
|
registerOnce(devid, gitrev, now);
|
|
|
|
state.closure = closure;
|
|
state.msgProc = msgProc;
|
|
document.getElementById("mqtt_span").textContent=devid;
|
|
|
|
function tellConnected(isConn) {
|
|
Module.ccall('MQTTConnectedChanged', null, ['number', 'boolean'],
|
|
[state.closure, isConn]);
|
|
}
|
|
|
|
state.client = new Paho.MQTT.Client("eehouse.org", 8883, '/wss', devid);
|
|
|
|
// set callback handlers
|
|
state.client.onConnectionLost = function onConnectionLost(responseObject) {
|
|
state.connected = false;
|
|
document.getElementById("mqtt_status").textContent="Disconnected";
|
|
tellConnected(false);
|
|
if (responseObject.errorCode !== 0) {
|
|
console.log("onConnectionLost:"+responseObject.errorMessage);
|
|
}
|
|
};
|
|
state.client.onMessageArrived = function onMessageArrived(message) {
|
|
let payload = message.payloadBytes;
|
|
let length = payload.length;
|
|
Module.ccall('cbckBinary', null, ['number', 'number', 'number', 'array'],
|
|
[state.msgProc, state.closure, length, payload]);
|
|
};
|
|
|
|
function onConnect() {
|
|
state.connected = true
|
|
document.getElementById("mqtt_status").textContent="Connected";
|
|
tellConnected(true);
|
|
|
|
var subscribeOptions = {
|
|
qos: 2, // QoS
|
|
// invocationContext: {foo: true}, // Passed to success / failure callback
|
|
// onSuccess: function() { alert('subscribe succeeded'); },
|
|
onFailure: function() { alert('subscribe failed'); },
|
|
timeout: 10,
|
|
};
|
|
state.client.subscribe('xw4/device/' + devid, subscribeOptions);
|
|
}
|
|
|
|
state.client.connect({mqttVersion: 3,
|
|
userName: "xwuser",
|
|
password: "xw4r0cks",
|
|
useSSL: true,
|
|
reconnect: true,
|
|
onSuccess: onConnect,
|
|
onFailure: function() { alert('onFailure'); },
|
|
});
|
|
|
|
// let's sum up storage
|
|
let total = 0;
|
|
for (let ii = 0; ii < localStorage.length; ++ii ) {
|
|
let key = localStorage.key(ii);
|
|
let val = localStorage.getItem(key);
|
|
total += key.length + val.length;
|
|
}
|
|
document.getElementById("storage_used").textContent=total + 'b';
|
|
}
|
|
|
|
function mqttSend( topic, ptr ) {
|
|
let canSend = null != state.client && state.connected;
|
|
if ( canSend ) {
|
|
message = new Paho.MQTT.Message(ptr);
|
|
message.destinationName = topic;
|
|
message.qos = 2;
|
|
state.client.send(message);
|
|
} else {
|
|
console.log('mqttSend: not connected');
|
|
}
|
|
return canSend;
|
|
}
|
|
|
|
function addDepthNote(dlg) {
|
|
let depth = dlg.parentNode.childElementCount;
|
|
if ( depth > 1 ) {
|
|
let div = document.createElement('div');
|
|
div.textContent = '(Depth: ' + depth + ')';
|
|
dlg.appendChild(div);
|
|
}
|
|
}
|
|
|
|
function newDlgWMsg(msg) {
|
|
let container = document.getElementById('nbalert');
|
|
|
|
let dlg = document.createElement('div');
|
|
dlg.classList.add('nbalert');
|
|
dlg.style.zIndex = 10000 + container.childElementCount;
|
|
container.appendChild( dlg );
|
|
|
|
let txtDiv = document.createElement('div');
|
|
txtDiv.textContent = msg
|
|
dlg.appendChild( txtDiv );
|
|
|
|
return dlg;
|
|
}
|
|
|
|
function newButtonDiv(buttons, proc) {
|
|
let div = document.createElement('div');
|
|
div.classList.add('buttonRow');
|
|
for ( let ii = 0; ii < buttons.length; ++ii ) {
|
|
let buttonTxt = buttons[ii];
|
|
let button = document.createElement('button');
|
|
button.textContent = buttonTxt;
|
|
button.onclick = function() { proc(ii); };
|
|
div.appendChild( button );
|
|
}
|
|
|
|
return div;
|
|
}
|
|
|
|
function nbDialog(msg, buttons, proc, closure) {
|
|
const dlg = newDlgWMsg( msg );
|
|
|
|
const butProc = function(indx) {
|
|
dlg.parentNode.removeChild(dlg);
|
|
ccallString(proc, closure, buttons[indx]);
|
|
}
|
|
dlg.appendChild( newButtonDiv( buttons, butProc ) );
|
|
addDepthNote(dlg);
|
|
}
|
|
|
|
function nbBlankPick(title, buttons, proc, closure) {
|
|
let dlg = newDlgWMsg( title );
|
|
|
|
const butProc = function(indx) {
|
|
dlg.parentNode.removeChild(dlg);
|
|
ccallString(proc, closure, indx.toString());
|
|
}
|
|
|
|
dlg.appendChild( newButtonDiv( buttons, butProc ) );
|
|
addDepthNote(dlg);
|
|
}
|
|
|
|
// To enable sorting of names on buttons while keeping existing code,
|
|
// I'm creating an array of pairs then sorting that.
|
|
function nbGamePick(title, gameMap, proc, closure) {
|
|
let dlg = newDlgWMsg( title );
|
|
|
|
let pairs = [];
|
|
Object.keys(gameMap).forEach( function(key) {
|
|
pairs.push({id:key, txt:gameMap[key]});
|
|
});
|
|
|
|
pairs.sort(function(a, b){
|
|
var stra = a.txt.toLowerCase();
|
|
var strb = b.txt.toLowerCase();
|
|
if (stra < strb) {return -1;}
|
|
if (stra > strb) {return 1;}
|
|
return parseInt(a.id) - parseInt(b.id);
|
|
});
|
|
let buttons = [];
|
|
for ( pair of pairs ) {
|
|
buttons.push(pair.txt);
|
|
}
|
|
|
|
butProc = function(indx) {
|
|
dlg.parentNode.removeChild(dlg);
|
|
ccallString(proc, closure, pairs[indx].id);
|
|
}
|
|
|
|
dlg.appendChild( newButtonDiv( buttons, butProc ) );
|
|
addDepthNote(dlg);
|
|
}
|
|
|
|
function setDivButtons(divid, buttons, proc, closure) {
|
|
let parent = document.getElementById(divid);
|
|
while ( parent.lastElementChild ) {
|
|
parent.removeChild(parent.lastElementChild);
|
|
}
|
|
|
|
butProc = function(indx) {
|
|
ccallString(proc, closure, buttons[indx]);
|
|
}
|
|
|
|
parent.appendChild( newButtonDiv( buttons, butProc ) );
|
|
}
|
|
|
|
function nbGetString(msg, dflt, proc, closure) {
|
|
let dlg = newDlgWMsg( msg );
|
|
|
|
let tarea = document.createElement('textarea');
|
|
tarea.classList.add('stringedit');
|
|
tarea.value = dflt;
|
|
dlg.appendChild( tarea );
|
|
|
|
dismissed = function(str) {
|
|
dlg.parentNode.removeChild(dlg);
|
|
ccallString(proc, closure, str);
|
|
}
|
|
|
|
let buttons = ["Cancel", "OK"];
|
|
butProc = function(indx) {
|
|
if ( indx == 0 ) { // CANCEL
|
|
dismissed(null);
|
|
} else if ( indx == 1 ) { // OK
|
|
dismissed(tarea.value);
|
|
}
|
|
}
|
|
dlg.appendChild( newButtonDiv( buttons, butProc ) );
|
|
addDepthNote(dlg);
|
|
}
|
|
|
|
function newRadio(txt, id, proc) {
|
|
let span = document.createElement('span');
|
|
let radio = document.createElement('input');
|
|
radio.type = 'radio';
|
|
radio.id = id;
|
|
radio.name = id;
|
|
radio.onclick = proc;
|
|
|
|
let label = document.createElement('label')
|
|
label.htmlFor = id;
|
|
var description = document.createTextNode(txt);
|
|
label.appendChild(description);
|
|
|
|
span.appendChild(label);
|
|
span.appendChild(radio);
|
|
|
|
return span;
|
|
}
|
|
|
|
function nbGetNewGame(closure, msg) {
|
|
const dlg = newDlgWMsg('Is your opponent a robot or someone you will invite?');
|
|
|
|
const radioDiv = document.createElement('div');
|
|
dlg.appendChild( radioDiv );
|
|
const robotSet = [null];
|
|
radioDiv.appendChild(newRadio('Robot', 'newgame', function() {robotSet[0] = true;}));
|
|
radioDiv.appendChild(newRadio('Remote player', 'newgame', function() {robotSet[0] = false;}));
|
|
|
|
const butProc = function(indx) {
|
|
if ( indx === 1 && null !== robotSet[0]) {
|
|
const types = ['number', 'boolean'];
|
|
const params = [closure, robotSet[0]];
|
|
Module.ccall('onNewGame', null, types, params);
|
|
}
|
|
dlg.parentNode.removeChild(dlg);
|
|
}
|
|
|
|
dlg.appendChild( newButtonDiv( ['Cancel', 'OK'], butProc ) );
|
|
addDepthNote(dlg);
|
|
}
|
|
|
|
for ( let one of ['paho-mqtt.js'] ) {
|
|
let script = document.createElement('script');
|
|
script.src = one;
|
|
document.body.appendChild(script);
|
|
}
|