Companies using Dojo
Client/Server Model on the Web
Prior to the popularity of the web, client/server applications often involved the creation of native applications which were deployed to clients. In this model, developers had a great deal of freedom in determining which parts of the entire client/server application would be in the client and which in the server. Consequently, very mature models for client/server development emerged, and often well designed optimal distribution of processing and logic could be achieved. When the web took off, the client was no longer a viable application platform, it was really more of a document viewer. Consequently the user interface logic existed almost entirely on the server. However, the web has matured substantially and has proven itself to be a reasonable application platform. We can once again start utilizing more efficient and well-structured client/server model design. There are certainly still technical issues, but we are in a position to better to build true client/server applications now.
The client/server model can be categorized into three parts:
- User Interface
- Business or Application Logic
- Data Management
Traditional web application development has distributed the implementation of the user interface across the network, with much of the user interface logic and code executed on the server (thin client, fat server). This has several key problems:
- Poor distribution of processing - With a large number of clients, doing all the processing on the server is inefficient.
- High user response latency - Traditional web applications are not responsive enough. High quality user interaction is very sensitive to latency, and very fast response is essential.
- Difficult programming model - Programming a user interface across client/server is simply difficult. When every interaction with a user must involves a request/response, user interface design with this model is complicated and error prone. The vast number of web frameworks for simplifying web development testifies to this inherent difficulty. Some have mitigated this difficulty to some degree.
- Increased vector of attack - Unorganized mingling of user interface code with business code can increase security risks. If access rules are distributed across user interface code, as user interface code grows and evolves, new vectors of attack emerge. With mixed code, new user interface features can easily create new security holes.
- Heavy state management on the servers - When client user interface state information is maintained by the server, this requires a significant increase in resource utilization as server side sessions must be maintained with potentially large object structures within them. Usually these resources can’t be released until a session times out, which is often 30 minutes after a user actually leaves the web site. This can reduce performance and scalability.
- Offline Difficulties - Adding offline capabilities to a web application can be a tremendous project when user interface code is predominantly on the server. The user interface code must be ported to run on the client in offline situations.
- Reduced opportunity for interoperability - When client/server communication is composed of transferring internal parts of the user interface to the browser, it can be very difficult to understand this communication and utilize it for other applications.
With the massive move of development to the web, developers have frequently complained of the idiosyncrasies of different browsers and demanded more standards compliance. However, the new major shift in development is towards interconnectivity of different services and mashups. We will once again feel the pain of programming against differing APIs. However, we won’t have browser vendors to point our fingers at. This will be the fault of web developers for creating web applications with proprietary communication techniques.
Much of the Ajax movement has been related to the move of user interface code to the client. The maturing of the browser platform and the availability of HTTP client capabilities in the XMLHttpRequest object, have allowed much more comprehensive client side user interface implementations. However, with these new found capabilities, it is important to understand how to build client/server applications.
So how do you decide what code should run on the client and what should the run on the server? I have mentioned the problems with user interface code running on the server. Conversely, running business logic and/or data management on the client is simply not acceptable for security reasons. Therefore, quite simply, user interface code is best run on the browser, and application/business logic and data management is best run on the server side. We can take a valuable lesson from object oriented programming to guide this model. Good OO design involves creating objects that encapsulate most of their behavior and have a minimal surface area. It should be intuitive and easy to interact with a well designed object interface. Likewise, client and server interaction should be built on a well-designed interface. Designing for a modular reusable remote interface is often called service oriented architecture (SOA); data is communicated with a defined API, rather than incoherent chunks of user interface. A high quality client/server implementation should have a simple interface between the client and server. The client side user interface should encapsulate as much of the presentation and user interaction code as possible, and the server side code should encapsulate the security, behavior rules, and data interaction. Web applications should be a cleanly divided into two basic elements, the user interface and the web service, with a strong emphasis on minimal surface area between them.
An excellent litmus test for a good client/server model is how easy is it to create a new user interface for the same application. A well designed client/server model should have clearly defined web services such that a new user interface could easily be designed without having to modify server side application logic. A new client could easily connect to the web services and utilize them. Communication should be primarily composed of data, not portions of user interface. The advantages of a clean client/server model where user interface logic and code is delegated to the browser:
- Scalability - It is quite easy to observe the significant scalability advantage of client side processing. The more clients that use an application, the more client machines that are available, whereas the server processing capabilities remain constant (until you buy more servers).
- Immediate user response - Client side code can immediately react to user input, rather than waiting for network transfers.
- Organized programming model - The user interface is properly segmented from application business logic. Such a model provides a cleaner approach to security. When all requests go through user interface code, data can flow through various interfaces before security checks take place. This can make security analysis more complicated, with complex flows to analyze. On the other hand, with a clean web service interface, there is well-defined gateway for security to work on and security analysis is more straightforward, holes can be quickly found and corrected.
- Client side state management - Maintaining transient session state information on the client reduces the memory load on the server. This also allows clients to leverage more RESTful interaction which can further improve scalability and caching opportunities.
- Offline applications - If much of the code for an application is already built to run on the client, creating an offline version of the application will almost certainly be easier.
- Interoperability - By using structured data with minimal APIs for interaction, it is much easier to connect additional consumers and producers to interact with existing systems.
There are certainly difficulties with applying the client/server model to the web. Accessibility and search engine optimization can certainly be particular challenges with the web services approach. Handling these issues may suggest a hybrid approach to web applications, some user interface generation may be done on the server to create search engine accessible pages. However, having a central architectural approach based around a client/server model, with extensions for handling search engines may be a more solid and future oriented technique for many complex web applications.
Our Efforts to Facilitate the Client Server ModelSitePen is certainly not alone in working to facilitate client/server architecture. However, since I am familiar with the projects we help create, I did want to mention our approaches to the client/server model:
DWR - From inception, DWR has provided an excellent framework for building client side user interfaces that can easily connect with server side business logic. DWR was years ahead of its time in establishing a framework that encouraged good client/server modeling. DWR has a solid structure for interacting with Java business logic objects. DWR has continued to progress, providing means for bi-directional Comet based communication (Reverse Ajax), and is adding more interoperability capabilities as well.
Dojo Toolkit - It should be obvious that building a good client-side user interface can benefit from a good toolkit, and Dojo has long provided just that. However, Dojo is more than just a library and set of widgets. Dojo provides real tools for building client/server applications. Dojo RPC can provides tools for connecting to web services, and can even auto-generate services based on SMD definitions. Dojo Data provides a powerful API for interacting with a data model. Dojo has lead the way with Comet technology, creating standards around browser based two-way communication. Recently we have built the JsonRestStore which allows one to connect to a REST web service and interact with it using the Dojo Data read and write API. This greatly simplifies the construction of user interfaces by simplifying the user interface-business logic interaction, and encouraging standards-based communication that can easily be used by others. Furthermore, Dojo provides comprehensive tools for robust data-driven applications; even templating can be done on the client instead of the server with Dojo’s DTL support.
The benefits of using standards-based client/server communication has facilitated integration with server frameworks like Zend, jabsorb, Persevere, and interoperability with other frameworks will be coming soon.
Cometd - Cometd provides real-time duplex communication between clients and servers. However, the distinguishing characteristic of the Cometd project is the focus on not only achieving Comet-style duplex communication, but doing so with an interoperable standard protocol, Bayeux. Comet uses a quintessential client/server approach. Any Cometd (Bayeux implementing) server can interact with any Cometd/Bayeux client. One can easily connect various different client implementations to a single server, by using the Bayeux standard.
Persevere - Persevere is a recently launched project, built with this service oriented client/server approach. Persevere is a web object database and application server with RESTful HTTP/JSON interfaces, allowing applications to quickly be built with a database backend that can be directly and securely accessed through Ajax. Persevere is focused on provided a comprehensive set of web services interaction capabilities through standard interoperable communication. Data can be accessed and modified with basic RESTful JSON interaction, clients can invoke methods on the server with simple JSON-RPC, and data can be queried with JSONQuery/JSONPath. With Dojo’s new REST data store, and SMD driven RPC services, Dojo clients can seamlessly build applications and interact with Persevere services using the Dojo APIs. Complex application logic can be added to the persisted objects in Persevere to facilitate building service oriented applications with a straightforward interface to the user interface code on the browser. Persevere is integrated with Rhino, so model and application logic can be written in JavaScript, providing a consistent language and environment for distributing client and server roles.
For more information about any of these open source projects, visit the SitePen Labs
SummaryAs the web platform matures, as applications evolve to use more interactive and rich interfaces, and as web services increasingly interact, architecting web applications with an intelligent client/server model will become increasingly important. A properly designed client/server model will provide a foundation for modular, adaptable, and interoperable applications equipped for future growth.
JSONQuery: Data Querying Beyond JSONPath
A new data querying tool for has been added to Dojo 1.2. JSONQuery is a new module intended to succeed and improve upon the JSONPath module introduced in Dojo 1.1. JSONQuery provides a comprehensive set of data querying tools including filtering, recursive search, sorting, mapping, range selection, and flexible expressions with wildcard string comparisons and various operators.
JSONQuery provides safe evaluation with language agnostic expressions that prevents arbitrary code execution. It also uses intuitive result-based evaluation that allows successive query operations. Furthermore, the new JSONQuery module provides significant performance improvements, with 20-100x faster execution with the common filter operation on large arrays than the JSONPath module. JSONQuery generally supersets the functionality of JSONPath and provides syntax that matches and behaves like JavaScript where the syntax intersects for maximum ease of use.
Usage APIA JSONQuery can be executed with the following call:
results = dojox.json.query(query,object);
Where query is the JSONPath query to execute and object is the root object or array to query. You can also create a “compiled” evaluation function that can be reused for multiple evaluations by only passing a query string:
evaluator = dojox.json.query(query);
The evaluator function can be then be performed on a query on data by calling it with the data as a parameter:
results = evaluator(object);
In situations where a single query may be executed multiple times, doing a single parse/compilation with dojox.json.query(query) and reusing the returned evaluation function will provide better performance. It is worth noting that dojox.json.query(query,object) is functionally equivalent to dojox.json.query(query)(object).
Query SyntaxJSONQuery evaluations begin with the provided object, which can referenced within queries with $. From the starting object, various operators can be successively applied, each operating on the result of the last operation. You may explicitly begin your query with $, but this is implicitly auto-inserted, so it is not necessary. This allows you to start queries with operators. JSONQuery uses syntax that is similar to JavaScript (with a number of extra operators), so a simple query could be performed like:
data = {foo:"bar"};
results = dojox.json.query("$.foo",data);
results -> "bar"
Query Capabilities
JSONQuery provides all the functionality of JSONPath, and additional query capabilities. By analogy, JSONQuery is to JSONPath, as XQuery is to XPath. In particular, JSONQuery provides the expressive equivalence of XQuery FLOWR expressions for mapping, sorting, and filtering object data. Several of the fundamental operators of JSONQuery follow the same syntax as JavaScript, as well behaving exactly as with JavaScript evaluation:
- .property - This will return the provided property of the object
- [expression] - This returns the property name/index defined by the evaluation of the provided expression
For example, the following JSONQuery will find the foo property of the 2nd item in the provided array (the $ is omitted):
[1].foo
The following operators are new in JSONQuery:
- [?expression] - This will perform a filter operation on an array, returning all the items in an array that match the provided expression. This operator does not need to be in brackets, you can simply use ?expression, but since it does not have any containment, no operators can be used afterward when used without brackets. The following JSONQuery will find all the array items that have a price less than 15:
[?price < 15]
And to add a condition for the rating property to be greater than 3 (and omit the brackets):
?price < 15 & rating > 3
- [/expression], [\expression], [/expression, /expression] - This performs a sort operation on an array, with sort based on the provide expression. Multiple comma delimited sort expressions can be provided for multiple sort orders (first being highest priority). / indicates ascending order and \ indicates descending order. For example to sort an array by lastName first and then firstName as the second priority:
[/lastName,/firstName]
- [=expression] - This performs a map operation on an array, creating a new array with each item being the evaluation of the expression for each item in the source array. For example, to create a list of the price value from an array of objects, we could use the query:
[=price]
You can also use object literals and and create a new array of objects that with a name and price properties generated from the source array:
[={price:price,name:firstName + " " + lastName}] - expr = expr - Performs a comparison (like JavaScript’s ==). When comparing to a string, the comparison string may contain wildcards * (matches any number of characters) and ? (matches any single character). For example to find all objects in an array where the name starts with “Mr”, one could use the query:
[?name='Mr*']
- expr ~ expr - Performs a string comparison with case insensitivity. For example to find all objects in an array with the word “the” in the description regardless of case:
[?description~'*the*']
- ..[?expression] - This will perform a deep search filter operation on all the objects and subobjects of the current data. Rather than only searching an array, this will search property values, arrays, and their children.
- $1, $2, $3... - This can be used to reference additional parameters passed to the query call. For example:
results = dojox.json.query("[?firstName=$1&lastName=$2]", myData,"John","Doe");or it can be applied to the evaluator function:
evaluator = dojox.json.query("[?firstName=$1&lastName=$2]"); results = evaluator(myData,"John","Doe");
The following operators from JSONPath are also supported:
- [start:end:step] - This performs an array slice/range operation, returning the elements from the optional start index to the optional end index, stepping by the optional step parameters. For example to get the first ten items in an array:
[0:10]
- [expr,expr] - The union operator returns an array of all the property/index values from the evaluation of the comma delimited expressions.
- .* or [*] - Returns the values of all the properties of the current object.
- $ - This is the root object.
- @ - This is the current object in filter, sort, and map expressions. Note that names are auto-converted to property references of the current object in expressions, but @ can be used for index access on the current object. The following queries are identical:
[?name='Fred'] [?@.name='Fred'] [?@['name']='Fred']
- ..property - Performs a recursive search for the given property name, returning an array of all values with such a property name in the current object and any subobjects.
- +, -, /, *, &, |, %, (, ), <, >, <=, >=, != - These operators behave just as they do in JavaScript.
Multiple operators can be used successively to create complex queries. For example, to find all the objects from the array in the products property that have a price under 15 and then sort them by descending order of rating and show the first twenty items from the resultant list, we could query:
$.products[?price < 15][\rating][0:20]
Queries can use the regular operators to form general expressions based on more complex query operations. For example, to find the difference between the lowest priced item and the highest priced item in an array:
$.store.book[\price][0].price - $.store.book[/price][0].priceSummary
The new Dojo JSONQuery module provides a powerful tool for general purpose data querying, and can be used in variety of situations. The JSONQuery module is already used by Persevere to parse and execute queries in it’s server side JavaScript object storage environment. JSONQuery is a flexible and complete query format for handling large JSON/object data structures with an intuitive JavaScript-like syntax.
Integrate encryption into Google Calendar with Firefox extensions
Integrating Flex into Ajax applications
Mastering Grails: Grails and legacy databases
Developing iPhone applications using Ruby on Rails and Eclipse, Part 3: Developing advanced views for iPhone
Dojo 1.2 Grid
With the release of Dojo 1.2 right around the corner, there’s an updated grid widget available. It offers new features and performance improvements over the existing grid including better Dojo data integration, simplified layout structures, and the ability to enable editing much more easily.
In order to make these improvements, we were forced to break backwards compatibility between the new grid and the old grid. This shouldn’t be a surprise since the grid is part of DojoX. We realized that enough people use, rely on, and extend the grid that we didn’t want to break applications based on Dojo 1.0 and 1.1, so both grids are available to you. The old grid will be available until Dojo 2.0, but it will not be improved in future versions of Dojo 1.x.
This article focuses on the use of the new grid widget. All of the examples presented in this article may be downloaded in a tarball so you can modify and follow along.
Logical File LayoutThe new grid API has been organized according to the Dojo conventions. For instance, the old dojox.VirtualGrid is now dojox.grid._Grid, dojox.Grid is now dojox.grid.DataGrid, and dojox.grid.selection is now dojox.grid.Selection. The namespace that objects are in now also reflect where you can find them in the file system.
Dojo Data IntegrationI mentioned that dojox.Grid is now dojox.grid.DataGrid, but why is it called DataGrid? The reason is that DataGrid natively supports dojo.data stores. In order to use dojo.data stores with the grid in previous releases, you needed the dojox.grid.data.DojoData model which would bridge the gap between the grid and the store. DataGrid has been engineered to remove that bridge. Instead of using stand-alone models to store data for the grid, any dojo.data store that implements the Dojo Data read API can be used. Additionally, DataGrid can use the write and notification API’s if they are available. Let’s look at an example (using the data from my previous grid tutorials modified to work with ItemFileReadStore):
var jsonStore = new dojo.data.ItemFileReadStore({ url: "json/gaskets.json" });
var grid = new dojox.grid.DataGrid({
id: 'grid',
query: { part_num: '*' },
store: jsonStore,
structure: layout
}, 'gridNode');
This will load our JSON file from a URL and give us access to the data through the Dojo Data API. We then pass the store to the DataGrid constructor along with the query we want the grid to run against the store. We’ll get to defining a layout in a bit, but for now let’s look at some more things we can do with this new grid.
I mentioned notification API support. This means if the store changes, the grid will update. Let’s set up an example:
var jsonStore = new dojo.data.ItemFileWriteStore({ url: "json/gaskets.json" });
var grid = new dojox.grid.DataGrid({
id: 'grid',
query: { part_num: '*' },
store: jsonStore,
structure: layout
}, 'gridNode');
var updateGasketTypes = function(){
jsonStore.fetch({
query: { part_num: '??1?' },
onComplete: function(items, result){
dojo.forEach(items, function(item){
jsonStore.setValue(item, "type", 2);
});
}
});
}
<button onclick="updateGasketTypes();">Change gasket types</button>
When the button is clicked, any item that has a ‘1′ in the third number of its part number will have its type changed to 2 and the grid will update its information.
Layout Structure SimplifiedIn previous versions of the grid, setting up how you wanted the grid to look was sometimes confusing. With this latest version we have tried to make setting up the look of the grid simpler while still allowing flexibility for those that need it. There are now two ways to lay out your grid: markup and programatically.
Layouts with markupPreviously the only way to set up your grid’s layout for use in markup was with a structure defined in a script block. Those days are long gone! Setting up a grid’s layout is now as simple as this:
<table id="gridNode" jsId="grid" dojoType="dojox.grid.DataGrid"
query="{ part_num: '*' }" store="jsonStore">
<thead>
<tr>
<th field="part_num">Part Number</th>
<th field="min_temp" width="100px">Minimum Temperature</th>
<th field="max_temp" width="100px">Maximum Temperature</th>
<th field="type" width="100px">Type</th>
<th field="thick" width="5em">Thickness</th>
</tr>
</thead>
</table>
One of the cool features of the grid is that you can group certain columns and lock them from scrolling horizontally. You can set that up with markup as well:
<table id="gridNode" jsId="grid" dojoType="dojox.grid.DataGrid"
query="{ part_num: '*' }" store="jsonStore">
<colgroup span="1" noscroll="true"></colgroup>
<colgroup span="4"></colgroup>
<thead>
<tr>
<th field="part_num" width="300px">Part Number</th>
<th field="min_temp" width="100px">Minimum Temperature</th>
<th field="max_temp" width="100px">Maximum Temperature</th>
<th field="type" width="100px">Type</th>
<th field="thick" width="5em">Thickness</th>
</tr>
</thead>
</table>
With colgroup we are able to lock the first column and allow the last four columns to be horizontally scrolled. You can even set up multiple rows within each row while spanning cells between rows and columns:
<table id="gridNode" jsId="grid" dojoType="dojox.grid.DataGrid"
query="{ part_num: '*' }" store="jsonStore">
<thead>
<tr>
<th field="part_num" rowspan="2">Part Number</th>
<th field="min_temp" width="100px">Minimum Temperature</th>
<th field="max_temp" width="100px">Maximum Temperature</th>
<th field="type" width="100px">Type</th>
</tr>
<tr>
<th field="thick" colspan="3" width="5em">Thickness</th>
</tr>
</thead>
</table>
You can even define formatters and getters for columns:
function formatDegrees(value){
return value + '°';
}
function getODtoID(rowIndex, item){
if(!item){
return this.defaultValue;
}
var grid = dijit.byId('gridNode');
var od = grid.model.getValue(item, "outer");
var id = grid.model.getValue(item, "inner");
return od - id;
}
<table id="gridNode" jsId="grid" dojoType="dojox.grid.DataGrid"
query="{ part_num: '*' }" store="jsonStore" rowSelector="20px">
<thead>
<tr>
<th field="part_num">Part Number</th>
<th field="min_temp" formatter="formatDegrees" width="100px">
Minimum Temperature
</th>
<th field="max_temp" formatter="formatDegrees" width="100px">
Maximum Temperature
</th>
<th field="type" width="50px">Type</th>
<th field="thick" width="5em">Thickness</th>
<th width="5em" get="getODtoID">OD to ID</th>
</tr>
</thead>
</table>
Another popular Dojo grid feature is dojox.GridRowView. It provides a view at the left side of the grid that allows you to select rows easily. For Dojo 1.2, that has been renamed to dojox.grid._RowSelector and can be enabled by passing an option to the grid:
<table id="gridNode" jsId="grid" dojoType="dojox.grid.DataGrid"
query="{ part_num: '*' }" store="jsonStore" rowSelector="20px">
<thead>
<tr>
<th field="part_num">Part Number</th>
<th field="min_temp" width="100px">Minimum Temperature</th>
<th field="max_temp" width="100px">Maximum Temperature</th>
<th field="type" width="100px">Type</th>
<th field="thick" width="5em">Thickness</th>
</tr>
</thead>
</table>
By passing “20px” to the grid in the rowSelector attribute, we are telling the grid we want the row selector to be 20 pixels wide. You can also pass a boolean value (if it’s true, you get the default size) or any other valid css size.
Putting this all together, we can port our previous application to the new API. Just look at the source of here.
Programmatic LayoutsIf markup isn’t your thing or you’re creating your grid on the fly you’ll be glad to know that we’ve simplified the structure you pass in to the grid to define the layout. Previously you were required to pass in a structure like this to get something similar to our first markup example:
var layout = [
// view 0
{
cells: [[
{ field: "part_num", name: "Part Number", width: 'auto' },
{ field: "min_temp", name: "Minimum Temperature",
width: "100px" },
{ field: "max_temp", name: "Maximum Temperature",
width: "100px" },
{ field: "type", name: "Type", width: "100px" },
{ field: "thick", name: "Thickness", width: "5em" }
]]
}
];
var grid = new dojox.grid.Grid({
structure: layout,
model: model
}, 'gridNode');
Now, you can trim it down to something like this:
var layout = [
{ field: "part_num", name: "Part Number", width: 'auto' },
{ field: "min_temp", name: "Minimum Temperature", width: "100px" },
{ field: "max_temp", name: "Maximum Temperature", width: "100px" },
{ field: "type", name: "Type", width: "100px" },
{ field: "thick", name: "Thickness", width: "5em" }
];
var grid = new dojox.grid.DataGrid({
query: { part_num: '*' },
store: jsonStore,
structure: layout
}, 'gridNode');
Multiple rows of cells is also easier. Before we had to do this:
var layout = [
// view 0
{
cells: [[
{ field: "part_num", name: "Part Number", width: 'auto',
rowSpan: 2 },
{ field: "min_temp", name: "Minimum Temperature",
width: "100px" },
{ field: "max_temp", name: "Maximum Temperature",
width: "100px" },
{ field: "type", name: "Type", width: "100px" }
],[
{ field: "thick", name: "Thickness", width: "5em",
colSpan: 3 }
]]
}
];
var grid = new dojox.grid.Grid({
structure: layout,
model: model
}, 'gridNode');
And now:
var layout = [[
{ field: "part_num", name: "Part Number", width: 'auto', rowSpan: 2 },
{ field: "min_temp", name: "Minimum Temperature", width: "100px" },
{ field: "max_temp", name: "Maximum Temperature", width: "100px" },
{ field: "type", name: "Type", width: "100px" }
],[
{ field: "thick", name: "Thickness", width: "5em",
colSpan: 3 }
]];
var grid = new dojox.grid.DataGrid({
query: { part_num: '*' },
structure: layout,
store: jsonStore
}, 'gridNode');
If you want to add the row selector, just change the constructor call to this:
var grid = new dojox.grid.DataGrid({
query: { part_num: '*' },
structure: layout,
store: jsonStore,
rowSelector: '20px'
}, 'gridNode');
Now you can see the application from my previous post ported to the new programmatic API.
Editing redefinedIn order to edit a value in the old grid, you would have to pass an editor class to your cell definition. In the new grid, each cell has a default editor defined; all you have to do is add editable: true to your cell definition and use a store that implements the Dojo Data write API:
var jsonStore = new dojo.data.ItemFileWriteStore({ url: "json/gaskets.json" });
var layout= [
{ field: "part_num", width: "auto", name: "Part Number" },
{ field: "min_temp", width: "100px", name: "Minimum Temperature" },
{ field: "max_temp", width: "100px", name: "Maximum Temperature" },
{ field: "type", width: "100px", name: "Type" },
{ field: "thick", width: "5em", name: "Thickness", editable: true }
];
var grid = new dojox.grid.DataGrid({
query: { part_num: '*' },
store: jsonStore,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
You can now edit the thickness property of each row of data. What if you don’t want to edit the data with a text box? Let’s say we want to set up a select to edit the type of gasket (cellType for the markup attribute name):
var layout= [
{ field: "part_num", width: "auto", name: "Part Number" },
{ field: "min_temp", width: "100px", name: "Minimum Temperature" },
{ field: "max_temp", width: "100px", name: "Maximum Temperature" },
{ field: "type", width: "100px", name: "Type", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "thick", width: "5em", name: "Thickness", editable: true }
];
Conclusion
The Dojo 1.2 Grid offers significant performance and API simplifications over previous versions of the Dojo Grid. As of Dojo version 1.2, you will be able to continue using the existing Dojo Grid, and also use the new Grid, even on the same page of an application if necessary. We highly recommend that you upgrade to the new version of the grid as soon as possible as the new feature set is worth the investment!
Touching and Gesturing on the iPhone
Everyone who owns an iPhone (or who has been holding out for an iPhone 3G) is bound to be excited about a lot of the new things the device can finally do, particularly the introduction of third-party applications. But those of us in the web development community have been itching for something further still: good web applications on the iPhone. This means we need a suitable replacement for mouse events. And boy did we get them! Though at first the APIs seem a little sketchy, once you’ve learned them you should be able to do amazing things in your application.
I’ll start with how to set up the iPhone console, since I found it invaluable while testing. Under Settings > Safari > Developer, you can turn it on or off. Simple log, error, and warn functions are provided (as part of the console object), all of which accept a single object.
My quest to understand the API led me to this Apple Developer Connection page that, while providing pretty thorough documentation about what’s available, left me a little confused about the details. Also, if you aren’t a member of ADC, trying to follow this link will leave you even more confused.
Clearing it UpApple introduced two new ideas with this API: touches and gestures. Touches are important for keeping track of how many fingers are on the screen, where they are, and what they’re doing. Gestures are important for determining what the user is doing when they have two fingers on the screen and are either pinching, pushing, or rotating them.
TouchesWhen you put a finger down on the screen, it kicks off the lifecycle of touch events. Each time a new finger touches the screen, a new touchstart event happens. As each finger lifts up, a touchend event happen. If, after touching the screen, you move any of your fingers around, touchmove events happen.
We have the following touch events:
- touchstart: Happens every time a finger is placed on the screen
- touchend: Happens every time a finger is removed from the screen
- touchmove: Happens as a finger already placed on the screen is moved across the screen
- touchcancel: The system can cancel events, but I’m not sure how this can happen. I thought it might happen when you receive something like an SMS during a drag, but I tested that with no success
node.ontouchstart = function(evt){
console.log(evt.pageX + "/" + evt.pageY);
// OH NO! These values are blank, this must be a bug
}
My first mistake was monitoring these events and trying to get location information from the events (pageX, pageY, etc). After consulting the ADC documentation again, I learned about three event lists that come attached to the object. But I wasn’t sure what they did, so I went back to testing, logging, and experimenting.
It helped when I figured out the problem the Apple developers were trying to solve. With a mouse, you really only have one point of contact: through the cursor. With your hand, you can keep two fingers held down on the left of the screen while you keep tapping the right side of the screen.
Our event object has a list, and this list contains information for every finger that’s currently touching the screen. It also contains two other lists, one which contains only the information for fingers that originated from the same node, and one which contains only the information for fingers that are associated with the current event. These lists are available to every touch event.
We have the following lists:
- touches: A list of information for every finger currently touching the screen
- targetTouches: Like touches, but is filtered to only the information for finger touches that started out within the same node
- changedTouches: A list of information for every finger involved in the event (see below)
To better understand what might be in these lists, let’s go over some examples quickly
- When I put a finger down, all three lists will have the same information. It will be in changedTouches because putting the finger down is what caused the event
- When I put a second finger down, touches will have two items, one for each finger. targetTouches will have two items only if the finger was placed in the same node as the first finger. changedTouches will have the information related to the second finger, because it’s what caused the event
- If I put two fingers down at exactly the same time, it’s possible to have two items in changedTouches, one for each finger
- If I move my fingers, the only list that will change is changedTouches and will contain information related to as many fingers as have moved (at least one).
- When I lift a finger, it will be removed from touches, targetTouches and will appear in changedTouches since it’s what caused the event
- Removing my last finger will leave touches and targetTouches empty, and changedTouches will contain information for the last finger
Using these lists, I can keep very close tabs on what the user is doing. Imagine creating a(nother) Super Mario clone in JavaScript. I’d be able to tell what direction the user currently has his or her thumb on, while also being able to watch for when the user wants to jump or shoot a fireball.
I’ve been saying that these lists contain information about the fingers touching the screen. These objects are very similar to what you’d normally see in an event object passed to an event handler A limited set of properties are available in these objects. Following is the full list of properties for these objects:
- clientX: X coordinate of touch relative to the viewport (excludes scroll offset)
- clientY: Y coordinate of touch relative to the viewport (excludes scroll offset)
- screenX: Relative to the screen
- screenY: Relative to the screen
- pageX: Relative to the full page (includes scrolling)
- pageY: Relative to the full page (includes scrolling)
- target: Node the touch event originated from
- identifier: An identifying number, unique to each touch event
For those of you coming from the normal web design world, in a normal mousemove event, the node passed in the target attribute is usually what the mouse is currently over. But in all iPhone touch events, the target is a reference to the originating node.
One of the annoyances of writing web applications for the iPhone has been that even if you set a viewport for your application, dragging your finger around will move the page around. Fortunately, the touchmove’s event object has a preventDefault() function (a standard DOM event function) that will make the page stay absolutely still while you move your finger around.
Drag and Drop with the Touch APIWe don’t have to worry about keeping track of down/up events as we do with mousemove since the only way touchmove is triggered is after touchstart.
node.ontouchmove = function(e){
if(e.touches.length == 1){ // Only deal with one finger
var touch = e.touches[0]; // Get the information for finger #1
var node = touch.target; // Find the node the drag started from
node.style.position = "absolute";
node.style.left = touch.pageX + "px";
node.style.top = touch.pageY + "px";
}
}
Gestures
This was much easier to figure out than the touch API. A gesture event occurs any time two fingers are touching the screen. If either finger lands in the node you’ve connected any of the gesture handlers (gesturestart, gesturechange, gestureend) to, you’ll start receiving the corresponding events.
scale and rotation are the two important keys of this event object. While scale gives you the multiplier the user has pinched or pushed in the gesture (relative to 1), rotation gives you the amount in degrees the user has rotated their fingers.
Resizing and Rotating with the Gestures APIWe’ll be using WebKit’s transform property to rotate the node.
var width = 100, height = 200, rotation = ;
node.ongesturechange = function(e){
var node = e.target;
// scale and rotation are relative values,
// so we wait to change our variables until the gesture ends
node.style.width = (width * e.scale) + "px";
node.style.height = (height * e.scale) + "px";
node.style.webkitTransform = "rotate(" + ((rotation + e.rotation) % 360) + "deg)";
}
node.ongestureend = function(e){
// Update the values for the next time a gesture happens
width *= e.scale;
height *= e.scale;
rotation = (rotation + e.rotation) % 360;
}
Conflicts
Some readers might have noticed that a gesture is just a prettier way of looking at touch events. It’s completely true, and if you don’t handle things properly, you can end up with some odd behavior. Remember to keep track of what’s currently happening in a page, as you’ll probably want to let one of these two operations “win” when they come in conflict.
In ActionI put together a quick demo:
This is a simple application that showcases the incredible flexibility and power of these APIs. It’s a simple gray square that can have its colors and borders restyled, can be dragged around, and can be resized and rotated.
Load http://tinyurl.com/sp-iphone up on your iPhone and try the following:
- Keep a finger over one of the colored squares, and put another finger on one of the border squares
- Try the same thing using two colored squares or two border squares
- Use one finger to drag the square around the page
- Pinch and rotate the square
- Start dragging the square, but put another finger down and turn it into a pinch and rotate. Lift one of your fingers back up, and resume dragging the square around
I’m not sure what sort of APIs we’ll be able to build on top of what Apple has provided for us. What I do know is that Apple has given us a very well thought out API.
mousedown and mouseup are events we can easily emulate with this new API. mousemove is a beast. First of all, we only get touch events after the finger has made contact (the equivalent of mousedown) while we get mousemove events regardless of whether the button is down or not. Also, preventing the page from jumping around isn’t something we can automate. Attach a handler to the document and the user wouldn’t be able to scroll at all!
Which brings us to DnD in general. Even though DnD only cares about mousemove in the context of the mouse button being down (the way that touchmove works), we don’t have any way to tell what node the user’s finger is over at the end of the drag (since target refers to the originating node). If a DnD system is to be used, it would have to be for registered drop targets who are aware of their position and size on the page.
Developing iPhone applications using Ruby on Rails and Eclipse, Part 2: Displaying iPhone content to the client
Annotating the Web with Atom
Ajax overhaul, Part 3: Retrofit existing sites with jQuery, Ajax tabs, and photo carousels
Integrate your PHP application with Google Calendar
Dojo Toolbox First Look
In the middle of May, we were given a mission: create a speedy, offline API documentation viewer and a graphical Dojo build tool. Here we are at the beginning of July, and the result is the Dojo Toolbox 1.0. This article is a first look at this new application.
Adobe® AIR™ has received a good deal of press attention over the past few months, and with good reason. It provides a way for web application developers to use the skills they already have to create cross-platform desktop applications. Starting with Dojo 1.1, Dojo has included support for AIR out-of-the-box. This made AIR an ideal target environment for the Dojo Toolbox.
The easiest way to get the Dojo Toolbox is from SitePen’s Dojo Toolbox page. There’s a widget on that page that can download the Adobe AIR runtime and the Dojo Toolbox quickly, and with a minimum of effort.
Installation of an AIR application is a breeze. When you double click the DojoToolbox.air file, you will see a security verification window like the one below:

Unlike a web application, AIR apps are able to access the files on your computer and have unrestricted access to the internet. For that reason, AIR applications are digitally signed so that you can decide if you trust the application’s publisher with this extra level of access.
We have signed the Dojo Toolbox with a certificate assigned to the Dojo Foundation by Thawte. So, installation of the Dojo Toolbox reflects that the publisher’s identity has been verified. The Dojo Toolbox requires access to your local file system, in order to be able to run builds, and to the internet in order to check for updates and to download the API documentation.
After you accept the security aspects of the installation, AIR asks you where you would like to place the newly installed app, presenting a default that is appropriate for your platform.

One thing you might already have noticed about AIR: its appearance is not the same as a native application. Though AIR apps have many of the capabilities of a native application, the way they are rendered is very much the same as a web application. If you imagine the incredible variety of appearances in web applications, you can imagine the diversity we’re likely to see in AIR applications.
The install process is easy, but updates are even easier. The Dojo Toolbox can check for updates for you and download and install an update with just a couple of clicks.
Unless you unchecked the checkbox on the installer screen, the Dojo Toolbox will launch as soon as it’s ready.
The ScreencastIf you’d like to see, rather than read about, the Dojo Toolbox you can either install it for yourself, or take 5 minutes and watch the screencast:
The Dojo Toolbox ApplicationWhen you launch the Dojo Toolbox, you’re greeted with a small “launcher” window:

You’ll notice right away that it’s not a native-looking window. That’s another nice feature of AIR: you can create “chromeless” windows that have their own distinctive look and feel.
The Dojo Toolbox is designed to be unobtrusive, so you can leave it sitting around and reach for it whenever you need it. You can drag the launcher around your desktop, and it will remember its position for the next time you launch it.
If you click on the Dojo logo, the launcher will expand so you can access the available tools. Currently, there are three: the API Viewer, Builder and Resources.

Let’s take a closer look at them in the order in which they’re presented.
API ViewerThe first time you launch the API Viewer, it will download the Dojo API documentation zip file and then uncompress it. This file is a version of api.dojotoolkit.org that has been reformatted specifically for the Dojo Toolbox.
When the API Viewer is ready, you’ll get a view of the Dojo API documentation that is similar to the one on the web.

The navigation on the left allows you to easily find a module within the Dijit, Dojo and DojoX packages. Not surprisingly, choosing a module on the left will give you the docs for that module on the right. At the top of the window, there are left and right arrow buttons. Those are like the back and forward buttons in your browser.
One feature that you’re likely to use all the time is the search box at the top. Type in the name of a Dojo function (for example dojo.forEach) or the words that you’re looking for and you’ll get a list of matching help topics.

All of the topics that match any of the terms you enter will be displayed, sorted with a simple scoring mechanism. You can also search for topics that do not include a particular word by putting a “-” before the word.
The API Viewer gives you amazingly fast access to the wealth of information available in the Dojo API documentation. Even on an airplane!
Introducing the BuilderThe Builder tool is the most complex tool in the current Dojo Toolbox. It has many options spread out over three tabs.

You can productively use the Builder without touching most of those options. The goal with the Builder is to provide an easier way to take advantage of the power of Dojo’s build system, which was previously available only as a command-line tool.
Dojo’s build system can dramatically reduce page load time by cutting the number of requests that the browser needs to make and reducing how much data is sent on each of those requests. Internet Explorer has a default limit of just two simultaneous requests, which means that reducing the number of requests required can have a huge impact on page load time.
To use the Builder, there are just two things you need to provide it: the top directory of the copy of Dojo of which you wish to make a build, and the location of a profile file. Assuming that you already have a copy of Dojo that you have downloaded previously, you can click the “Select Directory…” button to choose the directory containing your Dojo. The Builder will remember that choice so that you can select it next time.
Next, you need a profile file. The profile describes how JavaScript modules will be collapsed into separate JavaScript files. You can learn more about creating profiles from the online Dojo Book. Once you have a profile available, you can click the “Select File…” button to select that profile for building. As with your Dojo directory, the Builder will remember which profile was last used for building, so that you can easily grab it again.
And that’s all you absolutely have to choose before you can run a build. Click the “Build It!” button, and you’re off to the races.

While the build is running, you will see a progress meter with a short description of what is happening. In addition, you get a log window that displays the log output of the build. That is the same log output you get when running the Java build.
The amount of time it takes to run a build in the Dojo Toolbox is comparable to how long is spent on a build using the traditional Java-based build tool.
Speaking of the Java-based build tool, the mysterious-sounding “Command” button will give you the command line options to use with the Java build tool.

That means you can use the Builder as a graphical front end to the command line build tool, making it easy to run automated builds.
Build OptionsDojo’s build system can handle sophisticated needs. With so many options available, it can be difficult to know what each one is all about.
Luckily, help is built in to the Builder tool. You’ll notice that many of the field names are underlined. If you click those underlined headings, you’ll see a Tooltip that describes what that setting is for.

The text for these Tooltips is from the command line build tool help, so these will always stay in sync.
I want to mention a very important option: the ShrinkSafe option. ShrinkSafe is Dojo’s tool that compresses JavaScript files by eliminating excess whitespace and removing comments. ShrinkSafe is “safe”, unlike some other JavaScript compression tools, because it works off of the syntax tree produced by a customized version of Rhino, the JavaScript engine written in Java. Rhino is not available within the AIR environment.
As of this writing, the only ShrinkSafe optimization choice available is “none”. We left the option on the page, however, because we are actively working on providing two options for using ShrinkSafe with the AIR-based build. The first is “Remote ShrinkSafe”, which will use the ShrinkSafe web service to compress the files. We also recognize that many people might not want their custom JavaScript files being passed across the Internet. For this reason, we are also working on a “Local ShrinkSafe” option which will use a small Java-based service running on your computer for the compression.
The Resources ToolThe third tool in the Dojo Toolbox is called, simply, “Resources”.

Resources provides you with a consolidated, quick click-and-go list of some amazingly useful sources of knowledge and help for working with Dojo. We’ll be keeping this resource list up-to-date as new articles are published and new tools are created.
After 1.0We’re very excited to be offering this new, simple way to help you get more out of Dojo. And 1.0 is really just the beginning. We have tons of ideas for new tools and features for the Dojo Toolbox, and I’m sure you do, too.
Download the Dojo Toolbox today to take advantage of the great features today. Then, join us on the Dojo Toolbox mailing list and tell us how we can make this open source application even better.
Adobe, the Adobe logo and Adobe AIR are either registered trademarks or trademarks of Adobe Systems Incorporated in the United States and/or other countries.
Amazon S3 + Dojo
Dojo’s improved RPC and new REST services can be used with a wide array of web services. One particular use is Amazon’s Simple Storage Service (S3). Dojo can connect to Amazon S3’s with a trivial proxy and be used as a normal RPC service. Not only that, Amazon S3 is a REST service and therefore it can be used as a data store with Dojo’s new REST implementation of dojo.data, JsonRestStore. You can read and write to your S3 database using the convenient Dojo Data API, and use the data store in Dojo widgets.

To get started with Dojo + S3:
- First, you must be signed up to use Amazon S3.
- Dojo uses a simple proxy to communicate with S3 (doing so directly from the browser would be insecure). The proxy PHP script, proxy.example-php, located in /dojox/rpc/s3/, must be renamed to proxy.php to be used.
- The proxy requires PHP 5 (other languages hopefully will be added) and the following modules:
- Next you must enter your Amazon access key and secret access key into the proxy.php file on line 3 and 4:
$accessKey = "access key"; $secretAccessKey = "secret access key";
Now we can start using Dojo services to connect to S3. You use the Dojo RPC service with the “PROXIED-PATH” envelope:
dojo.require("dojox.rpc.Service");
dojo.require("dojox.rpc.ProxiedPath");
var s3Buckets = new dojox.rpc.Service({
target:"http://s3.amazonaws.com/",
proxyUrl:"../s3/proxy.php", // the path to the proxy
transport:"REST",
envelope:"PROXIED-PATH",
contentType:"application/json",
services:{
myBucket:{ // name of the service (usually the bucket)
target:"myBucket", // enter you bucket here
parameters:[{type:"string"}]
}
}
});
To use the S3 as a Dojo data store you can use the S3Store module. First setup an RPC service as shown above and then pass the RPC service to the S3JsonRestStore:
dojo.require("dojox.data.S3Store");
// and create a store for it
s3Store = new dojox.data.S3Store({service:s3Buckets.myBucket});
You can then use the s3Store as a normal Read/Write Dojo Data store. And since the S3Store is an extension of the JsonRestStore, you can use all the additional features of that store including fast, compact syntax, referencing, and offline capabilities are coming soon. With the S3Store you can build sophisticated apps with very thin server architecture; UI logic can be handled on the browser, and database storage can be delegated to Amazon’s S3. This is just one of the many new features available with Dojo 1.2.
Dojo In 6K
Some sites can defer most, if not all, of their JavaScript-driven progressive enhancements until well after the page has loaded. Even so-called “lightweight” libraries like JQuery are far too heavy for some environments…not because they (like Dojo) pull in all the code needed to use them, but because they do it all up-front. Often the best time to pay the expense of loading, parsing, and executing JavaScript code is when the user takes an action that needs the enhancement to run. Dojo already gives you the best tools available anywhere to defer loading modules until you actually use them; other than those provided by dojo.js itself…but what about dojo.js? What if even the small size of Dojo is too big for your page? This need to load as fast as possible and defer work is never greater than in mobile applications. The best-performing thing to do is always to hand-write all the code you’ll need and never use more script than that…but there are complications. Not being able to share your code between developers (let alone between pages) can be a huge disadvantage. The code is also likely to be slow, buggy, and unmaintainable. There’s a real reason why toolkits like Dojo are so incredibly popular: they make much of this pain go away at the cost of initial download size. Why would anyone want to invite the pain back?
So we need some middle ground: a common, expected API surface area combined with a trivial initial footprint to ensure that pages load as fast as possible. How small could we make such a thing? Turns out, 6K.
Dojo has, for many versions now, provided the ability to use the build system to cut out parts of dojo.js which weren’t in use by the application and load them later. In fact, everything but the package loader and the few functions it needs to operate are located in source packages in the dojo/_base/ directory. When running Dojo from source, these files get pulled in with dojo.require() the same way that any other module might. One possible option then is to use the build system to create a custom version of dojo.js which includes just the modules your application uses at page load. But we’re still faced with the essential problem created by hand-rolling everything: it becomes much harder to work with Dojo. You no longer know what functions and classes might be provided by the dojo.js file loaded in the header of your page, meaning that the advantages of maintainability, shared team knowledge, and dependability are reduced. Code developed for this situation is littered with statements like dojo.require("dojo._base.array"); in order to ensure that the right modules are available.
A better solution might be one which preserves the super-lightweight foot-print of including just the Dojo loader but which also provides access to the full, rich API that is provided by the stock dojo.js without developers needing to spend a lot of time worrying whether a particular base module has already been loaded.
Enter the stub loader.
I’ve prototyped an alternate dojo.js which you can download as part of a build (256K tarball, includes tests) of just Core (the stuff in the dojo.* namespace). When served gzipped, this new version of dojo.js weighs in at roughly 6K, yet it provides the full API of the normal dojo.js, provided off of the AOL CDN at roughly 24K, or 4x the size. Instead of including all of the modules in Dojo’s baseline set of functionality, this new version instead pulls in just the bootstrapping code for the module system and then creates stub functions and constructors for all of the functionality which isn’t included. When a function is first called, the stub loader ensures that dojo.require() is called to pull in the appropriate functionality synchronously. The result is an API which behaves exactly as though all of dojo.js had been loaded up front but which defers loading until the code is actually used.
On an iPhone with a clean cache the stubbed-out dojo.js cut in half the time required to load and evaluate. Sure, it’ll take more time on the network when parts of the toolkit are actually used (say, in response to a click event), but for mobile device scenarios, it’s going to be hard to beat the flexibility and speed of the stub loader when pulling Dojo into a page.
The code to implement this strategy can now be viewed. If you view that file, you’ll note that the first several lines read like this:
dojo.provide("dojo._base");
dojo.provide("dojo._base.browser");
dojo.provide("dojo._base._stubs");
This implies that if some other code subsequently calls dojo.require("dojo._base");, as the build system normally would, then no code will be loaded. In a source version of Dojo, dojo/_base.js reads as such:
dojo.provide("dojo._base");
dojo.require("dojo._base.lang");
dojo.require("dojo._base.declare");
dojo.require("dojo._base.connect");
dojo.require("dojo._base.Deferred");
dojo.require("dojo._base.json");
dojo.require("dojo._base.array");
dojo.require("dojo._base.Color");
dojo.requireIf(dojo.isBrowser, "dojo._base.browser");
By loading our stub loader instead, all of the modules in dojo._base.* which would normally be included are left out of the build and replaced with their stub counterparts. To get some bit of code included in a build instead of the stub, we’d then just add the corresponding require (e.g., dojo.require("dojo._base.lang")) at the top of dojo/_base/_stubs.js, at which point the corresponding stub functions won’t be generated.
In a build which uses dojo/_base/stubs.js, extra code is not loaded until the functions and constructors which are stubbed out there are actually called. You can drop this file into your dojo/_base/ directory if you’re working with Dojo from source and then use the following profile definition to create your own stubbed-out dojo.js. I placed mine in util/buildscripts/profiles/stubs.profile.js:
dependencies = {
layers: [
{
name: "dojo.js",
customBase: true,
dependencies: [
"dojo._base._stubs"
]
},
// your layers here!
],
prefixes: [
// uncomment these to copy dijit and dojox into your build output
// [ "dijit", "../dijit" ],
// [ "dojox", "../dojox" ]
]
};
And would normally build with:
%> cd dojo_repo/util/buildscripts/
%> ./build.sh profile=stubs action=clean,release copyTests=false cssOptimize=comments optimize=shrinksafe
clean: Deleting: ../../release/dojo
release: Using profile: profiles/stubs.profile.js
...
A Quick JavaScript Load Profiler
I was doing some research on script loading speed tests. Each script load required the page to be refreshed, making it difficult to log the time to Firebug and get an average. It was certainly too much trouble to write some PHP scripts and connect to a database; and possibly even worse would be having to pull out a pencil and paper and write the times down. I’m not even sure I have a pencil.
The obvious solution was to write the data to a cookie. I also thought the solution was universal enough to blog about so others could use it too.
The first thing we need is a timer. Firebug has a timer, but it doesn’t return the results so that they can be saved. The timer will be a singleton since we will only need one instance holding all of the data, and it will be a global object so we can access it anywhere. The code is quite simple:
dojo.provide("mikespace.timer");
timer = {
_map:{},
start: function(msg){
this._map[msg] = new Date().getTime();
},
end: function(msg){
this._map[msg] = new Date().getTime() - this._map[msg];
return this._map[msg];
},
show: function(msg){
console.log( "---------> " + msg + ": " + this._map[msg]);
}
};
// usage:
// timer.start("first test");
// var time = timer.end("first test);
This code is in a directory (or package) named mikespace. As you can see, the times are set and retrieved based on the label parameter, and stored in a hash map. There’s also a show() method - although I’m not going to use it here, I use this often. When conducting speed tests in an application, the timers are usually sprinkled everywhere throughout the code and show in various places in the log messages. By just calling timer.end() I store the time, and after all of the tests have been run, I call timer.show(msg) for each test, and log them all in one place.
Next we need our cookie aggregator. This too could be a singleton, but after considering that there could be multiple tests in a page, I decided to make it a PODO (Plain Old Dojo Object). Here is the full code for cookieData, and I’ll explain after:
dojo.provide("mikespace.cookieData");
dojo.require("dojo.cookie");
dojo.declare("mikespace.cookieData", null, {
cookieName:"scriptTests",
expires:1,
testData:[],
constructor: function(args){
for (var nm in args){
this[nm] = args[nm];
}
},
loadData: function(){
this.cookie = dojo.cookie(this.cookieName);
if(this.cookie){
this.testData = dojo.fromJson(dojo.cookie(this.cookieName));
}
},
getData: function(){
return this.testData;
},
showData: function(){
console.log("Test Name:", this.cookieName);
var data = this.getData();
var avr = 0;
var amt = 0;
dojo.forEach(data, function(d, i){
console.log(i, "test:", d);
avr+=d;
amt++;
});
console.log("Average:", Math.ceil(avr/amt));
},
saveData: function(data){
this.testData.push(data);
dojo.cookie(
this.cookieName,
dojo.toJson(this.testData),
{ expires: this.expires }
);
},
clearData: function(){
dojo.cookie(
this.cookieName,
dojo.toJson([]),
{ expires: this.expires }
);
},
deleteCookie: function(){
dojo.cookie(this.cookieName, null, { expires: -1 });
}
});
I’m utilizing dojo.cookie, so it is required. I declare mikespace.cookieData, and I’m not extending anything (like dijit._Widget), so the second parameter is null. Because I’m not using dijit._Widget, I’m mixing in the arguments object myself in the constructor. I then instantiate a few variables. expires is set to one day, which could be left as-is if you don’t want to leave the cookie hanging around on your system when your done. Or set it to an arbitrarily large number and use the deleteCookie() method.
The rest of the code should be quite easy to understand. in loadData() we check if the cookie exists, and if not, we create one with new data. In showData() I log each test and number it, so I can easily see how many tests I’ve run, and then I show the average. The showData() method could obviously be any kind of code, including parsing tests within tests. And semantically, it probably should be outside of this cookieData; if we wanted to just use it as a getter and setter and then create another object for displaying the results.
Now we’ll create our HTML page where we will run the tests. After including the Dojo script tag, and the djConfig, I register the mikespace namespace, and then require the files:
dojo.registerModulePath("mikespace", "../../tests/mikespace");
dojo.require("mikespace.timer");
dojo.require("mikespace.cookieData");
Now we’ll build our test. The test I’m conducting is how long it takes to load a script, and I chose dojox.data.jsonPathStore, because it’s nice and big and has a few dependencies. I modified the script to call the end of the test, after it loaded it’s dependent scripts:
...
dojo.require("dojo.date.stamp");
if(window.endTest){
window.endTest();
}
...
And our test functions:
startTest = function(){
timer.start("script loaded");
}
endTest = function(){
var time = timer.end("script loaded");
var testName = "dojox.data.jsonPathStore Load";
var c = new mikespace.cookieData({cookieName:testName});
//c.clearData();
c.loadData();
c.saveData(time);
c.showData();
}
When endTest() is called, the cookieData object is created and the test data is saved and logged. Since the timer has already ended, the amount of time it takes to create this object is not factored into the results. If I want to start the test over, I uncomment c.cleardata() and refresh the page.
Finally, to launch our test, start the timer and load the script:
startTest();
dojo.require("dojox.data.jsonPathStore");
Refresh the page ten times and you have a legitimate average of how long it takes to load the script.

And there you have a quick and dirty method for logging tests that involve page refreshes. This example could be taken even farther, enclosing the startTest() and endTest() in its own PODO which requires the timer and cookieData. Then it’s a simple matter of dropping in the one require to conduct some quick tests.
Build Ajax applications with Ext JS
Psych Desktop is now a Dojo Foundation Project
The Dojo Foundation has recently decided to support Psych Desktop. Our membership with the foundation will improve ties with Dojo. We will be donating some parts of the desktop, such as the sound api, to dojo as a result of this. The membership also will help us reach out to Dojo's community in order to improve the API, and deliver a better developer experience.
This is also a good time to announce that we will be redoing our site very soon. We understand that the forums we have are not very good, and seldom used. Also, a lot of the content is old, and it's good to start from scratch once 1.0 has been released.
Lastly, I'll tell you all what's happening in trunk. At the moment, it's broken because we're re-writing the server-side code completely using the Zend Framework. The new architecture will feature RPC, XHR long polling, and better server-side apis for apps that require server-side code. The 1.0 branch works, which you can get from http://svn.psychdesktop.net/branches/1.0/.
Development in the past month has been slow, but it seems to be picking up gradually. We really do need some more contributors though, so if you're interested, contact us.
Connect to the Mobile Web with SMS
SMS is a great way to push small amounts of text to mobile users. But what happens when your application needs to send more than 140 characters of information? Most modern phones, including Apple’s iPhone, support the ability to launch the mobile web browser using the URL embedded in the SMS message. Your application can create a short URL that points to the content you need to send and send the URL in the body of the SMS instead of the content itself. The user experience varies from phone to phone. On the iPhone, the user simply touches the link; on other phones, there is usually a menu option that will activate the url.
The URL is subject to the same size limits as any other SMS message. Keeping the URL as short as possible is key, allowing you to send descriptive text along with the message to give the user an idea as to what they will be viewing when they click the link. URL shortening services like tinyurl will keep your URL to around 25 characters. Twitter users are no doubt familiar with this idea, whether they send Tweets from their phone or not.
The web page that the URL points to needs to detect the capabilities of the mobile device and return the appropriate markup. In the case of the iPhone, a full HTML page can be returned, but for many phones, the content needs to be limited to the XHTML mobile profile, or as a worst case with the oldest of phones, WML might be the only supported markup. Device capability databases like WURFL will allow you to look at the user-agent of the devices and return the appropriate markup.
Make sure that your users actually want to receive the SMS before you send it. Most users have to pay for each SMS message, so filling up their phone with messages without their permission is not a good idea. Allow your users to opt-in to receiving SMS messages and allow them to place a maximum cap on the number of messages they can get per day or month. Also include a verification system that ensures that your user actually controls the phone number they give you. This can be as simple as sending an SMS message with a PIN number that they then feed back into your signup form. Lastly, include disclaimer text that lets the user know that standard text messaging rates apply to any messages you send them. Adding these safeguards will help to ensure that your users only receive the messages they want.
Adding SMS notification with embedded URLs has many advantages: you can reach your users wherever they are, provide links to detailed rich content, increase awareness of your mobile web offering, and drive additional traffic deep into your mobile web site. If your application already works on mobile devices and you already have the infrastructure in-place to send email or SMS alerts, then sending URLs in SMS messages is a simple addition that can add significant value to your users. A great example for this is the airline industry. Instead of just sending a user an SMS with their gate information and arrival and departure time, they could also send a link to a convenient, mobile optimized “My Flight” page that would include other essential details such as seat assignment, arrival time, airline phone number, first class update options, and airport map.
Web Service to dojo.data Store in 4 Easy Steps
A very useful feature of Dojo is the dojox.data.ServiceStore data store. It allows you to layer a dojo.data API on top of any web service, opening up a world of uses from your own client-side components. Kris Zyp briefly mentioned the topic in his recent article on JsonRestStore, and the past couple of weeks have seen a bunch of refinements to the component to get it ready for next month’s Dojo 1.2 release. Let’s take a quick look at how to make it work with the web service of your choice.
Building the WikipediaStoreDojo recently received a new data store that demonstrates exactly what we want: dojox.data.WikipediaStore. It does just what it sounds like, turning Wikipedia into a simple object you can query from your code. Building it with Dojo’s handy dojox.rpc package makes for a simple, compact, DRY implementation.
In only four steps:
- Create the web service object
- Declare the new data store, inheriting from ServiceStore
- Give it a fetch method
- Give it a _processResults method
That’s all there is to it.
Step 1: Create the web service objectProbably the simplest way to build an object that can talk to a web service is to create a Service Mapping Description (SMD) for the web service, and let dojox.rpc.Service do the heavy lifting. Since Wikipedia’s API can speak JSONP, it’s a natural fit. The SMD doesn’t need to be thorough, since it’s not necessarily meant for human consumption; the wikipedia.smd only defines a single API endpoint and lets the data store worry about how to talk to it:
{
"SMDVersion": "2.0",
"id": "http://en.wikipedia.org/w/api.php",
"description": "Wikipedia API",
transport: "JSONP",
envelope: "URL",
additionalParameters: true,
target: "http://en.wikipedia.org/w/api.php",
parameters: [
{ name: "format", optional: false, "default": "json" }
],
services: {
query: {
parameters: [
{ name: "action", type: "string", "default": "parse" }
]
}
}
}
This simple specification is enough to let you do something like the following, which stuffs the content of Wikipedia’s Dojo Toolkit page into the current page:
dojo.require("dojo.io.script"); // for cross domain JSONP
dojo.require("dojox.rpc.Service");
dojo.addOnLoad(function(){
var mu = dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd");
var wikipedia = new dojox.rpc.Service(mu);
wikipedia.query({
action: "parse",
page: "Main Page"
}).addCallback(this, function(article){
dojo.body().innerHTML = article.parse.text["*"];
});
});
Once we have this, we just need to wrap it in a data store.
Step 2: Declare the new data storeOut of the box, the ServiceStore does just about everything you likely need; however, it doesn’t automatically understand how to format queries for your service, nor does it automatically know the format of the incoming data. It simply acts as a bus, passing through whatever it is given. That’s acceptable if you don’t mind enforcing the underlying web service’s structure on people who use the data store, but for Wikipedia, it doesn’t take much to clean things up a little.
The declaration for the new store is pretty basic. It’s just the common dojo.declare approach (the real code has a tiny bit more than this, but this is the idea):
dojo.provide("dojox.data.WikipediaStore");
dojo.require("dojo.io.script");
dojo.require("dojox.rpc.Service");
dojo.require("dojox.data.ServiceStore");
dojo.declare("dojox.data.WikipediaStore", dojox.data.ServiceStore,{
constructor: function(){
var mu = dojo.moduleUrl("dojox.rpc.SMDLibrary", "wikipedia.smd");
var svc = new dojox.rpc.Service(mu);
this.service = svc.query;
// this lets ServiceStore's getLabel(), fetchItemByIdentity(),
// etc. all work correctly
this.idAttribute = this.labelAttribute = "title";
},
fetch: function(/* object */ request){
// to come
},
_processResults: function(results, def){
// to come
}
});
We haven’t done much yet, but we’re already halfway done! All that’s left is to shuttle data back and forth.
Step 3: Give it a fetch methodThe dojo.data API uses the fetch method to query data stores, but it leaves it up to the store to determine the actual format of the query. The ServiceStore can pass query data right through to the underlying web API, but since we don’t want our data store’s users to have to know the Wikipedia API itself, we can define our own convention for creating queries and map that to the real API.
WikipediaStore defines two basic operations, query (to do full text searches) and parse (to load page data).
- The query operation will be called thusly:
store.fetch({ query: { action: "query", text: "dojo" }, // onItem, onComplete, etc... }); - For parse, we want to be able to do the following:
store.fetch({ query: { // action: "parse", -- this is the default, so we can leave it out title: "Dojo Toolkit" }, // onItem, onComplete, etc... });
So we define our own fetch that takes the request object passed in and massages it a little bit to transform our custom query structure into something that will work for Wikipedia’s API (by virtue of the dojox.rpc.Service). After doing this in-place modification, we call the ServiceStore’s fetch to do the actual RPC call:
fetch: function(/* object */ request){
var rq = dojo.mixin({}, request.query);
if(rq && (!rq.action || rq.action === "parse")){
// api.php?action=parse&page=PAGE_TITLE
// default to a single page fetch
rq.action = "parse";
rq.page = rq.title;
delete rq.title;
}else if(rq.action === "query"){
// api.php?action=query&list=search&srwhat=text&srsearch=SEARCH_TEXT
// perform a full text search on page content
rq.list = "search";
rq.srwhat = "text";
rq.srsearch = rq.text;
if(request.start){
rq.sroffset = request.start-1;
}
if(request.count){
rq.srlimit = request.count >= 500 ? 500 : request.count;
}
delete rq.text;
}
request.query = rq;
return this.inherited(arguments);
}
Now we can use our simplified dojo.data query format above to retrieve data.
Step 4: Give it a _processResults methodOnce we’ve requested data, the store needs to know how to process it. That’s where the _processResults function is useful.
_processResults: function(results, def){
if(results.parse){
// loading a complete page
results.parse.title = dojo.queryToObject(def.ioArgs.url.split("?")[1]).page;
results = [results.parse];
}else if(results.query && results.query.search){
// loading some search results; all we have here is page titles,
// so we mark our items as incomplete
results = results.query.search;
var _thisStore = this;
for(i in results){
results[i]._loadObject = function(callback){
_thisStore.fetch({
query: { action:"parse", title:this.title },
onItem: callback
});
delete this._loadObject;
}
}
}
return this.inherited(arguments);
}
The theory here is simple; it’s the same process as with fetch, just in reverse. We receive JSON data from Wikipedia, and we need to restructure it such that when we call the ServiceStore’s _processResults at the end, it’ll set up the internal object correctly.
The most interesting thing about our _processResults function is thection with the _loadObject callback. When we do a search on Wikipedia, all we get back is a list of page titles in request.query.search; we can use these to create stub objects, but there obviously isn’t enough data here to really use. We’d like to be able to fill in the stub object with something like store.loadItem({ item: someStubItem }), and that’s exactly what _loadObject allows: it tells ServiceStore how to load individual items (ServiceStore even supports lazy loading of objects, but only with transports that can be made synchronous; since JSONP always runs asynchronously, WikipediaStore can’t take advantage of it).
That’s really all there is to it; WikipediaStore seamlessly integrates Wikipedia into our apps. The Wikipedia store demo shows how to use it.
Integrating with DijitOf course, one of the big advantages of using the dojo.data API is that quite a few Dijit components can already grok it. For example, one of the demos for the Grid is built around pulling in data from Yahoo. Using the same principles as the WikipediaStore, this demo creates a YahooStore that wraps the Yahoo webSearch API endpoint. By implementing custom fetch and _processResults functions (see the component source), we get a beautifully Yahoo-ish grid.

(Image shamelessly borrowed from Kris Zyp’s article on JsonRestStore)
So put together an SMD, a dojo.declare, and a couple of functions for your web service and create your own Dojo data store. When you can easily turn web services into data stores, the web is your oyster!

![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/dojo_data_1.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/dojo_data_2.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_markup_1.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_markup_2.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_markup_3.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_markup_4.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_markup_5.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_program_1.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_program_2.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/layout_program_3.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/editing_1.png)
![[Live Example] [Live Example]](http://www.sitepen.com/labs/code/grid/dojo_grid_1.2/images/editing_2.png)
