Collapse all descriptions
|
AppMenu
Example usage:
StageAssistant.prototype.setup = function() {
// Setup Application Menu with an About entry
//
newsMenuAttr = {omitDefaultItems: true};
newsMenuModel = {
visible: true,
items: [
{label: "About News...", command: 'do-aboutNews'},
Mojo.Menu.editItem,
Mojo.Menu.prefsItem,
Mojo.Menu.helpItem
]
};
this.controller.pushScene("feedList");
};
// handleCommand - Setup handlers for menus:
//
StageAssistant.prototype.handleCommand = function(event) {
var currentScene = this.controller.activeScene();
if(event.type == Mojo.Event.command) {
switch(event.command) {
case 'do-aboutNews':
currentScene.showAlertDialog({
onChoose: function(value) {},
title: "News - v1.0",
message: "Copyright 2008-2009, Palm Inc.",
choices:[
{label: "OK", value:""}
]
});
break;
}
}
};
To instantiate the menu in each scene, each scene assistant must then call:
// Setup Application Menu
this.controller.setupWidget(Mojo.Menu.appMenu, newsMenuAttr, newsMenuModel);
This code generates the following menu and dialog (when the user selects "About News..." from the menu):


|
4731 |
Button
Example:
In scene:
<div x-mojo-element="Button" id="previousStory"></div>
<div x-mojo-element="Button" id="nextStory"></div>
In scene assistant:
StoryViewAssistant.prototype.setup = function() {
// Hide Previous Button if first story, and Next Button if last one
if (this.storyIndex > 0) {
this.controller.setupWidget("previousStory",
this.attributes = {
disabledProperty: 'disabled'
},
this.model = {
buttonLabel : "Previous",
buttonClass: '',
disabled: false
});
this.controller.listen('previousStory', Mojo.Event.tap,
this.previousStory.bindAsEventListener(this));
} else {
$('previousStory').hide();
}
if (this.storyIndex < this.storyFeed.stories.length-1) {
this.controller.setupWidget("nextStory",
this.attributes = {
disabledProperty: 'disabled'
},
this.model = {
buttonLabel : "Next",
buttonClass: '',
disabled: false
});
this.controller.listen('nextStory', Mojo.Event.tap,
this.nextStory.bindAsEventListener(this));
} else {
$('nextStory').hide();
}
};
Screenshot (not representative of example above):

Note that the default visual styles can be overridden using CSS.
|
3125 |
CheckBox
Screenshot:

Note that the default visual styles can be overridden using CSS.
|
2054 |
CommandMenu
Example usage:
// StoryViewAssistant(storyFeed, storyIndex)
//
// Passed a story element, displays that element in a
// full scene view and offers options for next story (right
// command menu button), previous story (left command menu button)
// and to launch story URL in the browser (view menu).
function StoryViewAssistant(storyFeed, storyIndex) {
// Save the passed arguments for use in the scene.
//
this.storyFeed = storyFeed;
this.storyIndex = storyIndex;
}
StoryViewAssistant.prototype.setup = function() {
// Setup command menu for next and previous story as a menu group.
// If first story, suppress Previous menu; if last story, suppress Next menu
this.storyMenuAttr = {items: [{visable: false}, {items: []}, {visable: false}]};
if (this.storyIndex > 0) {
this.storyMenuAttr.items[1].items.push({icon: "back", command: 'do-viewPrevious'});
} else {
this.storyMenuAttr.items[1].items.push({icon: "", command: '', label: " "});
}
if (this.storyIndex < this.storyFeed.stories.length-1) {
this.storyMenuAttr.items[1].items.push({icon: "forward", command: 'do-viewNext'});
} else {
this.storyMenuAttr.items[1].items.push({icon: "", command: '', label: " "});
}
this.controller.setupWidget(Mojo.Menu.commandMenu, undefined, this.storyMenuAttr);
To handle the commands generated by the menu, set up a handleCommand() function:
// Handlers to go to next and previous stories
StoryViewAssistant.prototype.handleCommand = function(event) {
if(event.type == Mojo.Event.command) {
switch(event.command) {
case 'do-viewNext':
Mojo.Controller.stageController.swapScene("storyView",
this.storyFeed, this.storyIndex+1);
break;
case 'do-viewPrevious':
Mojo.Controller.stageController.swapScene("storyView", this.storyFeed,
this.storyIndex-1);
break;
}
}
};
The generated menu looks like this:

|
3976 |
DatePicker
Example usage:
Scene view file:
<div class="palm-group unlabeled">
<div class="palm-list">
<div id="DatePkrId" x-mojo-element="DatePicker"></div>
</div>
</div>
Scene assistant:
No info yet.
|
1838 |
|
Dialog*
|
1549 |
Drawer
Example:
Scene:
<!-- Adding text field within a drawer and group box -->
<div id='feedDrawer' x-mojo-element="Drawer">
<div class="palm-group">
<div class="palm-group-title">
<!-- Title and error status for invalid feeds -->
<span id="add-feed-title" x-mojo-loc="">Add News Feed Source</span>
</div>
<div class="palm-group unlabeled">
<div class="palm-list">
<div class='palm-row first'>
<div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div class="label">URL</div>
<div id="newFeedURL" x-mojo-element="TextField" align="left"></div>
</div>
</div>
</div>
<div class='palm-row last'>
<div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div class="label">Title</div>
<div id="newFeedName" x-mojo-element="TextField" align="left"></div>
</div>
</div>
</div>
</div>
</div>
<div x-mojo-element="Button" id="okButton"></div>
</div>
</div>
Scene assistant:
// Setup Drawer for add Feed; closed to start
//
this.controller.setupWidget('feedDrawer', {property:'myOpen
Property'}, this.feedDrawerModel={myOpenProperty:false});
// Setup text field for the new feed's URL
//
this.controller.setupWidget(
"newFeedURL",
this.urlAttributes = {
property: "value",
hintText: "RSS or ATOM feed",
focus: true,
limitResize: true,
textReplacement: false,
enterSubmits: false
},
this.urlModel = {value : ""});
// Setup text field for the new feed's name
//
this.controller.setupWidget(
"newFeedName",
this.nameAttributes = {
property: "value",
hintText: "Optional",
limitResize: true,
textReplacement: false,
enterSubmits: false
},
this.nameModel = {value : ""});
// Setup button and event handler
//
this.controller.setupWidget("okButton",
this.attributes = {
},
this.model = {
buttonLabel: "Add Feed",
buttonClass: "addFeedButton",
disabled: false
});
this.controller.listen('okButton', Mojo.Event.tap, this.checkIt.
bindAsEventListener(this));
|
2941 |
ErrorDialog
Example usage:
// feedRequestFailure
//
// Callback routine from a failed AJAX feed request (feedRequest);
// post a simple failure error message with the http status code.
//
FeedListAssistant.prototype.feedRequestFailure = function(transport) {
// Use the Prototype template object to generate a string from the return status.
//
var t = new Template("Status #{status} returned from newsfeed request.");
var m = t.evaluate(transport);
// Log error & post dialog
//
Mojo.Log.info("Invalid feed - http failure (", m");
Mojo.Controller.errorDialog("Invalid feed - http failure ("+m);
};
This dialog displays the Ajax error code appended to a static error message. Note that there does not need to be an x-mojo-element div in the scene's HTML view file to use an error dialog widget.
Screenshot:

|
3443 |
|
File*
|
1381 |
FilterList
Example usage:
In scene view file:
<div id="searchFieldContainer"> <div x-mojo-element="FilterList" id="startSearchField"> </div>
</div> <div id="feedListMain"> <!-- Rotating Feature Story --> . . .
In the scene assistant, the widget is configured by passing in the attributes of the list and specifying the filterFunction:
this.controller.setupWidget("startSearchField", this.searchFieldAttr = { itemTemplate: 'storyList/storyRowTemplate', listTemplate: 'storyList/storyListTemplate', filterFunction: this.searchList.bind(this), renderLimit: 70, delay: 350 }, this.searchFieldModel = { disabled: false }); this.controller.listen('startSearchField', Mojo.Event.listTap,
this.viewSearchStory.bindAsEventListener(this)); this.controller.listen('startSearchField', Mojo.Event.filter, this.searchFilter.bind(this), true);
This function hides the "regular" list when the user starts typing into the filterList:
// searchFilter (FeedListAssistant) - triggered by entry into search field. // First entry will hide the main feedList scene and clearing the entry // will restore the scene. // FeedListAssistant.prototype.searchFilter = function(event) { if (event.filterString !== "") { $("feedListMain").hide(); } else { $("feedListMain").show(); } };
The search function:
// searchList - filter function called from search field widget to update the // results list. This function will build results list by matching the // filterstring to the story titles and text content, and then return the subset // of the list that corresponds to the offset and size requested by the widget. // FeedListAssistant.prototype.searchList = function(filterString, listWidget, offset, count) { var subset = []; // Ultimately the results list var totalSubsetSize = 0; // Used to set counter in Filter Field // If search string is null, then return empty list, otherwise build results list if (filterString !== "") { // Search database for stories with the search string // and push on to the items array // var items = []; // Comparison function for matching strings in next for loop // var hasString = function(query, s) { if(s.text.toUpperCase().indexOf(query.toUpperCase())>=0) { return true; } if(s.title.toUpperCase().indexOf(query.toUpperCase())>=0) { return true; } return false; }; for (var i=0; i
This function defines what happens when a user taps on a story in the resulting list:
// viewSearchStory - triggered by tapping on an entry in the search results // list. Will push the storyView scene with the searchList // FeedListAssistant.prototype.viewSearchStory = function(event) { var searchList = {title: "Search for: "+this.filter, stories: this.entireList}; var storyIndex = this.entireList.indexOf(event.item); Mojo.Controller.stageController.pushScene("storyView", searchList, storyIndex); };
 
|
3641 |
GridList
Screenshot:

Note that the default visual styles can be overridden using CSS.
|
2075 |
|
ImageView*
|
1819 |
IntegerPicker
Example usage:
Scene view file:
<div id="featureFeedDelay" x-mojo-element="IntegerPicker"></div>
Scene assistant:
var featureDelayAttr = {
label: 'Interval',
modelProperty: 'value',
min: 1,
max: 60
};
this.featureDelayModel = {
value : featureStoryInterval/1000
};
this.controller.setupWidget('featureFeedDelay', featureDelayAttr, this.featureDelayModel);
|
2588 |
List
Example:
In scene:
<div id="storyListWgt" x-mojo-element="List"></div>
In listTemplate file (optional; defines HTML template for list's container; if omitted, list items are added to scene with no container):
<div class="palm-list">#{listElements}</div>
In itemTemplate file (required; defines HTML template for list items):
<div class="palm-row" x-mojo-tap-highlight="momentary">
<div id="storyTitle" class="listTitle truncating-text #{unReadStyle}">#{title}</div>
<div id="storyText" class="listText truncating-text">#{text}</div>
</div>
In scene assistant:
StoryListAssistant.prototype.setup = function() {
// Setup story list with standard news list templates.
//
this.controller.setupWidget("storyListWgt",
this.storyAttr = {
itemTemplate: "storyList/storyRowTemplate",
listTemplate: "storyList/storyListTemplate",
swipeToDelete: false,
renderLimit: 40,
reorderable: false
},
this.storyModel = {
items: this.feed.stories
}
);
this.controller.listen("storyListWgt", Mojo.Event.listTap,
this.readStory.bindAsEventListener(this));
};
|
4222 |
ListSelector
Screenshot:

Note that the default visual styles can be overridden using CSS.
|
2683 |
PasswordField
Screenshot:

|
1284 |
ProgressBar
Example usage:
In the scene's view file:
<div x-mojo-element="ProgressBar" class="loadProgressBar" name="loadProgressBar"></div>
In the scene assistant:
this.lgProgressBarModel = {
progress: 0;
}
this.controller.setupWidget ('loadProgressBar',this.lgProgressBarModel);
To update the progress bar:
if (this.progressCounter > 1) {
this.completeProgress();
}
else {
this.lgProgressBarModel.progress = this.lgProgressBarModel.progress + 0.2;
this.controller.modelChanged(this.lgProgressBarModel);
}
|
2294 |
ProgressPill
Example usage:
In the scene's view file:
<div x-mojo-element="ProgressPill" class="loadProgressPill" name="loadProgressPill"></div>
In the scene assistant:
this.lgProgressPillModel = {
progress: 0;
}
this.controller.setupWidget ('loadProgressPill',this.lgProgressPillModel);
To update the progress pill:
if (this.progressCounter > 1) {
this.completeProgress();
}
else {
this.lgProgressPillModel.progress = this.lgProgressPillModel.progress + 0.2;
this.controller.modelChanged(this.lgProgressPillModel);
}
|
2292 |
|
ProgressSlider*
|
1260 |
RadioButton
Screenshot:

Note that the default visual styles can be overridden using CSS.
|
1847 |
RichTextEdit
Screenshot:

|
1501 |
Scroller
Example usage:
In the scene's view file:
<div x-mojo-element="Scroller" id="featureScroller" class="featureScroller">
<div id="featureStoryDiv">
<div id="featureStoryTitle" class="featureTitle">#{title}</div>
<div id="featureStory" class="featureSummary">#{text}</div>
</div>
</div>
Everything inside the scroller div becomes the scrollable content.
The widget is instantiated in the scene assistant:
this.controller.setupWidget('featureScroller', this.featureScrollerModel);
Care must be taken to set up the proper CSS for the scroller, setting its height explicitly (or width, if it's a horizontal scroller) so that it the scroller div does not automatically expand to fit its contents and consequently not scroll at all.
.featureScroller {
height: 100px;
margin-bottom: 10px;
}
Here's how this example might look (with additional supporting code):

|
4613 |
ShowDialog() (Custom Dialog)
Example usage:
// addNewFeed - triggered by "Add..." item in feed list and invokes the AddDialog
// Assistant defined above.
//
FeedListAssistant.prototype.addNewFeed = function() {
this.controller.showDialog({
template: 'feedList/addFeed-dialog',
assistant: new AddDialogAssistant(this)
});
};
A custom dialog uses a template (HTML file) and an assistant (JavaScript file), similar to a scene. The HTML file defines what will be displayed in the custom dialog and the JavaScript file defines how the elements will behave.
Template file:
<div>
<span id="add-feed-title" class="palm-dialog-title">Add News Feed Source</span>
<div class="palm-group unlabeled">
<div class="palm-list">
<div class='palm-row first'>
<div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div class="label">URL</div>
<div id="newFeedURL" x-mojo-element="TextField" align="left"></div>
</div>
</div>
</div>
<div class='palm-row last'>
<div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
<div class="title">
<div class="label">Title</div>
<div id="newFeedName" x-mojo-element="TextField" align="left"></div>
</div>
</div>
</div>
</div>
</div>
<div x-mojo-element="Button" id="okButton"></div>
</div>
Here is the assistant for the dialog, which sets up the various widgets as well as the event handlers that will respond to user interaction with the widgets.
Assistant file:
// AddDialogAssistant - simple controller for adding new feeds to the list.
// Invoked by the FeedListAssistant when the "Add..." list item is selected
// on the feed list.
//
// The dialog displays two text fields (URL and Name) and an OK button.
// Either the user enters a feed URL and a name, followed by OK or a
// back swipe to close. If OK, the feed header is checked through an Ajax
// request and if valid, the feed updated and dialog closed. If an error,
// the posted in place of title and dialog remains open.
// Swipe back cancels and returns back to the FeedListAssistant.
//
function AddDialogAssistant(sceneAssistant) {
this.sceneAssistant = sceneAssistant;
}
AddDialogAssistant.prototype.setup = function(widget) {
this.widget = widget;
// Setup text field for the new feed's URL
//
this.sceneAssistant.controller.setupWidget("newFeedURL",
this.urlAttributes = {
property: "value",
hintText: "RSS or ATOM feed",
focus: true,
limitResize: true,
textReplacement: false,
enterSubmits: false
},
this.urlModel = {value : ""}
);
// Setup text field for the new feed's name
//
this.sceneAssistant.controller.setupWidget("newFeedName",
this.nameAttributes = {
property: "value",
hintText: "Optional",
limitResize: true,
textReplacement: false,
enterSubmits: false
},
this.nameModel = {value : ""
});
// Setup button and event handler
//
this.sceneAssistant.controller.setupWidget("okButton",
this.attributes = {},
this.model = {
buttonLabel: "Add Feed",
buttonClass: "addFeedButton",
disabled: false
}
);
Mojo.Event.listen($('okButton'), Mojo.Event.tap, this.checkIt.bindAsEventListener(this));
};
// ------------------------------------------------------------------------
// Add Feed Functions
//
// checkIt - called when Add feed OK button is clicked
//
AddDialogAssistant.prototype.checkIt = function() {
// Check for 'http://' on front or other legal prefix
// assume any string of 1 to 5 alpha characters followed
// by ':' is legal, otherwise prepend "http://"
//
var url = this.urlModel.value;
if (/^[a-z]{1,5}:/.test(url) === false) {
url = url.replace(/^\/{1,2}/,"");
url = "http://"+url;
}
// Update the submitted URL text model
this.urlModel.value = url;
this.sceneAssistant.controller.modelChanged(this.urlModel);
var request = new Ajax.Request(url, {
method: 'get',
evalJSON: 'false',
onSuccess: this.checkOK.bind(this),
onFailure: this.checkFailure.bind(this)
});
};
//
// checkOK - called when the Ajax request is successful.
//
AddDialogAssistant.prototype.checkOK = function(transport) {
// DEBUG - log the result
//
var t = new Template("Status #{status} returned from newsfeed request.");
var success = "Feed Request Success: "+t.evaluate(transport);
Mojo.Log.info(success);
// Work around when XML comes back in text response
if (transport.responseXML === null && transport.responseText !== null) {
Mojo.Log.info("••••••••","Request not in XML format - manually converting");
transport.responseXML = new DOMParser().parseFromString (transport.responseText, 'text/xml');
}
var feedError = errorNone;
// Push the entered feed onto feedlist and call processFeed to evaluate it.
// This is mainly to record the user's TITLE entry before processing
//
feedList.push({title:this.nameModel.value, url:this.urlModel.value, type:"", acFreq:0, numUnRead:0, stories:[]});
feedError = ProcessFeed(transport);
// If successful processFeed returns errorNone
//
if (feedError === errorNone) {
this.sceneAssistant.feedWgtModel.items = feedList;
this.sceneAssistant.controller.modelChanged(this.sceneAssistant.feedWgtModel);
this.widget.mojo.close();
}
else {
feedList.pop();
if (feedError == errorUnsupportedFeedType) {
Mojo.Log.info("Feed ", this.urlModel.value," isn't a recognized feed type (#", errorUnsupportedFeedType, ")");
$("add-feed-title").innerHTML ="Invalid Feed - not a supported feed type";
}
}
};
AddDialogAssistant.prototype.checkFailure = function(transport) {
// Use the Prototype template object to generate a string from the return
//
var t = new Template($L("#{status}"));
var m = t.evaluate(transport);
// Log error and put message in status
//
Mojo.Log.info("Invalid feed (Status ", m, " returned).");
$("add-feed-title").innerHTML = "Invalid feed - http failure ("+m+")";
};
This example is from the book Palm webOS by Mitch Allen.
|
6913 |
Slider
Screenshot:

Note that the default visual styles can be overridden using CSS.
|
1695 |
Spinner
Example usage:
<div x-mojo-element="Spinner" class='feedSpinner' name='feedSpinner'></div>
And in the scene assistant:
this.spinnerModel= {
value: false
};
this.controller.setupWidget('feedSpinner', this.spinnerModel);
To turn on the widget:
this.spinnerModel.value = true;
this.controller.modelChanged(spinnerModel, this);
|
3199 |
SubMenu
Example usage:
// showFeed - triggered by tapping a feed in the feedList.
// Detects taps on the unReadCount icon; anywhere else,
// the scene for the list view is pushed. If the icon is tapped,
// put up a submenu for the feedlist options
//
FeedListAssistant.prototype.showFeed = function(event) {
var target = event.originalEvent.target.className;
if (target !== "unReadCount") {
this.clearTimers();
Mojo.Controller.stageController.pushScene("storyList", event.index);
}
else {
this.popupIndex = event.index;
this.controller.popupSubmenu({
onChoose: this.popupHandler,
placeNear: event.target,
items: [
{label: 'All Unread', command: 'feed-unread'},
{label: 'All Read', command: 'feed-read'},
{label: 'Edit Feed', command: 'feed-edit'}]
});
}
};
// popupHandler - choose function for feedPopup
//
FeedListAssistant.prototype.popupHandler = function(command) {
var popupFeed=feedList[this.popupIndex];
switch(command) {
case 'feed-unread':
for (var i=0; i
This is how this menu appears:

|
3015 |
TextField
Screenshot:

|
3127 |
|
Time*
|
1317 |
ToggleButton
Example:
<div id="my-toggle" x-mojo-element="ToggleButton"></div>
this.toggle = { trueValue:'on', trueLabel:'On', falseValue:'off', falseLabel:'Off' };
this.toggleModel = { value:'on', disabled:false };
this.controller.setupWidget('my-toggle', this.toggle, this.toggleModel);
Screenshot (not representative of example above):

Note that the default visual styles can be overridden using CSS.
|
2068 |
ViewMenu
Example usage:
StoryListAssistant.prototype.setup = function() {
// Setup scene header with feed title and next/previous feed buttons
// If first feed, suppress Previous menu; if last feed, suppress Next menu
//
var feedMenuPrev = {};
var feedMenuNext = {};
if (this.feedIndex > 0) {
feedMenuPrev = {
icon: 'back',
command: 'do-feedPrevious'
};
}
else {
feedMenuPrev = {icon: "", command: '', label: " "};
}
if (this.feedIndex < feedList.length-1) {
feedMenuNext = {
iconPath: 'images/menu-icon-forward.png',
command: 'do-feedNext'
};
}
else {
feedMenuNext = {icon: "", command: '', label: " "};
}
// Define the model with next and previous menu items, and a title
// area for the feed title where the width is set to creating a
// style header
this.feedMenuModel =
{
visible: true,
items: [{
items: [
feedMenuPrev,
{ label: this.feed.title, width: 210 },
feedMenuNext
]
}]
};
// Setup the View Menu
this.controller.setupWidget(Mojo.Menu.viewMenu,
{ spacerHeight: 0, menuClass:'no-fade' },
this.feedMenuModel);
Here are the event handlers for the menu buttons:
// ----------------------------------------------------------------------------------
// Setup handlers for view menu
StoryListAssistant.prototype.handleCommand = function(event) {
if(event.type == Mojo.Event.command) {
switch(event.command) {
case 'do-feedNext':
Mojo.Controller.stageController.swapScene("storyList", this.feedIndex+1);
break;
case 'do-feedPrevious':
Mojo.Controller.stageController.swapScene("storyList", this.feedIndex-1);
break;
}
}
};
This code generates a menu that looks like this:

|
3220 |
WebView
Example usage:
In the scene view file:
<div id="storyWeb" x-mojo-element="WebView"></div>
The scene assistant:
// StoryWebAssistant(storyURL)
//
// Passed a URL and displays the corresponding story or link in a webview,
// handling any link selections within the view. User swipes back to
// return to the calling view.
//
// storyURL unescaped form of URL
function StoryWebAssistant(storyURL) {
// Save the passed URL for inclusion in the webView setup
this.storyURL = storyURL;
}
StoryWebAssistant.prototype.setup = function() {
// Setup up the WebView widget
this.controller.setupWidget('storyWeb', {
url: this.storyURL
},
this.storyViewModel = {
}
);
// Setup handlers for any links selected.
//
this.controller.listen('storyWeb', Mojo.Event.webViewLinkClicked,
this.linkClicked.bindAsEventListener(this));
// Setup App Menu
this.controller.setupWidget(Mojo.Menu.appMenu, newsMenuAttr, newsMenuModel);
};
To display the webview on click:
// Setup handlers for taps to the story body and links within the story
this.clickHandler = this.webStory.bindAsEventListener(this);
this.controller.listen("storyViewSummary", "click", this.clickHandler, true);
this.controller.listen("storyViewScene", Mojo.Event.tap, this.clickHandler);

|
4651 |
|