Initial commit
This commit is contained in:
commit
a4ceb7ffe3
32 files changed
+1896
No files matched your search
@@ -0,0 +1,2 @@
|
||||
secrets.js
|
||||
.DS_Store
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
palm-package "%cd%" -o "./_bin"
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
mkdir -p $DIR/../_bin
|
||||
palm-package $DIR/../ -o $DIR/../_bin
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
palm-run "%cd%"
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
palm-run $DIR/../
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "palm-run",
|
||||
"type": "shell",
|
||||
"command": "./.vscode/run.sh",
|
||||
"windows": {
|
||||
"command": ".\\.vscode\\run.bat"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "palm-build",
|
||||
"type": "shell",
|
||||
"command": "./.vscode/build.sh",
|
||||
"windows": {
|
||||
"command": ".\\.vscode\\build.bat"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Jon W
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Simple Chat client app for webOS
|
||||
|
||||
SimpleChat client for legacy Palm/HP webOS devices, depends on back-end services listed below.
|
||||
|
||||
Uses [simplechat-service](https://github.com/codepoet80/simplechat-service)
|
||||
|
||||
You can also self-host the services.
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
In the app assistant, we setup some app-wide global objects and handle different kinds of launches, creating and delegating to the main stage
|
||||
*/
|
||||
var appModel = null;
|
||||
var updaterModel = null;
|
||||
var systemModel = null;
|
||||
|
||||
function AppAssistant() {
|
||||
appModel = new AppModel();
|
||||
updaterModel = new UpdaterModel();
|
||||
systemModel = new SystemModel();
|
||||
Mojo.Additions = Additions;
|
||||
}
|
||||
|
||||
//This function will handle relaunching the app when an alarm goes off(see the device/alarm scene)
|
||||
AppAssistant.prototype.handleLaunch = function(params) {
|
||||
|
||||
//get the proxy for the stage in the event it already exists (eg: app is currently open)
|
||||
var mainStage = this.controller.getStageProxy("");
|
||||
Mojo.Log.info("SimpleChat is Launching! Launch params: " + JSON.stringify(params));
|
||||
|
||||
//if there was a search query, load with that
|
||||
if (params && params["query"] != undefined) {
|
||||
appModel.LaunchQuery = decodeURI(params["query"]);
|
||||
Mojo.Log.info("Launch query was: " + appModel.LaunchQuery);
|
||||
}
|
||||
|
||||
//if the stage already exists then just bring it into focus
|
||||
if (mainStage) {
|
||||
var stageController = this.controller.getStageController("");
|
||||
stageController.activate();
|
||||
}
|
||||
return;
|
||||
};
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
SimpleChat client app for webOS.
|
||||
This app depends on a SimpleChat service, provided by webOS Archive at no cost for what remains of the webOS mobile community.
|
||||
*/
|
||||
|
||||
function MainAssistant() {
|
||||
/* this is the creator function for your scene assistant object. It will be passed all the
|
||||
additional parameters (after the scene name) that were passed to pushScene. The reference
|
||||
to the scene controller (this.controller) has not be established yet, so any initialization
|
||||
that needs the scene controller should be done in the setup function below. */
|
||||
}
|
||||
|
||||
MainAssistant.prototype.setup = function() {
|
||||
|
||||
// setup message field
|
||||
this.messageFieldModel = {
|
||||
'original': ''
|
||||
};
|
||||
this.messageFieldModel.attributes = {
|
||||
hintText: $L("Enter a message..."),
|
||||
enterSubmits: true,
|
||||
focus: true,
|
||||
multiline: false,
|
||||
textCase: Mojo.Widget.steModeSentenceCase,
|
||||
focusMode: Mojo.Widget.focusSelectMode,
|
||||
autoReplace: true,
|
||||
requiresEnterKey: true,
|
||||
changeOnKeyPress: false
|
||||
};
|
||||
this.controller.setupWidget(
|
||||
'txtMessage',
|
||||
this.messageFieldModel.attributes,
|
||||
this.messageFieldModel
|
||||
);
|
||||
//Loading spinner - with global members for easy toggling later
|
||||
this.spinnerAttrs = {
|
||||
spinnerSize: Mojo.Widget.spinnerSmall
|
||||
};
|
||||
this.spinnerModel = {
|
||||
spinning: false
|
||||
}
|
||||
this.controller.setupWidget('workingSpinner', this.spinnerAttrs, this.spinnerModel);
|
||||
//Chat Log List (starts empty)
|
||||
this.listElement = this.controller.get('chatList');
|
||||
this.listInfoModel = {
|
||||
items: [] //{ uid: "-1", sender: "none", message: "" }
|
||||
};
|
||||
//Chat Log List templates (loads other HTML)
|
||||
this.template = {
|
||||
itemTemplate: 'main/item-template',
|
||||
listTemplate: 'main/list-template',
|
||||
swipeToDelete: false,
|
||||
renderLimit: 25,
|
||||
reorderable: false
|
||||
};
|
||||
this.controller.setupWidget('chatList', this.template, this.listInfoModel);
|
||||
//Scroller
|
||||
this.scrollerModel = {
|
||||
mode: 'vertical',
|
||||
weight: 'light',
|
||||
friction: 'low'
|
||||
};
|
||||
this.controller.setupWidget('chatScroller', {}, this.scrollerModel);
|
||||
this.chatScroller = this.controller.get("chatScroller");
|
||||
this.scalingFactor = this.controller.window.zoomFactor || 1;
|
||||
//TODO: This will be different on different devices
|
||||
this.scaledHeight = Math.floor(Mojo.Environment.DeviceInfo.screenHeight / this.scalingFactor) - 320;
|
||||
this.chatScroller.style.height = this.scaledHeight + "px";
|
||||
//Menu
|
||||
this.appMenuAttributes = { omitDefaultItems: true };
|
||||
this.appMenuModel = {
|
||||
label: "Settings",
|
||||
items: [
|
||||
Mojo.Menu.editItem,
|
||||
{ label: "Change Username", command: 'do-Username' },
|
||||
{ label: "Preferences", command: 'do-Preferences' },
|
||||
{ label: "About", command: 'do-myAbout' }
|
||||
]
|
||||
};
|
||||
this.controller.setupWidget(Mojo.Menu.appMenu, this.appMenuAttributes, this.appMenuModel);
|
||||
|
||||
/* Always on Event handlers */
|
||||
Mojo.Event.listen(this.controller.get("txtMessage"), Mojo.Event.propertyChange, this.handleSendMessage.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("chatList"), Mojo.Event.listTap, this.handleListClick.bind(this));
|
||||
|
||||
//Check for updates
|
||||
if (!appModel.UpdateCheckDone) {
|
||||
appModel.UpdateCheckDone = true;
|
||||
updaterModel.CheckForUpdate("SimpleChat", this.handleUpdateResponse.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
MainAssistant.prototype.handleUpdateResponse = function(responseObj) {
|
||||
if (responseObj && responseObj.updateFound) {
|
||||
updaterModel.PromptUserForUpdate(function(response) {
|
||||
if (response)
|
||||
updaterModel.InstallUpdate();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
MainAssistant.prototype.activate = function(event) {
|
||||
//Load preferences
|
||||
appModel.LoadSettings();
|
||||
Mojo.Log.info("settings now: " + JSON.stringify(appModel.AppSettingsCurrent));
|
||||
//Figure out server info
|
||||
this.serviceEndpointBase = appModel.ServiceEndpointBase;
|
||||
if (appModel.AppSettingsCurrent["UseCustomEndpoint"] && appModel.AppSettingsCurrent["EndpointURL"]) {
|
||||
this.serviceEndpointBase = appModel.AppSettingsCurrent["EndpointURL"];
|
||||
}
|
||||
this.clientId = appModel.ClientId;
|
||||
if (appModel.AppSettingsCurrent["UseClientAPIKey"] && appModel.AppSettingsCurrent["ClientAPIKey"]) {
|
||||
this.clientId = appModel.AppSettingsCurrent["ClientAPIKey"];
|
||||
}
|
||||
Mojo.Log.warn("*** Using secret: " + this.clientId + " because secrets were: " + secrets.clientid);
|
||||
//Figure out if this is our first time
|
||||
if (appModel.AppSettingsCurrent["FirstRun"]) {
|
||||
appModel.AppSettingsCurrent["FirstRun"] = false;
|
||||
var pretendEvent = {
|
||||
type: Mojo.Event.command,
|
||||
command: 'do-Username'
|
||||
}
|
||||
this.handleCommand(pretendEvent);
|
||||
} else {
|
||||
this.controller.get('txtMessage').mojo.focus();
|
||||
}
|
||||
//Find out what kind of device this is
|
||||
if (Mojo.Environment.DeviceInfo.platformVersionMajor >= 3) {
|
||||
this.DeviceType = "TouchPad";
|
||||
Mojo.Log.info("Device detected as TouchPad");
|
||||
} else {
|
||||
if (window.screen.width == 800 || window.screen.height == 800) {
|
||||
this.DeviceType = "Pre3";
|
||||
Mojo.Log.info("Device detected as Pre3");
|
||||
} else if ((window.screen.width == 480 || window.screen.height == 480) && (window.screen.width == 320 || window.screen.height == 320)) {
|
||||
this.DeviceType = "Pre";
|
||||
Mojo.Log.warn("Device detected as Pre or Pre2");
|
||||
} else {
|
||||
this.DeviceType = "Tiny";
|
||||
Mojo.Log.warn("Device detected as Pixi or Veer");
|
||||
}
|
||||
}
|
||||
systemModel.PreventDisplaySleep();
|
||||
this.pendingMessages = [];
|
||||
this.firstPoll = true;
|
||||
this.startPollingServer();
|
||||
|
||||
};
|
||||
|
||||
MainAssistant.prototype.startPollingServer = function() {
|
||||
this.getChats();
|
||||
var useInt = 10000;
|
||||
if (appModel.AppSettingsCurrent["ForegroundUpdate"])
|
||||
useInt = appModel.AppSettingsCurrent["ForegroundUpdate"];
|
||||
this.FileCheckInt = setInterval(this.getChats.bind(this), useInt);
|
||||
}
|
||||
|
||||
MainAssistant.prototype.pausePollingServer = function() {
|
||||
this.getChats();
|
||||
clearInterval(this.FileCheckInt);
|
||||
}
|
||||
|
||||
//Handle menu and button bar commands
|
||||
MainAssistant.prototype.handleCommand = function(event) {
|
||||
if (event.type == Mojo.Event.command) {
|
||||
switch (event.command) {
|
||||
case 'do-Username':
|
||||
this.pausePollingServer();
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
this.controller.showDialog({
|
||||
template: 'user/user-scene',
|
||||
assistant: new UserAssistant(this, function(val) {
|
||||
Mojo.Log.error("got value from dialog: " + val);
|
||||
this.startPollingServer();
|
||||
//this.handleDialogDone(val);
|
||||
}.bind(this)) //since this will be a dialog, not a scene, it must be defined in sources.json without a 'scenes' member
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'do-Preferences':
|
||||
this.pausePollingServer();
|
||||
var stageController = Mojo.Controller.stageController;
|
||||
stageController.pushScene({ name: "preferences", disableSceneScroller: false });
|
||||
break;
|
||||
case 'do-myAbout':
|
||||
Mojo.Additions.ShowDialogBox("SimpleChat - " + Mojo.Controller.appInfo.version, "SimpleChat client for webOS. Copyright 2021, Jon Wise. Distributed under an MIT License.<br>Source code available at: https://github.com/codepoet80/webos-simplechat");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Handle mojo button taps
|
||||
MainAssistant.prototype.handleClick = function(event) {
|
||||
|
||||
//Nothing to do right now
|
||||
}
|
||||
|
||||
MainAssistant.prototype.handleSendMessage = function(event) {
|
||||
|
||||
this.controller.get('txtMessage').mojo.blur();
|
||||
var newMessage = this.controller.get('txtMessage').mojo.getValue();
|
||||
this.disableUI();
|
||||
|
||||
this.postChatToService(appModel.AppSettingsCurrent["SenderName"], newMessage, "", function(response) {
|
||||
this.controller.get('txtMessage').mojo.setValue("");
|
||||
this.enableUI();
|
||||
try {
|
||||
var responseObj = JSON.parse(response);
|
||||
if (responseObj.error) {
|
||||
//Handle error
|
||||
Mojo.Log.error("Server error returned: " + responseObj.error);
|
||||
} else {
|
||||
Mojo.Log.info("message went through: " + responseObj.posted);
|
||||
if (responseObj.posted && responseObj.posted != "") {
|
||||
var newMsg = {
|
||||
uid: responseObj.posted,
|
||||
sender: appModel.AppSettingsCurrent["SenderName"],
|
||||
message: newMessage,
|
||||
timestamp: this.convertTimeStamp(new Date(), false),
|
||||
color: "gray"
|
||||
};
|
||||
this.pendingMessages.push(responseObj.posted);
|
||||
Mojo.Log.info("message:" + JSON.stringify(newMsg));
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
|
||||
thisWidgetSetup.model.items.push(newMsg);
|
||||
this.controller.modelChanged(thisWidgetSetup.model);
|
||||
this.chatScroller.mojo.revealBottom();
|
||||
this.chatScroller.mojo.adjustBy(0, -200);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
//Handle error
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
//Handle list item taps
|
||||
MainAssistant.prototype.handleListClick = function(event) {
|
||||
Mojo.Log.info("Item tapped: " + event.item.uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Send a search request to MeTube to send to Google for us (never worry about HTTPS encryption again)
|
||||
MainAssistant.prototype.getChats = function() {
|
||||
this.getChatsFromService("", function(response) {
|
||||
if (response != null && response != "") {
|
||||
var responseObj = JSON.parse(response);
|
||||
if (responseObj.status == "error") {
|
||||
Mojo.Log.error("Error message from server while fetch chats: " + responseObj.msg);
|
||||
Mojo.Additions.ShowDialogBox("Server Error", "The server responded to the chat request with: " + responseObj.msg.replace("ERROR: ", ""));
|
||||
} else {
|
||||
if (responseObj.messages && responseObj.messages.length > 0) {
|
||||
this.updateChatsList(responseObj.messages);
|
||||
} else {
|
||||
Mojo.Log.warn("Search results were empty. This is unlikely; server, API or connectivity problem possible");
|
||||
Mojo.Additions.ShowDialogBox("No results", "The server did not report any matches for the search.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Mojo.Log.error("No usable response from server while fetching chats: " + response);
|
||||
Mojo.Additions.ShowDialogBox("Server Error", "The server did not answer with a usable response to the chat request. Check network connectivity and/or self-host settings.");
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
//Update the UI with search results from Search Request
|
||||
MainAssistant.prototype.updateChatsList = function(results) {
|
||||
|
||||
Mojo.Log.info("Updating chat list widget with " + results.length + " results!");
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
|
||||
//figure out what messages we already have
|
||||
var knownMessages = [];
|
||||
var noLongerPending = [];
|
||||
for (var j = 0; j < thisWidgetSetup.model.items.length; j++) {
|
||||
knownMessages.push(thisWidgetSetup.model.items[j].uid);
|
||||
}
|
||||
//find out if there are new or updates messages
|
||||
var listUpdated = -1;
|
||||
for (var i = 0; i < results.length; i++) {
|
||||
if (knownMessages.indexOf(results[i].uid) == -1) { //brand new message
|
||||
listUpdated = 1;
|
||||
thisWidgetSetup.model.items.push({
|
||||
uid: results[i].uid,
|
||||
sender: results[i].sender,
|
||||
message: results[i].message,
|
||||
timestamp: this.convertTimeStamp(results[i].timestamp, true),
|
||||
color: "black"
|
||||
});
|
||||
} else {
|
||||
//update a pending message
|
||||
if (this.pendingMessages.length > 0 && this.pendingMessages.indexOf(results[i].uid) != -1) {
|
||||
for (var j = 0; j < thisWidgetSetup.model.items.length; j++) {
|
||||
if (thisWidgetSetup.model.items[j].uid == results[i].uid) {
|
||||
noLongerPending.push(results[i].uid);
|
||||
thisWidgetSetup.model.items[j].color = "black";
|
||||
listUpdated = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//rebuild pending messages array to remove confirmed messages
|
||||
if (noLongerPending && noLongerPending.length > 0) {
|
||||
var newPending = [];
|
||||
for (var k = 0; k < this.pendingMessages.length; k++) {
|
||||
if (noLongerPending.indexOf(this.pendingMessages[k]) == -1)
|
||||
newPending.push(this.pendingMessages[k]);
|
||||
}
|
||||
this.pendingMessages = newPending;
|
||||
Mojo.Log.info("pending messages now: " + this.pendingMessages);
|
||||
}
|
||||
//update the UI
|
||||
if (listUpdated > -1) {
|
||||
this.controller.modelChanged(thisWidgetSetup.model);
|
||||
if (listUpdated > 0 && !this.firstPoll) {
|
||||
systemModel.PlaySound("default_425hz");
|
||||
}
|
||||
this.chatScroller.mojo.revealBottom();
|
||||
this.chatScroller.mojo.adjustBy(0, -200);
|
||||
this.firstPoll = false;
|
||||
}
|
||||
}
|
||||
|
||||
MainAssistant.prototype.getChatsFromService = function(serviceURL, callback) {
|
||||
serviceURL = this.serviceEndpointBase + "get-chat.php";
|
||||
this.retVal = "";
|
||||
if (callback)
|
||||
callback = callback.bind(this);
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open("GET", serviceURL);
|
||||
xmlhttp.send();
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
|
||||
if (callback)
|
||||
callback(xmlhttp.responseText);
|
||||
}
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
MainAssistant.prototype.postChatToService = function(useSender, useMessage, serviceURL, callback) {
|
||||
serviceURL = this.serviceEndpointBase + "post-chat.php";
|
||||
var msgToPost = {
|
||||
sender: useSender,
|
||||
message: useMessage
|
||||
}
|
||||
Mojo.Log.info("Posting chat to: " + serviceURL);
|
||||
Mojo.Log.info(JSON.stringify(msgToPost));
|
||||
|
||||
this.retVal = "";
|
||||
if (callback)
|
||||
callback = callback.bind(this);
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open("POST", serviceURL);
|
||||
xmlhttp.setRequestHeader("Client-Id", this.clientId);
|
||||
xmlhttp.send(JSON.stringify(msgToPost));
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
|
||||
if (callback)
|
||||
callback(xmlhttp.responseText);
|
||||
}
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
MainAssistant.prototype.convertTimeStamp = function(timeStamp, isUTC) {
|
||||
if (isUTC) {
|
||||
var offset = new Date().getTimezoneOffset();
|
||||
timeStamp = Date.parse(timeStamp);
|
||||
timeStamp = timeStamp - ((offset * 60) * 1000);
|
||||
timeStamp = new Date(timeStamp);
|
||||
}
|
||||
timeStamp = timeStamp.toLocaleString();
|
||||
return timeStamp;
|
||||
}
|
||||
|
||||
//Try to make strings easier on tiny devices
|
||||
MainAssistant.prototype.cleanupString = function(str, mxwl, mxsl) {
|
||||
str = str.substring(0, mxsl);
|
||||
str = str.replace(/\s+/g, ' ').trim();
|
||||
str = str.replace(/[^a-z0-9\s]/gi, '');
|
||||
return str;
|
||||
}
|
||||
|
||||
MainAssistant.prototype.disableUI = function(statusValue) {
|
||||
//start spinner
|
||||
if (!this.spinnerModel.spinning) {
|
||||
this.spinnerModel.spinning = true;
|
||||
this.controller.modelChanged(this.spinnerModel);
|
||||
}
|
||||
}
|
||||
|
||||
MainAssistant.prototype.enableUI = function() {
|
||||
//stop spinner
|
||||
this.spinnerModel.spinning = false;
|
||||
this.controller.modelChanged(this.spinnerModel);
|
||||
this.controller.get('txtMessage').mojo.focus();
|
||||
}
|
||||
|
||||
MainAssistant.prototype.decodeEntities = function(text) {
|
||||
var entities = [
|
||||
['amp', '&'],
|
||||
['apos', '\''],
|
||||
['#x27', '\''],
|
||||
['#x2F', '/'],
|
||||
['#39', '\''],
|
||||
['#47', '/'],
|
||||
['lt', '<'],
|
||||
['gt', '>'],
|
||||
['nbsp', ' '],
|
||||
['quot', '"']
|
||||
];
|
||||
|
||||
for (var i = 0, max = entities.length; i < max; ++i)
|
||||
text = text.replace(new RegExp('&' + entities[i][0] + ';', 'g'), entities[i][1]);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
MainAssistant.prototype.deactivate = function(event) {
|
||||
/* remove any event handlers you added in activate and do any other cleanup that should happen before
|
||||
this scene is popped or another scene is pushed on top */
|
||||
Mojo.Event.stopListening(this.controller.get("chatList"), Mojo.Event.listTap, this.handleListClick);
|
||||
Mojo.Event.stopListening(this.controller.get("txtMessage"), Mojo.Event.propertyChange, this.handleSendMessage);
|
||||
this.pausePollingServer();
|
||||
};
|
||||
|
||||
MainAssistant.prototype.cleanup = function(event) {
|
||||
/* this function should do any cleanup needed before the scene is destroyed as
|
||||
a result of being popped off the scene stack */
|
||||
};
|
||||
@@ -0,0 +1,202 @@
|
||||
function PreferencesAssistant() {
|
||||
/* this is the creator function for your scene assistant object. It will be passed all the
|
||||
additional parameters (after the scene name) that were passed to pushScene. The reference
|
||||
to the scene controller (this.controller) has not be established yet, so any initialization
|
||||
that needs the scene controller should be done in the setup function below. */
|
||||
}
|
||||
|
||||
PreferencesAssistant.prototype.setup = function() {
|
||||
/* setup widgets here */
|
||||
|
||||
//Timeout picker
|
||||
this.controller.setupWidget("listForegroundUpdate",
|
||||
this.attributes = {
|
||||
label: $L("Foreground Update"),
|
||||
choices: [
|
||||
{ label: "5 seconds", value: 5000 },
|
||||
{ label: "10 seconds", value: 10000 },
|
||||
{ label: "30 second", value: 30000 },
|
||||
{ label: "1 minute", value: 60000 },
|
||||
{ label: "2 minutes", value: 120000 },
|
||||
{ label: "5 minutes", value: 180000 },
|
||||
]
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["ForegroundUpdate"],
|
||||
disabled: false
|
||||
}
|
||||
);
|
||||
this.controller.setupWidget("listBackgroundUpdate",
|
||||
this.attributes = {
|
||||
label: $L("Background Update"),
|
||||
choices: [
|
||||
{ label: "2 minutes", value: 120000 },
|
||||
{ label: "5 minutes", value: 300000 },
|
||||
{ label: "10 minutes", value: 600000 },
|
||||
{ label: "15 minutes", value: 900000 },
|
||||
{ label: "30 minutes", value: 1800000 },
|
||||
{ label: "1 Hour", value: 3600000 },
|
||||
]
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["BackgroundUpdate"],
|
||||
disabled: true
|
||||
}
|
||||
);
|
||||
//API Toggles
|
||||
this.controller.setupWidget("toggleClientAPI",
|
||||
this.attributes = {
|
||||
trueValue: true,
|
||||
falseValue: false
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["UseClientAPIKey"],
|
||||
disabled: false
|
||||
}
|
||||
);
|
||||
this.controller.setupWidget("toggleCustomEndPoint",
|
||||
this.attributes = {
|
||||
trueValue: true,
|
||||
falseValue: false
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["UseCustomEndpoint"],
|
||||
disabled: false
|
||||
}
|
||||
);
|
||||
//API Text fields
|
||||
this.controller.setupWidget("txtClientAPI",
|
||||
this.attributes = {
|
||||
hintText: $L("Your Client Id"),
|
||||
multiline: false,
|
||||
enterSubmits: false,
|
||||
autoReplace: false,
|
||||
textCase: Mojo.Widget.steModeLowerCase
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["ClientAPIKey"],
|
||||
disabled: !appModel.AppSettingsCurrent["UseClientAPIKey"]
|
||||
}
|
||||
);
|
||||
this.controller.setupWidget("txtEndpointURL",
|
||||
this.attributes = {
|
||||
hintText: $L("http://your-simplechat-server.com"),
|
||||
multiline: false,
|
||||
enterSubmits: false,
|
||||
autoReplace: false,
|
||||
textCase: Mojo.Widget.steModeLowerCase
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["EndpointURL"],
|
||||
disabled: !appModel.AppSettingsCurrent["EndpointURL"]
|
||||
}
|
||||
);
|
||||
//OK Button
|
||||
this.controller.setupWidget("btnOK", { type: Mojo.Widget.activityButton }, { label: "Done", disabled: false });
|
||||
//Menu
|
||||
this.appMenuAttributes = { omitDefaultItems: true };
|
||||
this.appMenuModel = {
|
||||
label: "Settings",
|
||||
items: [
|
||||
Mojo.Menu.editItem,
|
||||
{ label: "Reset Settings", command: 'do-resetSettings' }
|
||||
]
|
||||
};
|
||||
this.controller.setupWidget(Mojo.Menu.appMenu, this.appMenuAttributes, this.appMenuModel);
|
||||
|
||||
/* add event handlers to listen to events from widgets */
|
||||
Mojo.Event.listen(this.controller.get("listForegroundUpdate"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("listBackgroundUpdate"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("txtClientAPI"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("txtEndpointURL"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("toggleClientAPI"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("toggleCustomEndPoint"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.controller.get("btnOK"), Mojo.Event.tap, this.okClick.bind(this));
|
||||
};
|
||||
|
||||
PreferencesAssistant.prototype.activate = function(event) {
|
||||
/* put in event handlers here that should only be in effect when this scene is active. For
|
||||
example, key handlers that are observing the document */
|
||||
//this.showBetaFeatures();
|
||||
};
|
||||
|
||||
PreferencesAssistant.prototype.showBetaFeatures = function() {
|
||||
//No beta features right now
|
||||
}
|
||||
|
||||
PreferencesAssistant.prototype.handleValueChange = function(event) {
|
||||
|
||||
Mojo.Log.info(event.srcElement.id + " value changed to " + event.value);
|
||||
if (event.srcElement.id == "toggleGoogleAPI") {
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup("txtGoogleAPI");
|
||||
thisWidgetSetup.model.disabled = !event.value;
|
||||
this.controller.modelChanged(thisWidgetSetup.model);
|
||||
if (event.value)
|
||||
this.controller.get('txtGoogleAPI').mojo.focus();
|
||||
} else if (event.srcElement.id == "toggleClientAPI") {
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup("txtClientAPI");
|
||||
thisWidgetSetup.model.disabled = !event.value;
|
||||
this.controller.modelChanged(thisWidgetSetup.model);
|
||||
if (event.value)
|
||||
this.controller.get('txtClientAPI').mojo.focus();
|
||||
} else if (event.srcElement.id == "toggleServerKey") {
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup("txtServerKey");
|
||||
thisWidgetSetup.model.disabled = !event.value;
|
||||
this.controller.modelChanged(thisWidgetSetup.model);
|
||||
if (event.value)
|
||||
this.controller.get('txtServerKey').mojo.focus();
|
||||
} else if (event.srcElement.id == "toggleCustomEndPoint") {
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup("txtEndpointURL");
|
||||
thisWidgetSetup.model.disabled = !event.value;
|
||||
this.controller.modelChanged(thisWidgetSetup.model);
|
||||
if (event.value)
|
||||
this.controller.get('txtEndpointURL').mojo.focus();
|
||||
}
|
||||
|
||||
//We stashed the preference name in the title of the HTML element, so we don't have to use a case statement
|
||||
Mojo.Log.info(event.srcElement.title + " now: " + event.value);
|
||||
appModel.AppSettingsCurrent[event.srcElement.title] = event.value;
|
||||
appModel.SaveSettings();
|
||||
|
||||
//Show/hide beta features
|
||||
this.showBetaFeatures();
|
||||
};
|
||||
|
||||
//Handle menu and button bar commands
|
||||
PreferencesAssistant.prototype.handleCommand = function(event) {
|
||||
if (event.type == Mojo.Event.command) {
|
||||
switch (event.command) {
|
||||
case 'do-goBack':
|
||||
Mojo.Controller.stageController.popScene();
|
||||
break;
|
||||
case 'do-resetSettings':
|
||||
appModel.ResetSettings(appModel.AppSettingsDefaults);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PreferencesAssistant.prototype.okClick = function(event) {
|
||||
Mojo.Controller.stageController.popScene();
|
||||
}
|
||||
|
||||
PreferencesAssistant.prototype.deactivate = function(event) {
|
||||
/* remove any event handlers you added in activate and do any other cleanup that should happen before
|
||||
this scene is popped or another scene is pushed on top */
|
||||
|
||||
Mojo.Event.stopListening(this.controller.get("listSearchmax"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("listTimeout"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("txtGoogleAPI"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("txtClientAPI"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("toggleGoogleAPI"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("toggleClientAPI"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("toggleServerKey"), Mojo.Event.propertyChange, this.handleValueChange);
|
||||
Mojo.Event.stopListening(this.controller.get("btnOK"), Mojo.Event.tap, this.okClick.bind(this));
|
||||
|
||||
};
|
||||
|
||||
PreferencesAssistant.prototype.cleanup = function(event) {
|
||||
/* this function should do any cleanup needed before the scene is destroyed as
|
||||
a result of being popped off the scene stack */
|
||||
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
Mojo.Additions = Additions;
|
||||
|
||||
function StageAssistant() {
|
||||
/* this is the creator function for your stage assistant object */
|
||||
}
|
||||
|
||||
StageAssistant.prototype.setup = function() {
|
||||
/* this function is for setup tasks that have to happen when the stage is first created */
|
||||
this.controller.pushScene({ name: "main" });
|
||||
this.controller.setWindowOrientation("up");
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
function UserAssistant(sceneAssistant, doneCallBack) {
|
||||
this.doneCallBack = doneCallBack;
|
||||
/* this is the creator function for your scene assistant object. It will be passed all the
|
||||
additional parameters (after the scene name) that were passed to pushScene. The reference
|
||||
to the scene controller (this.controller) has not be established yet, so any initialization
|
||||
that needs the scene controller should be done in the setup function below. */
|
||||
Mojo.Log.info("new User assistant exists");
|
||||
this.sceneAssistant = sceneAssistant; //dialog's do not have their own controller, so need access to the launching scene's controller
|
||||
}
|
||||
|
||||
UserAssistant.prototype.setup = function(widget) {
|
||||
/* this function is for setup tasks that have to happen when the scene is first created */
|
||||
this.widget = widget;
|
||||
Mojo.Log.info("User assistant setup");
|
||||
Mojo.Log.info("Current username is: " + appModel.AppSettingsCurrent["SenderName"]);
|
||||
|
||||
/* setup widgets here */
|
||||
this.sceneAssistant.controller.setupWidget("txtUserName",
|
||||
this.attributes = {
|
||||
textFieldName: "userName",
|
||||
hintText: " Enter nickname",
|
||||
property: 'value',
|
||||
multi: false,
|
||||
enterSubmits: true,
|
||||
changeOnKeyPress: false,
|
||||
textReplacement: false,
|
||||
requiresEnterKey: false,
|
||||
focus: true
|
||||
},
|
||||
this.model = {
|
||||
value: appModel.AppSettingsCurrent["SenderName"],
|
||||
disabled: false
|
||||
}
|
||||
);
|
||||
this.sceneAssistant.controller.setupWidget("goButton", { type: Mojo.Widget.activityButton }, { label: "OK", disabled: false });
|
||||
this.sceneAssistant.controller.setupWidget("cancelButton", { type: Mojo.Widget.button }, { label: "Cancel", disabled: false });
|
||||
|
||||
/* add event handlers to listen to events from widgets */
|
||||
Mojo.Event.listen(this.sceneAssistant.controller.get("txtUserName"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.listen(this.sceneAssistant.controller.get("goButton"), Mojo.Event.tap, this.handleGoPress.bind(this));
|
||||
Mojo.Event.listen(this.sceneAssistant.controller.get("cancelButton"), Mojo.Event.tap, this.handleCancelPress.bind(this));
|
||||
};
|
||||
|
||||
UserAssistant.prototype.handleValueChange = function(event) {
|
||||
this.handleGoPress();
|
||||
};
|
||||
|
||||
UserAssistant.prototype.handleGoPress = function(event) {
|
||||
var newName = this.sceneAssistant.controller.get('txtUserName').mojo.getValue();
|
||||
//Remember the address they entered
|
||||
appModel.AppSettingsCurrent["SenderName"] = newName;
|
||||
appModel.SaveSettings();
|
||||
this.doneCallBack(newName);
|
||||
this.widget.mojo.close();
|
||||
}
|
||||
|
||||
UserAssistant.prototype.handleCancelPress = function(event) {
|
||||
this.doneCallBack("cancel");
|
||||
this.widget.mojo.close();
|
||||
}
|
||||
|
||||
UserAssistant.prototype.activate = function(event) {
|
||||
this.sceneAssistant.controller.get('txtUserName').mojo.focus();
|
||||
/* put in event handlers here that should only be in effect when this scene is active. For
|
||||
example, key handlers that are observing the document */
|
||||
};
|
||||
|
||||
UserAssistant.prototype.deactivate = function(event) {
|
||||
/* remove any event handlers you added in activate and do any other cleanup that should happen before
|
||||
this scene is popped or another scene is pushed on top */
|
||||
Mojo.Event.stopListening(this.sceneAssistant.controller.get("txtUserName"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
|
||||
Mojo.Event.stopListening(this.sceneAssistant.controller.get("goButton"), Mojo.Event.tap, this.handleGoPress.bind(this));
|
||||
Mojo.Event.stopListening(this.sceneAssistant.controller.get("cancelButton"), Mojo.Event.tap, this.handleCancelPress.bind(this));
|
||||
};
|
||||
|
||||
UserAssistant.prototype.cleanup = function(event) {
|
||||
Mojo.Log.info("user assistant cleaned up");
|
||||
/* this function should do any cleanup needed before the scene is destroyed as
|
||||
a result of being popped off the scene stack */
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Mojo Additions
|
||||
Version 0.3e
|
||||
Created: 2018
|
||||
Author: Jonathan Wise
|
||||
License: MIT
|
||||
Description: Functions I use often, that probably should have been part of Mojo.
|
||||
They either fix bugs in Mojo or make it easier to use.
|
||||
*/
|
||||
|
||||
this.Additions = function() {}
|
||||
|
||||
Mojo.Log.info("#### Mojo Additions added");
|
||||
|
||||
Additions.ShowDialogBox = function(title, message) {
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
|
||||
this.controller.showAlertDialog({
|
||||
onChoose: function(value) {},
|
||||
title: title,
|
||||
message: message,
|
||||
choices: [{ label: 'OK', value: 'OK' }],
|
||||
allowHTMLMessage: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Additions.DisableWidget = function(widgetName, disabledValue) {
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup(widgetName);
|
||||
var thisWidgetModel = thisWidgetSetup.model;
|
||||
thisWidgetModel.disabled = disabledValue;
|
||||
|
||||
this.controller.setWidgetModel(widgetName, thisWidgetModel);
|
||||
this.controller.modelChanged(thisWidgetModel);
|
||||
}
|
||||
}
|
||||
|
||||
Additions.SetPickerWidgetValue = function(widgetName, newvalue) {
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
|
||||
var thisWidgetModel = this.controller.getWidgetSetup(widgetName).model;
|
||||
thisWidgetModel.value = newvalue;
|
||||
this.controller.setWidgetModel(widgetName, thisWidgetModel);
|
||||
}
|
||||
}
|
||||
|
||||
Additions.SetWidgetLabel = function(widgetName, newvalue) {
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
|
||||
var thisWidgetModel = this.controller.getWidgetSetup(widgetName).model;
|
||||
thisWidgetModel.label = newvalue;
|
||||
this.controller.setWidgetModel(widgetName, thisWidgetModel);
|
||||
}
|
||||
}
|
||||
|
||||
Additions.SetToggleState = function(widgetName, toggledValue) {
|
||||
var stageController = Mojo.Controller.getAppController().getStageController("");
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
|
||||
var thisWidgetSetup = this.controller.getWidgetSetup(widgetName);
|
||||
var thisWidgetModel = thisWidgetSetup.model;
|
||||
thisWidgetModel.value = toggledValue;
|
||||
this.controller.setWidgetModel(widgetName, thisWidgetModel);
|
||||
|
||||
//There appears to be a bug in Mojo that means a toggle button doesn't reflect its model state during instantiation
|
||||
// This work-around fixes it.
|
||||
var children = document.getElementById(widgetName).querySelectorAll('*');
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
if (children[i].className.indexOf("toggle-button") != -1) {
|
||||
children[i].className = "toggle-button " + thisWidgetModel.value;
|
||||
}
|
||||
if (children[i].tagName == "SPAN") {
|
||||
if (thisWidgetModel.value.toString().toLowerCase() == "true")
|
||||
children[i].innerHTML = "on";
|
||||
else
|
||||
children[i].innerHTML = "off";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Additions.FindAncestorWithIdPart = function(currElement, namePartToSearch, expectedIndex, levelsToClimb) {
|
||||
var parentList = "";
|
||||
if (typeof levelsToClimb === undefined || levelsToClimb == null)
|
||||
levelsToClimb = 10;
|
||||
var foundElement;
|
||||
for (var i = 0; i < levelsToClimb; i++) {
|
||||
var parentElement = currElement.parentElement;
|
||||
if (parentElement != null && parentElement.id != null && parentElement.id != "") {
|
||||
parentList += parentElement.id + ",";
|
||||
if (parentElement.id.indexOf(namePartToSearch) == expectedIndex) {
|
||||
foundElement = parentElement;
|
||||
}
|
||||
} else {
|
||||
parentList += "null,";
|
||||
}
|
||||
currElement = parentElement;
|
||||
}
|
||||
Mojo.Log.info("Discovered ancestor ids: " + parentList);
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
Additions.EnumerateObject = function(objectToEnumerate) {
|
||||
for (var key in objectToEnumerate) {
|
||||
Mojo.Log.info("=== prop:" + key + ": " + objectToEnumerate[key]);
|
||||
if (objectToEnumerate.hasOwnProperty(key)) {
|
||||
var obj = objectToEnumerate[key];
|
||||
for (var prop in obj) {
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
Mojo.Log.info("...... sub: " + prop + " = " + obj[prop])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
App Model
|
||||
Version 0.4
|
||||
Created: 2018
|
||||
Author: Jonathan Wise
|
||||
License: MIT
|
||||
Description: Common functions for webOS apps, particularly for managing persisted options in cookies
|
||||
*/
|
||||
|
||||
var AppModel = function() {
|
||||
//Define your default scene (needed for settings management)
|
||||
this.DefaultScene = "main";
|
||||
|
||||
//Define your app-wide, non-persisted settings here
|
||||
this.UpdateCheckDone = false;
|
||||
this.LaunchQuery = "";
|
||||
this.ServiceEndpointBase = "http://chat.webosarchive.com/";
|
||||
this.ClientId = secrets.clientid;
|
||||
|
||||
//Define your app preferences (to be saved by OS)
|
||||
this.AppSettingsCurrent = null;
|
||||
this.AppSettingsDefaults = {
|
||||
ForegroundUpdate: 10000,
|
||||
UseClientAPIKey: false,
|
||||
UseCustomEndpoint: false,
|
||||
ClientAPIKey: "",
|
||||
EndpointURL: "",
|
||||
SenderName: "webOS User",
|
||||
FirstRun: true
|
||||
};
|
||||
}
|
||||
|
||||
//You probably don't need to change the below functions since they all work against the Cookie defaults you defined above.
|
||||
// LoadSettings: call when your app starts, or you want to load previously persisted options.
|
||||
// SaveSettings: call any time you want to persist an option.
|
||||
// ResetSettings: call if you want to forget stored settings and return to defaults. Your default scene will be popped and re-pushed.
|
||||
AppModel.prototype.LoadSettings = function(safe) {
|
||||
this.AppSettingsCurrent = this.AppSettingsDefaults;
|
||||
var loadSuccess = false;
|
||||
var settingsCookie = new Mojo.Model.Cookie("settings");
|
||||
try {
|
||||
appSettings = settingsCookie.get();
|
||||
if ((typeof appSettings == "undefined" || appSettings == null) || (safe && !this.checkSettingsValid(appSettings))) {
|
||||
Mojo.Log.warn("** Using first run default settings");
|
||||
} else {
|
||||
Mojo.Log.info("** Using cookie settings!");
|
||||
this.loadCookieIntoCurrent(appSettings);
|
||||
loadSuccess = true;
|
||||
}
|
||||
} catch (ex) {
|
||||
settingsCookie.put(null);
|
||||
Mojo.Log.error("** Settings cookie were corrupt and have been purged!");
|
||||
Mojo.Log.error(ex);
|
||||
}
|
||||
return loadSuccess;
|
||||
}
|
||||
|
||||
AppModel.prototype.loadCookieIntoCurrent = function(cookieSettings) {
|
||||
this.AppSettingsCurrent = cookieSettings;
|
||||
}
|
||||
|
||||
AppModel.prototype.checkSettingsValid = function(loadedSettings) {
|
||||
var retValue = true;
|
||||
for (var key in this.AppSettingsDefaults) {
|
||||
if (typeof loadedSettings[key] === undefined || loadedSettings[key] == null) {
|
||||
Mojo.Log.warn("** An expected saved setting, " + key + ", was null or undefined.");
|
||||
retValue = false;
|
||||
}
|
||||
if (typeof loadedSettings[key] !== typeof this.AppSettingsDefaults[key]) {
|
||||
Mojo.Log.warn("** A saved setting, " + key + ", was of type " + typeof(loadedSettings[key]) + " but expected type " + typeof(this.AppSettingsDefaults[key]));
|
||||
retValue = false;
|
||||
}
|
||||
if (typeof this.AppSettingsDefaults[key] === "string" && this.AppSettingsDefaults[key].indexOf(this.BaseDateString) != -1 && loadedSettings[key].indexOf(this.BaseDateString)) {
|
||||
Mojo.Log.warn("** A saved setting could not be compared to an expected date value.");
|
||||
retValue = false;
|
||||
}
|
||||
if (typeof this.AppSettingsDefaults[key] === "string" && (this.AppSettingsDefaults[key] == "false" || this.AppSettingsDefaults[key] == "true")) {
|
||||
if (loadedSettings[key] != "false" && loadedSettings[key] != "true") {
|
||||
Mojo.Log.warn("** A saved setting did not have the expected boolean value.");
|
||||
retValue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
AppModel.prototype.SaveSettings = function() {
|
||||
var settingsCookie = new Mojo.Model.Cookie("settings");
|
||||
Mojo.Log.info("Saving settings as: " + JSON.stringify(appModel.AppSettingsCurrent));
|
||||
Mojo.Log.info("Default settings at Save time are: " + JSON.stringify(appModel.AppSettingsDefaults));
|
||||
|
||||
settingsCookie.put(appModel.AppSettingsCurrent);
|
||||
}
|
||||
|
||||
AppModel.prototype.ResetSettings = function() {
|
||||
Mojo.Log.info("resetting settings");
|
||||
//Tell main scene to drop settings
|
||||
this.AppSettingsCurrent = this.AppSettingsDefaults;
|
||||
this.SaveSettings();
|
||||
Mojo.Log.info("settings have been reset");
|
||||
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
stageController.popScene(this.DefaultScene);
|
||||
Mojo.Log.info("closed default scene");
|
||||
|
||||
//Restart main scene
|
||||
stageController.pushScene(this.DefaultScene);
|
||||
Mojo.Log.info("re-opened default scene");
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
System Model
|
||||
Version 0.9
|
||||
Created: 2020
|
||||
Author: Jonathan Wise
|
||||
License: MIT
|
||||
Description: A generic and re-usable model for accessing webOS system features more easily
|
||||
Privileged functions can only be called if your App ID starts with com.palm.webos
|
||||
*/
|
||||
|
||||
var SystemModel = function() {
|
||||
|
||||
};
|
||||
|
||||
//Create a named System Alarm using relative time ("in")
|
||||
SystemModel.prototype.SetSystemAlarmRelative = function(alarmName, alarmTime) {
|
||||
this.wakeupRequest = new Mojo.Service.Request("palm://com.palm.power/timeout", {
|
||||
method: "set",
|
||||
parameters: {
|
||||
"key": Mojo.Controller.appInfo.id + "-" + alarmName,
|
||||
"in": alarmTime,
|
||||
"wakeup": true,
|
||||
"uri": "palm://com.palm.applicationManager/open",
|
||||
"params": {
|
||||
"id": Mojo.Controller.appInfo.id,
|
||||
"params": { "action": alarmName }
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
Mojo.Log.info("Alarm Set Success", JSON.stringify(response));
|
||||
},
|
||||
onFailure: function(response) {
|
||||
Mojo.Log.error("Alarm Set Failure, " + alarmTime + ":",
|
||||
JSON.stringify(response), response.errorText);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//Create a named System Alarm using absolute time ("at")
|
||||
SystemModel.prototype.SetSystemAlarmAbsolute = function(alarmName, alarmTime) {
|
||||
this.wakeupRequest = new Mojo.Service.Request("palm://com.palm.power/timeout", {
|
||||
method: "set",
|
||||
parameters: {
|
||||
"key": Mojo.Controller.appInfo.id + "-" + alarmName,
|
||||
"at": alarmTime,
|
||||
"wakeup": true,
|
||||
"uri": "palm://com.palm.applicationManager/open",
|
||||
"params": {
|
||||
"id": Mojo.Controller.appInfo.id,
|
||||
"params": { "action": alarmName }
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
Mojo.Log.info("Alarm Set Success", JSON.stringify(response));
|
||||
},
|
||||
onFailure: function(response) {
|
||||
Mojo.Log.error("Alarm Set Failure, " + alarmTime + ":",
|
||||
JSON.stringify(response), response.errorText);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//Remove a named System alarm
|
||||
SystemModel.prototype.ClearSystemAlarm = function(alarmName) {
|
||||
Mojo.Log.warn("Clearing alarm: " + alarmName);
|
||||
this.wakeupRequest = new Mojo.Service.Request("palm://com.palm.power/timeout", {
|
||||
method: "clear",
|
||||
parameters: { "key": Mojo.Controller.appInfo.id + "-" + alarmName },
|
||||
onSuccess: function(response) {
|
||||
Mojo.Log.info("Alarm Clear Success", JSON.stringify(response));
|
||||
success = true;
|
||||
},
|
||||
onFailure: function(response) {
|
||||
Mojo.Log.error("Alarm Clear Failure",
|
||||
JSON.stringify(response), response.errorText);
|
||||
success = false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//Play a pre-defined system sound
|
||||
SystemModel.prototype.PlaySound = function(soundName) {
|
||||
Mojo.Log.info("Playing sound: " + soundName);
|
||||
this.soundRequest = new Mojo.Service.Request("palm://com.palm.audio/systemsounds", {
|
||||
method: "playFeedback",
|
||||
parameters: {
|
||||
name: soundName
|
||||
},
|
||||
onSuccess: function() { success = true; },
|
||||
onFailure: function() { success = false; }
|
||||
});
|
||||
}
|
||||
|
||||
//Allow the display to sleep
|
||||
SystemModel.prototype.AllowDisplaySleep = function(stageController) {
|
||||
if (!stageController)
|
||||
stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
|
||||
//Tell the System it doesn't have to stay awake any more
|
||||
Mojo.Log.info("Allowing display sleep");
|
||||
|
||||
stageController.setWindowProperties({
|
||||
blockScreenTimeout: false
|
||||
});
|
||||
}
|
||||
|
||||
//Prevent the display from sleeping
|
||||
SystemModel.prototype.PreventDisplaySleep = function(stageController) {
|
||||
if (!stageController)
|
||||
stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
|
||||
//Ask the System to stay awake while timer is running
|
||||
Mojo.Log.info("Preventing display sleep");
|
||||
|
||||
stageController.setWindowProperties({
|
||||
blockScreenTimeout: true
|
||||
});
|
||||
}
|
||||
|
||||
//Show a notification window in its own small stage
|
||||
// Launches with sound: pass true for default, false for no sound, or pass the path to a specific sound file
|
||||
SystemModel.prototype.ShowNotificationStage = function(stageName, sceneName, heightToUse, sound, vibrate) {
|
||||
Mojo.Log.info("Showing notification stage.");
|
||||
//Determine what sound to use
|
||||
var soundToUse;
|
||||
if (!sound)
|
||||
soundToUse = "assets/silent.mp3";
|
||||
else if (sound == true || sound == "")
|
||||
soundToUse = "/media/internal/ringtones/Dulcimer (short).mp3"
|
||||
else
|
||||
soundToUse = sound;
|
||||
if (vibrate != null)
|
||||
this.Vibrate(vibrate);
|
||||
|
||||
var stageCallBack = function(stageController) {
|
||||
stageController.pushScene({ name: stageName, sceneTemplate: sceneName });
|
||||
}
|
||||
Mojo.Controller.getAppController().createStageWithCallback({
|
||||
name: stageName,
|
||||
lightweight: true,
|
||||
height: heightToUse,
|
||||
sound: soundToUse,
|
||||
clickableWhenLocked: true
|
||||
}, stageCallBack, 'popupalert');
|
||||
}
|
||||
|
||||
//Vibrate the device
|
||||
SystemModel.prototype.Vibrate = function(vibrate) {
|
||||
var success = true;
|
||||
Mojo.Log.info("Vibrating device.");
|
||||
if (!Number(vibrate)) {
|
||||
if (vibrate == true)
|
||||
vibeMax = 1;
|
||||
else
|
||||
vibeMax = 0;
|
||||
} else
|
||||
vibeMax = Number(vibrate);
|
||||
if (vibeMax > 0)
|
||||
vibeInterval = setInterval(doVibrate, 500);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
//Launch an app
|
||||
SystemModel.prototype.LaunchApp = function(appName, params) {
|
||||
if (!params)
|
||||
params = {};
|
||||
if (!params.id)
|
||||
params.id = appName;
|
||||
this.launchRequest = new Mojo.Service.Request("palm://com.palm.applicationManager", {
|
||||
method: "open",
|
||||
parameters: params,
|
||||
onSuccess: function(response) {
|
||||
Mojo.Log.info("App Launch Success", appName, JSON.stringify(response));
|
||||
},
|
||||
onFailure: function(response) {
|
||||
Mojo.Log.error("Alarm Launch Failure", appName, JSON.stringify(response));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
//Helper Functions
|
||||
var vibeInterval;
|
||||
var vibeCount = 0;
|
||||
var vibeMax = 5;
|
||||
doVibrate = function() {
|
||||
vibeCount++;
|
||||
new Mojo.Service.Request("palm://com.palm.vibrate", {
|
||||
method: "vibrate",
|
||||
parameters: { "period": 500, "duration": 1000 }
|
||||
});
|
||||
|
||||
if (vibeCount >= vibeMax) {
|
||||
clearInterval(vibeInterval);
|
||||
vibeCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Privileged functions
|
||||
/* These functions can only be called with apps that have com.palm.webos as the start of their App Id */
|
||||
|
||||
//Set the System Volume to a given level
|
||||
SystemModel.prototype.SetSystemVolume = function(newVolume) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
this.service_identifier = 'palm://com.palm.audio/system';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'setVolume',
|
||||
parameters: { volume: newVolume },
|
||||
onSuccess: function(response) { Mojo.Log.info("System volume set to " + newVolume); },
|
||||
onFailure: function(response) { Mojo.Log.warn("System volume not set!", JSON.stringify(response)); }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Get the current System Volume to a callback
|
||||
SystemModel.prototype.GetSystemVolume = function(callback) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
this.service_identifier = 'palm://com.palm.audio/system';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'getVolume',
|
||||
onSuccess: callback,
|
||||
onFailure: callback
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the Ringtone Volume to a given level
|
||||
SystemModel.prototype.SetRingtoneVolume = function(newVolume) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
this.service_identifier = 'palm://com.palm.audio/ringtone';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'setVolume',
|
||||
parameters: { volume: newVolume },
|
||||
onSuccess: function(response) { Mojo.Log.info("Ringtone volume set to " + newVolume); },
|
||||
onFailure: function(response) { Mojo.Log.warn("Ringtone volume not set!", JSON.stringify(response)); }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Get the current Ringtone Volume to a callback
|
||||
SystemModel.prototype.GetRingtoneVolume = function(callback) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
this.service_identifier = 'palm://com.palm.audio/ringtone';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'getVolume',
|
||||
onSuccess: callback,
|
||||
onFailure: callback
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the System Brightness to a given level
|
||||
SystemModel.prototype.SetSystemBrightness = function(newBrightness) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
this.service_identifier = 'palm://com.palm.display/control';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'setProperty',
|
||||
parameters: { maximumBrightness: newBrightness },
|
||||
onSuccess: function(response) { Mojo.Log.info("Screen brightness set to " + newBrightness); },
|
||||
onFailure: function(response) { Mojo.Log.warn("Screen brightess not set!", JSON.stringify(response)); }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Get the System Brightness
|
||||
SystemModel.prototype.GetSystemBrightness = function(callback) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Getting display state");
|
||||
new Mojo.Service.Request("palm://com.palm.display/control", {
|
||||
method: "getProperty",
|
||||
parameters: { properties: ['timeout', 'maximumBrightness'] },
|
||||
onSuccess: callback,
|
||||
onFailure: callback
|
||||
});
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Get the state of the display ("undefined", "dimmed", "off" or "on") to a callback
|
||||
SystemModel.prototype.GetDisplayState = function(callBack) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Getting display state");
|
||||
new Mojo.Service.Request("palm://com.palm.display/control", {
|
||||
method: "status",
|
||||
parameters: {},
|
||||
onSuccess: callBack,
|
||||
onFailure: callBack
|
||||
});
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the state of the display ("unlocked", "dimmed", "off" or "on")
|
||||
SystemModel.prototype.SetDisplayState = function(state) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Setting display state to " + state);
|
||||
new Mojo.Service.Request("palm://com.palm.display/control", {
|
||||
method: "setState",
|
||||
parameters: { "state": state },
|
||||
onSuccess: function(response) {
|
||||
Mojo.Log.info("Display set success: ", JSON.stringify(response));
|
||||
},
|
||||
onFailure: function(response) {
|
||||
Mojo.Log.error("Display set error: ", JSON.stringify(response), response.errorText);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the Notifications-When-Locked state
|
||||
SystemModel.prototype.setShowNotificationsWhenLocked = function(value) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Setting Notifications When Locked to " + value);
|
||||
this.service_identifier = 'palm://com.palm.systemservice';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'setPreferences',
|
||||
parameters: { showAlertsWhenLocked: value }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the LED Notification state
|
||||
SystemModel.prototype.setLEDLightNotifications = function(value) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Setting LED Notifications to " + value);
|
||||
this.service_identifier = 'palm://com.palm.systemservice';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'setPreferences',
|
||||
parameters: { BlinkNotifications: value }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the WAN state
|
||||
SystemModel.prototype.setWANEnabled = function(value) {
|
||||
var state = value ? 'off' : 'on';
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Setting WAN State to " + value);
|
||||
this.service_identifier = 'palm://com.palm.wan/';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'set',
|
||||
parameters: { disablewan: state },
|
||||
onSuccess: function(response) { Mojo.Log.info("WAN state set to " + value); },
|
||||
onFailure: function(response) { Mojo.Log.warn("WAN state not set!", JSON.stringify(response)); }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the WIFI state
|
||||
SystemModel.prototype.setWifiEnabled = function(value) {
|
||||
var state = value ? 'enabled' : 'disabled';
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Setting WIFI State to " + state);
|
||||
this.service_identifier = 'palm://com.palm.wifi';
|
||||
var request = new Mojo.Service.Request(this.service_identifier, {
|
||||
method: 'setstate',
|
||||
parameters: { 'state': state },
|
||||
onSuccess: function(response) { Mojo.Log.info("Wifi state set to " + state); },
|
||||
onFailure: function(response) { Mojo.Log.warn("Wifi state not set!", JSON.stringify(response)); }
|
||||
});
|
||||
return request;
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
//Set the Bluetooth radio state
|
||||
SystemModel.prototype.SetBluetoothEnabled = function(value) {
|
||||
//var state = value ? 'enabled':'disabled';
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Setting Bluetooth State to " + value);
|
||||
if (value == true)
|
||||
this.bluetoothControlService("palm://com.palm.btmonitor/monitor/radioon", { visible: true, connectable: true }, null);
|
||||
else
|
||||
this.bluetoothControlService("palm://com.palm.btmonitor/monitor/radiooff", null, null);
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
SystemModel.prototype.bluetoothControlService = function(url, params, cb) {
|
||||
return new Mojo.Service.Request(url, {
|
||||
onSuccess: cb,
|
||||
onFailure: cb,
|
||||
parameters: params,
|
||||
});
|
||||
};
|
||||
|
||||
SystemModel.prototype.GetRunningApps = function(callBack) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Getting list of running apps.");
|
||||
this.appRequest = new Mojo.Service.Request("palm://com.palm.applicationManager/running", {
|
||||
method: "",
|
||||
parameters: {},
|
||||
onSuccess: callBack,
|
||||
onFailure: callBack
|
||||
});
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
|
||||
SystemModel.prototype.KillApp = function(appId) {
|
||||
if (Mojo.Controller.appInfo.id.indexOf("com.palm.webos") != -1) {
|
||||
Mojo.Log.info("Killing app id: " + appId);
|
||||
this.appRequest = new Mojo.Service.Request("palm://com.palm.applicationManager", {
|
||||
method: "close",
|
||||
parameters: { "processId": appId },
|
||||
onSuccess: function(response) { Mojo.Log.info("App was killed: " + appId); },
|
||||
onFailure: function(response) { Mojo.Log.warn("App was not killed!", JSON.stringify(response)); }
|
||||
});
|
||||
} else {
|
||||
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm.webos'!");
|
||||
throw ("Privileged system service call not allowed for this App ID!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
Updater Model - Mojo
|
||||
Version 0.2
|
||||
Created: 2020
|
||||
Author: Jonathan Wise
|
||||
License: MIT
|
||||
Description: A model to check for and get updates from App Museum II web service.
|
||||
Does not require App Museum to be installed, but does require internet access, and Preware to do the actual install.
|
||||
Source: Find the latest version of this library and clean samples of how to use it on GitHub:
|
||||
https://github.com/codepoet80/webos-catalog-frontend/tree/main/Examples
|
||||
*/
|
||||
|
||||
var UpdaterModel = function() {
|
||||
this.updateURL = "http://appcatalog.webosarchive.com/WebService/getLatestVersionInfo.php?app=";
|
||||
this.lastUpdateResponse = null;
|
||||
};
|
||||
|
||||
/* "Public" Updater functions */
|
||||
|
||||
//Check App Museum II web service to see if there are any updates
|
||||
UpdaterModel.prototype.CheckForUpdate = function(appName, callback) {
|
||||
|
||||
var currVersion = this.getVersionObject(Mojo.Controller.appInfo.version);
|
||||
Mojo.Log.info("UpdaterModel identified current app " + appName + " version: " + JSON.stringify(currVersion));
|
||||
|
||||
// TODO: It would be nice to use the AppID, instead of an arbitrary name, but the performance
|
||||
// implications are overwhelming since the AppID is not a part of the masterData file.
|
||||
// We could get that from the app with: Mojo.Controller.appInfo.id
|
||||
this.updateURL = this.updateURL + encodeURI(appName);
|
||||
|
||||
// set scope for xmlhttp anonymous function callback
|
||||
if (callback)
|
||||
callBack = callback.bind(this);
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.open("GET", this.updateURL);
|
||||
Mojo.Log.info("Updater calling: " + this.updateURL);
|
||||
xmlhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
||||
xmlhttp.send();
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
|
||||
Mojo.Log.info("Museum responded: " + xmlhttp.responseText);
|
||||
if (xmlhttp.responseText != null && xmlhttp.responseText != "" && xmlhttp.responseText.indexOf("ERROR:") != 1) {
|
||||
var updateResponse = JSON.parse(xmlhttp.responseText);
|
||||
if (updateResponse.version != null) {
|
||||
var museumVersion = this.getVersionObject(updateResponse.version);
|
||||
//Mojo.Log.info("Museum version: " + JSON.stringify(museumVersion));
|
||||
if (this.isVersionHigher(currVersion, museumVersion)) {
|
||||
Mojo.Log.warn("UpdaterModel found an update in webOS App Museum II!");
|
||||
updateResponse.updateFound = true;
|
||||
} else {
|
||||
Mojo.Log.info("UpdaterModel did not find an update in webOS App Museum II!");
|
||||
updateResponse = false;
|
||||
}
|
||||
}
|
||||
this.lastUpdateResponse = updateResponse;
|
||||
} else {
|
||||
Mojo.Log.info("UpdaterModel: No useable response from App Museum II update API");
|
||||
}
|
||||
//Mojo.Log.info("New update response object: " + JSON.stringify(updateResponse));
|
||||
if (callback) {
|
||||
callBack(updateResponse);
|
||||
}
|
||||
}
|
||||
}.bind(this);
|
||||
}
|
||||
|
||||
//You can optionally call this function if you don't want to handle the user interaction related to prompting for an update
|
||||
// Pass the function to be called back with the user's response
|
||||
// Optionally pass a message to the user if you don't like the automatically constructed one
|
||||
UpdaterModel.prototype.PromptUserForUpdate = function(callback, message) {
|
||||
if (!this.lastUpdateResponse) {
|
||||
Mojo.Log.warn("UpdaterModel: Not prompting user for update when no update has been discovered.");
|
||||
} else {
|
||||
if (!message)
|
||||
message = "An update for " + Mojo.Controller.appInfo.title + " was found in App Museum II. Do you want to update now?! <br>" + this.lastUpdateResponse.versionNote + "<br>Do you want to update now?";
|
||||
|
||||
var stageController = Mojo.Controller.getAppController().getActiveStageController();
|
||||
if (stageController) {
|
||||
this.controller = stageController.activeScene();
|
||||
|
||||
this.controller.showAlertDialog({
|
||||
onChoose: function(value) {
|
||||
if (value) {
|
||||
Mojo.Log.info("User requested update now");
|
||||
} else {
|
||||
Mojo.Log.info("User deferred update.");
|
||||
}
|
||||
if (callback)
|
||||
callback(value);
|
||||
},
|
||||
allowHTMLMessage: true,
|
||||
title: $L("Update Available!"),
|
||||
message: $L(message),
|
||||
choices: [
|
||||
{ label: $L('Update Now'), value: true, type: 'affirmative' },
|
||||
{ label: $L("Later"), value: false, type: 'negative' }
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Ask Preware to actually install the update...
|
||||
UpdaterModel.prototype.InstallUpdate = function(callBack) {
|
||||
if (!this.lastUpdateResponse) {
|
||||
Mojo.Log.warn("UpdaterModel: Not performing update when no update has been discovered.");
|
||||
} else {
|
||||
var app = this.lastUpdateResponse.downloadURI;
|
||||
Mojo.Log.info("Asking PreWare to perform update to " + app);
|
||||
|
||||
//Ask webOS to launch the video player with the new url
|
||||
this.prewareRequest = new Mojo.Service.Request("palm://com.palm.applicationManager", {
|
||||
method: "open",
|
||||
parameters: {
|
||||
"id": "org.webosinternals.preware",
|
||||
params: { type: "install", file: app }
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
Mojo.Log.info("Preware launch success", JSON.stringify(response));
|
||||
},
|
||||
onFailure: function(response) {
|
||||
Mojo.Log.error("Preware launch failure, " + videoURL + ":",
|
||||
JSON.stringify(response), response.errorText);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* "Private" helper functions */
|
||||
|
||||
//Turn a version string into an object with three independent number values
|
||||
UpdaterModel.prototype.getVersionObject = function(versionNum) {
|
||||
versionNumParts = versionNum.split(".");
|
||||
if (versionNumParts.length <= 2 || versionNumParts > 3) {
|
||||
Mojo.Log.error("UpdaterModel: An invalid version number was passed, webOS version numbers are #.#.#");
|
||||
return false;
|
||||
} else {
|
||||
var versionObject = {
|
||||
majorVersion: versionNumParts[0] * 1,
|
||||
minorVersion: versionNumParts[1] * 1,
|
||||
buildVersion: versionNumParts[2] * 1
|
||||
}
|
||||
return versionObject;
|
||||
}
|
||||
}
|
||||
|
||||
//Given a current version and a version to compare, return true or false if the compare version is newer
|
||||
UpdaterModel.prototype.isVersionHigher = function(currVersion, compareVersion) {
|
||||
if (!currVersion || !compareVersion) {
|
||||
Mojo.Log.error("UpdaterModel: Pass the versions to compare. If the second version is higher than the first, this function will return true");
|
||||
} else {
|
||||
if (compareVersion.majorVersion > currVersion.majorVersion)
|
||||
return true;
|
||||
if (compareVersion.majorVersion == currVersion.majorVersion && compareVersion.minorVersion > currVersion.minorVersion)
|
||||
return true;
|
||||
if (compareVersion.majorVersion == currVersion.majorVersion && compareVersion.minorVersion == currVersion.minorVersion && compareVersion.buildVersion > currVersion.buildVersion)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="palm-row" x-mojo-tap-highlight='momentary'>
|
||||
<div class="palm-row-wrapper">
|
||||
<div class="chat-log" style="color:#{color}"><b>#{sender}:</b> #{message}</div>
|
||||
<div class="chat-time">#{timestamp}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="palm-list">
|
||||
<div> #{-listElements}</div>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="palm-hasheader" style="margin-bottom:60px">
|
||||
<div id="watchViewTitle" class="palm-header center">
|
||||
webOS SimpleChat
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="showChatList" name="showChatList" style="display:block;">
|
||||
<div class="palm-group">
|
||||
<div class="palm-group-title"><span x-mojo-loc=''>Chat Log</span></div>
|
||||
<div x-mojo-element="Scroller" id="chatScroller" class="scrollerClass" name="chatScroller">
|
||||
<div id="chatList" x-mojo-element="List"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-group" style="height:36px;">
|
||||
<div class="palm-group-title"><span x-mojo-loc=''>Send Message</span></div>
|
||||
<div x-mojo-element="TextField" id="txtMessage" class="textFieldClass" name="txtMessage" style="margin-right:12px"></div>
|
||||
<div id="divSpinnerContainer" style="width: 30px;margin-left: auto;margin-right: auto; align-items: center; margin-top:-25px;">
|
||||
<div x-mojo-element="Spinner" class="spinnerClass" id="workingSpinner" name="workingSpinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,64 @@
|
||||
<div class="palm-hasheader" style="margin-bottom:60px">
|
||||
<div id="watchViewTitle" class="palm-header center" onclick="Mojo.Controller.stageController.popScene();">
|
||||
Preferences
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-group">
|
||||
<div class="palm-group-title">
|
||||
<span x-mojo-loc=''>Basic</span>
|
||||
</div>
|
||||
|
||||
<div class="palm-list">
|
||||
<div class="palm-row first">
|
||||
<div class="palm-row-wrapper">
|
||||
<div id="listForegroundUpdate" x-mojo-element="ListSelector" title="ForegroundUpdate" class="right"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-row last">
|
||||
<div class="palm-row-wrapper">
|
||||
<div id="listBackgroundUpdate" x-mojo-element="ListSelector" title="BackgroundUpdate" class="right"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-group">
|
||||
<div class="palm-group-title">
|
||||
<span x-mojo-loc=''>Self Hosting</span>
|
||||
</div>
|
||||
|
||||
<div class="palm-list">
|
||||
|
||||
<div class="palm-row first">
|
||||
<div class="palm-row-wrapper">
|
||||
<div x-mojo-element="ToggleButton" id="toggleClientAPI" title="UseClientAPIKey"></div>
|
||||
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Custom Client Id</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-row">
|
||||
<div class="palm-row-wrapper" style="margin:14px 22px 4px 12px">
|
||||
<div id="txtClientAPI" name="txtClientAPI" x-mojo-element="TextField" title="ClientAPIKey"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-row first">
|
||||
<div class="palm-row-wrapper">
|
||||
<div x-mojo-element="ToggleButton" id="toggleCustomEndPoint" title="UseCustomEndpoint"></div>
|
||||
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Custom Endpoint URL</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-row last">
|
||||
<div class="palm-row-wrapper" style="margin:14px 22px 4px 12px">
|
||||
<div id="txtEndpointURL" name="txtEndpointURL" x-mojo-element="TextField" title="EndpointURL"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="explanation-text">For more information on self-hosting, please visit <a href="https://github.com/codepoet80/simplechat-service">https://github.com/codepoet80/simplechat-service</a></div>
|
||||
<div x-mojo-element="Button" id="btnOK" name="btnOK"></div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div class="palm-dialog-content">
|
||||
<div id="hue-title" class="palm-dialog-title">
|
||||
Getting Acquainted
|
||||
</div>
|
||||
<div class="palm-dialog-seperator" style="height: 8px;"></div>
|
||||
<div class="palm-body-text">
|
||||
SimpleChat is a lightweight message board for webOS. It keeps a log of recent messages, and allows you to post public messages.<br> How do you want to identify yourself?
|
||||
</div>
|
||||
|
||||
<div class="textfield-group" x-mojo-focus-highlight="true" id="divUserInfo" style="display:block; margin-left: 15px; margin-right: 15px;">
|
||||
<div class="title">
|
||||
<div id="txtUserName" name="txtUserName" x-mojo-element="TextField" title="senderName"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="palm-dialog-seperator" style="height: 18px;"></div>
|
||||
|
||||
<div class="palm-dialog-buttons">
|
||||
<div x-mojo-element="Button" id="goButton"></div>
|
||||
<div x-mojo-element="Button" id="cancelButton"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"id": "com.jonandnic.simplechat",
|
||||
"version": "0.1.1",
|
||||
"vendor": "Jon Wise",
|
||||
"type": "web",
|
||||
"main": "index.html",
|
||||
"title": "SimpleChat",
|
||||
"icon": "icon.png",
|
||||
"uiRevision": "2",
|
||||
"theme": "light"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"logLevel": 99,
|
||||
"debuggingEnabled": true,
|
||||
"timingEnabled": false,
|
||||
"logEvents": false,
|
||||
"escapeHTMLInTemplates" : true
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
+20
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name='viewport' content='height=device-height'>
|
||||
<title>Simple Chat</title>
|
||||
<script src="/usr/palm/frameworks/mojo/mojo.js" type="text/javascript" x-mojo-version="1"></script>
|
||||
|
||||
<!-- load my additions -->
|
||||
<script src="app/com.jonandnic.mojo.additions.js" type="text/javascript"></script>
|
||||
|
||||
<!-- application stylesheet should come in after the one loaded by the framework -->
|
||||
<link href="stylesheets/simplechat.css" media="screen" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
var secrets = {
|
||||
'clientid': 'SecretSharedWithServer'
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{ "source": "app/assistants/app-assistant.js" },
|
||||
{ "source": "app/assistants/stage-assistant.js" },
|
||||
{
|
||||
"scenes": "main",
|
||||
"source": "app/assistants/main-assistant.js"
|
||||
},
|
||||
{
|
||||
"scenes": "preferences",
|
||||
"source": "app/assistants/preferences-assistant.js"
|
||||
},
|
||||
{
|
||||
"source": "app/assistants/user-assistant.js",
|
||||
"comment": "since this will be a dialog, not a scene, we do not include the 'scenes' member (or we get errors)"
|
||||
},
|
||||
{ "source": "app/models/system-model.js" },
|
||||
{ "source": "app/models/updater-model.js" },
|
||||
{ "source": "app/models/app-model.js" },
|
||||
{ "source": "secrets.js" }
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
/* TODO: put your application-specific CSS here */
|
||||
|
||||
.explanation-text {
|
||||
margin-top: -5px;
|
||||
padding: 14px;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.chat-log {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.chat-time {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
color: dimgray;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
|
||||
.textFieldClass input {
|
||||
padding: 1px;
|
||||
}
|
||||
Reference in new issue
Block a user