Code Snippets
From WebOS101
Introduction
These snippets of code provide some useful functionality that you can copy directly into your applications. When adding snippets please group them by functional area. If relevant to a Palm API, Service or Widget feel free to add a link from the page that discusses it. Try to make sure snippets are general in nature and replace any application specific naming with generic naming (e.g. MatchemMenu -> AppMenu). For instructions on using the <code> tag visit Help:Editing.
Snippets - Appearance
Style the app menu (In this case, remove capitalization):
#palm-app-menu {
text-transform: none;
}
Change Text of class="palm-group-title" by adding a div id="id-name"
<div class="palm-group-title" x-mojo-loc='' id="id-name">enter search text</div>
this.controller.get('id-name').update('hello webos');
Get the actual screen dimensions (width and height) to adjust positions for Pré or Pixi:
You can use the window object to find out the current height:
var width = this.controller.window.innerWidth;
var height = this.controller.window.innerHeight;
But it's probably safer to use Mojo.Environment, which will likely continue to do the right thing on new platforms (like the hoped-for HP webOS tablet):
var width = Mojo.Environment.DeviceInfo.maximumCardWidth;
var height = Mojo.Environment.DeviceInfo.maximumCardHeight;
NOTE: The environment variables do NOT change so if you want to get the dynamic height of a scene, use the window object.
Adding icons to your submenu
You can add icons to your menus by including the "icon" or "secondaryIcon" to your items.
// menu grouping at bottom of scene
this.cmdMenuModel = { label: $L('Menu Demo'),
items: [
{label: $L('Resources'), icon: "increase", submenu:'resources-menu'},{},{},{},]};
this.resourcesMenuModel = { label: $L('Resources'), items: [
{icon: "reminder", secondaryIcon: "reminder", label: $L('Set Reminder'), command: "doCalendar"},
{label: $L('Armortization Table'),secondaryIcon:'table', command: "amortButtonPress"},
{label: $L('Increase Amount'), secondaryIcon: "increase", command: "incrButtonPress"},
{label: $L('Email'), secondaryIcon: "send", command:'email' }
]}
this.controller.setupWidget(Mojo.Menu.commandMenu, {spacerHeight: 0, menuClass: 'no-fade'}, this.cmdMenuModel);
this.controller.setupWidget('resources-menu', undefined, this.resourcesMenuModel);
Adding icons to your ListSelector
You can even add icons to a ListSelector. In the closed list the icon isn't drawn. You could add a label, which is drawn then.
in your css file define your icons:
.one {
background: url(../images/player_right_1.png) no-repeat center center;
}
.two {
background: url(../images/player_right_2.png) no-repeat center center;
}
.three {
background: url(../images/player_right_3.png) no-repeat center center;
}
in your assistant setup your widget:
this.controller.setupWidget("playerColor",
this.attributes = {
label: "Player",
choices: [
{label: "red", icon: "one", value: 1},
{label: "blue", icon: "two", value: 2},
{label: "mauve", icon: "three", value: 3}
]},
this.model = {
value: 3,
disabled: false
}
);
Alternately, you can use iconPath instead and specify the path to the icon. (Note: Move this to ListSelector page)
Setting the wallpaper
To set an image as the device's wallpaper, use the System service to import the image as a wallpaper object, then use the setPreferences method to actually set the wallpaper.
Import an image as a wallpaper object via the System service. On success, will return an object containing a success boolean and a wallpaper object. You will use this wallpaper object to set the device wallpaper.
You can use any image your app has access to locally, so if you want to set a wallpaper from a web image, for instance, you'd need to download it first, and then import that local file.
You can use file:// or a path relative to your app (as seen below).
MainAssistant.prototype.wallImport = function() {
this.controller.serviceRequest('palm://com.palm.systemservice/wallpaper',
{
method:"importWallpaper",
parameters:{"target": Mojo.appPath + "images/wallpaper.png"},
onSuccess: this.wallSet.bind(this),
onFailure: this.wallImportFailure.bind(this)
}
);
}
Now that you have the wallpaper object, it will be passed to this.wallSet(). Use this object to set the wallpaper via the System service's setPreferences method.
MainAssistant.prototype.wallSet = function(returnObj) {
var wallpaperObj = returnObj.wallpaper;
this.controller.serviceRequest('palm://com.palm.systemservice',
{
method:"setPreferences",
parameters:{"wallpaper":wallpaperObj},
onSuccess: this.wallSetSuccess.bind(this),
onFailure: this.wallSetFailure.bind(this)
}
);
}
Make your scene full-screen (clock, service, app menu go away, notifications do not):
MyViewAssistant.prototype = {
setup: function {},
activate: function(event) {
// this is the important bit:
this.controller.enableFullScreenMode(true);
}
// ... rest of your prototype methods follow
};
Note: Do not place enableFullScreenMode in your setup function as it can cause a gap to be left at the bottom of the screen that displays parts of index.html!
Adding a custom splash screen
With 1.4 you will see a instant opening of the application card in the background which might have a splash screen and a separate icon on it. How is it done? Add these 2 lines to your appinfo.json:
"splashicon": "icon-256×256.png",
"splashBackground":"images/splash-screenshot-default.png",
source: [1]
Adding a custom background
It's easy to add a custom background image to your app. And, you can even override the background on a scene-by-scene basis. The code below uses 'background.jpeg' for all scenes except the 'splash' scene.
body.palm-default {
background: url('../images/background.jpeg') top left no-repeat;
z-index: -1;
position: fixed;
top: 0px;
left: 0px;
width: 320px;
height: 480px;
}
#mojo-scene-splash-scene-scroller {
background: url('../images/splash.jpeg') top left no-repeat;
}
Change an application's icon based on today's date
To change application's icon based on today's date, you'll need 31 different icons. A call to the applicationManager serviceRequest will update your applications icon.
MyAssistant.prototype.updateIcon = function() {
var myDate = new Date(); //get today's date and extract the day (1-31)
var iconString = myDate.getDate() + '-64x64.png'; //requires 31 icons named 1-64x64 thru 31-64x4.png
var params =
{
method: 'updateLaunchPointIcon',
parameters:
{
launchPointId: 'com.my.app_default', //changes default icon for com.my.app
icon: 'icons/' + iconString //icons are stored in app/icon directory
}
};
new Mojo.Service.Request( 'palm://com.palm.applicationManager', params );
}
Creating a Dark Themed App
Here is the sure-fire way to make your app palm-dark. This is a two-step process:
- First, edit appinfo.json:
// File: appinfo.json
{
...,
"theme":"dark",
...
}
When this is set the light CSS styles are not loaded, improving the load-time for your app. You can also set "theme" to "light" if you're not using the dark theme. Alternatively you can just remove the theme line altogether. It doesn't make a very noticeable difference in the load-times either way. appinfo.json - Palm Developer Center
- Next, change index.html to set the body css class to "palm-dark". Feel free to add this line just above the </html> line if it there is not yet a body tag in this file. Like so:
<!-- File: index.html -->
<html>
...
<body class="palm-dark"></body>
</html>
Alternatively, you can add this to a scene or stage assistant. This is handy if you want to give the user the option of which theme they would like to see.
$$('body')[0].addClassName('palm-dark');
//or
this.controller.document.getElementsByTagName("body")[0].addClassName('palm-dark');
That's all there is to it. One quirk that I know of so far (there may be others) is that in order to get palm-page-header to show up dark you must set it to multi-line using:
<div class="palm-page-header multi-line">
<div class="palm-page-header-wrapper">
<div class="title left">
My Dark App
</div>
</div>
</div>
Override styles for individual dialogs and per scene
The following CSS will set different styles for the different dialogs available in webOS. (TO DO: link .showErrorDialog(), .showAlertDialog(), and .showDialog). This example assumes a scene named main. If you want, you can leave it out, but it will override all instances in all scenes.
.main-scene [x-mojo-element=_AlertDialog] { /* Mojo.Controller.showAlertDialog() */
/*overrides*/
}
.main-scene [x-mojo-element=_Dialog] { /* Mojo.Controller.showDialog() */
/*overrides*/
}
(TO DO: check what the x-mojo-element is for error dialogs)
Add Button Press Appearance to a Div
The following combination of HTML and CSS will create a div with a background that changes when the user presses on the div. You will have to create a graphic that has the two states, vertically stacked. You could use two images and change the background in .selected instead.
<div x-mojo-touch-feedback="spontaneous" id="buttonRegister"></div>
#buttonRegister,
#buttonRegister.selected {
background-image: url('../images/button_register.png');
height:50px;
width:107px;
float:left;
}
#buttonRegister.selected {
background-position: bottom;
}
Snippets - Behavior
Focus a TextField from a Button press:
MyAssistant.prototype.buttonClickHandler = function (event) {
this.controller.get("TextField").mojo.focus();
Event.stop(event);
}
Set initial focused element or prevent any element from getting focus:
this.controller.setInitialFocusedElement(null);
setInitialFocusedElement accepts null, an element ID or an element.
Show all properties of an object:
Object.toJSON(error);
Copying Text to the Clipboard
this.controller.stageController.setClipboard('hello webos',true);
Disable default scroll behavior of a scene
this.controller.stageController.pushScene({name:"YourDiv", disableSceneScroller: true});
Restricting a TextField to only numbers
During setupWidget() you can set a function to determine which characters are allowed in a TextField. In the example below, the TextField is also set to have the numLock turned on.
this.controller.setupWidget( 'myTextField',
{
hintText: $L('Enter a number'),
modifierState: Mojo.Widget.numLock,
autoFocus: true,
charsAllow: this.onlyNum.bind(this)
},
this.myTextFieldModel = {
value: ''
}
);
This function will allow only the '.' and characters '0-9' to be entered into the TextField.
MyAssistant.prototype.onlyNum = function(charCode) {
if (charCode == 46 || (charCode > 47 && charCode < 58)) return true;
return false;
}
Open up the App Catalog directly to your app:
-Don't forget that this has to be tested on the device! There is no catalog on the emulator.
This is how Paratrooper Mini links to the "premium" version of Paratrooper with the Upgrade menu button:
StageAssistant.prototype.buyGame = function (callback) {
var currentScene = Mojo.Controller.stageController.activeScene();
var launchParams = {
id: "com.palm.app.findapps",
params: {'target': "http://developer.palm.com/appredirect/?packageid=com.185vfx.paratrooper&applicationid=765"}
};
currentScene.serviceRequest('palm://com.palm.applicationManager',
{
method: 'open',
parameters: launchParams
});
if (callback) {
callback();
}
};
Another method of opening the App Catalog directly from your app
new Mojo.Service.Request('palm://com.palm.applicationManager', {
method: "open",
parameters: {
target: 'http://developer.palm.com/appredirect/?packageid=your.app.id'
}
});
Sense a press on the gesture area (meta tap):
When the gesture area is tapped it will trigger a keydown event and the metaKey property of the event will be set to true.
// You can also use just 'document' as the target is you have a single stage application. Otherwise you must set it to the correct stage's document as shown here
// You can also get the StageController like this: Mojo.Controller.getAppController().getStageController('NameOfStage');
Mojo.Event.listen(this.controller.stageController.document, 'keydown', MyFunction.bindAsEventListener(this));
function myFunction (event) {
// You can also check the keyCode instead. Ex: event.keyCode === Mojo.Char.metaKey
if (event.metaKey === true) {
// Do stuff when the meta key (gesture area) is tapped or held
}
}
Posting a status to Facebook
this.controller.serviceRequest("palm://com.palm.applicationManager", {
method: 'launch',
parameters: {
id: 'com.palm.app.facebook',
params: { status: "Check this out: " + this.storyFeed.stories[this.storyIndex].title +
" - " + this.storyFeed.stories[this.storyIndex].url }
}
});
Source: PDN Forum Post
Show a scene only at first run of an app
// Get Cookie
myCookie = new Mojo.Model.Cookie('MyCookie');
cookieData = myCookie.get() || '';
if (cookieData == '') {
this.controller.pushScene("first");
myCookie.put({ firstuse: true });
} else {
this.controller.pushScene("main");
}
Checking data connectivity
Here's a function that checks the data connectivity for any given webOS device. The function utilizes the Connection Manager service of the webOS SDK.
function checkConnectivity (c) {
var callback = c ? c.bind(this) : {};
var self = this;
this.controller.serviceRequest('palm://com.palm.connectionmanager', {
method: 'getstatus',
parameters: {},
onSuccess: function (response) {
var wifi = response.wifi;
var wan = response.wan;
if (wifi.state === "connected" | wan.state === "connected") {
callback();
} else {
self.controller.showAlertDialog({
onChoose: function(value) {},
title: $L("No Internet Connection"),
message: $L("You need an Internet connection to back up your data."),
choices:[
{label:$L('OK'), value:"ok", type:'dismiss'}
]
});
}
},
onFailure: function(response) {
// Handle failure here...
Mojo.Log.error(response.errorText);
}
});
}
Snippets - Other
Changing a Widget Model:
To change properties in a model such as the items in a List widget, use the modelChanged function. In this example, we will change the label of a Button.
this.buttonModel.label = 'New Label';
this.controller.modelChanged(this.buttonModel);
Be sure that you update the same model object which was originally assigned to that widget in the setupWidget method.
Send Device Information to a Web Service
Send Device Unique ID and Application Version
this.controller.serviceRequest('palm://com.palm.preferences/systemProperties', {
method:"Get",
parameters:{"key": "com.palm.properties.nduid" },
onSuccess: function(response){
var request = new Ajax.Request("http://www.yourserver.com/your-page.php?DeviceID="+response
['com.palm.properties.nduid']+"&Version="+Mojo.Controller.appInfo.version, {
method: 'get',
onSuccess: function(){},
onFailure: function(){}
});
}.bind(this)
});
Creating a copy of a two-dimensional array
Creating a copy of an array object is a simple task. The slice() method will return a duplicate copy of the array, for example:
var myArray = [0, 1, 2, 3, 4]
var myReference = myArray;
var myCopy = myArray.slice();
This is an interesting situation. myArray is an array with 5 elements (0-4). myReference is a reference to that array. A reference is just that. It refers to the memory that the original array occupies. In this way, any changes made to the values in either myArray or myReference are reflected to each other. They are locked as duplicates - the same array object with two names. This can cause havoc when you intend to make a copy of an array so that you can preserve it's elements' values. myCopy is an exact duplicate of myArray, but it occupies it's own chunk of memory and is completely separate from the original. Changes made in either array will not affect the other.
A two-dimensional array is generally created in this manner:
var myArray = new Array(5); // the Array constructor accepts an argument for the length of the array and like the Array.length property, it is not zero-based.
//initialize our two-dimensional array by assigning a new array to each element in our myArray object.
for (i = 0; i <= 4; i++) {
myArray[i] = Array();
}
//initialize the grid values of our two-dimensional array
for (x = 0; x <= 4; x++) {
for (y = 0; y <= 4; y++) {
myArray[x][y] = x * y;
count++;
}
}
This code creates a virtual times table with 25 total multiplication values. Now if you want a copy of that array (rather than a reference) you will run into a problem. Using the slice method will return a copy of an array which is storing references to other arrays, and you end up with what another mirror affect. A single Object with two names... again... Although the references to the second-dimensional arrays are stored in separate chunks of memory they do point to the same chunks of memory that are holding the values in the original two-dimensional array. The solution is very simple.
You need to call slice() for every element in the original array. To make it convenient, we can even prototype the method to our Array object:
Array.prototype.copy2DimArray = function() {
var newArray = new Array(this.length - 1);
for (i = 0; i < this.length; i++) {
newArray[i] = this[i].slice();
}
return arr;
}
And now getting a new copy is simple:
var myNewArray = myArray.copy2DimArray();
This yields a copy, not a reference of our two-dimensional array.

