Compare commits

37 Commits
Author SHA1 Message Date
codepoet80 71e143ecb1 build with new dependencies 2022-09-17 15:01:51 -04:00
codepoet80 4d56a992a0 update dependencies and attributions 2022-09-17 14:59:05 -04:00
codepoet80 78568bce56 sync from new PC 2022-01-01 14:41:54 -05:00
CodePoet80 4a1b85936e updater 2021-12-11 15:00:56 -05:00
CodePoet80 3dac57e20d update updater 2021-12-11 13:05:20 -05:00
CodePoet80 cdfa4013ee update updater 2021-12-11 12:50:47 -05:00
CodePoet80 9979b509df upgrade updater 2021-11-23 19:22:55 -05:00
CodePoet80 54f905a458 bug fix background sync 2021-10-17 20:34:40 -04:00
CodePoet80 295d774488 remove debug 2021-06-18 18:58:03 -04:00
CodePoet80 ab11c4ea57 bump version 2021-06-05 19:19:13 -04:00
CodePoet80 ef7cb5a2fb update readme 2021-06-01 14:09:11 -04:00
CodePoet80 5ca6c24ea0 tweak styles 2021-05-29 22:56:15 -04:00
CodePoet80 eca2e7537a put back download option 2021-05-29 22:49:32 -04:00
CodePoet80 be4f547873 more bug fixes 2021-05-29 21:24:48 -04:00
CodePoet80 ebe00543aa parts and service group for the forest moon 2021-05-29 20:40:02 -04:00
CodePoet80 4816b817dc fix bugs 2021-05-29 19:49:04 -04:00
CodePoet80 7b772c1d99 update links...again, and mini icon 2021-05-29 19:08:42 -04:00
CodePoet80 cf913f7900 change links again... i hate facebook 2021-05-29 16:45:08 -04:00
CodePoet80 323b8a81a0 change links again 2021-05-29 16:05:12 -04:00
CodePoet80 5bd35db2e0 change links 2021-05-29 15:55:28 -04:00
CodePoet80 dc49a6dbd9 tweak some strings 2021-05-29 15:42:32 -04:00
CodePoet80 af90efd60d 1.0 release candidate 2021-05-29 15:29:05 -04:00
CodePoet80 e32fcbbea2 download timer and camera added 2021-05-29 13:05:58 -04:00
CodePoet80 561b54bf97 configurable alarm time 2021-05-29 10:06:07 -04:00
CodePoet80 cbb507b4cb background sycn and dashboard 2021-05-28 18:18:43 -04:00
CodePoet80 ea2c54fcf4 improve error handling everywhere 2021-05-28 09:12:06 -04:00
CodePoet80 00657e6fb0 add default error handler 2021-05-27 17:17:07 -04:00
CodePoet80 946d21b431 improved error handling 2021-05-27 17:10:49 -04:00
CodePoet80 1f99489737 switch to using credential 2021-05-27 13:58:13 -04:00
CodePoet80 4dc4e96b85 fix up notifications 2021-05-27 12:33:03 -04:00
CodePoet80 b22e1ec4a2 finish detail menu, prep for query launch 2021-05-26 21:02:28 -04:00
CodePoet80 3f951e75d9 prep for sub menu 2021-05-26 20:05:29 -04:00
CodePoet80 c5a0997c11 tweak create key 2021-05-26 19:59:09 -04:00
CodePoet80 76149a40ac add example 2021-05-26 19:10:25 -04:00
CodePoet80 d61bee6ab3 add support for create key 2021-05-26 19:09:35 -04:00
CodePoet80 a990fca7bc bug fixes and tweaks 2021-05-26 18:03:04 -04:00
CodePoet80 4c9b302df1 first commit 2021-05-26 17:51:26 -04:00
118 changed files with 3542 additions and 7352 deletions

No files matched your search

Regular → Executable
+1 -1
View File
@@ -1,2 +1,2 @@
secrets.js
.DS_Store
secrets.js
Vendored Regular → Executable
View File
File mode changed.
+3
View File
@@ -1,3 +1,6 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
cp $DIR/../../webos-common/Mojo/updater-model.js $DIR/../app/models/updater-model.js
cp $DIR/../../webos-common/Mojo/system-model.js $DIR/../app/models/system-model.js
cp $DIR/../../webos-common/Mojo/com.wosa.mojo.additions.js $DIR/../app/com.wosa.mojo.additions.js
mkdir -p $DIR/../_bin
palm-package $DIR/../ -o $DIR/../_bin
Vendored Regular → Executable
View File
File mode changed.
+4 -4
View File
@@ -1,5 +1,5 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
palm-package simplechat-app simplechat-service simplechat-package
palm-install com.jonandnic.simplechat_2.0.0_all.ipk
palm-launch com.jonandnic.simplechat
palm-log -f com.jonandnic.simplechat.app
cp $DIR/../../webos-common/Mojo/updater-model.js $DIR/../app/models/updater-model.js
cp $DIR/../../webos-common/Mojo/system-model.js $DIR/../app/models/system-model.js
cp $DIR/../../webos-common/Mojo/com.wosa.mojo.additions.js $DIR/../app/com.wosa.mojo.additions.js
palm-run $DIR/../
Vendored Regular → Executable
View File
File mode changed.
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Jon W
Copyright (c) 2022 webOS Archive
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Executable
+12
View File
@@ -0,0 +1,12 @@
# Share Space app for webOS
![Share Space Icon](icon.png)
Share Space is a sharing service app for legacy Palm/HP webOS devices, which depends on the back-end service listed below.
You can download it for your webOS Device from the [webOS Archive App Museum II](appcatalog.webosarchive.org).
Tested on Pre, Pre2, Pre3, Veer and Touchpad.
Uses [Sharing Service](https://github.com/webosarchive/sharing-service) which is hosted by [webOSArchive](http://www.webosarchive.org) for the webOS community without guarantee of privacy or performance. Illegal or illicit use strictly prohibited.
You can also self-host the service, see the [instructions here](https://github.com/webosarchive/sharing-service/blob/main/README.md).
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

+103
View File
@@ -0,0 +1,103 @@
/*
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 serviceModel = null;
var MainStageName = "main";
var InDockMode = false;
function AppAssistant() {
appModel = new AppModel();
updaterModel = new UpdaterModel();
serviceModel = new ShareServiceModel();
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) {
Mojo.Log.info("Share Space is Launching! Launch params: " + JSON.stringify(params));
//Load preferences
appModel.LoadSettings();
Mojo.Log.info("settings now: " + JSON.stringify(appModel.AppSettingsCurrent));
if (!appModel.AppSettingsCurrent["AutoDownloadTime"]) {
appModel.AppSettingsCurrent["AutoDownloadTime"] = appModel.AppSettingsDefaults["AutoDownloadTime"];
appModel.SaveSettings();
}
appModel.EstablishAlarms();
if (!params || !params["action"]) {
var mainStage = this.controller.getStageProxy("main"); //get the proxy for the stage if it already exists (eg: app is currently open)
if (mainStage) //If the stage exists, use it
{
Mojo.Log.info("Found existing main stage, app was already running");
Mojo.Log.info("This is a non-alarm re-launch");
var stageController = this.controller.getStageController("main");
stageController.activate(); //bring existing stage into focus
if (params)
this.handleParameterLaunch(params, stageController);
} else //If not, determine if we should make one
{
Mojo.Log.info("Did not find existing main stage, app is not running");
Mojo.Log.info("This is a non-alarm launch of version " + Mojo.Controller.appInfo.version);
var stageArguments = { name: MainStageName, lightweight: true };
this.controller.createStageWithCallback(stageArguments, function(stageController) {
stageController.pushScene(MainStageName);
if (params)
this.handleParameterLaunch(params, stageController);
}.bind(this));
}
} else {
this.handleParameterLaunch(params);
}
};
AppAssistant.prototype.handleParameterLaunch = function(params) {
Mojo.Log.info("Launch params: " + JSON.stringify(params));
//Touch2Share Launch (goes to main)
if (params["sendDataToShare"]) {
Mojo.Log.info("Launch with Touch2Share request!");
systemModel.SendDataForTouch2Share(appModel.CurrentShareURL);
}
//Alarm Launch (goes to dashboard)
else if (params.action && params.action == "BackgroundDownload"){
Mojo.Log.info("Alarm launch for background sync...");
//TODO: Check if in Dock Mode (and remember)
systemModel.GetDisplayState(function(response) {
Mojo.Log.info("Called back from GetDisplayState with response: " + JSON.stringify(response));
InDockMode = false;
if (response && response.state == "on" && response.active == false) {
InDockMode = true;
systemModel.SetDisplayState("unlock");
systemModel.SetDisplayState("on");
}
appModel.ShowDownloaderStage();
}.bind(this));
}
//JustType or other (goes to main)
else {
Mojo.Log.info("Launch with item request to be handled by Main scene...");
appModel.LaunchQuery = params;
var mainStage = this.controller.getStageProxy(MainStageName);
if (mainStage) {
var stageController = this.controller.getStageController(MainStageName);
stageController.swapScene({ transition: Mojo.Transition.crossFade, name: MainStageName });
}
else {
var stageArguments = { name: MainStageName, lightweight: true };
this.controller.createStageWithCallback(stageArguments, function(stageController) {
stageController.pushScene(MainStageName);
}.bind(this));
}
}
}
+253
View File
@@ -0,0 +1,253 @@
function DetailAssistant() {
/* 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. */
}
DetailAssistant.prototype.setup = function() {
/* setup widgets here */
//Menu
this.appMenuAttributes = { omitDefaultItems: true };
this.appMenuModel = {
label: "Settings",
items: [{
label: $L('Select All'),
command: Mojo.Menu.selectAllCmd,
shortcut: 'a',
disabled: false
}, {
label: $L('Copy'),
command: Mojo.Menu.copyCmd,
shortcut: 'c',
disabled: false
}]
};
this.controller.setupWidget(Mojo.Menu.appMenu, this.appMenuAttributes, this.appMenuModel);
//Text content field
this.controller.setupWidget("divTextContent",
this.attributes = {
multiline: true,
enterSubmits: false,
focus: false
},
this.model = {
value: "",
disabled: false
}
);
//OK Button
this.controller.setupWidget("btnOK",
this.attributes = {
},
this.model = {
label : "OK",
disabled: false
}
);
//Command Menu
this.cmdMenuAttributes = {
spacerHeight: 0,
menuClass: 'no-fade'
},
this.cmdMenuModel = {
visible: true,
items: [{
items: [
{ label: 'Back', icon: 'back', command: 'do-goBack' }
]
},
{
items: [
]
},
{
items: [
{ label: 'Share', command: 'do-share' },
{ label: 'Save', icon: 'save', command: 'do-save' }
]
}
]
};
this.controller.setupWidget(Mojo.Menu.commandMenu, this.cmdMenuAttributes, this.cmdMenuModel);
/* add event handlers to listen to events from widgets */
};
DetailAssistant.prototype.activate = function(event) {
//Bind selected podcast to scene elements
Mojo.Log.info(JSON.stringify(appModel.LastShareSelected));
this.controller.get("divShareTitle").innerHTML = "Shared: " + appModel.ConvertTimeStamp(appModel.LastShareSelected.timestamp, true);
//Calculate links
if (appModel.LastShareSelected && appModel.LastShareSelected.contenttype && appModel.LastShareSelected.contenttype.indexOf("image") != -1) { //image links
var link = serviceModel.MakeShareURL(appModel.AppSettingsCurrent["Username"], appModel.LastShareSelected.guid, "image");
appModel.CurrentShareURL = link;
this.controller.get("divShareLinks").innerHTML += "• <a href='" + link + "'>View in Browser</a>";
this.controller.get("divShareLinks").innerHTML += " &nbsp;(<a href='javascript:this.doCopy(\"" + link + "\")'>Copy Link</a>)<br>";
link = this.makeDownloadURL(appModel.LastShareSelected.thumbnail, "i");
this.downloadLink = link;
} else { //text links
var link = serviceModel.MakeShareURL(appModel.AppSettingsCurrent["Username"], appModel.LastShareSelected.guid, "t");
appModel.CurrentShareURL = link;
this.downloadLink = link;
this.controller.get("divShareLinks").innerHTML = "• <a href='" + link + "'>View in Browser</a>";
this.controller.get("divShareLinks").innerHTML += " &nbsp;(<a href='javascript:this.doCopy(\"" + link + "\")'>Copy Link</a>)<br>";
}
if (appModel.AppSettingsCurrent["DebugMode"]) { //If debugging, show links
this.controller.get("divShareInfo").style.display = "block";
}
//Show actual content
if (appModel.LastShareSelected.contenttype == "application/json") {
this.controller.get("divTextContent").innerHTML = "<pre>" + JSON.stringify(appModel.LastShareSelected.content, null, 1) + "</pre>"
} else if (appModel.LastShareSelected.contenttype == "text/plain") {
this.controller.get("divTextContent").innerHTML = "<pre>" + appModel.LastShareSelected.content + "</pre>"
} else {
var useImg = appModel.LastShareSelected.content;
useImg = useImg.split("?");
useImg = useImg[0] + "?img=" + useImg[1] + "&size=" + this.setPhotoSize();
Mojo.Log.info("Rendering img: " + useImg);
this.controller.get("divImageContent").innerHTML = "<img src='" + useImg + "' style='max-width:90%;'>";
}
Mojo.Controller.getAppController().showBanner({ messageText: 'Touch2Share Ready!', icon: 'assets/notify.png' }, { source: 'notification' });
};
doCopy = function(link) {
Mojo.Log.info("Copy tapped for link " + link);
var stageController = Mojo.Controller.getAppController().getActiveStageController()
stageController.setClipboard(link);
Mojo.Controller.getAppController().showBanner({ messageText: 'Link copied!', icon: 'assets/notify.png' }, { source: 'notification' });
}
DetailAssistant.prototype.makeDownloadURL = function(thumbUrl, type) {
var newURL = thumbUrl;
newURL = newURL.replace("tthumb", type);
newURL = newURL.replace("ithumb", type);
return newURL;
}
DetailAssistant.prototype.setPhotoSize = function() {
switch(systemModel.DetectDevice()) {
case "TouchPad":
return 800;
break;
case "Pre3":
return 400;
break;
case "Pre":
return 300;
break;
case "Tiny":
return 280;
break;
default:
return 400;
}
}
//Handle menu and button bar commands
DetailAssistant.prototype.handleCommand = function(event) {
Mojo.Log.info("handling command button press for command: " + event.command);
if (event.type == Mojo.Event.command) {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
switch (event.command) {
case 'do-goBack':
stageController.popScene();
break;
case 'do-share':
var itemsToShow = [
{ label: 'Open in Browser', command: 'do-browserOpen' },
{ label: 'Email Link', command: 'do-emailLink' },
{ label: 'Message Link', command: 'do-messageLink' },
{ label: 'Copy Link', command: 'do-copyLink' }
]
if (appModel.LastShareSelected.contenttype.indexOf("image") == -1) {
itemsToShow.push({ label: 'Copy Content', command: 'do-copyContent' });
}
this.controller.popupSubmenu({
onChoose: this.handlePopupChoose.bind(this, event.item),
//placeNear: Mojo.Menu.commandMenu,
items: itemsToShow
});
break;
case 'do-save':
var usePath = "sharespace/" + appModel.AppSettingsCurrent["Username"];
if (appModel.AppSettingsCurrent["UseCustomDownloadPath"] && appModel.AppSettingsCurrent["CustomDownloadPath"] != "")
usePath = appModel.AppSettingsCurrent["CustomDownloadPath"];
Mojo.Controller.getAppController().showBanner({ messageText: 'Downloading content...', icon: 'assets/notify.png' }, { source: 'notification' });
systemModel.DownloadFile(this.downloadLink, appModel.LastShareSelected.contenttype, usePath, appModel.LastShareSelected.guid, true);
break;
}
}
};
DetailAssistant.prototype.handlePopupChoose = function(task, command) {
Mojo.Log.info("Perform: ", command);
var stageController = Mojo.Controller.getAppController().getActiveStageController();
switch (command) {
case "do-browserOpen":
this.controller.serviceRequest("palm://com.palm.applicationManager", {
method: "launch",
parameters: {
id: 'com.palm.app.browser',
params: {
"target": appModel.CurrentShareURL
}
}
});
Mojo.Log.warn("target: " + appModel.CurrentShareURL);
break;
case "do-emailLink":
this.controller.serviceRequest("palm://com.palm.applicationManager", {
method: 'open',
parameters: {
id: "com.palm.app.email",
params: {
summary: "Check out this link",
text: appModel.CurrentShareURL
}
}
});
Mojo.Log.warn("target: " + appModel.CurrentShareURL);
break;
case "do-messageLink":
this.controller.serviceRequest('palm://com.palm.applicationManager', {
method: 'open',
parameters: {
id: 'com.palm.app.messaging',
params: {
messageText: "Check out this link: " + appModel.CurrentShareURL
}
}
});
Mojo.Log.warn("target: " + appModel.CurrentShareURL);
break;
case "do-copyLink":
stageController.setClipboard(appModel.CurrentShareURL);
Mojo.Controller.getAppController().showBanner({ messageText: 'Link copied!', icon: 'assets/notify.png' }, { source: 'notification' });
Mojo.Log.warn("target: " + appModel.CurrentShareURL);
break;
case "do-copyContent":
stageController.setClipboard(appModel.LastShareSelected.content);
Mojo.Controller.getAppController().showBanner({ messageText: 'Content copied!', icon: 'assets/notify.png' }, { source: 'notification' });
break;
}
}
DetailAssistant.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 */
};
DetailAssistant.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 */
};
+210
View File
@@ -0,0 +1,210 @@
/*
This alarm scene is used for Touchpads, which will only pretend to change settings if running in the background
Launching this scene forces the Touchpad to wake up to deal with presenting it, and when it does we have a good
chance of successfully changing the settings. We can then close this window. All the smarts for this are in stage assistant.
*/
var downloadMgrInt = null;
function DownloadAssistant(argFromPusher) {
this.passedArguments = argFromPusher;
this.downloadList = [];
}
DownloadAssistant.prototype.setup = function() {
Mojo.Log.info("Dashboard notification stage setup at " + new Date());
this.displayDashboard(Mojo.Controller.appInfo.title, "Checking for new shares...");
//Event handlers
Mojo.Event.listen(this.controller.get("dashboardinfo"), Mojo.Event.tap, this.handleTap.bind(this));
}
DownloadAssistant.prototype.activate = function(event) {
Mojo.Log.info("Dashboard activating!");
//Set options for service model
serviceModel.ForceHTTP = appModel.AppSettingsCurrent["ForceHTTP"];
serviceModel.UseCustomEndpoint = appModel.AppSettingsCurrent["UseCustomEndpoint"];
serviceModel.CustomEndpointURL = appModel.AppSettingsCurrent["EndpointURL"];
serviceModel.CustomShortURL = appModel.AppSettingsCurrent["ShortURL"];
serviceModel.CustomCreateKey = appModel.AppSettingsCurrent["CustomCreateKey"];
serviceModel.UseCustomClientId = appModel.AppSettingsCurrent["UseCustomClientId"];
serviceModel.CustomClientId = appModel.AppSettingsCurrent["CustomClientId"];
this.fetchShares();
}
DownloadAssistant.prototype.displayDashboard = function(title, message, count) {
var infoElement = this.controller.get("dashboardinfo");
var info = { title: title, message: message };
if (count) {
info.count = count;
info.showCount = "inline";
} else {
info.showCount = "none";
}
var renderedInfo = Mojo.View.render({ object: info, template: "download/item-info" });
infoElement.innerHTML = renderedInfo;
this.controller.getSceneScroller().mojo.revealTop(true);
}
DownloadAssistant.prototype.fetchShares = function() {
serviceModel.DoShareListRequest(appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], function(responseObj){
if (responseObj != null) {
if (responseObj.shares) {
//If we got a good looking response, find out what to download
Mojo.Log.info("Download dashboard ready to process share list: " + JSON.stringify(responseObj));
this.checkIfDownloadNeeded(appModel.AppSettingsCurrent["Username"], responseObj.shares, responseObj.accesslevel);
} else {
Mojo.Log.warn("Share list was empty in Download dashboard. Either there was no matching result, or there were server or connectivity problems.");
appModel.CloseNotificationStage();
}
} else {
Mojo.Log.error("No usable response from server while Download dashboard loading shares: " + response);
appModel.CloseNotificationStage();
}
}.bind(this), this.errorHandler.bind(this));
}
DownloadAssistant.prototype.checkIfDownloadNeeded = function(username, results, accessLevel) {
Mojo.Log.info("Determining which shares need to be downloaded...");
this.downloadList = [];
this.getFileListForUser(username, function(fileList) {
for (var i = 0; i < results.length; i++) {
if (results[i].guid) {
//Figure out what mimetype to use
if (results[i].contenttype == "image/jpeg")
ext = ".jpg";
else if (results[i].contenttype == "text/plain")
ext = ".txt";
else if (results[i].contenttype == "text/json")
ext = ".json";
else {
var ext = results[i].contenttype.split("/");
ext = ext[ext.length - 1];
ext = "." + ext;
}
var fileToCheck = results[i].guid + ext;
var found = false;
if (fileList.items) {
for (var f = 0;f < fileList.items.length;f ++){
if (fileList.items[f].name == fileToCheck) {
Mojo.Log.info("Already downloaded: " + fileToCheck);
found = true;
}
}
}
if (!found) {
var newURL = results[i].thumbnail;
newURL = newURL.replace("tthumb", "t");
newURL = newURL.replace("ithumb", "i");
Mojo.Log.info("Need to download: " + newURL);
this.downloadList.push(newURL);
var usePath = "sharespace/" + username;
if (appModel.AppSettingsCurrent["UseCustomDownloadPath"] && appModel.AppSettingsCurrent["CustomDownloadPath"] != "")
usePath = appModel.AppSettingsCurrent["CustomDownloadPath"];
systemModel.DownloadFile(newURL, results[i].contenttype, usePath, results[i].guid);
}
}
}
Mojo.Log.info("Files in download list: " + this.downloadList.length);
if (this.downloadList.length > 0) {
this.displayDashboard(Mojo.Controller.appInfo.title, "Downloading shares...", this.downloadList.length);
} else {
this.displayDashboard(Mojo.Controller.appInfo.title, "No new shares to download.");
}
downloadMgrInt = this.controller.window.setInterval(function() {
this.checkDownloadManagerStatus();
}.bind(this), 1500);
}.bind(this));
}
DownloadAssistant.prototype.getFileListForUser = function(username, callback) {
if (callback)
callback.bind(this);
Mojo.Log.info("Asking FileMgr service for list of files");
this.controller.serviceRequest('palm://ca.canucksoftware.filemgr', {
method: 'listFiles',
parameters: {
path: "/media/internal/sharespace/" + username + "/",
},
onSuccess: callback,
onFailure: callback
});
}
DownloadAssistant.prototype.checkDownloadManagerStatus = function() {
if (this.downloadList.length > 0) {
Mojo.Log.info("checking if download manager is done...");
this.controller.serviceRequest('palm://com.palm.downloadmanager/', {
method: 'listPending',
parameters: {},
onSuccess : function (response){
//Mojo.Log.info("List Pending success, results="+JSON.stringify(response));
if (response) {
if (response.count && response.count > 0) {
Mojo.Log.info("Remaining downloads: " + response.count);
this.displayDashboard(Mojo.Controller.appInfo.title, "Downloading shares...", response.count);
} else {
Mojo.Log.info("Download Manager completed all downloads!");
this.controller.window.clearInterval(downloadMgrInt);
if (InDockMode)
systemModel.SetDisplayState("dock");
appModel.CloseDashboardStageByName("download");
}
} else {
Mojo.Log.error("List response failure");
this.controller.window.clearInterval(downloadMgrInt);
appModel.CloseDashboardStageByName("download");
}
}.bind(this),
onFailure : function (e){
Mojo.Log.error("List Pending failure, results="+JSON.stringify(e));
this.controller.window.clearInterval(downloadMgrInt);
appModel.CloseDashboardStageByName("download");
}.bind(this)
});
} else {
if (InDockMode)
systemModel.SetDisplayState("dock");
appModel.CloseDashboardStageByName("download");
}
}
DownloadAssistant.prototype.handleTap = function(event) {
Mojo.Log.info("Dashboard tapped!");
var appController = Mojo.Controller.getAppController();
var mainStage = appController.getStageProxy("main");
if (!mainStage) {
systemModel.LaunchApp(Mojo.Controller.appInfo.id);
appController.closeAllStages();
} else {
mainStage.activate();
appController.closeStage("dashboard")
}
}
DownloadAssistant.prototype.handleDrag = function(event) {
var infoElement = this.controller.get("dashboardinfo");
infoElement.style.opacity = "0.5"; //TODO: this only seems to effect the icon
}
DownloadAssistant.prototype.handleDragEnd = function(event) {
Mojo.Log.info("Dashboard drag end!");
this.controller.window.clearInterval(downloadMgrInt);
appModel.CloseDashboardStageByName("download");
}
DownloadAssistant.prototype.errorHandler = function (errorText, callback) {
Mojo.Log.error("Error in Dashboard scene: " + errorText);
}
DownloadAssistant.prototype.deactivate = function(event) {
Mojo.Event.stopListening(this.controller.get("dashboardinfo"), Mojo.Event.tap, this.handleTap);
}
// Cleanup anything we did in setup function
DownloadAssistant.prototype.cleanup = function() {
Mojo.Log.info("Dashboard notification stage closing at " + new Date());
//Event handlers
Mojo.Event.stopListening(this.controller.get("dashboardinfo"), Mojo.Event.tap, this.handleTap);
appModel.SaveSettings();
}
+143
View File
@@ -0,0 +1,143 @@
function LoginAssistant(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 Login assistant exists");
this.sceneAssistant = sceneAssistant; //dialog's do not have their own controller, so need access to the launching scene's controller
}
LoginAssistant.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("login assistant setup");
Mojo.Log.info("Current Move is: " + appModel.AppSettingsCurrent["Username"]);
/* setup widgets here */
this.sceneAssistant.controller.setupWidget("txtUsername",
this.attributes = {
textFieldName: "Username",
hintText: "User Name",
property: 'value',
multiline: false,
changeOnKeyPress: true,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase,
requiresEnterKey: false,
focus: false
},
this.model = {
value: appModel.AppSettingsCurrent["Username"],
disabled: false
}
);
this.sceneAssistant.controller.setupWidget("txtCredential",
this.attributes = {
textFieldName: "Credential",
hintText: "Admin Password",
property: 'value',
multiline: false,
changeOnKeyPress: true,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase,
requiresEnterKey: false,
focus: false
},
this.model = {
value: appModel.AppSettingsCurrent["Credential"],
disabled: false
}
);
this.sceneAssistant.controller.setupWidget("linkSpinner",
this.attributes = {
spinnerSize: "small"
},
this.model = {
spinning: true
}
);
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("txtCredential"), 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));
};
LoginAssistant.prototype.activate = function(event) {
Mojo.Log.info("Login assistant activated");
/* 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 */
};
LoginAssistant.prototype.handleValueChange = function(event) {
appModel.AppSettingsCurrent[event.srcElement.title] = event.value;
}
LoginAssistant.prototype.handleCancelPress = function(event) {
this.doneCallBack(false);
this.widget.mojo.close();
}
LoginAssistant.prototype.handleGoPress = function(event) {
if (this.sceneAssistant.controller.get("txtUsername").mojo.getValue() == "") {
this.sceneAssistant.controller.get("txtUsername").mojo.focus();
this.sceneAssistant.controller.get("goButton").mojo.deactivate();
return;
}
if (this.sceneAssistant.controller.get("txtCredential").mojo.getValue() == "") {
this.sceneAssistant.controller.get("txtCredential").mojo.focus();
this.sceneAssistant.controller.get("goButton").mojo.deactivate();
return;
}
//Update UI for this state
this.tryServiceLogin();
}
LoginAssistant.prototype.tryServiceLogin = function() {
serviceModel.DoShareListRequest(appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], function(response) {
if (response.error) {
this.doneCallBack(response);
} else {
if (response && response.shares && response.shares != "") {
Mojo.Log.info("Login success!");
appModel.SaveSettings();
this.doneCallBack(true);
} else {
this.doneCallBack({"error": "Login failure. Check connectivity and your credentials. If you haven't logged in for a long time, create a new account."});
}
}
this.widget.mojo.close(); //Dismiss this dialog
}.bind(this), this.errorHandler.bind(this));
}
LoginAssistant.prototype.deactivate = function(event) {
Mojo.Log.info("Login assistant deactivated");
/* 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);
Mojo.Event.stopListening(this.sceneAssistant.controller.get("txtCredential"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.sceneAssistant.controller.get("goButton"), Mojo.Event.tap, this.handleGoPress);
Mojo.Event.stopListening(this.sceneAssistant.controller.get("cancelButton"), Mojo.Event.tap, this.handleCancelPress);
};
LoginAssistant.prototype.cleanup = function(event) {
Mojo.Log.info("Login 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 */
};
LoginAssistant.prototype.errorHandler = function (errorText, callback) {
Mojo.Log.error(errorText);
if (callback) {
callback.bind(this);
callback({"error": errorText});
} else {
Mojo.Controller.getAppController().showBanner({ messageText: errorText, icon: "assets/notify.png" }, "", "");
}
}
+607
View File
@@ -0,0 +1,607 @@
/*
Share Space clipboard app for webOS.
This app depends on a sharing service, which is hosted by webOS Archive at no cost for what remains of the webOS mobile community.
You can also host the service yourself: http://www.github.com/webosarchive/sharing-service
*/
var refreshInt;
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() {
//Load preferences
appModel.LoadSettings();
Mojo.Log.info("settings now: " + JSON.stringify(appModel.AppSettingsCurrent));
this.deviceType = systemModel.DetectDevice();
this.errorCount = 5;
//Loading spinner - with global members for easy toggling later
this.spinnerAttrs = {
spinnerSize: Mojo.Widget.spinnerLarge
};
this.spinnerModel = {
spinning: false
}
this.controller.setupWidget('workingSpinner', this.spinnerAttrs, this.spinnerModel);
//Share List (starts empty)
this.emptyShares = [
{ id: "-1", videoName: "Empty", thumbnail: "", selectedState: true }
]
this.shareListElement = this.controller.get('shareList');
this.shareInfoModel = {
items: this.emptyShares
};
//Share List templates (loads other HTML)
this.template = {
itemTemplate: 'main/item-template',
listTemplate: 'main/list-template',
swipeToDelete: true,
preventDeleteProperty: "readonly",
addItemLabel: "New Share"
};
this.controller.setupWidget("shareList", this.template, this.shareInfoModel);
//Menu
this.appMenuAttributes = { omitDefaultItems: true };
this.appMenuModel = {
label: "Settings",
items: [
Mojo.Menu.editItem,
{ label: "Preferences", command: 'do-Preferences' },
{ label: "Handle URLs", chosen: false, command: 'do-HandleURLs' },
{ label: "Log In", command: 'do-LogInOut' },
{ label: "About", command: 'do-myAbout' },
{ label: "Version Info", command: 'do-versionInfo' },
//{ label: "Debug Mode", chosen: appModel.AppSettingsCurrent["DebugMode"], command: 'do-debugMode' } //TODO: Remove Debug Menu
]
};
if(appModel.AppSettingsCurrent["SharePhrase"] && appModel.AppSettingsCurrent["SharePhrase"] != "") {
this.appMenuModel.items.push({ label: "Show Share Phrase", command: 'do-mySharePhrase' });
}
this.controller.setupWidget(Mojo.Menu.appMenu, this.appMenuAttributes, this.appMenuModel);
//Command Buttons
this.cmdMenuAttributes = {
spacerHeight: 40,
//menuClass: 'no-fade'
},
this.cmdMenuModel = {
visible: false,
items: [{
items: [
{ label: 'Refresh', icon: 'refresh', command: 'do-refresh' }
]
},
{
items: [
{ label: 'New', icon: 'new', command: 'do-new' }
]
}
]
};
if (appModel.AppSettingsCurrent["DebugMode"]) {
this.cmdMenuModel.items[0].items.push({ label: "Download", command: 'do-download' });
}
this.controller.setupWidget(Mojo.Menu.commandMenu, this.cmdMenuAttributes, this.cmdMenuModel);
/* Always on Event handlers */
Mojo.Event.listen(this.controller.get("shareList"), Mojo.Event.listDelete, this.handleListDelete.bind(this));
Mojo.Event.listen(this.controller.get("shareList"), Mojo.Event.listTap, this.handleListClick.bind(this));
Mojo.Event.listen(this.controller.get("shareList"), Mojo.Event.listAdd, this.handleListAdd.bind(this));
//Just type
//this.keypressHandler = this.handleKeyPress.bindAsEventListener(this);
//this.controller.document.addEventListener("keypress", this.keypressHandler, true);
//Check for updates
if (!appModel.UpdateCheckDone) {
appModel.UpdateCheckDone = true;
updaterModel.CheckForUpdate("Share Space", this.handleUpdateResponse.bind(this));
//Check for dependencies
this.checkForFileMgr();
}
};
MainAssistant.prototype.activate = function(data) {
//Set options for service model
serviceModel.ForceHTTP = appModel.AppSettingsCurrent["ForceHTTP"];
serviceModel.UseCustomEndpoint = appModel.AppSettingsCurrent["UseCustomEndpoint"];
serviceModel.CustomEndpointURL = appModel.AppSettingsCurrent["EndpointURL"];
serviceModel.CustomShortURL = appModel.AppSettingsCurrent["ShortURL"];
serviceModel.CustomCreateKey = appModel.AppSettingsCurrent["CustomCreateKey"];
serviceModel.UseCustomClientId = appModel.AppSettingsCurrent["UseCustomClientId"];
serviceModel.CustomClientId = appModel.AppSettingsCurrent["CustomClientId"];
//Set correct menu label
var loggedInLabel = "Log In";
if (appModel.AppSettingsCurrent["Username"] != "" && appModel.AppSettingsCurrent["Credential"] != "") {
loggedInLabel = "Log Out";
var thisCommandModel = this.controller.getWidgetSetup(Mojo.Menu.commandMenu).model;
thisCommandModel.visible = true;
this.controller.modelChanged(thisCommandModel);
}
var thisMenuModel = this.controller.getWidgetSetup(Mojo.Menu.appMenu).model;
thisMenuModel.items[3].label = loggedInLabel;
this.controller.modelChanged(thisMenuModel);
if (appModel.AppSettingsCurrent["Username"] != "" && appModel.AppSettingsCurrent["Credential"] != "") {
Mojo.Log.info("About to fetch shares...");
this.controller.get('workingSpinner').mojo.start();
this.fetchShares();
//handle launch with query
Mojo.Log.info("Main scene loaded with launch query: " + JSON.stringify(appModel.LaunchQuery) + ", data: " + JSON.stringify(data));
var busy = false;
if (appModel.LaunchQuery && appModel.LaunchQuery != "") {
busy = true;
//JustType Launch
if (appModel.LaunchQuery.newshare) {
Mojo.Log.info("Creating new text Share from Just Type");
appModel.LastShareSelected = { guid: "new", contenttype: "text/plain", content: appModel.LaunchQuery.newshare };
this.showNewShareScene();
}
//URL Launch
else if (appModel.LaunchQuery.target && appModel.LaunchQuery.target != "") {
this.handleURLInvocation(appModel.LaunchQuery.target);
}
//Some other kind of query launch
else {
Mojo.Log.warn("Ignoring unhandled launch query");
}
appModel.LaunchQuery = "";
}
//Handle camera sub-launch
if (data && data.filename) {
busy = true;
Mojo.Log.info("Creating a new share from Camera sub-launch " + data.filename);
Mojo.Controller.getAppController().showBanner({ messageText: "Sharing camera image...", icon: "assets/notify.png" }, "", "");
this.uploadCameraImage(data.filename);
}
if (!busy && appModel.AppSettingsCurrent["LastVersionRun"] != Mojo.Controller.appInfo.version) {
appModel.AppSettingsCurrent["LastVersionRun"] = Mojo.Controller.appInfo.version;
appModel.SaveSettings();
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ name: "version", disableSceneScroller: false });
}
} else {
appModel.LaunchQuery = "";
this.showWelcomePrompt();
}
//Check if we're registered to handle URLs
useShortLink = Mojo.Controller.appInfo.shortURL;
if (appModel.AppSettingsCurrent["UseCustomEndpoint"] && appModel.AppSettingsCurrent["ShortURL"] && appModel.AppSettingsCurrent["ShortURL"] != "")
useShortLink = appModel.AppSettingsCurrent["ShortURL"];
systemModel.ListHandlersForURL("https://wosa.link",
function(response) {
var thisMenuModel = this.controller.getWidgetSetup(Mojo.Menu.appMenu).model;
if (JSON.stringify(response).indexOf(Mojo.Controller.appInfo.id) != -1) {
Mojo.Log.warn("This app handles the Short URL " + useShortLink);
thisMenuModel.items[2].chosen = true;
} else {
Mojo.Log.warn("This app does not handle the Short URL " + useShortLink);
thisMenuModel.items[2].chosen = false;
}
this.controller.modelChanged(thisMenuModel);
}.bind(this)
);
};
MainAssistant.prototype.checkForFileMgr = function() {
systemModel.GetInstalledApps(function(response) {
var found = false;
if (response && response.apps) {
for (var i=0; i<response.apps.length; i++)
{
if (response.apps[i].id == "ca.canucksoftware.filemgr")
found = true;
}
} else
Mojo.Log.info("Could not get listed of installed apps.")
if (found)
Mojo.Log.info("Found FileMgr by Jason Robitaille! Download features enabled.")
else {
appModel.AppSettingsCurrent["UseAutoDownload"] = false;
Mojo.Log.info("Could not find FileMgr installed! Download features disabled.")
}
appModel.FileMgrPresent = found;
}.bind(this));
}
MainAssistant.prototype.handleUpdateResponse = function(responseObj) {
if (responseObj && responseObj.updateFound) {
updaterModel.PromptUserForUpdate(function(response) {
if (response)
updaterModel.InstallUpdate();
}.bind(this));
}
}
MainAssistant.prototype.handleURLInvocation = function(query) {
Mojo.Log.info("Handling URL launch of: " + appModel.LaunchQuery.target);
serviceModel.QueryShareData(query, function(itemData) {
Mojo.Log.info("Item query response payload was: " + JSON.stringify(itemData));
if (itemData && itemData.guid) {
appModel.LastShareSelected = itemData;
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ transition: Mojo.Transition.crossFade, name: "detail" });
} else {
Mojo.Log.error("Item query data invalid.");
}
});
}
/* UI Event Handlers */
//Handles the enter key
MainAssistant.prototype.handleKeyPress = function(event) {
if (event.srcElement.toString().indexOf("HTMLInputElement") == -1) {
if (!event.shiftKey && !this.DoingEdit) {
Mojo.Log.info("Key pressed!" + String.fromCharCode(event.keyCode));
appModel.LastShareSelected = { guid: "new" };
this.showNewShareOptions();
}
}
};
MainAssistant.prototype.handleCommand = function(event) {
if (event.type == Mojo.Event.command) {
switch (event.command) {
case 'do-HandleURLs':
var thisMenuModel = this.controller.getWidgetSetup(Mojo.Menu.appMenu).model;
if (thisMenuModel.items[2].chosen) {
//remote URL handler
systemModel.removeHandlerForURL(Mojo.Controller.appInfo.id);
thisMenuModel.items[2].chosen = false;
} else {
//add URL handler
systemModel.AddHandlerForURL("^[^:]+://wosa.link", Mojo.Controller.appInfo.id);
thisMenuModel.items[2].chosen = true;
}
this.controller.modelChanged(thisMenuModel);
break;
case 'do-LogInOut':
this.doLogInOut();
break;
case 'do-Preferences':
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ name: "preferences", disableSceneScroller: false });
break;
case 'do-myAbout':
Mojo.Additions.ShowDialogBox("Share Space - " + Mojo.Controller.appInfo.version, "Share Space sharing service client for webOS. Copyright 2022, webOS Archive. Distributed under an MIT License.<br>Source code available at: https://github.com/webosarchive/webos-sharespace");
break;
case 'do-versionInfo':
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ name: "version", disableSceneScroller: false });
break;
case 'do-mySharePhrase':
Mojo.Additions.ShowDialogBox("Your Share Phrase", "<b>" + appModel.AppSettingsCurrent["SharePhrase"] + "</b><br><br>Remember that anyone who has your Share Space name and Share Phrase can share with you.");
break;
case 'do-debugMode':
if (appModel.AppSettingsCurrent["DebugMode"]) {
appModel.AppSettingsCurrent["DebugMode"] = false;
} else {
appModel.AppSettingsCurrent["DebugMode"] = true;
}
appModel.SaveSettings();
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.crossFade, name: "main" });
break;
case 'do-download':
Mojo.Log.info("Trying to download in background");
appModel.ShowDownloaderStage();
break;
case 'do-new':
appModel.LastShareSelected = { guid: "new" };
this.showNewShareOptions();
break;
case 'do-refresh':
this.fetchShares();
break;
}
}
}
MainAssistant.prototype.handleListDelete = function(event) {
Mojo.Log.warn("Deleting item: " + event.item.guid);
serviceModel.DoShareDeleteRequest(event.item.guid, appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"]);
}
MainAssistant.prototype.handleListClick = function(event) {
Mojo.Log.info("Item tapped with element class " + event.originalEvent.target.className);
Mojo.Log.info("Item details: " + JSON.stringify(event.item));
appModel.LastShareSelected = event.item;
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ transition: Mojo.Transition.crossFade, name: "detail" });
return false;
}
MainAssistant.prototype.handleListAdd = function(event) {
//TODO: Verify this works
appModel.LastShareSelected = { guid: "new" };
this.showNewShareOptions();
}
MainAssistant.prototype.handlePopupChoose = function(share, command) {
Mojo.Log.info("Perform: ", command, " on ", share.guid);
switch (command) {
case "do-complete":
break;
}
}
/* Get Share Stuff */
MainAssistant.prototype.fetchShares = function() {
this.controller.window.clearInterval(refreshInt);
if (appModel.AppSettingsCurrent["RefreshTimeout"] && appModel.AppSettingsCurrent["RefreshTimeout"] > 1000) {
Mojo.Log.info("Auto refresh interval: " + appModel.AppSettingsCurrent["RefreshTimeout"]);
refreshInt = this.controller.window.setInterval(this.fetchShares.bind(this), appModel.AppSettingsCurrent["RefreshTimeout"]);
} else {
Mojo.Log.warn("Using Manual Refresh");
}
serviceModel.DoShareListRequest(appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], function(responseObj){
//Mojo.Log.info("ready to process share list: " + JSON.stringify(responseObj));
if (responseObj != null) {
if (responseObj.shares) {
//If we got a good looking response, update the UI
this.updateShareListWidget(appModel.AppSettingsCurrent["Username"], responseObj.shares, responseObj.accesslevel);
if ((!appModel.AppSettingsCurrent["SharePhrase"] || appModel.AppSettingsCurrent["SharePhrase"] == "") && responseObj.sharephrase){
appModel.AppSettingsCurrent["SharePhrase"] = responseObj.sharephrase;
appModel.SaveSettings();
}
} else {
Mojo.Log.warn("Share list was empty. Either there was no matching result, or there were server or connectivity problems.");
}
} else {
Mojo.Log.error("No usable response from server while loading shares: " + response);
this.errorHandler("The server did not answer with a usable response to the share list request. Check network connectivity, SSL and/or self-host settings.");
}
}.bind(this), this.errorHandler.bind(this));
}
MainAssistant.prototype.updateShareListWidget = function(username, results, accessLevel) {
Mojo.Log.info("Displaying shares for: " + username + " with access level: " + accessLevel);
this.controller.get("spnUsername").innerHTML = username;
this.controller.get("workingSpinner").mojo.stop();
this.controller.get("divSpinnerContainer").style.display = "none";
var thisShareList = this.controller.getWidgetSetup("shareList");
thisShareList.model.items = []; //remove the previous list
for (var i = 0; i < results.length; i++) {
if (accessLevel && accessLevel == "admin") {
results[i].readonly = false;
} else {
results[i].readonly = true;
}
results[i].timestamp = appModel.ConvertTimeStamp(results[i].timestamp, true);
thisShareList.model.items.push(results[i]);
}
Mojo.Log.info("Updating share list widget with " + results.length + " results!");
this.controller.get("showShareList").style.display = "block";
this.controller.modelChanged(thisShareList.model);
this.errorCount = 0;
}
/* New Share Stuff */
MainAssistant.prototype.showNewShareOptions = function() {
var choices = [
{ label: "Text", value: "text/plain", type: "neutral" },
{ label: "JSON", value: "application/json", type: "neutral" },
{ label: "Image", value: "image", type: "neutral" },
];
if (this.deviceType != "TouchPad")
choices.push({ label: "Camera", value: "camera", type: "neutral" });
choices.push({ label: "Cancel", value: "cancel", type: "negative" });
this.controller.showAlertDialog({
onChoose: function(value) {
Mojo.Log.info("Choice was: " + value);
if (value == "image") {
this.pickAndUploadImage();
} else if (value == "camera") {
this.getCameraImage();
} else if (value == "text/plain" || value == "application/json") {
appModel.LastShareSelected.contenttype = value;
Mojo.Log.info("LastShareSelected: " + JSON.stringify(appModel.LastShareSelected));
this.showNewShareScene();
} else {
Mojo.Log.info("Cancelled new share");
}
},
title: "New Share",
message: "What do you want to share?",
choices: choices
});
}
MainAssistant.prototype.pickAndUploadImage = function() {
var self = this; //Retain the reference for the callback
var params = { kind: 'image', actionName: 'Share Photo',
onSelect: function(file){
Mojo.Log.info("selected file was: " + JSON.stringify(file));
Mojo.Controller.getAppController().showBanner({ messageText: 'Sharing image...', icon: 'assets/notify.png' }, { source: 'notification' });
//Figure out what mimetype to use
var ext = file.fullPath.split(".");
ext = ext[ext.length - 1].toLowerCase();
if (ext == "jpg")
mimetype = "image/jpeg";
else
mimetype = "image/" + ext;
serviceModel.DoShareAddRequestImage(file.fullPath, appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], mimetype, function(responseObj) {
if (responseObj != null) {
Mojo.Log.info("image share response: " + JSON.stringify(responseObj));
if (appModel.AppSettingsCurrent["CopyLinkOnShare"]) {
var stageController = Mojo.Controller.getAppController().getActiveStageController()
stageController.setClipboard(serviceModel.MakeShareURL(appModel.AppSettingsCurrent["Username"], responseObj.guid, "image"));
Mojo.Controller.getAppController().showBanner({ messageText: "Image shared, link copied!", icon: "assets/notify.png" }, "", "");
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Image shared!", icon: "assets/notify.png" }, "", "");
}
self.fetchShares();
} else {
Mojo.Log.error("No usable response from server while uploading share");
Mojo.Controller.getAppController().showBanner({ messageText: "Bad response uploading image", icon: "assets/notify.png" }, "", "");
}
}.bind(self), function(errorText) {
Mojo.Log.error(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: "Upload failure: " + errorText, icon: "assets/notify.png" }, "", "");
});
}
}
Mojo.FilePicker.pickFile(params, this.controller.stageController);
}
MainAssistant.prototype.getCameraImage = function() {
this.controller.stageController.pushScene(
{ appId: "com.palm.app.camera", name: "capture" },
{ sublaunch: true, mode: "stil" }
);
}
MainAssistant.prototype.uploadCameraImage = function(file) {
var self = this;
serviceModel.DoShareAddRequestImage(file, appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], "image/jpeg", function(responseObj) {
if (responseObj != null) {
if (appModel.AppSettingsCurrent["CopyLinkOnShare"]) {
var stageController = Mojo.Controller.getAppController().getActiveStageController()
stageController.setClipboard(serviceModel.MakeShareURL(appModel.AppSettingsCurrent["Username"], responseObj.guid, "image"));
Mojo.Controller.getAppController().showBanner({ messageText: "Image shared, link copied!", icon: "assets/notify.png" }, "", "");
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Image shared!", icon: "assets/notify.png" }, "", "");
}
self.fetchShares();
} else {
Mojo.Log.error("No usable response from server while uploading share");
Mojo.Controller.getAppController().showBanner({ messageText: "Bad response uploading image", icon: "assets/notify.png" }, "", "");
}
}.bind(self), function(errorText) {
Mojo.Log.error(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: "Upload failure: " + errorText, icon: "assets/notify.png" }, "", "");
});
}
MainAssistant.prototype.showNewShareScene = function() {
this.DoingEdit = true;
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (!stageController)
stageController = Mojo.Controller.getAppController().getStageController(MainStageName);
stageController.pushScene({ transition: Mojo.Transition.crossFade, name: "newshare" });
}
/* Login Stuff */
MainAssistant.prototype.showWelcomePrompt = function() {
this.controller.showAlertDialog({
onChoose: function(value) {
if (value == "login") {
this.showLogin();
} else if (value == "new") {
Mojo.Log.info("new selected!");
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.crossFade, name: "newuser" });
}
},
title: "Welcome to Share Space!",
message: "Your shared clipboard for webOS! This client app can be used with a web service and web app that lets you share content between devices, or with other users! Do you want to Log In to an existing share space, or create a new one?",
choices: [
{ label: 'Log In', value: "login", type: 'affirmative' },
{ label: 'Create New', value: "new", type: 'neutral' },
]
});
}
MainAssistant.prototype.doLogInOut = function() {
if (appModel.AppSettingsCurrent["Username"] == "" && appModel.AppSettingsCurrent["Credential"] == "") {
this.showWelcomePrompt();
} else { //Log out
//Clear credentials
appModel.AppSettingsCurrent["Username"] = "";
appModel.AppSettingsCurrent["Credential"] = "";
appModel.AppSettingsCurrent["SharePhrase"] = "";
appModel.SaveSettings();
//Re-paint scene
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.crossFade, name: "main", disableSceneScroller: false });
}
}
MainAssistant.prototype.showLogin = function() {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (stageController) {
this.controller = stageController.activeScene();
this.controller.showDialog({
template: 'login/login-scene',
assistant: new LoginAssistant(this, function(dialogResponse) {
if (dialogResponse == true) {
Mojo.Log.info("Re-painting scene after successful login!");
//Re-paint scene
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.crossFade, name: "main", disableSceneScroller: false });
} else {
if (dialogResponse.error) {
var readableError = dialogResponse.error.replace("Share service error: ", "");
readableError = readableError.charAt(0).toUpperCase() + readableError.slice(1);
Mojo.Additions.ShowDialogBox("Share Service Error", readableError);
}
}
}.bind(this))
});
}
}
/* End of Life Stuff */
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 */
this.controller.window.clearInterval(refreshInt);
Mojo.Event.stopListening(this.controller.get("shareList"), Mojo.Event.listDelete, this.handleListDelete);
Mojo.Event.stopListening(this.controller.get("shareList"), Mojo.Event.listTap, this.handleListClick);
Mojo.Event.stopListening(this.controller.get("shareList"), Mojo.Event.listAdd, this.handleListAdd);
//Just type
//this.controller.document.removeEventListener("keypress", this.keypressHandler);
};
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 */
};
/* Helper Functions */
MainAssistant.prototype.errorHandler = function (errorText, callback) {
this.errorCount++;
Mojo.Log.error("error count: " + this.errorCount + ", " + errorText);
if (this.errorCount > 5) {
Mojo.Additions.ShowDialogBox("Sync Error", errorText);
this.controller.window.clearInterval(refreshInt);
Mojo.Controller.getAppController().showBanner({ messageText: "Offline", icon: "assets/notify.png" }, "", "");
}
if (callback) {
callback.bind(this);
callback({"error": errorText});
}
}
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
}
+132
View File
@@ -0,0 +1,132 @@
function NewshareAssistant(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 NewShare assistant exists");
this.sceneAssistant = sceneAssistant; //dialog's do not have their own controller, so need access to the launching scene's controller
}
NewshareAssistant.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("Current Share Target is: " + appModel.AppSettingsCurrent["Username"]);
/* setup widgets here */
//Text box
var textAttrib = {
textFieldName: "ShareContent",
hintText: "Text to share",
property: 'value',
multiline: true,
changeOnKeyPress: true,
autoReplace: true,
textCase: Mojo.Widget.steModeSentenceCase,
requiresEnterKey: false,
focus: true
};
if (appModel.LastShareSelected.contenttype == "application/json") {
textAttrib.hintText = "JSON to share"
textAttrib.autoReplace = false;
textAttrib.textCase = Mojo.Widget.steModeLowerCase;
}
Mojo.Log.info("Using Last Share: " + JSON.stringify(appModel.LastShareSelected));
this.controller.setupWidget("txtShareContent",
this.attributes = textAttrib,
this.model = {
value: appModel.LastShareSelected.content,
disabled: false
}
);
this.controller.setupWidget("goButton", { type: Mojo.Widget.activityButton }, { label: "OK", disabled: false });
this.controller.setupWidget("cancelButton", { type: Mojo.Widget.button }, { label: "Cancel", disabled: false });
};
NewshareAssistant.prototype.activate = function(event) {
Mojo.Log.info("NewShare assistant activated for share: " + appModel.LastShareSelected.guid);
if (appModel.LastShareSelected.guid == "new") {
this.controller.get("divEditTitle").innerHTML = "New Share Content";
} else {
this.controller.get("divEditTitle").innerHTML = "Edit Share Content";
}
/* add event handlers to listen to events from widgets */
Mojo.Event.listen(this.controller.get("goButton"), Mojo.Event.tap, this.handleOKPress.bind(this));
Mojo.Event.listen(this.controller.get("cancelButton"), Mojo.Event.tap, this.handleCancelPress.bind(this));
//Resize event
this.controller.window.onresize = this.calculateControlsPosition.bind(this);
this.calculateControlsPosition();
};
NewshareAssistant.prototype.calculateControlsPosition = function() {
var maxHeight = 380;
if ((this.controller.window.innerHeight - 210) < maxHeight)
maxHeight = this.controller.window.innerHeight - 210;
this.controller.get("txtShareContent").style.height = maxHeight + "px";
}
NewshareAssistant.prototype.handleCancelPress = function(event) {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ transition: Mojo.Transition.crossFade, name: "main" });
}
NewshareAssistant.prototype.handleOKPress = function(event) {
var shareContent = this.controller.get("txtShareContent").mojo.getValue();
if (shareContent && shareContent != "") {
if (appModel.LastShareSelected.contenttype == "application/json") {
try {
var testObj = JSON.parse(shareContent);
} catch (ex) {
Mojo.Controller.getAppController().showBanner({ messageText: 'JSON content not be parsed', icon: 'assets/notify.png' }, { source: 'notification' });
this.controller.get('goButton').mojo.deactivate();
this.controller.window.setTimeout(this.controller.get('txtShareContent').mojo.focus(), 500);
return false;
}
}
appModel.LastShareSelected.content = shareContent;
this.tryAddShare();
} else {
this.controller.get('goButton').mojo.deactivate();
this.controller.get("txtShareContent").mojo.focus();
}
}
NewshareAssistant.prototype.tryAddShare = function() {
serviceModel.DoShareAddRequestText(appModel.LastShareSelected.content, appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], appModel.LastShareSelected.contenttype, function(responseObj) {
if (responseObj && responseObj.guid && responseObj.guid != "") {
Mojo.Log.info("Add share success!");
if (appModel.AppSettingsCurrent["CopyLinkOnShare"]) {
var stageController = Mojo.Controller.getAppController().getActiveStageController()
stageController.setClipboard(serviceModel.MakeShareURL(appModel.AppSettingsCurrent["Username"], responseObj.guid, "text"));
Mojo.Controller.getAppController().showBanner({ messageText: 'Content shared, link copied!', icon: 'assets/notify.png' }, { source: 'notification' });
} else {
Mojo.Controller.getAppController().showBanner({ messageText: 'Content shared!', icon: 'assets/notify.png' }, { source: 'notification' });
}
} else {
this.errorHandler("Share failure: " + JSON.stringify(responseObj))
}
this.handleCancelPress();
}.bind(this), this.errorHandler.bind(this));
}
NewshareAssistant.prototype.errorHandler = function (errorText, callback) {
Mojo.Log.error(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText, icon: "assets/notify.png" }, "", "");
errorText = errorText.charAt(0).toUpperCase() + errorText.slice(1);
this.controller.get('goButton').mojo.deactivate();
Mojo.Additions.ShowDialogBox("Share Sevice Error", errorText);
}
NewshareAssistant.prototype.deactivate = function(event) {
Mojo.Log.info("NewShare assistant deactivated");
Mojo.Event.stopListening(this.controller.get("goButton"), Mojo.Event.tap, this.handleOKPress);
Mojo.Event.stopListening(this.controller.get("cancelButton"), Mojo.Event.tap, this.handleCancelPress);
};
NewshareAssistant.prototype.cleanup = function(event) {
Mojo.Log.info("NewShare 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 */
};
+173
View File
@@ -0,0 +1,173 @@
function NewuserAssistant() {
/* 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. */
}
NewuserAssistant.prototype.setup = function() {
/* setup widgets here */
this.controller.setupWidget("scrollTnC",
this.attributes = {
mode: 'vertical'
},
this.model = {}
);
//Agree Button
this.controller.setupWidget("btnAgree", { type: Mojo.Widget.defaultButton }, { label: "Agree", buttonClass: "affirmative", disabled: true });
//Cancel Button
this.controller.setupWidget("btnCancel", { type: Mojo.Widget.defaultButton }, { label: "Cancel", buttonClass: "negative", disabled: false });
//OK Button
this.controller.setupWidget("btnOK", { type: Mojo.Widget.activityButton }, { label: "Create Space", buttonClass: "affirmative", disabled: false });
//Username Field
this.controller.setupWidget("txtUsername",
this.attributes = {
hintText: $L("Choose a username"),
multiline: false,
enterSubmits: false,
focus: true,
textCase: Mojo.Widget.steModeLowerCase,
autoReplace: false
},
this.model = {
disabled: false
}
);
//Username Field
this.controller.setupWidget("txtPassword",
this.attributes = {
hintText: $L("Enter a Password"),
multiline: false,
enterSubmits: false,
focus: false,
textCase: Mojo.Widget.steModeLowerCase,
autoReplace: false
},
this.model = {
disabled: false
}
);
//Menu
this.appMenuAttributes = { omitDefaultItems: true };
this.appMenuModel = {};
this.controller.setupWidget(Mojo.Menu.appMenu, this.appMenuAttributes, this.appMenuModel);
};
NewuserAssistant.prototype.activate = function(event) {
/* add event handlers to listen to events from widgets */
Mojo.Event.listen(this.controller.get("btnAgree"), Mojo.Event.tap, this.agreeClick.bind(this));
Mojo.Event.listen(this.controller.get("btnCancel"), Mojo.Event.tap, this.cancelClick.bind(this));
Mojo.Event.listen(this.controller.get("btnOK"), Mojo.Event.tap, this.okClick.bind(this));
//bind tryagain listener
this.controller.get("imgTryagain").addEventListener("click", this.generateSharePhrase.bind(this));
//Get terms and conditions from service and put into UI
serviceModel.GetTnC(function(response) {
this.controller.get("divTnC").innerHTML = response;
Mojo.Additions.DisableWidget("btnAgree", false);
}.bind(this));
this.sharephrase = "";
//Resize event
this.controller.window.onresize = this.calculateControlsPosition.bind(this);
this.calculateControlsPosition();
};
NewuserAssistant.prototype.calculateControlsPosition = function() {
var maxHeight = 380;
if ((this.controller.window.innerHeight - 180) < maxHeight)
maxHeight = this.controller.window.innerHeight - 180;
this.controller.get("scrollTnC").style.height = maxHeight + "px";
}
NewuserAssistant.prototype.agreeClick = function(event) {
Mojo.Additions.DisableWidget("btnAgree", true);
this.controller.get("divCredentials").style.display = "block";
this.controller.get("btnOK").style.display = "block";
this.generateSharePhrase();
this.controller.getSceneScroller().mojo.revealElement(this.controller.get("txtPassword"));
}
NewuserAssistant.prototype.generateSharePhrase = function() {
//Get new credentials from service and put into UI
serviceModel.GetRandomWords(function(response) {
if (response) {
this.controller.get("divSharephrase").innerHTML = response;
this.sharephrase = response;
} else {
Mojo.Additions.ShowDialogBox("Service Error", "Did not receive random words from service.");
}
}.bind(this));
}
NewuserAssistant.prototype.okClick = function(event) {
//validate input
var disallowed = ["con", "prn", "aux", "nul", "com", "do", "done", "elif", "else", "esac", "fi", "for", "function", "if", "in", "select", "then", "until", "while", "time"];
this.username = this.controller.get('txtUsername').mojo.getValue().toLowerCase();
if (this.username == "" || disallowed.indexOf(this.username) != -1 || this.username.match(/[^a-z]/g) || this.username.indexOf(" ") != -1) {
Mojo.Additions.ShowDialogBox("Invalid username", "Your username must be one word, alphabetic characters only, and cannot contain any reserved words for Unix or Windows operating systems.");
this.controller.get('btnOK').mojo.deactivate();
} else {
if (this.sharephrase && this.sharephrase != "") {
this.password = this.controller.get('txtPassword').mojo.getValue().toLowerCase();
if (this.password == "" || disallowed.indexOf(this.password) != -1 || this.password.match(/[^A-Za-z0-9 ]/g) || this.password.indexOf(" ") != -1) {
Mojo.Additions.ShowDialogBox("Invalid password", "Your password must be one word, alpha-numeric characters only, and cannot contain any reserved words for Unix or Windows operating systems.");
this.controller.get('btnOK').mojo.deactivate();
} else {
serviceModel.DoNewUserRequest(this.username, this.sharephrase, this.password, function(responseObj) {
if (responseObj != null) {
appModel.AppSettingsCurrent["Username"] = this.username;
appModel.AppSettingsCurrent["Credential"] = this.password;
appModel.AppSettingsCurrent["SharePhrase"] = this.sharephrase;
appModel.SaveSettings();
Mojo.Log.info("Successfully created account ", appModel.AppSettingsCurrent["Username"], appModel.AppSettingsCurrent["Credential"], appModel.AppSettingsCurrent["SharePhrase"]);
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.zoomFade, name: "welcome" });
} else {
Mojo.Log.error("No usable response from server while uploading share");
Mojo.Controller.getAppController().showBanner({ messageText: "Bad response uploading image", icon: "assets/notify.png" }, "", "");
}
this.controller.get('btnOK').mojo.deactivate();
}.bind(this), this.errorHandler.bind(this));
}
} else {
Mojo.Additions.ShowDialogBox("Missing Share Phrase", "The share phrase is provided by the server, but appears to be missing. This likely means the server is unreachable, down, or improperly configured.");
this.controller.get('btnOK').mojo.deactivate();
}
}
}
NewuserAssistant.prototype.cancelClick = function(event) {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.zoomFade, name: "main" });
}
NewuserAssistant.prototype.errorHandler = function (errorText, callback) {
Mojo.Log.error(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText, icon: "assets/notify.png" }, "", "");
errorText = errorText.charAt(0).toUpperCase() + errorText.slice(1);
Mojo.Additions.ShowDialogBox("Share Sevice Error", errorText);
this.controller.get('btnOK').mojo.deactivate();
}
NewuserAssistant.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("btnAgree"), Mojo.Event.tap, this.agreeClick);
Mojo.Event.stopListening(this.controller.get("btnCancel"), Mojo.Event.tap, this.cancelClick);
Mojo.Event.stopListening(this.controller.get("btnOK"), Mojo.Event.tap, this.cancelClick);
this.controller.window.removeEventListener("resize", this.calculateControlsPosition);
this.controller.window.onresize = null;
};
NewuserAssistant.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 */
};
+310
View File
@@ -0,0 +1,310 @@
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 */
//Refresh timeout picker
this.controller.setupWidget("listRefresh",
this.attributes = {
label: $L("Refresh"),
choices: [
{ label: "Manual", value: "" },
{ label: "1 minute", value: 60000 },
{ label: "2 minutes", value: 120000 },
{ label: "3 minutes", value: 180000 },
{ label: "5 minutes", value: 300000 }
]
},
this.model = {
value: appModel.AppSettingsCurrent["RefreshTimeout"],
disabled: false
}
);
//Toggles
var disableDL = true;
if (appModel.FileMgrPresent)
disableDL = false;
this.controller.setupWidget("toggleAutoDownload",
this.attributes = {
trueValue: true,
falseValue: false
},
this.model = {
value: appModel.AppSettingsCurrent["UseAutoDownload"],
disabled: disableDL
}
);
//List boxes
this.controller.setupWidget("listAutoDownloadTime",
this.attributes = {
label: $L("Download Interval"),
choices: [
{ label: "5 minutes", value: "00:05:00" },
{ label: "30 minutes", value: "00:30:00" },
{ label: "1 Hour", value: "01:00:00" },
{ label: "2 Hours", value: "02:00:00" },
{ label: "3 Hours", value: "03:00:00" },
{ label: "6 Hours", value: "06:00:00" },
{ label: "12 Hours", value: "12:00:00" },
{ label: "24 Hours", value: "23:59:59" }
]
},
this.model = {
value: appModel.AppSettingsCurrent["AutoDownloadTime"],
disabled: disableDL
}
);
if (appModel.FileMgrPresent)
this.controller.get("divDownloadExplain").innerHTML = "Frequent automatic downloads can have significant impact on battery life.";
//More toggles
this.controller.setupWidget("toggleCopyLinkOnShare",
this.attributes = {
trueValue: true,
falseValue: false
},
this.model = {
value: appModel.AppSettingsCurrent["CopyLinkOnShare"],
disabled: false
}
);
this.controller.setupWidget("toggleForceHTTP",
this.attributes = {
trueValue: true,
falseValue: false
},
this.model = {
value: appModel.AppSettingsCurrent["ForceHTTP"],
disabled: false
}
);
this.controller.setupWidget("toggleCustomClientId",
this.attributes = {
trueValue: true,
falseValue: false
},
this.model = {
value: appModel.AppSettingsCurrent["UseCustomClientId"],
disabled: false
}
);
this.controller.setupWidget("toggleCustomEndPoint",
this.attributes = {
trueValue: true,
falseValue: false
},
this.model = {
value: appModel.AppSettingsCurrent["UseCustomEndpoint"],
disabled: false
}
);
//Text fields
this.controller.setupWidget("txtCustomClientId",
this.attributes = {
hintText: $L("Your Service Client Id"),
multiline: false,
enterSubmits: false,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase
},
this.model = {
value: appModel.AppSettingsCurrent["CustomClientId"],
disabled: !appModel.AppSettingsCurrent["UseCustomEndpoint"]
}
);
this.controller.setupWidget("txtEndpointURL",
this.attributes = {
hintText: $L("http://your-server.com/"),
multiline: false,
enterSubmits: false,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase
},
this.model = {
value: appModel.AppSettingsCurrent["EndpointURL"],
disabled: !appModel.AppSettingsCurrent["UseCustomEndpoint"]
}
);
this.controller.setupWidget("txtShortURL",
this.attributes = {
hintText: $L("http://short.link/"),
multiline: false,
enterSubmits: false,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase
},
this.model = {
value: appModel.AppSettingsCurrent["ShortURL"],
disabled: !appModel.AppSettingsCurrent["UseCustomEndpoint"]
}
);
this.controller.setupWidget("txtCustomCreateKey",
this.attributes = {
hintText: $L("Your Service Create Key"),
multiline: false,
enterSubmits: false,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase
},
this.model = {
value: appModel.AppSettingsCurrent["CustomCreateKey"],
disabled: !appModel.AppSettingsCurrent["UseCustomEndpoint"]
}
);
//Drawer
this.controller.setupWidget("drawerSelfHost",
this.attributes = {
modelProperty: 'open',
unstyled: false
},
this.model = {
open: appModel.AppSettingsCurrent["UseCustomEndpoint"]
}
);
//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);
};
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 */
/* add event handlers to listen to events from widgets */
Mojo.Event.listen(this.controller.get("listRefresh"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("toggleAutoDownload"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("listAutoDownloadTime"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("toggleCopyLinkOnShare"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("toggleForceHTTP"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("toggleCustomClientId"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("txtCustomClientId"), 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("txtEndpointURL"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("txtShortURL"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("txtCustomCreateKey"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("btnOK"), Mojo.Event.tap, this.okClick.bind(this));
};
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);
switch (event.srcElement.id) {
case "toggleCustomClientId":
{
var thisWidgetSetup = this.controller.getWidgetSetup("txtCustomClientId");
thisWidgetSetup.model.disabled = !event.value;
this.controller.modelChanged(thisWidgetSetup.model);
if (event.value)
this.controller.get('txtCustomClientId').mojo.focus();
break;
}
case "toggleAutoDownload":
{
var thisWidgetSetup = this.controller.getWidgetSetup("listAutoDownloadTime");
thisWidgetSetup.model.disabled = !event.value;
this.controller.modelChanged(thisWidgetSetup.model);
break;
}
case "toggleCustomEndPoint":
{
//Toggle enabled on related text boxes
var thisWidgetSetup = this.controller.getWidgetSetup("drawerSelfHost");
thisWidgetSetup.model.open = event.value;
this.controller.modelChanged(thisWidgetSetup.model);
var thisWidgetSetup = this.controller.getWidgetSetup("txtEndpointURL");
thisWidgetSetup.model.disabled = !event.value;
this.controller.modelChanged(thisWidgetSetup.model);
var thisWidgetSetup = this.controller.getWidgetSetup("txtShortURL");
thisWidgetSetup.model.disabled = !event.value;
this.controller.modelChanged(thisWidgetSetup.model);
var thisWidgetSetup = this.controller.getWidgetSetup("txtCustomCreateKey");
thisWidgetSetup.model.disabled = !event.value;
this.controller.modelChanged(thisWidgetSetup.model);
if (event.value)
this.controller.get('txtEndpointURL').mojo.focus();
break;
}
case "txtEndpointURL":
var lastChar = event.value[event.value.length - 1]
if (lastChar != "/") {
event.value = event.value + "/";
Mojo.Log.warn("Custom end point URL was missing trailing slash, it has been added. Value is now: " + event.value);
}
if(!appModel.AppSettingsCurrent["ShortURL"] || appModel.AppSettingsCurrent["ShortURL"] == "") {
this.controller.get('txtShortURL').mojo.setValue(event.value);
appModel.AppSettingsCurrent["ShortURL"] = event.value;
}
break;
}
//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();
};
//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) {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.popScene();
}
PreferencesAssistant.prototype.deactivate = function(event) {
if (this.controller.get('txtEndpointURL').mojo.getValue() == "") {
appModel.AppSettingsCurrent["UseCustomEndpoint"] = false;
}
appModel.SaveSettings();
appModel.EstablishAlarms();
/* 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("listRefresh"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("toggleAutoDownload"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("listAutoDownloadTime"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("toggleForceHTTP"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("toggleCustomClientId"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("txtCustomClientId"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("toggleCustomEndPoint"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("txtEndpointURL"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("txtShortURL"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("txtCustomCreateKey"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("btnOK"), Mojo.Event.tap, this.okClick);
};
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 */
};
+11
View File
@@ -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("free");
};
@@ -2,29 +2,41 @@ function VersionAssistant(startup) {
this.isStartup = startup;
// on first start, this message is displayed, along with the current version message from below
this.startupMessage = $L("<img src='icon.png' style='float:left; margin-right:8px'>Welcome to SimpleChat! We're so excited you're here chatting with us 10 years after this platform was supposed to have died!<br>You can also join this chat from Discord: <a href='discord.gg/7NrrT8exrn'>discord.gg/7NrrT8exrn</a> or view the log on the web: <a href='http://chat.webosarchive.com'>chat.webosarchive.com</a>");
this.startupMessage = Mojo.Controller.appInfo.startupMessage;
//New Features
this.changelog = [{
version: "Version 1.1.3",
detail: [
"Bug fixes and general maintenance"
]
}, {
version: "Version 1.1.2",
detail: [
"Show/Hide the Command menu buttons by tapping the Compose area title bar",
"Enable/Disable Discord Emojis in Preferences",
]
}, {
version: "Version 1.1.1",
detail: [
"Improved Discord integration, including showing Emojis!<span class='emoji-outer emoji-sizer'><span class='emoji-inner emoji1f388'></span></span>",
"Now with a smaller emoji file!",
"Improved timeout logic.",
"Trying different background sync logic."
]
}];
this.changelog = [
{
version: "Version 1.0.2 Changes",
detail: [
"Fixes a bug that prevents background downloads when Always Use HTTP is on",
]
},
{
version: "Version 1.0.1 Changes",
detail: [
"Add download button to detail scene",
"Fix a layout issue",
]
},
{
version: "Version 1.0.0 Features",
detail: [
"Wirelessly add photos to Exhibition from anywhere with automatic downloads -- just turn it on in Preferences (requires FileMgr)",
"Touch2Share content between your devices, either loading it in a web page, or right in the app (turn on 'Handle URLs' in the menu)",
"Share photos you take on your device via email, text message, or other web apps by copying the link the app makes for you -- grab it from the content detail scene, or turn on 'Copy Link on Share' in Preferences",
"Start a new share from JustType -- turn it on in your JustType settings",
"Host your own server for more control and privacy!"
]
},
{
version: "Coming Soon",
detail: [
"Shared Clipboard mode will minimize to a Dashboard to make it quick and easy to share text and links between your devices",
"Share with other users, without needing to log out and back in",
]
}
];
// setup command menu
this.cmdMenuModel = {
+68
View File
@@ -0,0 +1,68 @@
function WelcomeAssistant(startup) {
this.isStartup = startup;
// on first start, this message is displayed, along with the current version message from below
//this.startupMessage = Mojo.Controller.appInfo.startupMessage;
this.startupMessage = "Your account has been created!";
// setup command menu
this.cmdMenuModel = {
visible: false,
items: [
{},
{
label: $L("Got it!"),
command: 'do-continue'
},
{}
]
};
};
WelcomeAssistant.prototype.setup = function() {
//this.titleElement = this.controller.get('title');
this.dataElement = this.controller.get('welcome-data');
//this.titleElement.innerHTML = $L('Version Info');
var useURL = serviceModel.buildURL(null, "");
useURL = useURL.replace(".php", "");
var html = '<p>';
html += '<b>Name:</b> <em>' + appModel.AppSettingsCurrent["Username"] + "</em><br>";
html += '<b>Share Phrase:</b> <em>' + appModel.AppSettingsCurrent["SharePhrase"] + "</em><br>";
html += '<b>Admin Password:</b> <em>' + appModel.AppSettingsCurrent["Credential"] + "</em><br>";
html += '<b>URL:</b> <em>' + useURL + "</em><br></p>";
// set data
this.dataElement.innerHTML = html;
// setup menu
this.controller.setupWidget(Mojo.Menu.appMenu, { omitDefaultItems: true }, { visible: false });
//if (this.isStartup) {
// set command menu
this.controller.setupWidget(Mojo.Menu.commandMenu, { menuClass: 'no-fade' }, this.cmdMenuModel);
//}
};
WelcomeAssistant.prototype.activate = function(event) {
this.timer = this.controller.window.setTimeout(this.showContinue.bind(this), 4 * 1000);
};
WelcomeAssistant.prototype.deactivate = function(event) {};
WelcomeAssistant.prototype.cleanup = function(event) {};
WelcomeAssistant.prototype.showContinue = function() {
// show the command menu
this.controller.setMenuVisible(Mojo.Menu.commandMenu, true);
};
WelcomeAssistant.prototype.handleCommand = function(event) {
if (event.type == Mojo.Event.command) {
switch (event.command) {
case 'do-continue':
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.swapScene({ transition: Mojo.Transition.zoomFade, name: "main" });
break;
}
}
};
@@ -2,14 +2,18 @@
Mojo Additions
Version 0.3e
Created: 2018
Author: Jonathan Wise
Author: Jon W
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.
*/
//** Note: If you synced this file from a common repository, local edits may be over-written! */
this.Additions = function() {}
Mojo.Log.info("#### Mojo Additions added");
Additions.ShowDialogBox = function(title, message) {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (stageController) {
@@ -20,7 +24,6 @@ Additions.ShowDialogBox = function(title, message) {
title: title,
message: message,
choices: [{ label: 'OK', value: 'OK' }],
preventCancel: true,
allowHTMLMessage: true
});
}
@@ -62,6 +65,16 @@ Additions.SetWidgetLabel = function(widgetName, newvalue) {
}
}
Additions.GetWidgetLabel = function(widgetName) {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (stageController) {
this.controller = stageController.activeScene();
var thisWidgetModel = this.controller.getWidgetSetup(widgetName).model;
return thisWidgetModel.label;
}
}
Additions.SetToggleState = function(widgetName, toggledValue) {
var stageController = Mojo.Controller.getAppController().getStageController("");
if (stageController) {
+97 -16
View File
@@ -2,7 +2,6 @@
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
*/
@@ -12,31 +11,113 @@ var AppModel = function() {
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;
this.LastMessageSelected = null;
this.LastShareSelected = null;
this.CurrentShareURL = null;
this.FileMgrPresent = true;
//Define your app preferences (to be saved by OS)
this.AppSettingsCurrent = null;
this.AppSettingsDefaults = {
ForegroundUpdate: 10000,
BackgroundUpdate: "00:30:00",
AlertSound: "Subtle (short)",
ShowEmojis: true,
UseClientAPIKey: false,
Username: "",
Credential: "",
SharePhrase: "",
RefreshTimeout: 60000,
UseAutoDownload: false,
AutoDownloadTime: "01:00:00",
CopyLinkOnShare: true,
ForceHTTP: false,
UseCustomEndpoint: false,
ClientAPIKey: "",
EndpointURL: "",
SenderName: "webOS User",
FirstRun: true,
MyMessages: null,
LastKnownMessage: null,
LastVersionRun: 0
ShortURL: "",
UseCustomClientId: false,
CustomClientId: "",
CustomCreateKey: "",
UseCustomDownloadPath: false,
CustomDownloadPath: null,
LastVersionRun: "0.0.1",
DebugMode: false,
};
}
AppModel.prototype.ShowDownloaderStage = function() {
Mojo.Log.info("App Model checking if we should to show Dashboard stage...");
this.serviceEndpointBase = appModel.ServiceEndpointBase;
if (appModel.AppSettingsCurrent["UseCustomEndpoint"] && appModel.AppSettingsCurrent["EndpointURL"]) {
this.serviceEndpointBase = appModel.AppSettingsCurrent["EndpointURL"];
}
//Check for connectivity, then check for messages
systemModel.GetInternetConnectionState(this.checkForMessages.bind(this));
}
AppModel.prototype.checkForMessages = function(connMgrResponse) {
Mojo.Log.info("App Model got response from connection manager: " + JSON.stringify(connMgrResponse));
if (connMgrResponse && connMgrResponse.isInternetConnectionAvailable) {
this.actuallyShowDownloaderStage();
}
}
AppModel.prototype.actuallyShowDownloaderStage = function() {
this.controller = Mojo.Controller.getAppController();
var dashboardStage = this.controller.getStageProxy("download");
var pushDashScene = function(stageController) {
stageController.pushScene('download', "download/download-scene");
}.bind(this);
if (!dashboardStage) {
Mojo.Log.info("Download dashboard not open, creating it...");
this.controller.createStageWithCallback({
name: 'download',
lightweight: true,
height: 100,
soundclass: "assets/silent.mp3"
}, pushDashScene, 'dashboard');
} else {
Mojo.Log.info("Download dashboard already open, activating it...");
var dashboardStageController = this.controller.getStageController("download");
if (dashboardStageController) {
dashboardStageController.activate();
dashboardStageController.delegateToSceneAssistant("activate");
}
}
}
AppModel.prototype.CloseDashboardStageByName = function(stageName) {
this.controller = Mojo.Controller.getAppController();
var dashboardStage = this.controller.getStageProxy(stageName);
if (!dashboardStage) {
Mojo.Log.info(stageName + " dashboard not open, nothing to do");
} else {
Mojo.Log.info(stageName + " dashboard open, closing it...");
this.controller.closeStage(stageName);
}
}
AppModel.prototype.EstablishAlarms = function() {
//Reset alarms
systemModel.ClearSystemAlarm("BackgroundDownload");
if (appModel.AppSettingsCurrent["UseAutoDownload"] && appModel.AppSettingsCurrent["Username"] != "" && appModel.AppSettingsCurrent["Credential"]) {
Mojo.Log.warn("Re-establishing background download alarm with time: " + appModel.AppSettingsCurrent["AutoDownloadTime"]);
systemModel.SetSystemAlarmRelative("BackgroundDownload", appModel.AppSettingsCurrent["AutoDownloadTime"]);
} else {
Mojo.Log.warn("Not setting background download alarm since conditions weren't met.");
}
}
AppModel.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();
timeStamp = timeStamp.split("GMT");
timeStamp = timeStamp[0];
return timeStamp;
}
//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.
+449
View File
@@ -0,0 +1,449 @@
/*
ShareBoard Model - Mojo
Version 1.0
Created: 2021
License: MIT
Description: A model to interact with a share service
*/
var ShareServiceModel = function() {
this.urlBase = Mojo.Controller.appInfo.serviceURL;
this.shortUrlBase = Mojo.Controller.appInfo.shortURL;
};
//Properties
ShareServiceModel.prototype.ForceHTTP = false;
ShareServiceModel.prototype.UseCustomShare = false;
ShareServiceModel.prototype.UseCustomEndpoint = false;
ShareServiceModel.prototype.CustomEndpointURL = "";
ShareServiceModel.prototype.CustomShortURL = "";
ShareServiceModel.prototype.CustomCreateKey = "";
ShareServiceModel.prototype.UseCustomClientId = false;
ShareServiceModel.prototype.CustomClientId = "";
//ShareServiceModel.prototype.ServiceCompatWarning = 0;
ShareServiceModel.prototype.buildURL = function(username, actionType) {
var urlBase = this.urlBase;
if (this.UseCustomEndpoint == true && this.CustomEndpointURL != "") {
urlBase = this.CustomEndpointURL;
}
//Make sure we don't end up with double slashes in the built URL if there's a custom endpoint
var urlTest = urlBase.split("://");
if (urlTest[urlTest.length - 1].indexOf("/") != -1) {
urlBase = urlBase.substring(0, urlBase.length - 1);
}
var path = urlBase + "/" + actionType + ".php";
if (username)
path += "?username=" + username;
if (this.ForceHTTP)
path = path.replace ("https:", "http:");
return path;
}
ShareServiceModel.prototype.MakeShareURL = function(username, guid, type) {
var urlBase = this.shortUrlBase;
if (this.CustomShortURL == true && this.CustomShortURL != "") {
urlBase = this.CustomShortURL;
}
if (type.indexOf("image") != -1)
urlBase = urlBase + "download.php?";
else
urlBase = urlBase + "t.php?";
var data = username + "|" + guid;
data = btoa(data);
urlBase = urlBase + data;
if (urlBase.indexOf("==") == urlBase.length - 2)
urlBase = urlBase.slice(0, urlBase.length - 2)
return urlBase;
}
//HTTP request for list files
ShareServiceModel.prototype.DoShareListRequest = function(username, credential, callback, errorhandler) {
if (callback)
callback = callback.bind(this);
if (!errorhandler) {
errorhandler = function(errorText) {
Mojo.Log.warn(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText }, "", "");
}
} else {
errorhandler = errorhandler.bind(this);
}
var useURL = this.buildURL(username, "get-shares");
Mojo.Log.info("Calling sharing service at " + useURL + " with key " + this.getCurrentClientKey());
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", useURL);
xmlhttp.setRequestHeader("client-id", this.getCurrentClientKey());
xmlhttp.setRequestHeader("credential", credential);
xmlhttp.send();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status >= 400) {
errorhandler(xmlhttp.status + " Error getting share list: " + responseObj.error, callback);
return false;
} else {
if (xmlhttp.responseText && xmlhttp.responseText != "") {
try {
var responseObj = JSON.parse(xmlhttp.responseText);
if (responseObj.error) {
errorhandler("Share service error: " + responseObj.error, callback);
return false;
} else {
//Mojo.Log.info("Share List success! " + xmlhttp.responseText);
if (callback) {
callback(responseObj);
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Shares retreived!" }, "", "");
}
return true;
}
} catch (ex) {
errorhandler("Error parsing share list response: " + ex + ", response: " + xmlhttp.responseText, callback);
return false;
}
} else {
errorhandler("Share list response was empty", callback);
return false;
}
}
}
}.bind(this);
}
//HTTP request for add file
ShareServiceModel.prototype.DoShareAddRequestText = function(content, username, credential, contenttype, callback, errorhandler) {
if (callback)
callback = callback.bind(this);
if (!errorhandler) {
errorhandler = function(errorText) {
Mojo.Log.warn(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText }, "", "");
}
}
var useURL = this.buildURL(username, "share-text");
Mojo.Log.info("Adding text share: " + content + " of type " + contenttype + " from URL " + useURL);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", useURL);
xmlhttp.setRequestHeader("client-id", this.getCurrentClientKey());
xmlhttp.setRequestHeader("credential", credential);
xmlhttp.setRequestHeader("content-type", contenttype);
xmlhttp.send(content);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status >= 400) {
errorhandler(xmlhttp.status + " Error adding share text: " + responseObj.error);
return false;
} else {
Mojo.Log.info("Share service sent response while sharing text: " + xmlhttp.responseText);
if (xmlhttp.responseText && xmlhttp.responseText != "") {
try {
var responseObj = JSON.parse(xmlhttp.responseText);
if (responseObj.error) {
errorhandler("Error sharing text: " + responseObj.error);
return false;
} else {
Mojo.Log.info("Share text success! " + xmlhttp.responseText);
if (callback) {
callback(responseObj);
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Content shared!" }, "", "");
}
return true;
}
} catch (ex) {
errorhandler("Error parsing add share response: " + ex);
return false;
}
} else {
errorhandler("Add share response was empty");
return false;
}
}
}
}.bind(this);
}
//Upload a file
ShareServiceModel.prototype.DoShareAddRequestImage = function (fullFilePath, username, credential, contenttype, callback, errorhandler) {
if (callback)
callback = callback.bind(this);
if (!errorhandler) {
errorhandler = function(errorText) {
Mojo.Log.warn(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText }, "", "");
}
}
var useURL = this.buildURL(username, "share-image");
Mojo.Log.info("Adding image share: " + fullFilePath + " of type " + contenttype + " from URL " + useURL);
this.uploadRequest = new Mojo.Service.Request('palm://com.palm.downloadmanager/', {
method: 'upload',
parameters: {
fileName: fullFilePath,
url: useURL,
fileLabel: "image",
contentType: contenttype,
postParameters: [
{"key":"username", "data": username},
{"key":"credential" , "data": credential},
],
customHttpHeaders : [
'client-id: ' + this.getCurrentClientKey(),
],
subscribe: true
},
onSuccess: function(response) {
Mojo.Log.info("Upload progress", JSON.stringify(response));
if ((response.completed && response.completed == true) || (response.completionCode && response.completionCode != 0)) {
if (response.error) {
errorhandler(response.error);
return false;
} else if (response.completionCode && response.completionCode != 0) {
var errMsg = "error code " + response.completionCode;
switch(Math.abs(response.completionCode)) {
case 1:
errMsg = "General Error";
break;
case 2:
errMsg = "Connection Timeout";
break;
case 3:
errMsg = "Corrupt File";
break;
case 4:
errMsg = "File System Error";
break;
case 5:
errMsg = "HTTP Error";
break;
case 6:
errMsg = "Connection Offline";
break;
}
errorhandler(errMsg);
return false;
} else {
if (response.responseString) {
Mojo.Log.info("Upload response: " + response.responseString);
var responseObj = JSON.parse(response.responseString);
if (responseObj.error) {
errorhandler(responseObj.error);
return false;
} else {
if (callback) {
callback(responseObj);
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Image uploaded!" }, "", "");
}
return true;
}
} else {
errorhandler("Error uploading image: server response was empty");
return false;
}
}
}
},
onFailure: function(response) {
errorhandler("Error uploading image share: " + response);
return false;
}
});
}
//HTTP request for list files
ShareServiceModel.prototype.DoShareDeleteRequest = function(itemid, username, credential, callback, errorhandler) {
if (callback)
callback = callback.bind(this);
if (!errorhandler) {
errorhandler = function(errorText) {
Mojo.Log.warn(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText }, "", "");
}
}
var useURL = this.buildURL(username, "delete-share-item");
Mojo.Log.info("Calling sharing service at " + useURL + " for item " + itemid + " with key " + this.getCurrentClientKey());
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", useURL);
xmlhttp.setRequestHeader("client-id", this.getCurrentClientKey());
xmlhttp.setRequestHeader("credential", credential);
xmlhttp.setRequestHeader("itemid", itemid);
xmlhttp.send();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status >= 400) {
errorhandler(xmlhttp.status + " Error deleting share: " + responseObj.error);
return false;
} else {
Mojo.Log.info("Share service sent response while deleting share: " + xmlhttp.responseText);
if (xmlhttp.responseText && xmlhttp.responseText != "") {
try {
var responseObj = JSON.parse(xmlhttp.responseText);
if (responseObj.error) {
errorhandler("Error deleting share: " + responseObj.error);
return false;
} else {
Mojo.Log.info("Share Delete success! " + xmlhttp.responseText);
if (callback) {
callback(responseObj);
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Content deleted!" }, "", "");
}
return true;
}
} catch (ex) {
errorhandler("Error parsing delete share response: " + ex);
return false;
}
} else {
errorhandler("Delete share response was empty");
return false;
}
}
}
}.bind(this);
}
//HTTP request for new user
ShareServiceModel.prototype.DoNewUserRequest = function(username, sharephrase, password, callback, errorhandler) {
if (callback)
callback = callback.bind(this);
if (!errorhandler) {
errorhandler = function(errorText) {
Mojo.Log.warn(errorText);
Mojo.Controller.getAppController().showBanner({ messageText: errorText }, "", "");
}
}
var useURL = this.buildURL(null, "new-user");
Mojo.Log.info("Creating user: " + username + " with share-phrase " + sharephrase + " and password " + password + " from URL " + useURL);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", useURL);
xmlhttp.setRequestHeader("client-id", this.getCurrentClientKey());
var request = {
"username":username,
"sharephrase":sharephrase,
"password":password,
"createkey":this.getCurrentCreateKey()
}
xmlhttp.send(JSON.stringify(request));
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (xmlhttp.status >= 400) {
errorhandler(xmlhttp.status + " Error creating user: " + responseObj.error);
return false;
} else {
Mojo.Log.info("Share service sent response while creating user: " + xmlhttp.responseText);
if (xmlhttp.responseText && xmlhttp.responseText != "") {
try {
var responseObj = JSON.parse(xmlhttp.responseText);
if (responseObj.error) {
errorhandler("Error creating user: " + responseObj.error);
return false;
} else {
Mojo.Log.info("New user success! " + xmlhttp.responseText);
if (callback) {
callback(responseObj);
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "User created!" }, "", "");
}
return true;
}
} catch (ex) {
errorhandler("Error parsing create user response: " + ex);
return false;
}
} else {
errorhandler("Create user response was empty");
return false;
}
}
}
}.bind(this);
}
//HTTP request to get Terms and Conditions
ShareServiceModel.prototype.GetTnC = function(callback) {
if (callback)
callback = callback.bind(this);
var useURL = this.buildURL(null, "tandc");
Mojo.Log.info("Getting Terms and Conditions with query: " + useURL);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", useURL);
xmlhttp.send();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (callback)
callback(xmlhttp.responseText);
}
}.bind(this);
}
//HTTP request to get random share phrase
ShareServiceModel.prototype.GetRandomWords = function(callback) {
if (callback)
callback = callback.bind(this);
var theQuery = this.buildURL(null, "random-words");
Mojo.Log.info("Getting Random words query: " + theQuery);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", theQuery);
xmlhttp.send();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (callback)
callback(xmlhttp.responseText);
}
}.bind(this);
}
//HTTP request to get random share phrase
ShareServiceModel.prototype.QueryShareData = function(query, callback) {
if (callback)
callback = callback.bind(this);
var theQuery = query;
theQuery = theQuery.replace("download.php", "q.php");
theQuery = theQuery.replace("image.php", "q.php");
theQuery = theQuery.replace("t.php", "q.php");
Mojo.Log.warn("Querying share item with URL: " + theQuery);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", theQuery);
xmlhttp.send();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
try {
var itemData = JSON.parse(xmlhttp.responseText);
if (callback)
callback(itemData);
} catch (ex) {
Mojo.Log.error("Got bad item query response payload: " + ex, xmlhttp.responseText);
}
}
}.bind(this);
}
ShareServiceModel.prototype.getCurrentClientKey = function() {
var retVal = atob(appKeys['shareBoardClientKey']);
if (this.UseCustomEndpoint) {
retVal = this.CustomClientId;
Mojo.Log.info("Using custom shareboard client key: " + retVal);
}
//Mojo.Log.info("Using shareboard client key: " + retVal);
return retVal;
}
ShareServiceModel.prototype.getCurrentCreateKey = function() {
var retVal = atob(appKeys['shareBoardCreateKey']);
if (this.UseCustomEndpoint) {
retVal = this.CustomCreateKey;
Mojo.Log.info("Using custom shareboard create key: " + retVal);
}
//Mojo.Log.info("Using shareboard create key: " + retVal);
return retVal;
}
+345 -24
View File
@@ -1,16 +1,42 @@
/*
System Model
Version 1.2
Created: 2021
Author: Jonathan Wise
Version 1.6
Created: 2022
Author: Jon W
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
*/
var SystemModel = function() {
//** Note: If you synced this file from a common repository, local edits may be over-written! */
var SystemModel = function() {
};
SystemModel.WOSAPrefs = {};
//You should probably use Mojo.Environment.DeviceInfo for this
// http://sdk.webosarchive.org/docs/docs.html#reference/mojo/classes/mojo-environment.html#summary
SystemModel.prototype.DetectDevice = function() {
//find out what kind of device this is
var deviceType;
if (Mojo.Environment.DeviceInfo.platformVersionMajor >= 3) {
deviceType = "TouchPad";
Mojo.Log.info("Device detected as TouchPad");
} else {
if (window.screen.width == 800 || window.screen.height == 800) {
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)) {
deviceType = "Pre";
Mojo.Log.info("Device detected as Pre or Pre2");
} else {
deviceType = "Tiny";
Mojo.Log.info("Device detected as Pixi or Veer");
}
}
return deviceType;
}
//Create a named System Alarm using relative time ("in")
SystemModel.prototype.SetSystemAlarmRelative = function(alarmName, alarmTime) {
@@ -82,15 +108,20 @@ SystemModel.prototype.ClearSystemAlarm = function(alarmName) {
//Allow the display to sleep
SystemModel.prototype.AllowDisplaySleep = function(stageController) {
if (!stageController)
stageController = Mojo.Controller.getAppController().getActiveStageController();
try {
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");
//Tell the System it doesn't have to stay awake any more
Mojo.Log.info("Allowing display sleep");
stageController.setWindowProperties({
blockScreenTimeout: false
});
stageController.setWindowProperties({
blockScreenTimeout: false
});
} catch(e) {
//If the stage has already gone away, this function will return an error
// But it doesn't matter because webOS will perform this function anyway during stage clean-up
}
}
//Prevent the display from sleeping
@@ -108,6 +139,7 @@ SystemModel.prototype.PreventDisplaySleep = function(stageController) {
//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
// Note: This implementation will not work for pure background notifications as it requires the app to be active
SystemModel.prototype.ShowNotificationStage = function(stageName, sceneName, heightToUse, sound, vibrate) {
Mojo.Log.info("Showing notification stage.");
//Determine what sound to use
@@ -153,14 +185,34 @@ SystemModel.prototype.PlaySound = function(soundName) {
}
//Play an alert sound
//Note: If you want more control, include an HTML5 audio tag with the id "audioPlayer"
// Otherwise a system function will be used
SystemModel.prototype.PlayAlertSound = function(sound) {
if (!sound || sound == "") {
sound = "Subtle (short)";
}
if (sound != "off") {
var soundPath = "/media/internal/ringtones/" + sound + ".mp3";
Mojo.Log.info("trying to play: " + soundPath);
Mojo.Controller.getAppController().playSoundNotification("media", soundPath, 2500);
if (sound.indexOf("/") != -1)
soundPath = sound;
var audioPlayer = document.getElementById("audioPlayer");
if (audioPlayer) {
Mojo.Log.info("Playing notification sound " + soundPath + " using audioPlayer element");
if (soundPath) {
Mojo.Log.info("trying to play audio: " + soundPath);
audioPlayer.src = soundPath;
audioPlayer.load();
}
audioPlayer.play();
} else {
Mojo.Log.warn("No audio element named audioPlayer, using notification sound to play " + soundPath);
if (!sound || sound == "") {
sound = "Subtle (short)";
}
Mojo.Log.info("trying to play: " + soundPath);
Mojo.Controller.getAppController().playSoundNotification("media", soundPath, 2500);
}
}
}
@@ -176,11 +228,26 @@ SystemModel.prototype.Vibrate = function(vibrate) {
} else
vibeMax = Number(vibrate);
if (vibeMax > 0)
vibeInterval = setInterval(doVibrate, 500);
vibeInterval = window.setInterval(doVibrate, 500);
return success;
}
//Lock the physical volume buttons to the media volume (instead of default, notification volume)
SystemModel.prototype.LockVolumeKeys = function() {
Mojo.Log.info("Locking media volume to hardware buttons");
this.wakeupRequest = new Mojo.Service.Request("palm://com.palm.audio/media", {
method: "lockVolumeKeys",
onSuccess: function(response) {
Mojo.Log.info("Lock Volume Keys Success", JSON.stringify(response));
},
onFailure: function(response) {
Mojo.Log.error("Lock Volume Keys Failure: ", JSON.stringify(response));
}
});
return true;
}
//Launch an app
SystemModel.prototype.LaunchApp = function(appName, params) {
if (!params)
@@ -194,12 +261,148 @@ SystemModel.prototype.LaunchApp = function(appName, params) {
Mojo.Log.info("App Launch Success", appName, JSON.stringify(response));
},
onFailure: function(response) {
Mojo.Log.error("Alarm Launch Failure", appName, JSON.stringify(response));
Mojo.Log.error("App Launch Failure", appName, JSON.stringify(response));
}
});
return true;
}
//Use in combination with a Touch2Share launch to send a URI to a tapped device
SystemModel.prototype.SendDataForTouch2Share = function(url, callback) {
if (!url) {
Mojo.Log.error("Share URL not supplied");
return false;
}
if (callback)
callback = callback.bind(this);
var params = {data: { target: url, type: "rawdata", mimetype: "text/html" }};
Mojo.Log.info("Touch2Share payload is ", JSON.stringify(params));
this.shareRequest = new Mojo.Service.Request("palm://com.palm.stservice", {
method: "shareData",
parameters: params,
subscribe: true,
onSuccess: function(response) {
Mojo.Log.info("Touch2Share Success!", JSON.stringify(response));
if (callback) {
callback(response);
return true;
}
},
onFailure: function(response) {
Mojo.Log.error("Touch2Share Failure: ", JSON.stringify(response));
if (callback) {
callback(response);
return false;
}
}
});
return true;
}
//Download a file
//A fire-and-forget way to interact with the DownloadManager. The user will get a generic notification when the download is complete.
SystemModel.prototype.DownloadFile = function (url, mimetype, pathFromInteral, fileName, subscribe, callback) {
if (!url) {
Mojo.Log.error("Download URL not supplied");
return false;
}
if (!pathFromInteral){
Mojo.Log.error("Save path not supplied");
return false;
}
if (callback)
callback = callback.bind(this);
//Figure out what extension to use
var ext = "";
mimetype = mimetype.toLowerCase();
if (mimetype.indexOf("image/") != -1) {
if (mimetype == "image/jpeg")
ext = "jpg"
else {
ext = mimetype.split("/");
ext = ext[ext.length - 1];
}
} else if (mimetype == "text/plain") {
ext = "txt";
} else if (mimetype == "application/json") {
ext = "json";
} else {
if (mimetype.indexOf("/") != -1) {
ext = mimetype.split("/");
ext = ext[1];
}
}
ext = "." + ext;
this.downloadRequest = new Mojo.Service.Request('palm://com.palm.downloadmanager/', {
method: 'download',
parameters: {
target: url,
mime: mimetype,
targetDir : "/media/internal/" + pathFromInteral,
targetFilename : fileName + ext,
keepFilenameOnRedirect: true,
subscribe: subscribe
},
onSuccess: function(response) {
Mojo.Log.info("Download Success!", JSON.stringify(response));
if (callback) {
callback(response);
return true;
} else {
if (response.completed == true) {
Mojo.Controller.getAppController().showBanner({ messageText: "Content downloaded!" }, "", "");
}
}
},
onFailure: function(response) {
Mojo.Log.error("Download Failure: ", JSON.stringify(response));
if (callback) {
callback(response);
return false;
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Download error:" + JSON.stringify(response) }, "", "");
}
}
});
}
SystemModel.prototype.LoadWOSAPrefs = function (callback) {
if (callback)
callback.bind(this);
var req = new Ajax.Request("/media/internal/.wosaprefs", {
method: 'get',
onFailure: function() {
Mojo.Log.warn("Could not load .wosaprefs file. Preferences cannot be opened.");
callback(null);
},
on404: function() {
Mojo.Log.warn("Could not find .wosaprefs file. Preferences cannot be opened.");
callback(null);
},
onSuccess: function(response) {
if (response && response.responseText) {
Mojo.Log.info("Loaded .wosaprefs file: " + JSON.stringify(response.responseText));
try {
prefsObj = JSON.parse(response.responseText);
this.WOSAPrefs = prefsObj.preferences;
} catch(ex) {
Mojo.Log.warn("Could not parse .wosaprefs file. Preferences cannot be opened. " + ex);
callback(null);
return;
}
callback(prefsObj);
return;
} else {
Mojo.Log.warn("Could not read .wosaprefs file. Preferences cannot be opened.");
}
callback(null);
}.bind(this)
});
}
//Helper Functions
var vibeInterval;
var vibeCount = 0;
@@ -356,7 +559,7 @@ SystemModel.prototype.SetDisplayState = function(state) {
}
//Set the Notifications-When-Locked state
SystemModel.prototype.setShowNotificationsWhenLocked = function(value) {
SystemModel.prototype.SetShowNotificationsWhenLocked = function(value) {
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
Mojo.Log.info("Setting Notifications When Locked to " + value);
this.service_identifier = 'palm://com.palm.systemservice';
@@ -372,7 +575,7 @@ SystemModel.prototype.setShowNotificationsWhenLocked = function(value) {
}
//Set the LED Notification state
SystemModel.prototype.setLEDLightNotifications = function(value) {
SystemModel.prototype.SetLEDLightNotifications = function(value) {
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
Mojo.Log.info("Setting LED Notifications to " + value);
this.service_identifier = 'palm://com.palm.systemservice';
@@ -394,7 +597,7 @@ SystemModel.prototype.GetInternetConnectionState = function(callback) {
throw ("Privileged system service call not allowed for this App ID!");
}
Mojo.Log.info("Requesting Internet connection state from Connection Manager");
this.connectedRequest = new Mojo.Service.Request("palm://com.palm.connectionamanager", {
this.connectedRequest = new Mojo.Service.Request("palm://com.palm.connectionmanager", {
method: "getStatus",
parameters: {
subscribe: false
@@ -405,7 +608,7 @@ SystemModel.prototype.GetInternetConnectionState = function(callback) {
}
//Set the WAN state
SystemModel.prototype.setWANEnabled = function(value) {
SystemModel.prototype.SetWANEnabled = function(value) {
var state = value ? 'off' : 'on';
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
Mojo.Log.info("Setting WAN State to " + value);
@@ -424,7 +627,7 @@ SystemModel.prototype.setWANEnabled = function(value) {
}
//Set the WIFI state
SystemModel.prototype.setWifiEnabled = function(value) {
SystemModel.prototype.SetWifiEnabled = function(value) {
var state = value ? 'enabled' : 'disabled';
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
Mojo.Log.info("Setting WIFI State to " + state);
@@ -457,6 +660,7 @@ SystemModel.prototype.SetBluetoothEnabled = function(value) {
}
}
//Helper for Bluetooth functions, do not call directly
SystemModel.prototype.bluetoothControlService = function(url, params, cb) {
return new Mojo.Service.Request(url, {
onSuccess: cb,
@@ -465,14 +669,16 @@ SystemModel.prototype.bluetoothControlService = function(url, params, cb) {
});
}
SystemModel.prototype.GetInstalledApps = function(callBack) {
SystemModel.prototype.GetInstalledApps = function(callback) {
if (callback)
callback.bind(this);
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
Mojo.Log.info("Getting list of running apps.");
Mojo.Log.info("Getting list of installed apps.");
this.appRequest = new Mojo.Service.Request("palm://com.palm.applicationManager/listApps", {
method: "",
parameters: {},
onSuccess: callBack,
onFailure: callBack
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'!");
@@ -505,6 +711,121 @@ SystemModel.prototype.KillApp = function(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'!");
throw ("Privileged system service call not allowed for this App ID!");
}
}
SystemModel.prototype.ListHandlersForURL = function(url, callback) {
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
if (callback)
callback.bind(this);
Mojo.Log.info("Listing handlers for URL: " + url);
this.serviceRequest = new Mojo.Service.Request("palm://com.palm.applicationManager", {
method: "listAllHandlersForUrl",
parameters: {
"url": url
},
onSuccess: function(response) {
//Mojo.Log.info("Handler list success: " + JSON.stringify(response));
if (response && response.redirectHandlers) {
if (callback)
callback(response.redirectHandlers);
} else {
Mojo.Log.error("Unexpected payload in system service request");
}
}.bind(this),
onFailure: function(response) {
Mojo.Log.error("Handler list failure: " + JSON.stringify(response));
if (callback)
callback(response);
}.bind(this)
});
} else {
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm'!");
throw ("Privileged system service call not allowed for this App ID!");
}
}
SystemModel.prototype.AddHandlerForURL = function(urlPattern, appId, callback) {
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
if (!urlPattern)
throw "No urlPattern specified";
if (callback)
callback.bind(this);
if (!appId)
appId = Mojo.Controller.appInfo.id;
Mojo.Log.info("Add handler for URL: " + urlPattern + " with app " + appId);
if (urlPattern && urlPattern != "" && appId && appId != "" && appId.indexOf(".") > -1) {
this.serviceRequest = new Mojo.Service.Request("palm://com.palm.applicationManager", {
method: "addRedirectHandler",
parameters: {
"appId": appId,
"urlPattern": urlPattern,
"schemeForm": false,
},
onSuccess: function(response) {
Mojo.Log.info("Handler registration success: " + JSON.stringify(response));
if (callback)
callback(response);
else
Mojo.Controller.getAppController().showBanner("URL Helper added: " + appId + ".", { source: 'notification' });
}.bind(this),
onFailure: function(response) {
Mojo.Log.error("Handler registration failure: " + JSON.stringify(response));
if (callback)
callback(response);
else
Mojo.Controller.getAppController().showBanner("URL Helper failed: " + appId + ".", { source: 'notification' });
}.bind(this)
});
} else {
Mojo.Log.error("Invalid parameters specified. Not attempting to add Redirect Handler!");
}
} else {
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm'!");
throw ("Privileged system service call not allowed for this App ID!");
}
}
SystemModel.prototype.RemoveHandlerForURL = function(appId, callback) {
if (Mojo.Controller.appInfo.id.indexOf("com.palm") != -1) {
if (!appId)
appId = Mojo.Controller.appInfo.id;
if (callback)
callback.bind(this);
Mojo.Log.info("Remove URL handling for app: " + appId);
this.serviceRequest = new Mojo.Service.Request("palm://com.palm.applicationManager", {
method: "removeHandlersForAppId",
parameters: {
"appId": appId
},
onSuccess: function(response) {
Mojo.Log.info("Handler remove success: " + JSON.stringify(response));
if (callback)
callback(response);
else
Mojo.Controller.getAppController().showBanner("URL Helper removed: " + appId + ".", { source: 'notification' });
}.bind(this),
onFailure: function(response) {
Mojo.Log.error("Handler list failure: " + JSON.stringify(response));
if (callback)
callback(response)
else
Mojo.Controller.getAppController().showBanner("URL Helper failed: " + appId + ".", { source: 'notification' });
}.bind(this)
});
} else {
Mojo.Log.error("Privileged system services can only be called by apps with an ID that starts with 'com.palm'!");
throw ("Privileged system service call not allowed for this App ID!");
}
+87 -66
View File
@@ -1,68 +1,36 @@
/*
Updater Model - Mojo
Version 0.3
Created: 2020
Author: Jonathan Wise
Version 1.0
Created: 2022
Author: Jon W
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
Source: Find the latest version of this library and info on how to use it on GitHub:
https://github.com/webosarchive/webos-common
*/
//** Note: If you synced this file from a common repository, local edits may be over-written! */
var UpdaterModel = function() {
this.updateURL = "http://appcatalog.webosarchive.com/WebService/getLatestVersionInfo.php?app=";
this.updateURL = "http://appcatalog.webosarchive.org/WebService/getLatestVersionInfo.php?app=";
this.lastUpdateResponse = null;
};
/* "Public" Updater functions */
//Check App Museum II web service to see if there are any updates
//Gather information and 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
var 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", updateURL);
Mojo.Log.info("Updater calling: " + 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.updateFound = 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);
this.deviceInfoRequest = new Mojo.Service.Request("palm://com.palm.preferences/systemProperties", {
method: "Get",
parameters:{"key": "com.palm.properties.nduid" },
onSuccess: this.performIdentifiedUpdateCheck.bind(this, appName, currVersion, callback),
onFailure: this.performIdentifiedUpdateCheck.bind(this, appName, currVersion, callback)
});
}
//You can optionally call this function if you don't want to handle the user interaction related to prompting for an update
@@ -73,7 +41,10 @@ UpdaterModel.prototype.PromptUserForUpdate = function(callback, message) {
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: <br>" + this.lastUpdateResponse.versionNote + "<br>Do you want to update now?";
message = "An update for " + Mojo.Controller.appInfo.title + " was found in App Museum II:<br>" + this.lastUpdateResponse.versionNote + "<br>Do you want to update now?";
if (callback) // set scope for xmlhttp anonymous function callback
callBack = callback.bind(this);
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (stageController) {
@@ -86,8 +57,8 @@ UpdaterModel.prototype.PromptUserForUpdate = function(callback, message) {
} else {
Mojo.Log.info("User deferred update.");
}
if (callback)
callback(value);
if (callBack)
callBack(value);
},
allowHTMLMessage: true,
title: $L("Update Available!"),
@@ -102,33 +73,83 @@ UpdaterModel.prototype.PromptUserForUpdate = function(callback, message) {
}
//Ask Preware to actually install the update...
UpdaterModel.prototype.InstallUpdate = function(callBack) {
UpdaterModel.prototype.InstallUpdate = function() {
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);
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, " + app + ":",
JSON.stringify(response), response.errorText);
}
});
this.InstallViaPreware(app);
return true;
}
}
UpdaterModel.prototype.InstallViaPreware = function(app) {
//Ask webOS to launch the PreWare with install 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));
}.bind(this),
onFailure: function(response) {
Mojo.Log.error("Preware launch failure, " + app + ":", JSON.stringify(response), response.errorText);
}.bind(this)
});
}
/* "Private" helper functions */
//Internal Function that uses the resolved information to actually do the check
UpdaterModel.prototype.performIdentifiedUpdateCheck = function(appName, currVersion, callback, response) {
// Build appropriate URL for conditions
var updateURL = this.updateURL + encodeURI(appName + "/" + Mojo.Controller.appInfo.version);
// If we have a device identifier, use that
if(response && JSON.stringify(response).indexOf("com.palm.properties.nduid") != -1) {
updateURL = updateURL + "&clientid=" + response[Object.keys(response)[0]];
}
// Send some info about the device (could be used for compat checks)
deviceData = Mojo.Environment.DeviceInfo.modelName + "/" + Mojo.Environment.DeviceInfo.platformVersion + "/" + (Mojo.Environment.DeviceInfo.carrierName || "WiFi") + "/" + Mojo.Locale.getCurrentLocale();
updateURL = updateURL + "&device=" + encodeURIComponent(deviceData);
if (callback) // set scope for xmlhttp anonymous function callback
callBack = callback.bind(this);
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", updateURL);
Mojo.Log.info("Updater calling: " + 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);
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.updateFound = false;
}
}
this.lastUpdateResponse = updateResponse;
} else {
Mojo.Log.info("UpdaterModel: No useable response from App Museum II update API");
}
if (callBack) {
callBack(updateResponse);
}
}
}.bind(this);
}
//Turn a version string into an object with three independent number values
UpdaterModel.prototype.getVersionObject = function(versionNum) {
versionNumParts = versionNum.split(".");
@@ -158,4 +179,4 @@ UpdaterModel.prototype.isVersionHigher = function(currVersion, compareVersion) {
return true;
return false;
}
}
}
+26
View File
@@ -0,0 +1,26 @@
<div class="palm-hasheader" style="margin-bottom:60px">
<div id="divShareTitle" class="palm-header center" onclick="Mojo.Controller.stageController.swapScene({ transition: Mojo.Transition.crossFade, name: 'main' });">
Share Details
</div>
</div>
<!-- only visible in debug mode -->
<div class="palm-group" id="divShareInfo" style="display:none">
<div class="palm-group-title">
<span x-mojo-loc=''>Debug Links</span>
</div>
<div id="divShareLinks" name="divShareLinks">
</div>
</div>
<div class="palm-group" id="divTextDetail">
<div class="palm-group-title">
<span x-mojo-loc=''>Content</span>
</div>
<div id="divTextContent" name="divTextContent" style="background-color:white; border: 1px solid dimgray; margin: 0px;">
<div id="divImageContent" style="width:100%; margin:auto; align-content: center; text-align: center;margin-top: 6px !important;">
<img src="icon-256.png" style="width:128px;height:128px" /><br/>
</div>
</div>
</div>
File renamed without changes.
@@ -1,12 +1,9 @@
<div class="dashboard-notification-module">
<div class="palm-dashboard-icon-container">
<div class="dashboard-newitem">
<div class="dashboard-newitem" style="display:#{showCount}">
<span>#{count}</span>
</div>
<div id="dashboard-icon" class="palm-dashboard-icon dashboard-icon-chat">
<div id="dashboard-icon" class="palm-dashboard-icon dashboard-icon-download">
</div>
</div>
<div class="palm-dashboard-text-container">
+31
View File
@@ -0,0 +1,31 @@
<div class="palm-dialog-content">
<div id="login-title" class="palm-dialog-title">
Log In to a Share Space
</div>
<div class="palm-dialog-seperator"></div>
<div class="textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div id="txtUsername" name="txtUsername" x-mojo-element="TextField" title="Username"></div>
</div>
</div>
<div class="textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div id="txtCredential" name="txtCredential" x-mojo-element="TextField" title="Credential"></div>
</div>
</div>
<div class="palm-group" id="linkError" style="display:none" name="linkError">
<div class="palm-group-title"><span style="color:white">Error</span></div>
<div class="errorresponse">
There was no usable response from the Service before its timeout.
</div>
</div>
<div class="palm-dialog-buttons">
<div x-mojo-element="Button" id="goButton"></div>
<div x-mojo-element="Button" id="cancelButton"></div>
</div>
</div>
+10
View File
@@ -0,0 +1,10 @@
<div class="palm-row" x-mojo-tap-highlight='momentary'>
<div class="palm-row-wrapper">
<div class="task-row">
<div style="height:48px; float:left;"><img class="thumbnail" src="#{thumbnail}" style="width:48px; height: 48px; margin-right: 8px;"></div>
<div title="#{guid}"><span style="font-size:18px;">#{timestamp}</span>
<p style="margin:0px; padding: 0px;padding-bottom: 3px;">#{contenttype}</p>
</div>
</div>
</div>
</div>
+3
View File
@@ -0,0 +1,3 @@
<div class="palm-list">
#{-listElements}
</div>
+17
View File
@@ -0,0 +1,17 @@
<div class="palm-page-header">
<div class="header-icon sharespace"></div>
<div class="header-text">
<span x-mojo-loc=''>Share Space</span>
</div>
</div>
<div id="showShareList" name="showShareList" style="display:none">
<div class="palm-group">
<div class="palm-group-title"><span x-mojo-loc='' id="spnUsername">Task List</span></div>
<div id="shareList" x-mojo-element="List" style="padding: 1px;margin:-12px;"></div>
</div>
</div>
<div id="divSpinnerContainer" style="margin-top: 100px; width: 130px;margin-left: auto;margin-right: auto; align-items: center;">
<div x-mojo-element="Spinner" class="spinnerClass" id="workingSpinner" name="workingSpinner"></div>
</div>
+24
View File
@@ -0,0 +1,24 @@
<div class="palm-page-header">
<div class="header-icon sharespace"></div>
<div class="header-text">
<span x-mojo-loc=''>Share Space</span>
</div>
</div>
<div class="palm-group">
<div class="palm-group-title" id="divEditTitle">New Content</div>
<div class="palm-list">
<div class="palm-row single">
<div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div class="search-bar">
<div class="textfield-search" id="txtShareContent" x-mojo-element="TextField" style="height: 300px;" title="ShareContent"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div x-mojo-element="Button" id="goButton" class="buttonClass" name="goButton"></div>
<div x-mojo-element="Button" id="cancelButton" class="buttonClass" name="cancelButton"></div>
+87
View File
@@ -0,0 +1,87 @@
<div class="palm-page-header">
<div class="header-icon sharespace"></div>
<div class="header-text">
<span x-mojo-loc=''>Share Space - New User</span>
</div>
</div>
<div class="palm-group">
<div class="palm-group-title">
<span x-mojo-loc=''>Terms and Conditions (Scroll)</span>
</div>
<div x-mojo-element="Scroller" class="scrollerClass" id="scrollTnC" name="scrollTnC" style="height:200px;margin:-15px 10px 10px -15px;padding:0px;">
<div id="divTnC">
<ul>
<li>Loading Terms and Conditions...</li>
</ul>
</div>
<div x-mojo-element="Button" id="btnAgree" name="btnAgree" style="margin-left: 20%; margin-right:14%"></div>
</div>
</div>
<div class="palm-group" id="divCredentials" style="display:none">
<div class="palm-group-title">
<span x-mojo-loc=''>Credentials</span>
</div>
<div class="palm-list">
<div class="palm-row first">
<div class="palm-row-wrapper">
<div class="title" style="float:left; margin-left: 8px; margin-top: 14px;padding:0px;"><span class="label ">Username</span></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper" style="height: 12px">
<div class="textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div id="txtUsername" name="txtUsername" x-mojo-element="TextField" title="Username"></div>
</div>
</div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper" style="height: 12px">
<div class="title" class="truncating-text" style="float:left; margin-left: 8px; margin-top: 14px;padding:0px"><span class="label ">Share phrase</span></div>
<img id="imgTryagain" src="assets/tryagain.png" style="height:24px; width:24px; margin-top: 12px; margin-left: 12px;">
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div id="divSharephrase" style="margin: 12px 14px;font-style: italic;">&nbsp;</div>
<div class="explainer" style="margin-left:10px; padding-bottom: 12px;">Anyone who knows this phrase can share with you.</div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper" style="height: 12px">
<div class="title" class="truncating-text" style="float:left; margin-left: 8px; margin-top: 14px;padding:0px"><span class="label ">Admin Password</span></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div class="textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div id="txtPassword" name="txtPassword" x-mojo-element="TextField" title="Password"></div>
</div>
</div>
</div>
</div>
<div class="palm-row last">
<div class="palm-row-wrapper">
<div id="divPasswordInstructions" style="padding:6px 10px 12px 10px;line-height: 16px">
<span class="explainer">Keep this one safe, you'll use it to delete shares or configure your space.</span>
</div>
</div>
</div>
</div>
</div>
<div x-mojo-element="Button" id="btnOK" name="btnOK" style="display: none;"></div>
<div x-mojo-element="Button" id="btnCancel" name="btnCancel"></div>
+107
View File
@@ -0,0 +1,107 @@
<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" id="listBasic">
<div class="palm-row first">
<div class="palm-row-wrapper">
<div id="listRefresh" x-mojo-element="ListSelector" title="RefreshTimeout" class="right"></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div x-mojo-element="ToggleButton" id="toggleAutoDownload" title="UseAutoDownload"></div>
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Auto Download</span></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div id="listAutoDownloadTime" x-mojo-element="ListSelector" title="AutoDownloadTime" class="right"></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div class="explanation-text" id="divDownloadExplain">Download features require FileMgr, which you can install from PreWare.</div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div x-mojo-element="ToggleButton" id="toggleCopyLinkOnShare" title="CopyLinkOnShare"></div>
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Copy Link on Share</span></div>
</div>
</div>
<div class="palm-row last">
<div class="palm-row-wrapper">
<div x-mojo-element="ToggleButton" id="toggleForceHTTP" title="ForceHTTP"></div>
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Always use HTTP</span></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="toggleCustomClientId" title="UseCustomClientId"></div>
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Custom Client API Key</span></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper" style="margin:14px 22px 4px 12px">
<div id="txtCustomClientId" name="txtCustomClientId" x-mojo-element="TextField" title="CustomClientId"></div>
</div>
</div>
<div class="palm-row last">
<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</span></div>
</div>
</div>
<div x-mojo-element="Drawer" id="drawerSelfHost" class="drawerClass" name="drawerSelfHost">
<div class="palm-row">
<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 class="palm-row">
<div class="palm-row-wrapper" style="margin:14px 22px 4px 12px">
<div id="txtShortURL" name="txtShortURL" x-mojo-element="TextField" title="ShortURL"></div>
</div>
</div>
<div class="palm-row last">
<div class="palm-row-wrapper" style="margin:14px 22px 4px 12px">
<div id="txtCustomCreateKey" name="txtCustomCreateKey" x-mojo-element="TextField" title="CustomCreateKey"></div>
</div>
</div>
</div>
<div class="explanation-text">For more information on self-hosting, please visit <a href="https://github.com/webosarchive/webos-sharespace">https://github.com/webosarchivewebos-sharespace</a></div>
</div>
</div>
<div x-mojo-element="Button" id="btnOK" name="btnOK"></div>
@@ -7,7 +7,7 @@
</div>
<div class="palm-header-spacer"></div>
<div id="data"></div>
<div id="data" style="margin-right: 12px"></div>
<div class="my-scene-fade-bottom" x-mojo-scroll-fade="bottom"></div>
</div>
+26
View File
@@ -0,0 +1,26 @@
<div class="palm-page-header">
<div class="header-icon sharespace"></div>
<div class="header-text">
<span x-mojo-loc=''>Share Space - Welcome</span>
</div>
</div>
<div class="welcome">
<div class="my-scene-fade-top" x-mojo-scroll-fade="top"></div>
<h3>Getting Started</h3>
<div class="welcome-text">
<p>Your Share Space has been created! Keep your credentials somewhere safe:</p>
</div>
<div id="welcome-data" class="welcome-text"></div>
<div class="welcome-text">
<p>Anyone who has your <b>Name</b> and <b>Share Phrase</b> can share content with you from their webOS device, or from the web.</p>
<p>Any time content is shared to your Share Space, a public URL will be generated that you can give to others.
This URL will contain your Share Space <b>Name</b>, but <i>not</i> your <b>Share Phrase</b>.</p>
<p>Only users with your <b>Admin Password</b> can delete content, or manage your Share Space.</p>
</div>
<div class="my-scene-fade-bottom" x-mojo-scroll-fade="bottom"></div>
</div>
Executable
+26
View File
@@ -0,0 +1,26 @@
{
"id": "com.palm.webos.sharespace",
"version": "1.0.3",
"vendor": "webOS Archive",
"type": "web",
"main": "index.html",
"title": "Share Space",
"icon": "icon.png",
"miniicon": "assets/notify.png",
"noWindow": true,
"splashicon": "icon-256.png",
"uiRevision": "2",
"serviceURL": "https://share.webosarchive.org/",
"shortURL": "https://wosa.link/",
"tapToShareSupported": true,
"universalSearch": {
"action": {
"displayName": "New Share",
"url": "com.palm.webos.sharespace",
"launchParam": {
"newshare": "#{searchTerms}"
}
}
},
"startupMessage": "<img src='icon.png' style='float:right; margin-left: 6px'>Welcome to ShareSpace -- a powerful app for getting content on and off your webOS device! You can interact with this app from anywhere at <a href='http://share.webosarchive.org'>share.webosarchive.org</a>!<br>Here are some things you can do..."
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
File renamed without changes.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

+2 -2
View File
@@ -1,6 +1,6 @@
{
"logLevel": 99,
"debuggingEnabled": true,
"logLevel": 0,
"debuggingEnabled": false,
"timingEnabled": false,
"logEvents": false,
"escapeHTMLInTemplates": true
Executable
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
View File
Binary file not shown.
+3 -4
View File
@@ -3,15 +3,14 @@
<head>
<meta name='viewport' content='height=device-height'>
<title>Simple Chat</title>
<title>Share Space</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>
<script src="app/com.wosa.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">
<link href="emoji.css" rel="stylesheet" type="text/css" />
<link href="stylesheets/sharespace.css" media="screen" rel="stylesheet" type="text/css">
</head>
<body>
+4
View File
@@ -0,0 +1,4 @@
var appKeys = {
'shareBoardClientKey': 'base64 encode the client key from the server',
'shareBoardCreateKey': 'base64 encode the create key from the server (if needed)'
};
-7
View File
@@ -1,7 +0,0 @@
# 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.
@@ -1,133 +0,0 @@
/*
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;
var MainStageName = "main";
var welcomed = false;
Mojo.Additions = Additions;
function AppAssistant() {
appModel = new AppModel();
updaterModel = new UpdaterModel();
systemModel = new SystemModel();
serviceModel = new ServiceModel();
}
//This function will handle relaunching the app when an alarm goes off(see the device/alarm scene)
AppAssistant.prototype.handleLaunch = function(params) {
//Load preferences
appModel.LoadSettings();
Mojo.Log.info("settings now: " + JSON.stringify(appModel.AppSettingsCurrent));
if (!appModel.AppSettingsCurrent["BackgroundUpdate"]) {
appModel.AppSettingsCurrent["BackgroundUpdate"] = "00:30:00";
appModel.SaveSettings();
}
//get the proxy for the stage in the event it already exists (eg: app is currently open)
var mainStage = this.controller.getStageProxy("main");
Mojo.Log.info("SimpleChat is Launching! Launch params: " + JSON.stringify(params));
//Reset alarms
systemModel.ClearSystemAlarm("SimpleChat");
if (appModel.AppSettingsCurrent["BackgroundUpdate"] && appModel.AppSettingsCurrent["BackgroundUpdate"] != "" && appModel.AppSettingsCurrent["BackgroundUpdate"] != -1)
systemModel.SetSystemAlarmRelative("SimpleChat", appModel.AppSettingsCurrent["BackgroundUpdate"]);
var AppRunning = false;
if (mainStage) {
Mojo.Log.info("Found existing stage, app was already running");
AppRunning = true;
} else {
Mojo.Log.info("Did not find existing stage, this is a new app launch!");
}
if (AppRunning) //If the stage exists, use it
{
var stageController = this.controller.getStageController("main");
if (!params || params["action"] == undefined) //If no parameters were passed, this is a normal launch
{
Mojo.Log.info("This is a normal re-launch");
stageController.activate(); //bring existing stage into focus
return;
} else //If parameters were passed, this is a launch from a system alarm
{
Mojo.Log.info("This is a re-launch with parameters: " + JSON.stringify(params) + ". Safe to ignore, since app is already running.");
systemModel.ShowNotificationStage("dashboard", "dashboard/dashboard-scene", 60, false, false);
return;
}
} else //If not, determine if we should make one
{
if (!params || params["action"] == undefined) //If no parameters were passed, this is a normal launch
{
var currVersion = Mojo.Controller.appInfo.version;
Mojo.Log.info("This is a normal launch of version " + currVersion);
var stageArguments = { name: MainStageName, lightweight: true };
this.controller.createStageWithCallback(stageArguments, function(stageController) {
stageController.pushScene(MainStageName);
}.bind(this));
return;
} else //If parameters were passed, this is a launch from a system alarm
{
Mojo.Log.info("This is an alarm launch: " + JSON.stringify(params));
/*
return;*/
var pushClass0AlertScene = function(stageController) {
stageController.pushScene('dashboard', "dashboard/dashboard-scene");
}.bind(this);
Mojo.Controller.getAppController().showBanner("New messages in the chat!", { source: 'notification' });
//var stageArguments = { name: MainStageName, lightweight: true };
//this.controller.createStageWithCallback(stageArguments, function(stageController) {
// Mojo.Controller.appController.closeStage("main");
//}.bind(this));
this.controller.createStageWithCallback({
name: 'dashboard',
lightweight: true,
height: 100,
soundclass: "assets/silent.mp3"
}, pushClass0AlertScene, 'popupalert');
//systemModel.ShowNotificationStage("dashboard", "dashboard/dashboard-scene", 60, false, false);
}
}
};
/*
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)
this.Vibrate(vibrate);
var stageCallBack = function(stageController) {
stageController.pushScene({ name: stageName, sceneTemplate: sceneName });
}.bind(this);
var appController = Mojo.Controller.getAppController();
var stageController = appController.getStageController(stageName);
if (stageController) {
stageCallBack(stageController);
} else {
Mojo.Controller.getAppController().createStageWithCallback({
name: stageName,
lightweight: true,
height: heightToUse,
sound: soundToUse,
clickableWhenLocked: true
}, stageCallBack, 'dashboard');
}
}
*/
@@ -1,114 +0,0 @@
/*
This alarm scene is used for Touchpads, which will only pretend to change settings if running in the background
Launching this scene forces the Touchpad to wake up to deal with presenting it, and when it does we have a good
chance of successfully changing the settings. We can then close this window. All the smarts for this are in stage assistant.
*/
function DashboardAssistant(argFromPusher) {
this.passedArguments = argFromPusher;
}
DashboardAssistant.prototype.setup = function() {
Mojo.Log.info("Notification stage setup at " + new Date());
this.displayDashboard("SimpleChat", "Checking for new messages...");
this.serviceEndpointBase = appModel.ServiceEndpointBase;
if (appModel.AppSettingsCurrent["UseCustomEndpoint"] && appModel.AppSettingsCurrent["EndpointURL"]) {
this.serviceEndpointBase = appModel.AppSettingsCurrent["EndpointURL"];
}
if (!appModel.AppSettingsCurrent["AlertSound"] || appModel.AppSettingsCurrent["AlertSound"] == "") {
appModel.AppSettingsCurrent["AlertSound"] = "Subtle (short)";
}
serviceModel.getChats(this.serviceEndpointBase, function(response) {
var appController = Mojo.Controller.getAppController();
if (response != null && response != "") {
var responseObj = JSON.parse(response);
if (responseObj.status == "error") {
Mojo.Log.error("Error message from server during background check: " + responseObj.msg);
appController.closeAllStages();
} else {
if (responseObj.messages && responseObj.messages.length > 0) {
var newMessageCount = this.findNewMessageCount(appModel.AppSettingsCurrent["LastKnownMessage"], responseObj.messages);
if (newMessageCount > 0) {
Mojo.Log.info("Found new chat on server during background check!");
this.displayDashboard("SimpleChat", "New messages in the chat!", newMessageCount);
//If the main app window is open, it will play a sound. If not, play a sound here.
var appController = Mojo.Controller.getAppController();
var mainStage = appController.getStageController("main");
if (!mainStage) {
systemModel.PlayAlertSound(appModel.AppSettingsCurrent["AlertSound"]);
}
} else {
Mojo.Log.info("No new chats on server during background check.");
appController.closeStage("dashboard");
}
} else {
Mojo.Log.warn("Chat results were empty during scheduled background check. This is unlikely; server, API or connectivity problem possible");
appController.closeStage("dashboard");
}
}
} else {
Mojo.Log.error("No usable response from server during background check: " + response);
appController.closeStage("dashboard");
}
}.bind(this));
//Event handlers
Mojo.Event.listen(this.controller.get("dashboardinfo"), Mojo.Event.tap, this.handleTap.bind(this));
}
DashboardAssistant.prototype.findNewMessageCount = function(LastKnownMessage, newMessages) {
var actuallyNewMessages = [];
var startCounting = false;
for (var j = 0; j < newMessages.length; j++) {
if (startCounting) {
actuallyNewMessages.push(newMessages[j].uid);
appModel.AppSettingsCurrent["LastKnownMessage"] = newMessages[j].uid;
}
if (newMessages[j].uid == LastKnownMessage) {
startCounting = true;
}
}
return actuallyNewMessages.length;
}
DashboardAssistant.prototype.handleTap = function(event) {
Mojo.Log.info("Dashboard tapped!");
var appController = Mojo.Controller.getAppController();
var mainStage = appController.getStageProxy("main");
if (!mainStage) {
systemModel.LaunchApp(Mojo.Controller.appInfo.id);
appController.closeAllStages();
} else {
mainStage.activate();
appController.closeStage("dashboard")
}
}
DashboardAssistant.prototype.handleDrag = function(event) {
var infoElement = this.controller.get("dashboardinfo");
infoElement.style.opacity = "0.5"; //TODO: this only seems to effect the icon
}
DashboardAssistant.prototype.handleDragEnd = function(event) {
Mojo.Log.info("Dashboard drag end!");
var appController = Mojo.Controller.getAppController();
appController.closeAllStages();
}
DashboardAssistant.prototype.displayDashboard = function(title, message, count) {
var infoElement = this.controller.get("dashboardinfo");
var info = { title: title, message: message, count: count };
var renderedInfo = Mojo.View.render({ object: info, template: "dashboard/item-info" });
infoElement.innerHTML = renderedInfo;
this.controller.getSceneScroller().mojo.revealTop(true);
}
DashboardAssistant.prototype.deactivate = function(event) {
Mojo.Event.stopListening(this.controller.get("dashboardinfo"), Mojo.Event.tap, this.handleTap);
}
// Cleanup anything we did in setup function
DashboardAssistant.prototype.cleanup = function() {
Mojo.Log.info("notification stage closing at " + new Date());
}
@@ -1,85 +0,0 @@
/* Copyright 2010 Palm, Inc. All rights reserved. */
/*global Mojo, $L, Class, window, document, PalmSystem, AppAssistant, palmGetResource, Foundations, _, */
var EmoticonPickerDialogAssistant = function(sceneAssistant, onselect) {
this.sceneAssistant = sceneAssistant;
this.controller = sceneAssistant.controller;
this.onselect = onselect;
};
EmoticonPickerDialogAssistant.prototype.show = function() {
this.controller.showDialog({
template: 'main/emoticonpicker-dialog',
assistant: this
});
};
EmoticonPickerDialogAssistant.prototype.setup = function(widget) {
this.widget = widget;
var emoTable = this.controller.get('emo-table');
var emoticonList = [
"B-)",
">:(",
":'(",
":[",
":-!",
"X(",
":@",
"o_O",
":O",
">:-)",
"O:)",
"<3",
":-*",
":D",
":/",
":(",
":|",
":P",
";)",
":)"
];
var row = emoTable.insertRow();
var insertCell = this.insertCell;
emoticonList.forEach(function(emoticon) {
if (row.cells.length === 5) {
row = emoTable.insertRow(0);
}
insertCell(row, emoticon);
});
emoTable.addEventListener(Mojo.Event.tap, this.handleEmoticonSelect.bind(this));
this.controller.get('cancel_button').addEventListener(Mojo.Event.tap, this.handleClose.bind(this));
};
EmoticonPickerDialogAssistant.prototype.insertCell = function(row, emoticon) {
var cell = row.insertCell(0);
var MojoHTML = Mojo.Format.runTextIndexer(emoticon.escapeHTML());
MojoHTML = MojoHTML.replace("file:///usr/palm/", "");
cell.innerHTML = MojoHTML;
cell.setAttribute("emo", emoticon);
cell.style.height = "40px";
};
EmoticonPickerDialogAssistant.prototype.handleEmoticonSelect = function(event) {
event.stopPropagation(); // stop the tap because it causes the underlying text item to lose focus
event.preventDefault();
var td = event.target;
if (td && td.tagName !== "TD") {
td = event.target.up('td');
}
if (td) {
this.onselect(td.getAttribute("emo"));
}
this.widget.mojo.close();
};
EmoticonPickerDialogAssistant.prototype.handleClose = function(event) {
event.stopPropagation(); // stop the tap because it causes the underlying text item to lose focus
event.preventDefault();
this.widget.mojo.close();
};
@@ -1,830 +0,0 @@
/*
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. */
this.doingMessageEdit = false;
this.serverRetries = 0;
this.serverGiveUp = 4;
}
MainAssistant.prototype.setup = function() {
// setup message field
this.messageFieldModel = {
'original': ''
};
this.messageFieldModel.attributes = {
hintText: $L(" Enter a message..."),
enterSubmits: false,
focus: true,
multiline: true,
textCase: Mojo.Widget.steModeSentenceCase,
focusMode: Mojo.Widget.focusSelectMode,
autoReplace: true,
autoEmoticons: true,
emoticons: 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: "", formattedMessage: "" }
};
//Chat Log List templates (loads other HTML)
this.template = {
itemTemplate: 'main/item-template',
listTemplate: 'main/list-template',
hasNoWidgets: true,
swipeToDelete: false,
renderLimit: 25,
reorderable: false,
hasNoWidgets: true,
onItemRendered: this.handleItemRendered.bind(this)
};
this.controller.setupWidget('chatList', this.template, this.listInfoModel);
//Scroller
this.scrollerModel = {
mode: 'vertical',
weight: 'light',
friction: 'low'
};
this.controller.setupWidget('chatScroller', {}, this.scrollerModel);
//Menu
this.appMenuAttributes = { omitDefaultItems: true };
this.appMenuModel = {
label: "Settings",
items: [
Mojo.Menu.editItem,
{ label: "SimpleChat Info", command: 'do-Info' },
{ 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);
//Command Buttons
this.cmdMenuAttributes = {
spacerHeight: 0,
menuClass: 'no-fade'
};
this.cmdMenuModel = {
visible: true,
items: [{
items: [
{ label: 'Emoticon', iconPath: 'assets/emoticon.png', command: 'do-Emoticon' }
]
},
{
items: [
{ label: 'Send', icon: 'send', command: 'do-Send' }
]
}
]
};
this.controller.setupWidget(Mojo.Menu.commandMenu, this.cmdMenuAttributes, this.cmdMenuModel);
this.menuOn = true;
/* Always on Event handlers */
Mojo.Event.listen(this.controller.get("chatList"), Mojo.Event.listTap, this.handleListClick.bind(this));
Mojo.Event.listen(this.controller.stageController.document, Mojo.Event.stageActivate, this.activateWindow.bind(this));
Mojo.Event.listen(this.controller.stageController.document, Mojo.Event.stageDeactivate, this.deactivateWindow.bind(this));
//Check for updates
this.checkForUpdates();
};
MainAssistant.prototype.activate = function(event) {
//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"];
}
//Set an alert sound
if (!appModel.AppSettingsCurrent["AlertSound"] || appModel.AppSettingsCurrent["AlertSound"] == "") {
appModel.AppSettingsCurrent["AlertSound"] = "Subtle (short)";
}
//Check for Emoji option
if (appModel.AppSettingsCurrent["ShowEmojis"] == null) {
Mojo.Log.info("Adding Emoji setting with default value!")
appModel.AppSettingsCurrent["ShowEmojis"] = appModel.AppSettingsDefaults["ShowEmojis"];
appModel.SaveSettings();
}
//Init Message memory
if (!appModel.AppSettingsCurrent["MyMessages"]) {
appModel.AppSettingsCurrent["MyMessages"] = [];
}
if (appModel.AppSettingsCurrent["LastKnownMessages"]) { //This was the old way, clean it up
appModel.AppSettingsCurrent["LastKnownMessages"] = null;
}
//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 (this.controller.window.screen.width == 800 || this.controller.window.screen.height == 800) {
this.DeviceType = "Pre3";
Mojo.Log.info("Device detected as Pre3");
} else if ((this.controller.window.screen.width == 480 || this.controller.window.screen.height == 480) && (this.controller.window.screen.width == 320 || this.controller.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.serverRetries = 0;
this.pendingMessages = [];
this.firstPoll = true;
this.startPollingServer();
this.handleTextFocus();
var currVersion = Mojo.Controller.appInfo.version;
if (!welcomed && (!appModel.AppSettingsCurrent["LastVersionRun"] || appModel.AppSettingsCurrent["LastVersionRun"] != currVersion)) {
this.pausePollingServer();
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ name: "version", disableSceneScroller: false });
appModel.AppSettingsCurrent["LastVersionRun"] = currVersion;
welcomed = true;
} else {
//Scale UI for orientation and listen for future orientation changes
this.controller.window.addEventListener('resize', this.orientationChanged.bind(this)); //we have to do this for TouchPad because it does not get orientationChanged events
this.orientationChanged();
//Add event handlers
Mojo.Event.listen(this.controller.get("chatList"), Mojo.Event.listTap, this.handleListClick.bind(this));
Mojo.Event.listenForFocusChanges(this.controller.get("txtMessage"), this.handleTextFocus.bind(this));
this.controller.document.getElementById("divComposeTitle").addEventListener("click", this.toggleCommandMenu.bind(this));
//Figure out if this is our first time
if (appModel.AppSettingsCurrent["FirstRun"] || !appModel.AppSettingsCurrent["SenderName"] || (appModel.AppSettingsCurrent["SenderName"] && appModel.AppSettingsCurrent["SenderName"].toLowerCase() == "webos user")) {
appModel.AppSettingsCurrent["FirstRun"] = false;
this.getUsername();
} else {
this.controller.get('txtMessage').mojo.focus();
}
}
};
/* Screen Adjustments */
MainAssistant.prototype.handleTextFocus = function(event) {
this.adustScrollerForKeyboard(this.lastOrientation);
}
//This is called by Mojo on phones, but has to be manually attached on TouchPad
MainAssistant.prototype.orientationChanged = function() {
if (this.DeviceType != "TouchPad") {
//For phones, it doesn't make sense to allow wide orientations
// But we need this for initial setup, so we'll force it to always be tall
this.controller.stageController.setWindowOrientation("up");
this.scaleScroller("tall")
} else {
if (this.controller.window.screen.height < this.controller.window.screen.width) { //touchpad orientations are sideways from phones
this.scaleScroller("tall");
} else {
this.scaleScroller("wide");
}
}
};
MainAssistant.prototype.scaleScroller = function(orientation) {
if (!this.lastOrientation || orientation != this.lastOrientation) {
this.adustScrollerForKeyboard(orientation);
this.controller.get('palm_anon_element_0mojo-scene-maintxtMessage-write').rows = "2";
this.controller.get('palm_anon_element_0mojo-scene-maintxtMessage-write').setAttribute("x-palm-enable-emoticons", true);
}
this.lastOrientation = orientation;
}
MainAssistant.prototype.adustScrollerForKeyboard = function(orientation) {
//Handle virtual keyboard on Touchpad
var bottomBuffer;
this.chatScroller = this.controller.get("chatScroller");
this.scalingFactor = this.controller.window.zoomFactor || 1;
if (this.DeviceType == "TouchPad") {
if (this.controller.document.activeElement.id == "palm_anon_element_0mojo-scene-maintxtMessage-write")
bottomBuffer = 600;
else
bottomBuffer = 260;
} else
bottomBuffer = 250;
if (orientation == "tall")
this.scaledHeight = Math.floor(Mojo.Environment.DeviceInfo.screenHeight / this.scalingFactor) - bottomBuffer;
else
this.scaledHeight = Math.floor(Mojo.Environment.DeviceInfo.screenWidth / this.scalingFactor) - bottomBuffer;
Mojo.Log.info(this.DeviceType + " orientation is " + orientation + " bottom buffer is: " + bottomBuffer + " scaled height: " + this.scaledHeight);
this.chatScroller.style.height = this.scaledHeight + "px";
//this.scrollToBottom();
setTimeout(fixScroll = function() {
this.controller.getSceneScroller().mojo.revealTop(true);
}.bind(this), 100);
}
/* Start and Stop service polling */
MainAssistant.prototype.startPollingServer = function() {
this.getChats();
var useInt = 10000;
if (appModel.AppSettingsCurrent["ForegroundUpdate"])
useInt = appModel.AppSettingsCurrent["ForegroundUpdate"];
var stageController = Mojo.Controller.getAppController().getActiveStageController();
this.controller = stageController.activeScene();
this.FileCheckInt = this.controller.window.setInterval(this.getChats.bind(this), useInt);
}
MainAssistant.prototype.pausePollingServer = function() {
this.controller.window.clearInterval(this.FileCheckInt);
}
/* UI Handlers */
MainAssistant.prototype.handleListClick = function(event) {
appModel.LastMessageSelected = event.item;
this.listTarget = event.originalEvent.target;
//Decide what items to put in pop-up menu
var popupMenuItems = [];
var isMine = false;
for (var m = 0; m < appModel.AppSettingsCurrent["MyMessages"].length; m++) {
if (appModel.AppSettingsCurrent["MyMessages"][m].uid == appModel.LastMessageSelected.uid)
isMine = true;
}
if (isMine)
popupMenuItems.push({ label: 'Edit Message', command: 'do-editMessage' });
else {
if (event.item.links != null) {
popupMenuItems.push({ label: 'Copy Link', command: 'do-copyLink' });
popupMenuItems.push({ label: 'Follow Link', command: 'do-followLink' });
}
popupMenuItems.push({ label: 'Copy Message', command: 'do-copy' });
popupMenuItems.push({ label: 'Like', command: 'do-like' });
}
this.controller.popupSubmenu({
onChoose: this.handlePopupChoose.bind(this, event.item),
placeNear: this.listTarget,
items: popupMenuItems
});
return true;
}
MainAssistant.prototype.handlePopupChoose = function(message, command) {
//Mojo.Log.info("Perform: ", command, " on ", message.uid);
switch (command) {
case "do-copy":
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.setClipboard(appModel.LastMessageSelected.message);
Mojo.Controller.getAppController().showBanner("Message copied!", { source: 'notification' });
break;
case "do-copyLink":
var useLink = appModel.LastMessageSelected.links[0];
if (useLink.toLowerCase().indexOf("http://") == -1 && useLink.toLowerCase().indexOf("https://") == -1)
useLink = "http://" + appModel.LastMessageSelected.links[0];
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.setClipboard(useLink);
Mojo.Controller.getAppController().showBanner("Link copied!", { source: 'notification' });
break;
case "do-followLink":
var useLink = appModel.LastMessageSelected.links[0];
if (useLink.toLowerCase().indexOf("http://") == -1 && useLink.toLowerCase().indexOf("https://") == -1)
useLink = "http://" + appModel.LastMessageSelected.links[0];
//TODO: Handle more than one link
Mojo.Log.info("Launching browser for URL " + useLink);
var parameters = {
"id": "com.palm.app.browser",
"params": {
"target": useLink
}
}
systemModel.LaunchApp("com.palm.app.browser", parameters);
break;
case "do-editMessage":
this.doEditMessage(message);
break;
case "do-like":
this.doLikeMessage(message);
break;
}
}
MainAssistant.prototype.handleCommand = function(event) {
if (event.type == Mojo.Event.command) {
switch (event.command) {
case 'do-Emoticon':
var controller = this.controller;
var onselect = function onEmoticonSelect(emoticon) {
controller.document.execCommand("insertText", true, emoticon);
};
var emoticonPicker = new EmoticonPickerDialogAssistant(this, onselect);
emoticonPicker.show();
break;
case 'do-Send':
this.handleSendMessage();
break;
case 'do-Username':
this.getUsername();
break;
case 'do-Preferences':
this.pausePollingServer();
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ name: "preferences", disableSceneScroller: false });
break;
case 'do-Info':
this.pausePollingServer();
var stageController = Mojo.Controller.getAppController().getActiveStageController();
stageController.pushScene({ name: "version", 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;
}
}
};
MainAssistant.prototype.doEditMessage = function(message) {
this.controller.get('txtMessage').mojo.setValue(appModel.LastMessageSelected.message);
this.controller.get('spanCompose').innerHTML = "Edit";
this.doingMessageEdit = true;
}
MainAssistant.prototype.doLikeMessage = function(message) {
//post a like, update message, play a sound?
if (!message.likes || message.likes == "" || message.likes == 0)
message.likes = 1;
else
message.likes++;
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
for (var i = 0; i < thisWidgetSetup.model.items.length; i++) {
if (thisWidgetSetup.model.items[i].uid == appModel.LastMessageSelected.uid) {
thisWidgetSetup.model.items[i].likes = message.likes;
thisWidgetSetup.model.items[i].color = "gray";
this.controller.get('chatList').mojo.noticeUpdatedItems(i, [thisWidgetSetup.model.items[i]]);
}
}
this.likeMessageToService();
}
/* Server Interactions */
MainAssistant.prototype.handleSendMessage = function(event) {
this.controller.get('txtMessage').mojo.blur();
var newMessage = this.controller.get('txtMessage').mojo.getValue();
this.disableUI();
systemModel.PlaySound("down2");
if (this.doingMessageEdit) {
this.editMessageToService(newMessage);
} else {
this.postMessageToService(newMessage);
}
}
MainAssistant.prototype.postMessageToService = function(newMessage) {
//Mojo.Log.info("Sending message:" + newMessage);
serviceModel.postChat(appModel.AppSettingsCurrent["SenderName"], newMessage, this.serviceEndpointBase, this.clientId, 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 accepted by server: " + responseObj.posted); //{ "posted": "604529e19fb7f", "senderKey": "604529e19fb99" }
if (responseObj.posted) {
Mojo.Log.info("responseObj: " + JSON.stringify(responseObj));
var MojoHTML = Mojo.Format.runTextIndexer(newMessage);
MojoHTML = MojoHTML.replace("file:///usr/palm/", "");
var newMsg = {
uid: responseObj.posted,
sender: appModel.AppSettingsCurrent["SenderName"],
message: newMessage,
formattedMessage: MojoHTML,
timestamp: this.convertTimeStamp(new Date(), false),
color: "gray"
};
if (responseObj.senderKey) {
appModel.AppSettingsCurrent["MyMessages"].push({ uid: responseObj.posted, senderKey: responseObj.senderKey })
}
this.pendingMessages.push(responseObj.posted);
Mojo.Log.info("MyMessages now: " + JSON.stringify(appModel.AppSettingsCurrent["MyMessages"]));
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
thisWidgetSetup.model.items.push(newMsg);
this.controller.modelChanged(thisWidgetSetup.model);
this.scrollToBottom();
} else {
Mojo.Log.warn("Did not understand server reponse to post!");
}
}
} catch (error) {
//Handle error
}
}.bind(this));
}
MainAssistant.prototype.editMessageToService = function(newMessage) {
Mojo.Log.info("trying to edit: " + appModel.LastMessageSelected.uid);
//Set UI back to compose mode
this.controller.get('txtMessage').mojo.setValue("");
this.controller.get('spanCompose').innerHTML = "Compose";
this.doingMessageEdit = false;
this.enableUI();
var editKey; //Make sure we are allowed to edit this item
for (var m = 0; m < appModel.AppSettingsCurrent["MyMessages"].length; m++) {
if (appModel.AppSettingsCurrent["MyMessages"][m].uid == appModel.LastMessageSelected.uid)
editKey = appModel.AppSettingsCurrent["MyMessages"][m].senderKey;
}
if (editKey) {
//Force list item to update with new content (without informing Mojo, so the list doesn't bounce around. Mojo will get informed later)
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
for (var i = 0; i < thisWidgetSetup.model.items.length; i++) {
if (thisWidgetSetup.model.items[i].uid == appModel.LastMessageSelected.uid) {
thisWidgetSetup.model.items[i].message = newMessage;
var MojoHTML = Mojo.Format.runTextIndexer(newMessage);
MojoHTML = MojoHTML.replace("file:///usr/palm/", "");
thisWidgetSetup.model.items[i].formattedMessage = MojoHTML;
thisWidgetSetup.model.items[i].color = "gray";
this.controller.get('chatList').mojo.noticeUpdatedItems(i, [thisWidgetSetup.model.items[i]]);
}
}
//Tell server about the edit
serviceModel.editChat(appModel.AppSettingsCurrent["SenderName"], newMessage, appModel.LastMessageSelected.uid, editKey, this.serviceEndpointBase, this.clientId, function(response) {
try {
var responseObj = JSON.parse(response);
if (responseObj.error) {
Mojo.Log.error("Server error returned: " + responseObj.error);
} else {
if (responseObj.warning) {
Mojo.Log.warn("Warning from server: " + responseObj.warning)
} else {
Mojo.Log.info("Edit accepted by server: " + responseObj.edited);
}
}
} catch (error) {
//TODO: Handle error
}
}.bind(this));
}
}
MainAssistant.prototype.likeMessageToService = function() {
Mojo.Log.info("trying to like: " + appModel.LastMessageSelected.uid);
serviceModel.likeChat(appModel.LastMessageSelected.uid, this.serviceEndpointBase, this.clientId, function(response) {
this.enableUI();
try {
var responseObj = JSON.parse(response);
Mojo.Log.info("like response from server: " + response);
if (responseObj.error) {
Mojo.Log.error("Server error returned: " + responseObj.error);
} else {
Mojo.Log.info("Like accepted by server: " + responseObj.liked);
}
} catch (error) {
//TODO: Handle error
}
}.bind(this));
}
//Send a request to Service to get chat messages
MainAssistant.prototype.getChats = function() {
if (this.serverRetries <= this.serverGiveUp) {
serviceModel.getChats(this.serviceEndpointBase, function(response) {
//Mojo.Log.info("getChat response: " + 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.serverRetries = 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);
this.serverRetries++;
if (this.serverRetries == this.serverGiveUp) {
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 and re-launch the app.");
}
}
}.bind(this));
}
}
//Update the UI with search results from chat request
MainAssistant.prototype.updateChatsList = function(results) {
Mojo.Log.info("Proccessing " + results.length + " chat messages...");
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
//now make the new list
var newMessages = [];
for (var i = 0; i < results.length; i++) {
var MojoHTML = Mojo.Format.runTextIndexer(results[i].message);
MojoHTML = MojoHTML.replace("file:///usr/palm/", "");
newMessages.push({
uid: results[i].uid,
sender: results[i].sender,
message: results[i].message,
formattedMessage: MojoHTML,
postedFrom: results[i].postedFrom,
links: this.detectURLs(results[i].message),
timestamp: this.convertTimeStamp(results[i].timestamp, true),
likes: results[i].likes,
color: "black"
});
}
var scrollPos = this.chatScroller.mojo.getState();
//Compare the known list to the new list to see if anything has changed...
//Delete old messages
var deleted = false;
for (var m = 0; m < thisWidgetSetup.model.items.length; m++) {
var found = false;
for (var n = 0; n < newMessages.length; n++) {
if (newMessages[n].uid == thisWidgetSetup.model.items[m].uid)
found = true;
}
if (!found) {
Mojo.Log.info("Removing deleted message from list at position " + m);
this.controller.get('chatList').mojo.noticeRemovedItems(m, 1);
thisWidgetSetup.model.items.splice(m, 1);
deleted = true;
}
}
//Update existing messages (content or likes)
var updated = false;
for (var m = 0; m < thisWidgetSetup.model.items.length; m++) {
for (var n = 0; n < newMessages.length; n++) {
if (newMessages[n].uid == thisWidgetSetup.model.items[m].uid) {
var changed = false;
if (newMessages[n].message != thisWidgetSetup.model.items[m].message ||
newMessages[n].likes != thisWidgetSetup.model.items[m].likes ||
newMessages[n].color != thisWidgetSetup.model.items[m].color) {
Mojo.Log.info("Updating message from list at position " + m);
Mojo.Log.info("Old Message: " + JSON.stringify(thisWidgetSetup.model.items[m]));
thisWidgetSetup.model.items[m] = newMessages[n];
this.controller.get('chatList').mojo.noticeUpdatedItems(m, [thisWidgetSetup.model.items[m]]);
changed = true;
Mojo.Log.info("New Message should be: " + JSON.stringify(newMessages[n]));
Mojo.Log.info("New Message is : " + JSON.stringify(thisWidgetSetup.model.items[m]));
}
}
if (changed)
updated = true;
}
}
//Insert new messages
var inserted = [];
if (thisWidgetSetup.model.items.length > 0) {
for (var n = 0; n < newMessages.length; n++) {
var found = false;
for (var m = 0; m < thisWidgetSetup.model.items.length; m++) {
if (thisWidgetSetup.model.items[m].uid == newMessages[n].uid) {
found = true;
}
}
if (!found) {
inserted.push(newMessages[n]);
Mojo.Log.info("Found new message to add to the list: " + newMessages[n].uid);
}
}
} else {
Mojo.Log.info("The message list was empty, pushing entire server payload to the list.");
inserted = newMessages;
}
//Handle UI due to changes
if (inserted.length > 0 || updated || deleted) {
if (inserted.length > 0) {
if (!this.firstPoll) {
systemModel.PlayAlertSound(appModel.AppSettingsCurrent["AlertSound"]);
var offset = thisWidgetSetup.model.items.length - 1;
for (var i = 0; i < inserted.length; i++) {
thisWidgetSetup.model.items.push(inserted[i]);
}
this.controller.get('chatList').mojo.noticeAddedItems(offset, inserted);
} else {
thisWidgetSetup.model.items = inserted;
}
this.controller.modelChanged(thisWidgetSetup.model);
this.scrollToBottom();
}
} else {
Mojo.Log.info("No changes to apply to list.")
}
this.firstPoll = false;
//Also clean-up MyMessage history
for (var m = 0; m < appModel.AppSettingsCurrent["MyMessages"].length; m++) {
var found = false;
for (var n = 0; n < thisWidgetSetup.model.items.length; n++) {
if (thisWidgetSetup.model.items[n].uid == appModel.AppSettingsCurrent["MyMessages"][m].uid)
found = true;
}
if (!found) {
appModel.AppSettingsCurrent["MyMessages"].splice(m, 1);
}
}
}
//Called by Mojo once the list has been painted, gives us an opportunity to force HTML into the message
MainAssistant.prototype.handleItemRendered = function(listWidget, itemModel, itemNode) {
itemNode.innerHTML = this.unescapeEntities(itemNode.innerHTML);
//Hide emojis if app settings say to do so
if (!appModel.AppSettingsCurrent["ShowEmojis"]) {
var emojis = this.controller.document.getElementsByName('emoji');
for (var i = 0; i < emojis.length; i++) {
emojis[i].style.display = 'none';
}
}
}
//Used to show or hide command menu buttons
MainAssistant.prototype.toggleCommandMenu = function(show) {
Mojo.Log.warn("Toggling command menu");
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (stageController) {
this.controller = stageController.activeScene();
var thisWidgetSetup = this.controller.getWidgetSetup(Mojo.Menu.commandMenu);
var thisWidgetModel = thisWidgetSetup.model;
if (!this.menuOn || show == true) {
thisWidgetModel.visible = true;
this.menuOn = true;
this.controller.document.getElementById("imgTwisty").src = "assets/twisty-down.png";
} else {
thisWidgetModel.visible = false;
this.menuOn = false;
this.controller.document.getElementById("imgTwisty").src = "assets/twisty-right.png";
}
this.controller.modelChanged(thisWidgetModel);
}
}
/* Helper Functions */
MainAssistant.prototype.checkForUpdates = function() {
if (!appModel.UpdateCheckDone) {
appModel.UpdateCheckDone = true;
updaterModel.CheckForUpdate("webOS SimpleChat", function(responseObj) {
if (responseObj && responseObj.updateFound) {
updaterModel.PromptUserForUpdate(function(response) {
if (response)
updaterModel.InstallUpdate();
}.bind(this));
}
}.bind(this));
}
}
MainAssistant.prototype.getUsername = function() {
var stageController = Mojo.Controller.getAppController().getActiveStageController();
if (stageController) {
this.controller = stageController.activeScene();
this.controller.showDialog({
template: 'user/user-scene',
preventCancel: true,
assistant: new UserAssistant(this, function(val) {
Mojo.Log.info("got value from dialog: " + val);
}.bind(this)) //since this will be a dialog, not a scene, it must be defined in sources.json without a 'scenes' member
});
}
}
MainAssistant.prototype.scrollToBottom = function() {
this.chatScroller.mojo.revealBottom();
setTimeout(function() {
this.chatScroller.mojo.revealBottom();
}.bind(this), 500);
//this.controller.get('txtMessage').mojo.focus();
}
MainAssistant.prototype.scrollToPosition = function(scrollPos) {
Mojo.Log.info("Scrolling back to: " + JSON.stringify(scrollPos));
this.chatScroller.mojo.setState(scrollPos, true);
}
MainAssistant.prototype.unescapeEntities = function(str) {
str = str.replace(/&gt;/g, ">");
str = str.replace(/&lt;/g, "<");
return str;
}
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;
}
MainAssistant.prototype.detectURLs = function(message) {
if (!message) return
var urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g;
return message.match(urlRegex)
}
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.rememberLastMessage = function() {
var thisWidgetSetup = this.controller.getWidgetSetup("chatList");
if (thisWidgetSetup.model.items.length > 0) {
appModel.AppSettingsCurrent["LastKnownMessage"] = thisWidgetSetup.model.items[thisWidgetSetup.model.items.length - 1].uid;
appModel.SaveSettings();
}
}
/* Mojo Lifecycle Stuff */
MainAssistant.prototype.activateWindow = function(event) {
this.rememberLastMessage();
};
MainAssistant.prototype.deactivateWindow = function(event) {
this.rememberLastMessage();
};
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.Log.info("Main scene deactivated " + Mojo.Controller.appInfo.id);
//Remember last known messages
this.pausePollingServer();
this.rememberLastMessage();
//Detach UI
Mojo.Event.stopListening(this.controller.get("chatList"), Mojo.Event.listTap, this.handleListClick);
this.controller.window.removeEventListener('resize', this.orientationChanged);
this.controller.document.getElementById("divComposeTitle").removeEventListener("click", this.toggleCommandMenu.bind(this));
Mojo.Event.stopListening(this.controller.stageController.document, Mojo.Event.stageActivate, this.activateWindow);
Mojo.Event.stopListening(this.controller.stageController.document, Mojo.Event.stageDeactivate, this.deactivateWindow);
};
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 */
var appController = Mojo.Controller.getAppController();
appController.closeAllStages();
};
@@ -1,296 +0,0 @@
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
}
);
var backgroundValue;
if (!appModel.AppSettingsCurrent["BackgroundUpdate"] || appModel.AppSettingsCurrent["BackgroundUpdate"] == 0 || appModel.AppSettingsCurrent["BackgroundUpdate"] == "")
backgroundValue = "-1";
else
backgroundValue = appModel.AppSettingsCurrent["BackgroundUpdate"]
this.controller.setupWidget("listBackgroundUpdate",
this.attributes = {
label: $L("Background Update"),
choices: [
{ label: "Off", value: -1 },
{ label: "30 minutes", value: "00:30:00" },
{ label: "1 Hour", value: "01:00:00" },
{ label: "2 Hours", value: "02:00:00" },
{ label: "5 Hours", value: "05:00:00" },
]
},
this.model = {
value: backgroundValue,
disabled: false
}
);
//Emoji toggle
this.controller.setupWidget("toggleEmojis",
this.attributes = {
trueValue: true,
falseValue: false
},
this.model = {
value: appModel.AppSettingsCurrent["ShowEmojis"],
disabled: false
}
);
//Setup sound picker
this.soundChoices = {
choices: [
{ label: "Choose...", value: "choose" },
{ label: "Off", value: "off" }
]
};
if (appModel.AppSettingsCurrent["AlertSound"] != "off") {
var useVal = appModel.AppSettingsCurrent["AlertSound"];
var labelVal = useVal;
if (Mojo.Environment.DeviceInfo.platformVersionMajor < 3) {
labelVal = useVal.substring(0, 8) + "...";
}
this.soundChoices.choices.unshift({ label: labelVal, value: useVal });
}
this.controller.setupWidget("listAlertSound",
this.attributes = this.soundChoices,
this.model = {
value: appModel.AppSettingsCurrent["AlertSound"],
disabled: false
}
);
//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,
focus: false,
autoFocus: 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-chat-server.com/"),
multiline: false,
enterSubmits: false,
autoReplace: false,
textCase: Mojo.Widget.steModeLowerCase
},
this.model = {
value: appModel.AppSettingsCurrent["EndpointURL"],
disabled: !appModel.AppSettingsCurrent["UseCustomEndpoint"]
}
);
//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("toggleEmojis"), Mojo.Event.propertyChange, this.handleValueChange.bind(this));
Mojo.Event.listen(this.controller.get("listAlertSound"), 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);
switch (event.srcElement.id) {
case "toggleEmojis":
Mojo.Log.info("Should show banner!");
Mojo.Controller.getAppController().showBanner({ messageText: "Re-launch app to apply this setting" }, "", "");
break;
case "listAlertSound":
if (event.value == "choose") {
this.setAlarmSound();
} else {
appModel.AppSettingsCurrent["AlertSound"] = event.value;
}
break;
case "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();
break;
case "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();
break;
case "txtEndpointURL":
var lastChar = event.value[event.value.length - 1]
if (lastChar != "/") {
event.value = event.value + "/";
Mojo.Log.warn("Custom end point URL was missing trailing slash, it has been added. Value is now: " + event.value);
}
break;
}
//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();
};
// opens ringtone picker.
PreferencesAssistant.prototype.setAlarmSound = function() {
var self = this;
var params = {
defaultKind: 'ringtone',
onSelect: function(file) {
var fileToUse = file.fullPath.replace("/media/internal/ringtones/", "").replace(".mp3", "");
appModel.AppSettingsCurrent["AlertSound"] = fileToUse;
appModel.SaveSettings();
var useVal = fileToUse;
var labelVal = useVal;
try {
if (Mojo.Environment.DeviceInfo.platformVersionMajor < 3) {
labelVal = useVal.substring(0, 8) + "...";
}
} catch (ex) {
Mojo.Log.error("Could not set ringtone value");
}
self.soundChoices.choices[0].label = labelVal;
self.soundChoices.choices[0].value = useVal;
var thisWidgetSetup = self.controller.getWidgetSetup("listAlertSound");
var thisWidgetModel = thisWidgetSetup.model;
thisWidgetModel.value = fileToUse;
self.controller.setWidgetModel("listAlertSound", thisWidgetModel);
self.controller.modelChanged(thisWidgetModel);
Mojo.Log.info("alarm sound changed to: " + fileToUse)
}
}
var appController = Mojo.Controller.getAppController();
var stageController = appController.getStageProxy("main");
Mojo.FilePicker.pickFile(params, stageController);
}
//Handle menu and button bar commands
PreferencesAssistant.prototype.handleCommand = function(event) {
if (event.type == Mojo.Event.command) {
switch (event.command) {
case 'do-goBack':
this.okClick();
break;
case 'do-resetSettings':
appModel.ResetSettings(appModel.AppSettingsDefaults);
break;
}
}
};
PreferencesAssistant.prototype.okClick = function(event) {
var appController = Mojo.Controller.getAppController();
var stageController = appController.getStageProxy("main");
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 */
systemModel.ClearSystemAlarm("SimpleChat");
if (appModel.AppSettingsCurrent["BackgroundUpdate"] && appModel.AppSettingsCurrent["BackgroundUpdate"] != "" && appModel.AppSettingsCurrent["BackgroundUpdate"] != -1)
systemModel.SetSystemAlarmRelative("SimpleChat", appModel.AppSettingsCurrent["BackgroundUpdate"]);
Mojo.Event.stopListening(this.controller.get("listForegroundUpdate"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("listBackgroundUpdate"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("listAlertSound"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("txtClientAPI"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("toggleClientAPI"), Mojo.Event.propertyChange, this.handleValueChange);
Mojo.Event.stopListening(this.controller.get("toggleCustomEndPoint"), 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 */
};
@@ -1,14 +0,0 @@
function StageAssistant() {
/* this is the creator function for your stage assistant object */
}
StageAssistant.prototype.activate = function() {
Mojo.Log.error("Stage activated");
stageController = Mojo.Controller.stageController;
if (stageController.getScenes().length < 1)
stageController.pushScene('main');
}
StageAssistant.prototype.setup = function() {
Mojo.Log.info("Stage assistant created!");
};
@@ -1,79 +0,0 @@
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.defaultButton }, { 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();
if (newName && newName != "" && newName != " " && newName.toLowerCase() != "webos user") {
//Remember the name they entered
appModel.AppSettingsCurrent["SenderName"] = newName;
appModel.SaveSettings();
this.doneCallBack(newName);
this.widget.mojo.close();
} else {
Mojo.Controller.getAppController().showBanner({ messageText: "Provide a unique username!" }, "", "");
}
}
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 */
};
-103
View File
@@ -1,103 +0,0 @@
var ServiceModel = function() {
};
ServiceModel.prototype.getChats = function(serviceBase, callback) {
serviceURL = serviceBase + "get-chat.php";
Mojo.Log.info("Trying to get chats from: " + serviceURL);
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);
}
ServiceModel.prototype.postChat = function(useSender, useMessage, serviceBase, clientId, callback) {
serviceURL = serviceBase + "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", clientId);
xmlhttp.timeout = 2000;
xmlhttp.send(JSON.stringify(msgToPost));
xmlhttp.onreadystatechange = function() {
Mojo.Log.info("ready state: " + xmlhttp.readyState);
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (callback)
callback(xmlhttp.responseText);
}
}.bind(this);
xmlhttp.ontimeout = function() {
callback();
}.bind(this);
}
ServiceModel.prototype.editChat = function(useSender, useMessage, uid, editKey, serviceBase, clientId, callback) {
serviceURL = serviceBase + "edit-chat.php";
var msgToPost = {
sender: useSender,
message: useMessage,
uid: uid,
editKey: editKey
}
Mojo.Log.info("Edit 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", clientId);
xmlhttp.send(JSON.stringify(msgToPost));
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (callback)
callback(xmlhttp.responseText);
}
}.bind(this);
}
ServiceModel.prototype.likeChat = function(uid, serviceBase, clientId, callback) {
serviceURL = serviceBase + "like-chat.php";
var msgToPost = {
uid: uid,
like: "+1"
}
Mojo.Log.info("Like 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", clientId);
xmlhttp.send(JSON.stringify(msgToPost));
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE) {
if (callback)
callback(xmlhttp.responseText);
}
}.bind(this);
}
@@ -1,8 +0,0 @@
<div>
<table width="100%" id="emo-table">
</table>
<div id="cancel_button" class="palm-button" x-mojo-tap-highlight="momentary">
<span x-mojo-loc="">Cancel</span>
</div>
</div>
@@ -1,6 +0,0 @@
<div class="palm-row" x-mojo-tap-highlight='momentary'>
<div class="palm-row-wrapper">
<div id="divMsg-#{uid}" class="chat-log" style="color:#{color}"><span class="sender #{postedFrom}">#{sender}:</span> #{formattedMessage}</div>
<div class="chat-time">#{timestamp} <span class="likes">#{likes}</span></div>
</div>
</div>
@@ -1,3 +0,0 @@
<div class="palm-list">
<div> #{-listElements}</div>
</div>
@@ -1,24 +0,0 @@
<div class="palm-page-header">
<div class="header-icon preferences"></div>
<div class="header-text">
<span x-mojo-loc='' style="text-transform:none;">webOS SimpleChat 2</span>
</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" style="height: 100px;padding: 0px;margin:-8px;">
<div id="chatList" x-mojo-element="List"></div>
</div>
</div>
</div>
<div class="palm-group" style="min-height:64px">
<div class="palm-group-title" id="divComposeTitle"><span x-mojo-loc='' id="spanCompose">Compose</span> &nbsp;<img src="assets/twisty-down.png" id="imgTwisty"></div>
<div x-mojo-element="TextField" id="txtMessage" class="textFieldClass" name="txtMessage" style="min-height:56px; background-color:white;border: 1px solid dimgray"></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>
&nbsp;
@@ -1,79 +0,0 @@
<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" id="divTest">
<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">
<div class="palm-row-wrapper">
<div id="listBackgroundUpdate" x-mojo-element="ListSelector" title="BackgroundUpdate" class="right"></div>
</div>
</div>
<div class="palm-row">
<div class="palm-row-wrapper">
<div x-mojo-element="ToggleButton" id="toggleEmojis" title="ShowEmojis"></div>
<div class="title" style="margin-left: -4px; float:left;"><span class="label">Discord Emojis</span></div>
</div>
</div>
<div class="palm-row last">
<div class="palm-row-wrapper">
<span x-mojo-loc="" id="SoundOptionLabel" width="100%" class="label truncating-text" style="float:left; min-width:120px; margin-left: 14px; margin-top: 14px;">Alert Sound</span>
<div x-mojo-element="ListSelector" id="listAlertSound" class="listselectorClass" style="float:right" name="listAlertSound"></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>
@@ -1,21 +0,0 @@
<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>
</div>
-12
View File
@@ -1,12 +0,0 @@
{
"id": "com.jonandnic.simplechat.app",
"version": "2.0.0",
"vendor": "Jon Wise",
"type": "web",
"main": "index.html",
"noWindow": true,
"title": "SimpleChat",
"icon": "icon.png",
"uiRevision": "2",
"theme": "light"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

File diff suppressed because it is too large. Load diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Loaded 100 of 118 files, more files were not shown because too many files have changed in this diff. Show more