2020-08-21 13:59:43 +00:00
if ( window . location . host == "banglejs.com" ) {
document . getElementById ( "apploaderlinks" ) . innerHTML =
'This is the official Bangle.js App Loader - you can also try the <a href="https://espruino.github.io/BangleApps/">Development Version</a> for the most recent apps.' ;
} else if ( window . location . host == "espruino.github.io" ) {
document . title += " [Development]" ;
document . getElementById ( "apploaderlinks" ) . innerHTML =
'This is the development Bangle.js App Loader - you can also try the <a href="https://banglejs.com/apps/">Official Version</a> for stable apps.' ;
2022-01-19 19:19:56 +00:00
} else if ( window . location . hostname === 'localhost' ) {
document . title += " [Local]" ;
Const . APPS _JSON _FILE = "apps.local.json" ;
document . getElementById ( "apploaderlinks" ) . innerHTML =
'This is your local Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.' ;
2020-08-21 13:59:43 +00:00
} else {
document . title += " [Unofficial]" ;
document . getElementById ( "apploaderlinks" ) . innerHTML =
'This is not the official Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.' ;
}
2024-08-07 07:56:54 +00:00
var RECOMMENDED _VERSION = "2v24" ;
2020-10-21 13:48:30 +00:00
// could check http://www.espruino.com/json/BANGLEJS.json for this
2021-10-20 14:20:25 +00:00
// We're only interested in Bangles
DEVICEINFO = DEVICEINFO . filter ( x => x . id . startsWith ( "BANGLEJS" ) ) ;
2022-11-04 15:14:05 +00:00
// Where we get our usage data from
Const . APP _USAGE _JSON = "https://banglejs.com/apps/appusage.json" ;
Const . APP _DATES _CSV = "appdates.csv" ;
2021-10-20 14:20:25 +00:00
// Set up source code URL
2020-08-21 13:59:43 +00:00
( function ( ) {
let username = "espruino" ;
2022-04-28 09:14:10 +00:00
let githubMatch = window . location . href . match ( /\/([\w-]+)\.github\.io/ ) ;
2020-08-21 13:59:43 +00:00
if ( githubMatch ) username = githubMatch [ 1 ] ;
2020-10-21 13:48:30 +00:00
Const . APP _SOURCECODE _URL = ` https://github.com/ ${ username } /BangleApps/tree/master/apps ` ;
2020-08-21 13:59:43 +00:00
} ) ( ) ;
2020-10-21 13:48:30 +00:00
2021-10-20 14:20:25 +00:00
// When a device is found, filter the apps accordingly
2020-10-21 13:48:30 +00:00
function onFoundDeviceInfo ( deviceId , deviceVersion ) {
2022-01-27 16:51:06 +00:00
var fwURL = "#" , fwExtraText = "" ;
2021-06-30 12:24:58 +00:00
if ( deviceId == "BANGLEJS" ) {
2021-10-29 07:33:31 +00:00
fwURL = "https://www.espruino.com/Bangle.js#firmware-updates" ;
2021-06-30 12:24:58 +00:00
Const . MESSAGE _RELOAD = 'Hold BTN3\nto reload' ;
}
if ( deviceId == "BANGLEJS2" ) {
2022-01-27 16:51:06 +00:00
fwExtraText = "with the <b>Firmware Update</b> app in this App Loader, or "
2021-10-29 07:33:31 +00:00
fwURL = "https://www.espruino.com/Bangle.js2#firmware-updates" ;
2021-06-30 15:22:49 +00:00
Const . MESSAGE _RELOAD = 'Hold button\nto reload' ;
2021-06-30 12:24:58 +00:00
}
2021-10-20 14:20:25 +00:00
2021-10-29 07:33:31 +00:00
if ( deviceId != "BANGLEJS" && deviceId != "BANGLEJS2" ) {
showToast ( ` You're using ${ deviceId } , not a Bangle.js. Did you want <a href="https://espruino.com/apps">espruino.com/apps</a> instead? ` , "warning" , 20000 ) ;
} else if ( versionLess ( deviceVersion , RECOMMENDED _VERSION ) ) {
2022-12-18 12:26:45 +00:00
showToast ( ` You're using an old Bangle.js firmware ( ${ deviceVersion } ) and ${ RECOMMENDED _VERSION } is available (<a href="https://www.espruino.com/ChangeLog" target="_blank">see changes</a>). You can update ${ fwExtraText } <a href=" ${ fwURL } " target="_blank">with the instructions here</a> ` , "warning" , 20000 ) ;
2021-10-29 07:33:31 +00:00
}
2021-10-20 14:20:25 +00:00
// check against features shown?
filterAppsForDevice ( deviceId ) ;
/ * i f w e ' d s a v e d a d e v i c e I D b u t t h i s d e v i c e i s d i f f e r e n t , e n s u r e
we ask again next time * /
var savedDeviceId = getSavedDeviceId ( ) ;
if ( savedDeviceId !== undefined && savedDeviceId != deviceId )
setSavedDeviceId ( undefined ) ;
}
2022-10-19 14:33:41 +00:00
// Called when we refresh the list of installed apps
function onRefreshMyApps ( ) {
/ * i f w e ' r e a l l o w e d t o , s e n d u s a g e s t a t s . W e ' l l o n l y
actually send if the data has changed * /
sendUsageStats ( ) ;
}
var submittedUsageInfo = "" ;
/* Send usage stats to servers if it has changed */
function sendUsageStats ( ) {
if ( ! SETTINGS . sendUsageStats ) return ; // not allowed!
if ( device . uid === undefined ) return ; // no data yet!
2023-01-18 14:14:18 +00:00
if ( ! device . appsInstalled . length ) return ; // no installed apps or disconnected
2022-10-19 14:33:41 +00:00
/ * W o r k o u t w h a t w e ' l l s e n d :
* A suitably garbled UID so we can avoid too many duplicates
* firmware version
* apps installed
* apps favourited
* /
var usageInfo = ` uid= ${ encodeURIComponent ( device . uid ) } &fw= ${ encodeURIComponent ( device . version ) } &apps= ${ encodeURIComponent ( device . appsInstalled . map ( a => a . id ) . join ( "," ) ) } &favs= ${ encodeURIComponent ( SETTINGS . favourites . join ( "," ) ) } ` ;
// Do a quick check for unchanged data to reduce server load
if ( usageInfo != submittedUsageInfo ) {
console . log ( "sendUsageStats: Submitting usage stats..." ) ;
var xmlhttp = new XMLHttpRequest ( ) ; // new HttpRequest instance
xmlhttp . open ( "POST" , "https://banglejs.com/submit_app_stats.php" , true /*async*/ ) ;
xmlhttp . setRequestHeader ( "Content-Type" , "application/x-www-form-urlencoded" ) ;
xmlhttp . onload = ( e ) => {
if ( xmlhttp . readyState === 4 )
console . log ( ` sendUsageStats ( ${ xmlhttp . status } ): ${ xmlhttp . responseText } ` ) ;
} ;
xmlhttp . onerror = ( e ) => {
console . error ( "sendUsageStats ERROR: " + xmlhttp . statusText ) ;
} ;
xmlhttp . send ( usageInfo ) ;
submittedUsageInfo = usageInfo ;
}
}
2021-10-20 14:20:25 +00:00
var originalAppJSON = undefined ;
function filterAppsForDevice ( deviceId ) {
2021-11-10 13:06:15 +00:00
if ( originalAppJSON === undefined && appJSON . length )
2021-10-20 14:20:25 +00:00
originalAppJSON = appJSON ;
var device = DEVICEINFO . find ( d => d . id == deviceId ) ;
// set the device dropdown
document . querySelector ( ".devicetype-nav span" ) . innerText = device ? device . name : "All apps" ;
2023-05-05 12:52:00 +00:00
if ( originalAppJSON ) { // JSON might not have loaded yet
if ( ! device ) {
if ( deviceId !== undefined )
showToast ( ` Device ID ${ deviceId } not recognised. Some apps may not work ` , "warning" ) ;
appJSON = originalAppJSON ;
} else {
// Now filter apps
appJSON = originalAppJSON . filter ( app => {
var supported = [ "BANGLEJS" ] ;
if ( ! app . supports ) {
console . log ( ` App ${ app . id } doesn't include a 'supports' field - ignoring ` ) ;
return false ;
}
if ( app . supports . includes ( deviceId ) ) return true ;
//console.log(`Dropping ${app.id} because ${deviceId} is not in supported list ${app.supports.join(",")}`);
2021-10-20 14:20:25 +00:00
return false ;
2023-05-05 12:52:00 +00:00
} ) ;
}
2021-10-20 14:20:25 +00:00
}
refreshLibrary ( ) ;
2020-10-21 13:48:30 +00:00
}
2021-10-20 14:20:25 +00:00
// If 'remember' was checked in the window below, this is the device
function getSavedDeviceId ( ) {
let deviceId = localStorage . getItem ( "deviceId" ) ;
if ( ( "string" == typeof deviceId ) && DEVICEINFO . find ( d => d . id == deviceId ) )
return deviceId ;
return undefined ;
}
function setSavedDeviceId ( deviceId ) {
localStorage . setItem ( "deviceId" , deviceId ) ;
}
// At boot, show a window to choose which type of device you have...
window . addEventListener ( 'load' , ( event ) => {
let deviceId = getSavedDeviceId ( )
2021-11-10 13:06:15 +00:00
if ( deviceId !== undefined ) return ; // already chosen
2021-10-20 14:20:25 +00:00
var html = ` <div class="columns">
$ { DEVICEINFO . map ( d => `
< div class = "column col-6 col-xs-6" >
< div class = "card devicechooser" deviceid = "${d.id}" style = "cursor:pointer" >
< div class = "card-header" >
< div class = "card-title h5" > $ { d . name } < / d i v >
<!-- < div class = "card-subtitle text-gray" > ... < / d i v > - - >
< / d i v >
< div class = "card-image" >
< img src = "${d.img}" alt = "${d.name}" width = "256" height = "256" class = "img-responsive" >
< / d i v >
< / d i v >
< / d i v > ` ) . j o i n ( " \ n " ) }
< / d i v > < d i v c l a s s = " c o l u m n s " >
< div class = "column col-12" >
< div class = "form-group" >
2022-11-07 12:03:32 +00:00
< label class = "form-switch" >
< input type = "checkbox" id = "usage_stats" $ { SETTINGS . sendUsageStats ? "checked" : "" } >
2022-11-07 14:09:07 +00:00
< i class = "form-icon" > < /i> Send favourite and installed apps to banglejs.com<br/ >
2022-11-07 14:45:33 +00:00
< small > For 'Sort by Installed/Favourited' functionality ( see < a href = "http://www.espruino.com/Privacy" > privacy policy < / a > ) < / s m a l l >
2022-11-07 12:03:32 +00:00
< / l a b e l >
2021-10-20 14:20:25 +00:00
< label class = "form-switch" >
< input type = "checkbox" id = "remember_device" >
< i class = "form-icon" > < / i > D o n ' t a s k a g a i n
< / l a b e l >
< / d i v >
< / d i v >
< / d i v > ` ;
showPrompt ( "Which Bangle.js?" , html , { } , false ) ;
2022-11-07 14:09:07 +00:00
var usageStats = document . getElementById ( "usage_stats" ) ;
usageStats . addEventListener ( "change" , event => {
console . log ( "Send Usage Stats " + ( event . target . checked ? "on" : "off" ) ) ;
SETTINGS . sendUsageStats = event . target . checked ;
saveSettings ( ) ;
} ) ;
2021-10-20 14:20:25 +00:00
htmlToArray ( document . querySelectorAll ( ".devicechooser" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
2022-11-07 12:03:32 +00:00
let rememberDevice = ! ! document . getElementById ( "remember_device" ) . checked ;
2021-10-20 14:20:25 +00:00
let button = event . currentTarget ;
let deviceId = button . getAttribute ( "deviceid" ) ;
hidePrompt ( ) ;
console . log ( "Chosen device" , deviceId ) ;
setSavedDeviceId ( rememberDevice ? deviceId : undefined ) ;
filterAppsForDevice ( deviceId ) ;
} ) ;
} ) ;
} ) ;
window . addEventListener ( 'load' , ( event ) => {
2021-10-21 09:14:40 +00:00
// Hook onto device chooser dropdown
2021-10-20 14:20:25 +00:00
htmlToArray ( document . querySelectorAll ( ".devicetype-nav .menu-item" ) ) . forEach ( button => {
button . addEventListener ( "click" , event => {
var a = event . target ;
var deviceId = a . getAttribute ( "dt" ) || undefined ;
filterAppsForDevice ( deviceId ) ; // also sets the device dropdown
setSavedDeviceId ( undefined ) ; // ask at startup next time
document . querySelector ( ".devicetype-nav span" ) . innerText = a . innerText ;
} ) ;
} ) ;
2021-10-21 09:14:40 +00:00
2023-05-05 09:47:49 +00:00
var el ;
2022-03-30 14:55:31 +00:00
// Button to install all default apps in one go
2023-05-05 09:47:49 +00:00
el = document . getElementById ( "reinstallall" ) ;
if ( el ) el . addEventListener ( "click" , event => {
2022-03-30 14:55:31 +00:00
var promise = showPrompt ( "Reinstall" , "Really re-install all apps?" ) . then ( ( ) => {
2022-11-11 14:26:53 +00:00
Comms . reset ( ) . then ( _ =>
getInstalledApps ( )
) . then ( installedapps => {
2022-03-30 14:55:31 +00:00
console . log ( installedapps ) ;
var promise = Promise . resolve ( ) ;
installedapps . forEach ( app => {
if ( app . custom )
return console . log ( ` Ignoring ${ app . id } as customised ` ) ;
var oldApp = app ;
app = appJSON . find ( a => a . id == oldApp . id ) ;
if ( ! app )
return console . log ( ` Ignoring ${ oldApp . id } as not found ` ) ;
2022-11-11 14:26:53 +00:00
promise = promise . then ( ( ) => updateApp ( app , { noReset : true , noFinish : true } ) ) ;
2022-03-30 14:55:31 +00:00
} ) ;
return promise ;
2022-11-11 14:26:53 +00:00
} ) . then ( _ =>
Comms . showUploadFinished ( )
) . catch ( err => {
2022-03-30 14:55:31 +00:00
Progress . hide ( { sticky : true } ) ;
showToast ( "App re-install failed, " + err , "error" ) ;
} ) ;
} ) ;
} ) ;
2023-09-14 09:36:42 +00:00
2021-10-21 09:14:40 +00:00
// Button to install all default apps in one go
2023-05-05 09:47:49 +00:00
el = document . getElementById ( "installdefault" ) ;
if ( el ) el . addEventListener ( "click" , event => {
2021-10-21 09:14:40 +00:00
getInstalledApps ( ) . then ( ( ) => {
if ( device . id == "BANGLEJS" )
2021-10-29 07:33:31 +00:00
return httpGet ( "defaultapps_banglejs1.json" ) ;
2021-10-21 09:14:40 +00:00
if ( device . id == "BANGLEJS2" )
return httpGet ( "defaultapps_banglejs2.json" ) ;
throw new Error ( "Unknown device " + device . id ) ;
} ) . then ( json => {
return installMultipleApps ( JSON . parse ( json ) , "default" ) ;
} ) . catch ( err => {
Progress . hide ( { sticky : true } ) ;
showToast ( "App Install failed, " + err , "error" ) ;
} ) ;
} ) ;
2022-01-18 16:21:30 +00:00
2023-05-04 11:07:01 +00:00
// Button to reset the Bangle's settings
2023-05-05 09:47:49 +00:00
el = document . getElementById ( "defaultbanglesettings" ) ;
if ( el ) el . addEventListener ( "click" , event => {
2023-05-04 11:07:01 +00:00
showPrompt ( "Reset Settings" , "Really reset Bangle.js settings?" ) . then ( ( ) => {
Comms . write ( "\x10require('Storage').erase('setting.json');load()\n" ) ;
showToast ( "Settings reset!" , "success" ) ;
} , function ( ) { /* cancelled */ } ) ;
} ) ;
2023-09-14 09:36:42 +00:00
2023-05-04 11:07:01 +00:00
2022-05-16 13:32:35 +00:00
// BLE Compatibility
2022-07-26 08:58:54 +00:00
var selectBLECompat = document . getElementById ( "settings-ble-compat" ) ;
2023-01-19 12:54:46 +00:00
if ( selectBLECompat ) {
2022-05-16 13:32:35 +00:00
Puck . increaseMTU = ! SETTINGS . bleCompat ;
2023-01-19 12:54:46 +00:00
selectBLECompat . checked = ! ! SETTINGS . bleCompat ;
selectBLECompat . addEventListener ( "change" , event => {
console . log ( "BLE compatibility mode " + ( event . target . checked ? "on" : "off" ) ) ;
SETTINGS . bleCompat = event . target . checked ;
Puck . increaseMTU = ! SETTINGS . bleCompat ;
saveSettings ( ) ;
} ) ;
}
2022-05-16 13:32:35 +00:00
2022-10-19 14:33:41 +00:00
// Sending usage stats
var selectUsageStats = document . getElementById ( "settings-usage-stats" ) ;
2023-01-19 12:54:46 +00:00
if ( selectUsageStats ) {
selectUsageStats . checked = ! ! SETTINGS . sendUsageStats ;
selectUsageStats . addEventListener ( "change" , event => {
console . log ( "Send Usage Stats " + ( event . target . checked ? "on" : "off" ) ) ;
SETTINGS . sendUsageStats = event . target . checked ;
saveSettings ( ) ;
} ) ;
}
2022-10-19 14:33:41 +00:00
2022-01-18 16:21:30 +00:00
// Load language list
httpGet ( "lang/index.json" ) . then ( languagesJSON => {
var languages ;
try {
languages = JSON . parse ( languagesJSON ) ;
} catch ( e ) {
console . error ( "lang/index.json Corrupted" , e ) ;
}
2022-02-11 11:33:07 +00:00
languages = languages . filter ( l => l . disabled === undefined ) ;
2022-01-18 16:21:30 +00:00
function reloadLanguage ( ) {
LANGUAGE = undefined ;
if ( SETTINGS . language ) {
var language = languages . find ( l => l . code == SETTINGS . language ) ;
if ( language ) {
var langURL = "lang/" + language . url ;
httpGet ( langURL ) . then ( languageJSON => {
try {
LANGUAGE = JSON . parse ( languageJSON ) ;
console . log ( ` ${ langURL } loaded successfully ` ) ;
} catch ( e ) {
console . error ( ` ${ langURL } Corrupted ` , e ) ;
}
} ) ;
} else {
console . error ( ` Language code ${ JSON . stringify ( SETTINGS . language ) } not found in lang/index.json ` ) ;
}
}
}
var selectLang = document . getElementById ( "settings-lang" ) ;
languages . forEach ( lang => {
selectLang . innerHTML += ` <option value=" ${ lang . code } " ${ SETTINGS . language == lang . code ? "selected" : "" } > ${ lang . name } ( ${ lang . code } )</option> ` ;
} ) ;
selectLang . addEventListener ( "change" , event => {
SETTINGS . language = event . target . value ;
2022-01-27 16:51:06 +00:00
reloadLanguage ( ) ;
2022-01-18 16:21:30 +00:00
saveSettings ( ) ;
} ) ;
reloadLanguage ( ) ;
} ) ;
2021-10-20 14:20:25 +00:00
} ) ;
2021-10-26 12:15:21 +00:00
function onAppJSONLoaded ( ) {
2021-11-10 13:06:15 +00:00
let deviceId = getSavedDeviceId ( )
if ( deviceId !== undefined )
filterAppsForDevice ( deviceId ) ;
2021-11-24 12:56:56 +00:00
/ * D i s a b l e e x t e r n a l s c r e e n s h o t l o a d i n g - s e e m s w e p r o b a b l y h a v e e n o u g h
screenshots added manually in apps . json * /
/ * r e t u r n n e w P r o m i s e ( r e s o l v e = > {
2021-10-26 12:15:21 +00:00
httpGet ( "screenshots.json" ) . then ( screenshotJSON => {
var screenshots = [ ] ;
try {
screenshots = JSON . parse ( screenshotJSON ) ;
} catch ( e ) {
console . error ( "Screenshot JSON Corrupted" , e ) ;
}
screenshots . forEach ( s => {
var app = appJSON . find ( a => a . id == s . id ) ;
if ( ! app ) return ;
if ( ! app . screenshots ) app . screenshots = [ ] ;
app . screenshots . push ( { url : s . url } ) ;
} )
} ) . catch ( err => {
console . log ( "No screenshots.json found" ) ;
resolve ( ) ;
} ) ;
2021-11-24 12:56:56 +00:00
} ) ; * /
2021-10-26 12:15:21 +00:00
}