mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-11 08:48:06 +01:00
add name button and store wordlist
As a p.o.c., load the built-in wordlist into storage once, and thereafter use it from there rather than as a file. Now it can go away and a wordlist for the user's locale be downloaded and stored instead.
This commit is contained in:
parent
c829017150
commit
2ab2f5642e
7 changed files with 131 additions and 46 deletions
|
@ -62,6 +62,7 @@
|
||||||
|
|
||||||
#define KEY_LAST_GID "cur_game"
|
#define KEY_LAST_GID "cur_game"
|
||||||
#define KEY_PLAYER_NAME "player_name"
|
#define KEY_PLAYER_NAME "player_name"
|
||||||
|
#define KEY_DICTS "dicts_3"
|
||||||
#define KEY_GAME "game_data"
|
#define KEY_GAME "game_data"
|
||||||
#define KEY_NAME "game_name"
|
#define KEY_NAME "game_name"
|
||||||
#define KEY_NEXT_GAME "next_game"
|
#define KEY_NEXT_GAME "next_game"
|
||||||
|
@ -89,6 +90,7 @@
|
||||||
#define BUTTON_GAME_OPEN "Open Game"
|
#define BUTTON_GAME_OPEN "Open Game"
|
||||||
#define BUTTON_GAME_RENAME "Rename Game"
|
#define BUTTON_GAME_RENAME "Rename Game"
|
||||||
#define BUTTON_GAME_DELETE "Delete Game"
|
#define BUTTON_GAME_DELETE "Delete Game"
|
||||||
|
#define BUTTON_NAME "My Name"
|
||||||
#define MAX_BUTTONS 20 /* not sure what's safe here */
|
#define MAX_BUTTONS 20 /* not sure what's safe here */
|
||||||
|
|
||||||
typedef struct _NewGameParams {
|
typedef struct _NewGameParams {
|
||||||
|
@ -471,6 +473,7 @@ onGameRanamed( void* closure, const char* newName )
|
||||||
if ( !!newName ) {
|
if ( !!newName ) {
|
||||||
CAST_GS(GameState*, gs, closure);
|
CAST_GS(GameState*, gs, closure);
|
||||||
nameGame( gs, newName );
|
nameGame( gs, newName );
|
||||||
|
show_name( newName );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,6 +592,28 @@ onDeleteConfirmed( void* closure, bool confirmed )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
getPlayerName( Globals* globals, char* playerName, size_t buflen )
|
||||||
|
{
|
||||||
|
XP_U32 len = buflen;
|
||||||
|
dutil_loadPtr( globals->dutil, NULL, KEY_PLAYER_NAME, NULL,
|
||||||
|
playerName, &len );
|
||||||
|
XP_LOGFF( "after: len: %d", len );
|
||||||
|
if ( 0 == len ) { /* not found? */
|
||||||
|
strcpy( playerName, "Player 1" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
onPlayerNamed( void* closure, const char* name )
|
||||||
|
{
|
||||||
|
CAST_GLOB(Globals*, globals, closure);
|
||||||
|
if ( !!name ) {
|
||||||
|
dutil_storePtr( globals->dutil, NULL, KEY_PLAYER_NAME,
|
||||||
|
name, 1 + strlen(name) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
onDeviceButton( void* closure, const char* button )
|
onDeviceButton( void* closure, const char* button )
|
||||||
{
|
{
|
||||||
|
@ -610,6 +635,11 @@ onDeviceButton( void* closure, const char* button )
|
||||||
"\nThis action cannot be undone.",
|
"\nThis action cannot be undone.",
|
||||||
curGS->gameName );
|
curGS->gameName );
|
||||||
call_confirm( globals, msg, onDeleteConfirmed, curGS );
|
call_confirm( globals, msg, onDeleteConfirmed, curGS );
|
||||||
|
} else if ( 0 == strcmp(button, BUTTON_NAME ) ) {
|
||||||
|
char playerName[32];
|
||||||
|
getPlayerName( globals, playerName, sizeof(playerName)-1 );
|
||||||
|
call_get_string( "Set your (local) player name", playerName,
|
||||||
|
onPlayerNamed, globals );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,6 +673,7 @@ updateDeviceButtons( Globals* globals )
|
||||||
buttons[cur++] = BUTTON_GAME_RENAME;
|
buttons[cur++] = BUTTON_GAME_RENAME;
|
||||||
buttons[cur++] = BUTTON_GAME_DELETE;
|
buttons[cur++] = BUTTON_GAME_DELETE;
|
||||||
}
|
}
|
||||||
|
buttons[cur++] = BUTTON_NAME;
|
||||||
buttons[cur++] = NULL;
|
buttons[cur++] = NULL;
|
||||||
|
|
||||||
setButtons( BUTTONS_ID_DEVICE, buttons, onDeviceButton, globals );
|
setButtons( BUTTONS_ID_DEVICE, buttons, onDeviceButton, globals );
|
||||||
|
@ -669,6 +700,46 @@ onFocussed( void* closure, const char* ignored )
|
||||||
/* } */
|
/* } */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DictionaryCtxt*
|
||||||
|
playLoadingDict( Globals* globals )
|
||||||
|
{
|
||||||
|
/* Looking at whether the storage system can hold a dict. Let's see if
|
||||||
|
it's stored, and if it isn't store it. They try passing a ptr for
|
||||||
|
making the actual dict from. */
|
||||||
|
|
||||||
|
XP_U32 len = 0;
|
||||||
|
dutil_loadIndxPtr( globals->dutil, NULL, KEY_DICTS, DICTNAME, NULL, &len );
|
||||||
|
if ( 0 == len ) {
|
||||||
|
XP_LOGFF( "not found; storing now..." );
|
||||||
|
XP_U32 dictLen;
|
||||||
|
uint8_t* dictBytes = wasm_dictionary_load(MPPARM(globals->mpool)
|
||||||
|
DICTNAME, &dictLen);
|
||||||
|
|
||||||
|
if ( !!dictBytes ) {
|
||||||
|
XP_LOGFF( "loaded %d bytes of dict", dictLen );
|
||||||
|
dutil_storeIndxPtr( globals->dutil, NULL, KEY_DICTS, DICTNAME,
|
||||||
|
dictBytes, dictLen );
|
||||||
|
XP_FREE( globals->mpool, dictBytes );
|
||||||
|
}
|
||||||
|
len = dictLen;
|
||||||
|
} else {
|
||||||
|
XP_LOGFF( "using stored wordlist of len %d", len );
|
||||||
|
}
|
||||||
|
|
||||||
|
XP_U32 oldLen = len;
|
||||||
|
XP_LOGFF( "oldLen: %d", oldLen );
|
||||||
|
uint8_t* bytes = XP_MALLOC( globals->mpool, len );
|
||||||
|
dutil_loadIndxPtr( globals->dutil, NULL, KEY_DICTS, DICTNAME,
|
||||||
|
bytes, &len );
|
||||||
|
XP_ASSERT( len == oldLen );
|
||||||
|
|
||||||
|
DictionaryCtxt* dict =
|
||||||
|
wasm_dictionary_make( MPPARM(globals->mpool) NULL,
|
||||||
|
globals, DICTNAME, false, bytes );
|
||||||
|
LOG_RETURNF( "%p", dict );
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
initDeviceGlobals( Globals* globals )
|
initDeviceGlobals( Globals* globals )
|
||||||
{
|
{
|
||||||
|
@ -687,8 +758,8 @@ initDeviceGlobals( Globals* globals )
|
||||||
globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globals->mpool) );
|
globals->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globals->mpool) );
|
||||||
globals->dutil = wasm_dutil_make( MPPARM(globals->mpool) globals->vtMgr, globals );
|
globals->dutil = wasm_dutil_make( MPPARM(globals->mpool) globals->vtMgr, globals );
|
||||||
globals->dictMgr = dmgr_make( MPPARM_NOCOMMA(globals->mpool) );
|
globals->dictMgr = dmgr_make( MPPARM_NOCOMMA(globals->mpool) );
|
||||||
globals->dict = wasm_dictionary_make( MPPARM(globals->mpool) NULL,
|
|
||||||
globals, DICTNAME, true );
|
globals->dict = playLoadingDict( globals );
|
||||||
|
|
||||||
dict_ref( globals->dict, NULL );
|
dict_ref( globals->dict, NULL );
|
||||||
|
|
||||||
|
@ -750,34 +821,6 @@ startGame( GameState* gs, const char* name )
|
||||||
LOG_RETURN_VOID();
|
LOG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct _AskReplaceState {
|
|
||||||
Globals* globals;
|
|
||||||
NetLaunchInfo invite;
|
|
||||||
} AskReplaceState;
|
|
||||||
|
|
||||||
static void
|
|
||||||
onPlayerNamed( void* closure, const char* name )
|
|
||||||
{
|
|
||||||
CAST_GS(GameState*, gs, closure);
|
|
||||||
if ( !!name ) {
|
|
||||||
dutil_storePtr( gs->globals->dutil, NULL, KEY_PLAYER_NAME,
|
|
||||||
name, 1 + strlen(name) );
|
|
||||||
startGame( gs, name );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
getPlayerName( Globals* globals, char* playerName, size_t buflen )
|
|
||||||
{
|
|
||||||
XP_U32 len = buflen;
|
|
||||||
dutil_loadPtr( globals->dutil, NULL, KEY_PLAYER_NAME, NULL,
|
|
||||||
playerName, &len );
|
|
||||||
XP_LOGFF( "after: len: %d", len );
|
|
||||||
if ( 0 == len ) { /* not found? */
|
|
||||||
strcpy( playerName, "Player 1" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static GameState*
|
static GameState*
|
||||||
newFromInvite( Globals* globals, const NetLaunchInfo* invite )
|
newFromInvite( Globals* globals, const NetLaunchInfo* invite )
|
||||||
{
|
{
|
||||||
|
@ -1051,8 +1094,6 @@ main_onGameMessage( Globals* globals, XP_U32 gameID,
|
||||||
if ( game_receiveMessage( &gs->game, NULL, stream, from ) ) {
|
if ( game_receiveMessage( &gs->game, NULL, stream, from ) ) {
|
||||||
updateScreen( gs, true );
|
updateScreen( gs, true );
|
||||||
}
|
}
|
||||||
if ( gs != getCurGame(gs->globals) ) {
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
char msg[128];
|
char msg[128];
|
||||||
snprintf( msg, sizeof(msg), "Dropping move for deleted game (id: %X/%d)",
|
snprintf( msg, sizeof(msg), "Dropping move for deleted game (id: %X/%d)",
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
.emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; }
|
||||||
textarea.emscripten { font-family: monospace; width: 80%; }
|
textarea.emscripten { font-family: monospace; width: 80%; }
|
||||||
body.centered { text-align: center; }
|
body.centered { text-align: center; }
|
||||||
|
table.centered {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
div.emscripten { text-align: center; }
|
div.emscripten { text-align: center; }
|
||||||
div.emscripten_border { border: 1px solid black; }
|
div.emscripten_border { border: 1px solid black; }
|
||||||
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
/* the canvas *must not* have any border or padding, or mouse coords will be wrong */
|
||||||
|
@ -90,8 +94,11 @@
|
||||||
<hr/>
|
<hr/>
|
||||||
<div class='buttonRow' id="device_buttons"></div>
|
<div class='buttonRow' id="device_buttons"></div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div><span>MQTT Dev ID:</span><span id="mqtt_span"></span></div>
|
<table class='centered'>
|
||||||
<div><span>MQTT Status:</span><span id="mqtt_status">Unconnected</span></div>
|
<tr><td>MQTT Dev ID:</td><td id="mqtt_span"></td></tr>
|
||||||
|
<tr><td>MQTT Status:</td><td id="mqtt_status">Unconnected</td></tr>
|
||||||
|
<tr><td>Storage used:</td><td id="storage_used">0</td></tr>
|
||||||
|
</table>
|
||||||
<hr/>
|
<hr/>
|
||||||
<!-- Remove until I figure out how to this works
|
<!-- Remove until I figure out how to this works
|
||||||
<div class="emscripten">
|
<div class="emscripten">
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* -*- compile-command: "cd ../wasm && make main.html -j3"; -*- */
|
/* -*- compile-command: "cd ../wasm && make MEMDEBUG=TRUE install -j3"; -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 by Eric House (xwords@eehouse.org). All rights reserved.
|
* Copyright 2021 by Eric House (xwords@eehouse.org). All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -49,7 +49,8 @@ getShortName( const DictionaryCtxt* dict )
|
||||||
}
|
}
|
||||||
|
|
||||||
static XP_Bool
|
static XP_Bool
|
||||||
initFromDictFile( WasmDictionaryCtxt* dctx, const char* fileName )
|
initFromDictFile( WasmDictionaryCtxt* dctx, const char* fileName,
|
||||||
|
uint8_t* dictBase )
|
||||||
{
|
{
|
||||||
XP_Bool formatOk = XP_TRUE;
|
XP_Bool formatOk = XP_TRUE;
|
||||||
size_t dictLength;
|
size_t dictLength;
|
||||||
|
@ -71,7 +72,9 @@ initFromDictFile( WasmDictionaryCtxt* dctx, const char* fileName )
|
||||||
{
|
{
|
||||||
FILE* dictF = fopen( path, "r" );
|
FILE* dictF = fopen( path, "r" );
|
||||||
XP_ASSERT( !!dictF );
|
XP_ASSERT( !!dictF );
|
||||||
if ( dctx->useMMap ) {
|
if ( !!dictBase ) {
|
||||||
|
dctx->dictBase = dictBase;
|
||||||
|
} else if ( dctx->useMMap ) {
|
||||||
dctx->dictBase = mmap( NULL, dctx->dictLength, PROT_READ,
|
dctx->dictBase = mmap( NULL, dctx->dictLength, PROT_READ,
|
||||||
MAP_PRIVATE, fileno(dictF), 0 );
|
MAP_PRIVATE, fileno(dictF), 0 );
|
||||||
} else {
|
} else {
|
||||||
|
@ -189,7 +192,8 @@ wasm_dictionary_destroy( DictionaryCtxt* dict, XWEnv xwe )
|
||||||
|
|
||||||
DictionaryCtxt*
|
DictionaryCtxt*
|
||||||
wasm_dictionary_make( MPFORMAL XWEnv xwe, Globals* globals,
|
wasm_dictionary_make( MPFORMAL XWEnv xwe, Globals* globals,
|
||||||
const char* dictFileName, bool useMMap )
|
const char* dictFileName, bool useMMap,
|
||||||
|
uint8_t* base )
|
||||||
{
|
{
|
||||||
WasmDictionaryCtxt* result = NULL;
|
WasmDictionaryCtxt* result = NULL;
|
||||||
if ( !!dictFileName ) {
|
if ( !!dictFileName ) {
|
||||||
|
@ -207,7 +211,7 @@ wasm_dictionary_make( MPFORMAL XWEnv xwe, Globals* globals,
|
||||||
result->useMMap = useMMap;
|
result->useMMap = useMMap;
|
||||||
|
|
||||||
if ( !!dictFileName ) {
|
if ( !!dictFileName ) {
|
||||||
XP_Bool success = initFromDictFile( result, dictFileName );
|
XP_Bool success = initFromDictFile( result, dictFileName, base );
|
||||||
if ( success ) {
|
if ( success ) {
|
||||||
result->super.func_dict_getShortName = getShortName;
|
result->super.func_dict_getShortName = getShortName;
|
||||||
setBlankTile( &result->super );
|
setBlankTile( &result->super );
|
||||||
|
@ -227,6 +231,25 @@ wasm_dictionary_make( MPFORMAL XWEnv xwe, Globals* globals,
|
||||||
return &result->super;
|
return &result->super;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t*
|
||||||
|
wasm_dictionary_load(MPFORMAL const char* dictFileName, XP_U32* len )
|
||||||
|
{
|
||||||
|
uint8_t* result = NULL;
|
||||||
|
*len = 0;
|
||||||
|
struct stat statbuf;
|
||||||
|
if ( 0 == stat( dictFileName, &statbuf ) && 0 != statbuf.st_size ) {
|
||||||
|
result = XP_MALLOC( mpool, statbuf.st_size );
|
||||||
|
FILE* dictF = fopen( dictFileName, "r" );
|
||||||
|
size_t nRead = fread( result, 1, statbuf.st_size, dictF );
|
||||||
|
XP_ASSERT( nRead == statbuf.st_size );
|
||||||
|
fclose( dictF );
|
||||||
|
*len = statbuf.st_size;
|
||||||
|
XP_LOGFF( "loaded %d bytes", statbuf.st_size );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dict_splitFaces( DictionaryCtxt* dict, XWEnv xwe, const XP_U8* utf8,
|
dict_splitFaces( DictionaryCtxt* dict, XWEnv xwe, const XP_U8* utf8,
|
||||||
XP_U16 nBytes, XP_U16 nFaces )
|
XP_U16 nBytes, XP_U16 nFaces )
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
DictionaryCtxt* wasm_dictionary_make( MPFORMAL XWEnv xwe, Globals* globals,
|
DictionaryCtxt* wasm_dictionary_make( MPFORMAL XWEnv xwe, Globals* globals,
|
||||||
const char* dictFileName, bool useMMap );
|
const char* dictFileName, bool useMMap,
|
||||||
|
uint8_t* base );
|
||||||
|
uint8_t* wasm_dictionary_load(MPFORMAL const char* dictFileName, XP_U32* len );
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -243,27 +243,29 @@ static void
|
||||||
wasm_dutil_loadPtr( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* key,
|
wasm_dutil_loadPtr( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* key,
|
||||||
const XP_UCHAR* keySuffix, void* data, XP_U32* lenp )
|
const XP_UCHAR* keySuffix, void* data, XP_U32* lenp )
|
||||||
{
|
{
|
||||||
// XP_LOGFF( "(key: %s)", key );
|
// XP_LOGFF( "(key: %s, len: %d)", key, *lenp );
|
||||||
MAKE_PREFIX(fullKey, key);
|
MAKE_PREFIX(fullKey, key);
|
||||||
|
|
||||||
size_t len;
|
size_t len;
|
||||||
get_stored_value(fullKey, NULL, &len);
|
get_stored_value(fullKey, NULL, &len);
|
||||||
|
|
||||||
char val[len];
|
char* val = XP_MALLOC( duc->mpool, len );
|
||||||
if ( get_stored_value( fullKey, val, &len ) ) {
|
if ( get_stored_value( fullKey, val, &len ) ) {
|
||||||
// XP_LOGFF( "get_stored_value(%s) => %s", fullKey, val );
|
// XP_LOGFF( "get_stored_value(%s) => %s", fullKey, val );
|
||||||
len = XP_STRLEN(val);
|
len = XP_STRLEN(val);
|
||||||
XP_ASSERT( (len % 2) == 0 );
|
XP_ASSERT( (len % 2) == 0 );
|
||||||
XP_U8 decodeBuf[len/2];
|
XP_U8* decodeBuf = XP_MALLOC( duc->mpool, len/2 );
|
||||||
len = VSIZE(decodeBuf);
|
len = len/2;
|
||||||
if ( len <= *lenp ) {
|
if ( len <= *lenp ) {
|
||||||
base16Decode( decodeBuf, len, val );
|
base16Decode( decodeBuf, len, val );
|
||||||
XP_MEMCPY( data, decodeBuf, len );
|
XP_MEMCPY( data, decodeBuf, len );
|
||||||
}
|
}
|
||||||
*lenp = len;
|
*lenp = len;
|
||||||
|
XP_FREE( duc->mpool, decodeBuf );
|
||||||
} else {
|
} else {
|
||||||
*lenp = 0; /* signal failure */
|
*lenp = 0; /* signal failure */
|
||||||
}
|
}
|
||||||
|
XP_FREE( duc->mpool, val );
|
||||||
// XP_LOGFF("(%s)=> len: %d", fullKey, *lenp );
|
// XP_LOGFF("(%s)=> len: %d", fullKey, *lenp );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,10 +273,11 @@ static void
|
||||||
wasm_dutil_storePtr( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* key,
|
wasm_dutil_storePtr( XW_DUtilCtxt* duc, XWEnv xwe, const XP_UCHAR* key,
|
||||||
const void* data, XP_U32 len )
|
const void* data, XP_U32 len )
|
||||||
{
|
{
|
||||||
XP_UCHAR out[len*2+1];
|
XP_UCHAR* out = XP_MALLOC( duc->mpool, len*2+1 );
|
||||||
base16Encode( data, len, out, sizeof(out) );
|
base16Encode( data, len, out, sizeof(out) );
|
||||||
MAKE_PREFIX(fullKey, key);
|
MAKE_PREFIX(fullKey, key);
|
||||||
set_stored_value( fullKey, out );
|
set_stored_value( fullKey, out );
|
||||||
|
XP_FREE( duc->mpool, out );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -411,7 +411,8 @@ static DictionaryCtxt*
|
||||||
wasm_util_makeEmptyDict( XW_UtilCtxt* uc, XWEnv xwe )
|
wasm_util_makeEmptyDict( XW_UtilCtxt* uc, XWEnv xwe )
|
||||||
{
|
{
|
||||||
LOG_FUNC(); /* firing */
|
LOG_FUNC(); /* firing */
|
||||||
return wasm_dictionary_make( MPPARM(uc->mpool) NULL, uc->closure, NULL, false );
|
return wasm_dictionary_make( MPPARM(uc->mpool) NULL, uc->closure,
|
||||||
|
NULL, false, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -136,6 +136,14 @@ function onHaveDevID(closure, devid, gitrev, now, noTabProc, focusProc) {
|
||||||
onFailure: function() { alert('onFailure'); },
|
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 ) {
|
function mqttSend( topic, ptr ) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue