urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development CommunityThis community site is for software developers interested in topics related to Web 2.0 and mobile device user interface development. This includes both general trends and technology discussions, as well as specific discussions and other resources involv030632014-12-11T00:08:06-05:00IBM Connections - Blogsurn:lsid:ibm.com:blogs:entry-6d837250-9c3e-4507-8510-d0a676872842Dojox.Gfx Recipe #2: From Composite Shape to ComponentPatrickRuzand270002HRN2activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2013-01-07T10:45:14-05:002013-01-08T04:42:09-05:00
<div style="text-align: left;">In the <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/entry/dojox_gfx_recipe_1_composite_shapes_the_basics45?lang=en">first recipe</a>, I introduced the composite shape approach and its implementation in gfx. I explained how it works and how to handle the challenge of complex graphical representation like coordinates conversion and user interactions. In this new recipe, we will go one step further and see how to implement a custom composite shape to build a data-aware graphic component. </div>
<div> </div>
<div style="text-align: left;"><h1>Custom composite shape</h1></div>
<div> </div><div style="text-align: left;">The first step in a custom composite shape implementation is to create a new Group subclass that will implement the domain specific behavior.
<p /><h2>Inheriting from Group</h2><p>
Writing a new Group subclass is done following the standard dojo/declare approach, using the dojox.gfx.Group type as the base class:
</p><pre>var Symbol = declare([gfx.Group],{
constructor:function(){
},
build:function(data){
this.createRect({x:0, y:0, width:230, height:110, r:6})
.setFill(&quot;#F5F5DC&quot;)
.setStroke({color:&quot;#EED5B7&quot;, width:3});
this.img = this.createImage({x:8, y:8, width:55, height:60, src:'male.png'});
this._gfxName = this.createText({x:67, y:25, text:&quot;John ith&quot;})
.setFill(&quot;black&quot;).setFont({family:'sans-serif', size:'13pt'});
this._gfxJob = this.createText({x:67, y:45, text:&quot;Software Engineer&quot;})
.setFill(&quot;black&quot;).setFont({family:'sans-serif', size:'10pt'});
this._gfxPhone = this.createText({x:67, y:65, text:&quot;Phone: 6060-842&quot;})
.setFill(&quot;black&quot;).setFont({family:'sans-serif', size:'08pt'});
this._gfxEmail = this.createText({x:67, y:80, text:&quot;Email: jsmith@mycompany.com&quot;})
.setFill(&quot;black&quot;).setFont({family:'sans-serif', size:'08pt'});
return this;
}
});
Symbol.nodeType = gfx.Group.nodeType;
</pre>
We have added a build() method to build the content of the symbol. This cannot be done in the constructor as it needs to be fully initialized before.
The Symbol.nodeType property indicates the type of the “native” node (e.g. a &lt;g&gt; for svg) to use when the underlying renderer creates a Symbol. In our case, we want it to match the Group.nodeType property.
<p /><h2>Integrate the shape creation process</h2><p>
In gfx, a shape is created from a Surface or a Group (let’s call them “container”) by means of one of the createRect(), createLine(), etc. methods. All these methods in fact yield to invoking the container createObject() method, passing the class constructor as first parameter and an initialization object (usually the shape) as second parameter.
</p><p>Knowing that, adding our custom type to the creation process is quite straightforward. We simply need to add a createSymbol() method to both the Surface and Group classes, that will call the createObject() method, passing the Symbol constructor:
</p><pre>Symbol.Creator = {
createSymbol: function(kwArgs){
return this.createObject(Symbol).build(kwArgs);
}
};
gfx.Surface.extend(Symbol.Creator);
gfx.Group.extend(Symbol.Creator);
</pre>
<p>
</p><p>
</p><h2>VML Support: hack needed</h2>
<p>
Creating a custom Group subclass requires additional work if you need to support the VML renderer. Indeed, the vml implementation in gfx today performs specific initialization tasks depending on the shape type, but only for built-in shape type. For example, creating a Group requires additional initializations that are needed too for our custom type since we are inheriting from Group and its implementation. To fix that, it needs a little hack to make it work with our custom type (basically, do what the vml implementation does for Group). You can of course skip this step if VML (that is, IE 7 and 8) is not a deployment target.:
</p><pre>(function(){
if(gfx.renderer == &quot;vml&quot;){
var fixVMLCreateObject = function(shape) {
if(shape instanceof Symbol){
// Call Creator._overrideSize
var node = shape.rawNode;
this._overrideSize(node);
// Need to do vml-specific createGroup tasks
// see dojox.gfx.vml.js/createGroup()
var r = shape.rawNode.ownerDocument.createElement(&quot;v:rect&quot;);
r.style.left = r.style.top = 0;
r.style.width = shape.rawNode.style.width;
r.style.height = shape.rawNode.style.height;
r.filled = r.stroked = &quot;f&quot;;
shape.rawNode.appendChild(r);
shape.bgNode = r;
}
return shape;
};
aspect.after(gfx.Surface.prototype, 'createObject', fixVMLCreateObject);
aspect.after(gfx.Group.prototype, 'createObject', fixVMLCreateObject);
}
})();
</pre>
<p>At this step, we have a fully functional custom Shape type with a specific graphical representation which can be instantiated with one line of code from any shape container (Group or Surface instances):</p>
<pre>var symbol = surface.createSymbol();</pre>
<p>Next step is to get rid of the hard coded values and make it a real data-aware symbol.</p>
<h1>Data-aware symbol</h1>
In most of the cases, such domain-specific symbols are used in conjunction with data provider whose contents is dynamic and cannot therefore contain any hard coded values. In other words, such a graphical representation should be bound to a data provider, and update itself when a change occurs in its data.<br />
One way to implement this behavior is to make the symbol a dojo/Stateful and use the dojox/mvc binding capabilities to bind the symbol with the data. (For more information about dojox/mvc, please see <a href="http://dojotoolkit.org/reference-guide/1.8/dojox/mvc.html#dojox-mvc">the dojox/mvc documentation</a>).
<h2>Stateful symbol</h2>
<p>
Make our symbol a <a href="http://dojotoolkit.org/reference-guide/1.8/dojo/Stateful.html#dojo-stateful">dojo/Stateful</a> is a mandatory step to integrate with the dojox/mvc/sync binding API, as the latter requires the binding source and target to be Stateful. Basically, making our symbol a dojo/Stateful simply consist on inheriting from dojo/Stateful:
</p>
<pre> require([“dojo/Stateful”, … ], function(Stateful, …){
var Symbol = declare([gfx.Group, Stateful],{
...
});
...
});
</pre>
<p>The symbol also needs to export public properties (e.g. ‘jobRole’’) to which data could be bound to and update the corresponding gfx shape (e.g. the gfx Text shape that displays the job role) with the correct value. Another benefit our Symbol automatically gets of being Stateful is the ability to fire property change notifications to listeners. Also, because setting one of these public properties imply updating the corresponding graphical shape, we need to define custom setters (see dojo/Stateful API for more information) to properly handle the shape update when the property is set.
</p><p>For example, the job Role is exported as:</p>
<pre>
_jobRole:null,
_jobRoleSetter: function(v){
this._jobRole = v;
if(this._gfxJob){
this._gfxJob.setShape({text:this._jobRole});
}
}
</pre>
where <code>_gfxJob</code> references the gfx.Text shape that represents (i.e. displays) the employee job role.
<p>In the same way, the symbol also defines the <code>_email</code>, <code>_phone</code> and <code>_name</code> properties, and the build() method is updated accordingly to take into account these new properties:</p>
<pre>build:function(){
this.createRect({x:0, y:0, width:230, height:110, r:6})
.setFill(&quot;#F5F5DC&quot;)
.setStroke({color:&quot;#EED5B7&quot;, width:3});
this.img = this.createImage({x:8, y:8, width:55, height:60, src:'male.png'});
this._gfxName = this.createText({x:67, y:25, text:this._name})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'13pt'});
this._gfxJob = this.createText({x:67, y:45, text:this._jobRole})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'10pt'});
this._gfxPhone = this.createText({x:67, y:65, text:&quot;Phone: &quot; + this._phone})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'08pt'});
this._gfxEmail = this.createText({x:67, y:80, text:&quot;Email: &quot; + this._email})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'08pt'});
return this;
},
</pre>
With these modifications, our symbol is ready for data binding.<p>
</p><h2>Binding to data</h2>
<p>The final step to make the symbol fully data-aware is to implement the data binding mechanism, leveraging the dojox/mvc/sync API.
The dojox/mvc/sync API provides a simple but powerful way to define binding between two Stateful objects, with automatic property change notifications. For more information on this API, please refer to <a href="http://dojotoolkit.org/reference-guide/1.8/dojox/mvc/sync.html#dojox-mvc-sync">http://dojotoolkit.org/reference-guide/1.8/dojox/mvc/sync.html#dojox-mvc-sync</a> .</p>
<p>The dojox/mvc/sync module defines a function “that can be directly called to synchronize two dojo/Stateful properties”. For example, binding the ‘name’ property of a Stateful object (‘data’ in the following code) with the ‘label’ property of another Stateful object (‘shape’ in the following code) is done by:</p>
<pre> sync(data, “name”, shape, “label”, opt);
</pre>
<p>where ‘opt’ in an optional parameter that allows to configure the binding (to define a value converter, the binding direction, etc.).</p>
<p>To make the symbol easier to use and avoid to have to register all the bindings manually, we add a new sync(data, bindings) method to the Symbol class whose purpose is to register the bindings specified as parameter with dojox/mvc/sync():</p>
<pre>require([“dojox/mvc/sync”,...],function(sync, …){
var Symbol = declare([gfx.Group, Stateful],{
...
sync: function(/*Stateful*/data, /*Object[]*/bindings){
array.forEach(bindings, lang.hitch(this, function(b){
sync(data, b.from, this, b.to, b.opt);
}));
}
}
</pre>
<p>where the Symbol.sync() parameters are defined as:</p>
<ul>
<li>data : A dojo/Stateful instance. Represents the data object to bind to.</li>
<li>bindings: An array of binding definitions. A binding definition object is defined by the following properties:
<ul><li>from: a string representing the property name to bind from (i.e. a property of the data object)</li>
<li>to: a string representing the property name to bind to (i.e. a property of the symbol)</li>
<li>opt: An object defining binding configuration as defined by dojox/mvc/sync.</li>
</ul>
</li>
</ul>
<p>For example, assuming we have some data that represents employees:</p>
<pre> var employee = new Stateful({
firstName: &quot;John&quot;,
lastName: &quot;Smith&quot;,
jobTitle: &quot;Software Engineer&quot;,
phone: &quot;6060-842&quot;,
email: &quot;jsmith@mycompany.com&quot;
});
</pre>
<p>binding the data jobTitle property with the symbol jobRole property is done by:</p>
<pre> symbol.sync(employee, [{
from: &quot;jobTitle&quot;,
to : &quot;jobRole&quot;,
opt : {bindDirection: sync.from}
}]);
</pre>
<p>You can find below the full example, from the symbol creation to the binding definition. </p>
<pre> var symbol = surface.createSymbol();
var nameConverter = {
format: function(value){
return employee.firstName + &quot; &quot; + employee.lastName;
}
};
symbol.sync(employee, [{
from: &quot;jobTitle&quot;,
to : &quot;jobRole&quot;,
opt : {bindDirection: sync.from}
}, {
from: &quot;phone&quot;,
to : &quot;phone&quot;,
opt : {bindDirection: sync.from}
},{
from: &quot;email&quot;,
to : &quot;email&quot;,
opt : {bindDirection: sync.from}
},{
from: &quot;firstName&quot;,
to : &quot;name&quot;,
opt: {
bindDirection: sync.from,
converter: nameConverter
}
},{
from: &quot;lastName&quot;,
to : &quot;name&quot;,
opt: {
bindDirection: sync.from,
converter: nameConverter
}
}
]);
</pre>
With these bindings in place, any changes on the data object will be propagated to its graphical representation via the custom setters defined previously. As you can see, having an all-packaged, data-aware, custom composite shape simplify greatly the developer experience.
<p>To illustrate this, the full example that you can find on github (link below) adds a button to the page that changes the lastName property of the data object (i.e. “employee”) when it is clicked. Thanks to the binding engine, this change is propagated to the Symbol “name” property (which is bound to “lastName”, see the definition above) via the Symbol._nameSetter() custom setter. Finally, the setter updates the “text” property of the corresponding gfx Text shape.
</p><p>You can find the full source code here: <a href="https://github.com/pruzand/dojox-gfx-samples/tree/master/gfx-recipe-2">https://github.com/pruzand/dojox-gfx-samples/tree/master/gfx-recipe-2</a>
</p><h1>Conclusion</h1>
In this article, we have learnt how to create a custom composite shape inheriting from the gfx Group gfx class. We have then extended it to make it a Stateful object and expose public properties whose value changes can be monitored. Finally, we integrated the dojox/mvc/sync API to leverage its binding capabilities to make our Symbol a fully data aware citizen. By integrating these functionalities into the Symbol class, we have made our original, purely graphic, composite shape a full-featured, ready-to-use component.</div>
In the first recipe , I introduced the composite shape approach and its implementation in gfx. I explained how it works and how to handle the challenge of complex graphical representation like coordinates conversion and user interactions. In this new recipe,...004661urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-70a14e4f-9ce5-46cc-b597-29e945511f08Using a custom Dojo gauges with Dojo MVC bindingChristopheJolif2700024FBYactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2013-01-03T09:24:55-05:002013-01-03T09:26:56-05:00
<div>As promised in my previous article about <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/entry/creating_bullet_graphs_in_dojo_with_the_gauges_package195?lang=en">creating a custom Dojo gauge representing a bullet graph</a>, here is a quicjk article that shows how to leverage the dojox/mvc module to automatically bind the value of the gauge to a data model being modified over time. Thanks to the flexibiltiy of the dojox/mvc module, all you need to do for that is use the dojox/mvc/at binding syntax in your markup to link the value of the BulletGraph gauges instance to a dojo/Stateful</div><div> </div><pre> &lt;head&gt;<br /> &lt;!-- ... loading dojo, css ... --&gt; <br /> &lt;script type=&quot;text/javascript&quot;&gt;<br /> // declaring the model that we will use<br /> var model;<br /><br /> require([&quot;dojo/parser&quot;, &quot;dojo/Stateful&quot;, &quot;dijit/registry&quot;, &quot;dojo/domReady!&quot;],<br /> function(parser, Stateful, registry){<br /> // instantiate a Dojo Stateful model with a single value (could be several ones)<br /> model = new Stateful({value: 10});<br /> // have the model value change regularly (this is random here<br /> // but would typically come from a server udpate).<br /> setInterval(function(){<br /> model.set(&quot;value&quot;, Math.round(Math.random()*150));<br /> }, 3000);<br /> // instantiate the widget from the markup<br /> parser.parse().then(function(){<br /> // enable animation after first display<br /> registry.byId(&quot;gauge&quot;).set(&quot;animationDuration&quot;, 200);<br /> });<br /> });<br /> &lt;/script&gt;<br /> &lt;/head&gt;<br /> &lt;body class=&quot;claro&quot;&gt;<br /> &lt;!-- import at function into the markup context --&gt;<br /> &lt;script type=&quot;dojo/require&quot;&gt;at: &quot;dojox/mvc/at&quot;&lt;/script&gt;<br /> &lt;!-- here we bind the gauge value to the model value using at function, no further update is needed --&gt;<br /> &lt;div data-dojo-type=&quot;bulletgraph/BulletGraph&quot; id=&quot;gauge&quot; style=&quot;height:60px&quot;<br /> data-dojo-props=&quot;value: at(model, 'value'), target: 130, low: 33, medium: 120, high: 150&quot;&gt;&lt;/div&gt;<br /> &lt;/body&gt;</pre><div> </div><div>If you don't use markup you can achieve a similar result by using the exact same syntax programmatically as follows:<br /><br /><br /><br /><br /><pre> new BulletGraph({ value: at(model, &quot;value&quot;), taget: 130, low: 33, medium 120, high: 150 }, &quot;gauge&quot;);</pre><div> </div></div><div>Please note that the BulletGraph class has been slightly modified from the previous blog post to disable animation of the value by default. This is necessary if you don't want to see an animation at the application startup between the gauge default value (50) and the model default value (10). Once the startup has been done, we set back the animationDuration to the desired value so that the effect is preserved. In Dojo 1.9 the dojox/mvc/at function will be smarter and apply before the default value is set thus avoiding to implement similar trick.<br /></div><div> </div><div>Once you have done that you will notice that the gauge value will automatically update when the model value changes every three seconds without requiring us to explicitly set the value on the gauge. You can check it out running live <a href="http://cjolif.github.com/dojo-samples/dgauges-mvc/dgaugemvc.html">here</a>.<br /><br />Using dojox/mvc is obviously not very useful when you have a single value and a single gauge as you could easily achieve the same result with &quot;manual&quot; binding, but when your model is more complex and bound to several graphical elements instead of just one, the separation of concerns that dojox/mvc binding introduces is particularly useful to manage your model &amp; values in one places and your widgets another place without having to manually updates the widgets from the model.</div><div> </div><div>dojox/mvc binding being bi-directional you can also automatically update the model from the widgets. In gauges case it would be useful for example if a gauge was here to set threshold values. Modifying the threshold using mouse or touch interactions would be automatically replicated on the model.</div><div> </div><div>The full source code of this blog post is available here: <a href="https://github.com/cjolif/dojo-samples/tree/master/dgauges-mvc">https://github.com/cjolif/dojo-samples/tree/master/dgauges-mvc</a><div class="css-entry"><wbr />.
</div></div>
As promised in my previous article about creating a custom Dojo gauge representing a bullet graph , here is a quicjk article that shows how to leverage the dojox/mvc module to automatically bind the value of the gauge to a data model being modified over time....005351urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-e41674c3-684f-438c-afe5-288676837af0Introduction to Mobile GridxOliverZ060001XC00activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2012-12-18T09:11:28-05:002012-12-19T05:06:45-05:00
<p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><div>Author: Nate (Pei Wang) </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">GridX mobile is based on dojox.mobile framework and is designed for mobile devices. So it's usually used together with other dojox.mobile widgets. </span><span style="font-size: 10.5pt; font-family: 'Times New Roman';">Compared to list widget, grid provides column header and cell concept, which is especially useful for showing data report. For a quick look and feel, you can visit </span><a href="http://oria.github.com/gridx/gridx/mobile/demos/demo.html">the demo page on the GridX website</a><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><font color="#0000ff">.</font></span></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">Mobile GridX's core features is a small set so that it is easy and fast in simple use cases. Other features are provided by the way of dojoxmixins. By the new introduced property 'data-dojo-mixins' in Dojo 1.8, other features beyond core features are able to be used in HTML declaration way. They can also be called as plug-ins. So typically a GridX mobile declaration markup is like below:</span><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p></div><p /><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">&lt;div data-dojo-type=&quot;gridx/mobile/Grid&quot; </span></p></div><p /><p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> data-dojo-mixins=&quot;gridx/mobile/PullRefresh, gridx/mobile/LazyLoad&quot;</span></p></div><p /><p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> data-dojo-props=&quot;rowCount: 50, showHeader: false, detectProperty: 'Artist'&quot; </span></p></div><p /><p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> jsId=&quot;grid&quot;</span></p></div><p /><p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">&gt;&lt;/div&gt;</span></p></div><p /></blockquote><p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><div class="Section0"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">This code creates a grid with &quot;Pull Refresh&quot; and &quot;Lazy Load&quot; features enabled.</span><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><br /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';">Features</span><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';">1. Easy to customize the look and feel</span><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">Mobile GridX provides clean dom structure, it's easy to style them to different look and feel.</span><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile1.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile1.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';">2. Large number of columns support</span><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">When large number of columns needed, provide a horizontal scroll bar and scrolling with the fixed header.</span><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile2.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile2.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';">3. Tap on the column header to sort</span><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">Tap on the column header to sort.</span></p><div> </div><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile3.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile3.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';">4. Pull refresh to get new rows</span><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">It's a very popular feature for mobile apps. When pull and release, it sends a request to server side to fetch newer data with defined page size.</span><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile4.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile4.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';">5. Internal lazy load support</span><span style="font-weight: bold; font-size: 10.5pt; font-family: 'Times New Roman';"><o:p /></span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="font-size: 10.5pt; font-family: 'Times New Roman';">It's similar with the pagination function of desktop version GridX. When pressing the load more button, it sends a request to server side to fetch older data with defined page size.</span></p><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/mobile5.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /><div> </div><div> </div><div> </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "> </p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="mso-spacerun:'yes'; font-size:10.5000pt; font-family:'Times New Roman'; "><o:p /></span></p></div><p>
</p>
Author: Nate (Pei Wang) GridX mobile is based on dojox.mobile framework and is designed for mobile devices. So it's usually used together with other dojox.mobile widgets. Compared to list widget, grid provides column header and cell concept, which is espec...004804urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-dc6338ca-d5e7-4e62-bae8-7812f5cc2492Introduction to Gridx: Intentions of the DesignOliverZ060001XC00activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2012-12-18T02:20:40-05:002012-12-18T02:20:40-05:00
<p class="p0" style="margin-bottom:0pt; margin-top:0pt; " /><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">I've written an article introducing basics of Gridx </span></font><a href="https://github.com/oria/gridx/wiki/Introduction-to-Gridx">here</a>.<font face="宋体"><span style="font-size: 14px; line-height: 21px;"> If you've never heard of Gridx, you might need that one instead of this one. You can also visit </span></font><a href="http://oria.github.com/gridx/">Gridx's website</a><font face="宋体"><span style="font-size: 14px; line-height: 21px;"> or </span></font><a href="https://github.com/oria/gridx">the GitHub site</a><font face="宋体"><span style="font-size: 14px; line-height: 21px;"> for more details. In this article, I'd like to share some intentions of the design in Gridx, explaining why and how it grew up in the current way. If you have experience using Dojo's </span></font><a href="http://dojotoolkit.org/reference-guide/1.8/dojox/grid/DataGrid.html#dojox-grid-datagrid">DataGrid</a><font face="宋体"><span style="font-size: 14px; line-height: 21px;"> or </span></font><a href="http://dojotoolkit.org/reference-guide/1.8/dojox/grid/EnhancedGrid.html#dojox-grid-enhancedgrid">EnhancedGrid</a><font face="宋体"><span style="font-size: 14px; line-height: 21px;">, you might find it more interesting.</span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><br /></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;"><b>Some History</b></span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Before Gridx, let's talk about some history first.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">dojox/grid/DataGrid has been in Dojo for a very long time. It has been widely adopted and customized. But it is very hard to add new features to it. So EnhancedGrid came to rescue. It tried to add a &quot;plugin system&quot; to DataGrid so that new features can be implemented as plugins. But it was still a nightmare since almost every plugin needs to do a lot of &quot;hacking&quot; to get to work. And sometimes the &quot;hack&quot; of one plugin would conflict with that of another.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">With the increasing number of requirements, the performance of EnhancedGrid got poorer and poorer, while the code size quickly grew. So we decided to write a new grid, which can completely solve these problems. Thus we have Gridx.</span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;"><br /></span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;"><b>The Module System</b></span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">The &quot;x&quot; in Gridx means &quot;extensible&quot;. From our experience of DataGrid, we learned we have to be ready to add a lot of new features to Gridx, while not all of these feature should run at the same time. So the &quot;plugin system&quot; is crucial. This system must minimize the conflicts among &quot;plugins&quot;, and also allow multiple implementations of one feature (like different UIs for sorting). After careful investigation, we find it more suitable to use smaller &quot;modules&quot; instead of big &quot;plugins&quot; as those in EnhancedGrid. For instance, when dealing with a PaginationBar plugin, it actually involves 2 parts: one is the paging mechanism logic, the other is the UI. The paging mechanism is much more likely to be reused than the UI part, and different scenarios might need different UIs, so it makes perfect sense if we split the PaginationBar plugin into two separate but cooperating &quot;modules&quot;.</span></font></p><div> </div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';">define([</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> &quot;gridx/Grid&quot; ,</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> &quot;gridx/modules/pagination/Pagination&quot;, //Require only the modules that are necessary</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> &quot;gridx/modules/pagination/PaginationBar&quot;,</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> ...... </span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';">], function(Grid, Pagination, PaginationBar, ...){</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> var grid = new Grid({</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> ...... </span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> modules: [</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> Pagination, //The logic pagination module, no UI in this module</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> PaginationBar //Providing a bar with link buttons to do paging</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> ]</span></p><p class="p0" style="margin-top: 0pt; margin-bottom: 0pt;"><span style="font-size: 10.5pt; font-family: 'Times New Roman';"> }); </span></p><div><span style="font-family: 'Times New Roman'; font-size: 10.5pt;">}); </span></div><div> </div><div> Two different paging bar UI in Gridx:</div><div><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/pagingbar.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/pagingbar.png" style=" width:400px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a></div><div> </div><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/pagingbardd.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/pagingbardd.png" style=" width:400px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a><div> </div></blockquote><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Things looked good at first, but other problems occurred when we wanted to make some more basic grid components extensible too, like the vertical scrolling bar. The logic for vertical scrolling is the core of DataGrid, because it implements &quot;virtual scrolling&quot;, a mechanism that only renders a few rows at a time while still makes the user believe that all rows are there. But we find the &quot;virtual scrolling&quot; logic is too complicated and unnecessary in many cases, such as a small store. So why not make this feature optional? But to accomplish this we have to make the vertical scroll a &quot;module&quot; too, then why don't we go even further? </span></font><span style="font-size: 14px; line-height: 21px; font-family: 宋体;">So now in Gridx, every UI component is a &quot;module&quot;, including header and body, and vertical scroller and horizontal scroller. The core of Gridx has no UI at all, it's just a logic grid, allowing any possible UI to be implemented upon it. </span><span style="font-size: 14px; line-height: 21px; font-family: 宋体;">Every &quot;module&quot; is nothing but a simple Dojo class plus an implicit life cycle. When you need a feature, load the &quot;module&quot; file of it and add it to your grid. If you don't need the feature, you won't have any code about it. If you have a better implementation of this feature, use that new &quot;module&quot;, and the old module won't bother you anymore.</span></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">The starting up process of Gridx is just to initialize all these modules, one by one. So here comes two questions. </span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">First, which modules go first? Virtual scrolling can not start working if the grid body and header have not been created. PaginationBar must come after the logic paging module Pagination. And the width of the grid body can not be decided when the width of RowHeader or VScroller are not calculated out yet. So there must be some mechanism to manage module initialization order.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Dijit has already been using a simple system for &quot;plugins&quot;: dojo.declare. </span></font><span style="font-size: 14px; line-height: 21px; font-family: 宋体;">&quot;Plugins&quot; are just &quot;Mixin&quot; classes for dijits. This works perfect if the &quot;plugins&quot; do not affect each other. But it will cause problems if initialization order is restricted. We can not require Gridx users to remember which mixins must be put before other mixins. And it'll also be hard to write new mixins, because we'll never know where it'll be placed in the base class list.</span></p><div> </div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="mso-spacerun:'yes'; font-size:10.5000pt; font-family:'Times New Roman'; ">//Declaring a new class: a dijit approach for plugins.</span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="mso-spacerun:'yes'; font-size:10.5000pt; font-family:'Times New Roman'; ">var myGridClass = declare([GridBase, VirtualScrollMixin, SortingMixin, FilterMixin, ...], {}); </span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="mso-spacerun:'yes'; font-size:10.5000pt; font-family:'Times New Roman'; ">var grid = new myGridClass({...}); </span></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><span style="mso-spacerun:'yes'; font-size:10.5000pt; font-family:'Times New Roman'; ">//Any difference for this grid? (Another order of mixins)</span></p><div><span style="font-family: 'Times New Roman'; font-size: 10.5pt;">var myAnotherGridClass = declare([GridBase, SortingMixin, FilterMixin, VirtualScrollMixin,...], {}); </span> </div></blockquote><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">The second question is, what if a module depends on another module, or, how to deal with module dependencies? Some basic/nonUI modules are likely to be reused, such as the logic Pagination module. Different UI implementations can be based on this same module. Similarly, RowDragAndDrop module drags selected rows, so it can take advantage of RowSelect module, but it really doesn't care how the row is selected, single selection or multiple selection, swipe selection or keyboard selection, anything can do the job. So here comes the dependency solving problem.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Dependency solving is already well done by the Dojo loader system. But it can't be used in Gridx because the Dojo loader solves dependency of &quot;implementation&quot; instead of &quot;interface&quot;. That means, once my RowDragAndDrop module depends on my RowSelect module using Dojo loader, it'll be very hard to have a new RowSelect implementation. Yes, I know the &quot;alias&quot; in dojoConfig can do the job, but that'll be for the whole page/app. If you have two grids on one page in need of two different RowSelect implementations, that will not do the trick. </span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">S</span></font><span style="font-size: 14px; line-height: 21px; font-family: 宋体;">o it looks like Gridx needs its own &quot;module/plugin&quot; system, solving dependency and managing module life cycle. This is exactly what the core of Gridx does. </span></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;"> </span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;"><b>The Data Model</b></span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Besides extensibility, another major problem of DataGrid is its data model (dojo store). It is not powerful enough to support common grid operations, especially in the case of virtual scrolling. In DataGrid, virtual scrolling and lazy loading are the same thing. There's no API for you to get a row that is not currently loaded. This is okay when grid is only displaying store data. But it'll be very inconvenient when the data shown in grid is not the same as those in store. This is really a common case since we have &quot;get&quot; and &quot;formatter&quot; widely used in DataGrid column definition. If a page developer wants to go through all the rows in the grid, or just get the data of 100th row (probably not loaded) in current view, he/she has to fetch raw data from the store and then call some private function in grid to transform it into grid data. Note if the grid is sorted or filtered, things get much more complicated because you have to pass the exact same fetch arguments to the store to get the current row index order. All the grid data loading facilities can not be used because they tightly bind to the rendering logic. This feels really wasteful.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">It looks like we definitely need a new data model that best suits grid operations, if we want to do anything interesting programmatically. So we designed &quot;Model&quot; in Gridx.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">The &quot;Model&quot; in Gridx is row-oriented, just like store. But it provides not only raw store data, but also formatted grid data. This &quot;Model&quot; can be accessed via grid.model. It provides a small set of API that can conveniently retrieve data from grid. For example, grid.model.byIndex(99).data, gets the grid data of the 100th row, and grid.model.byIndex(99).rawData, get the store data instead. And this &quot;Model&quot; manages row index in a way that sorting/filtering/row moving are all taken into consideration. So the following code can easily determine where the current first row will be after sorting:</span></font></p><div> </div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">var id = grid.model.indexToId(0);</span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">grid.model.sort([{colId: 'col1'}]);</span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">grid.model.when({id: id}, function(){</span></font></p></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">var index = grid.model.idToIndex(id);</span></font></p></blockquote></blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">});</span></font></p><div> </div></blockquote><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">The &quot;when&quot; function here is another story of Gridx Model. It handles all asynchronous operations, thus makes all other functions in grid.model synchronous. For more details on this function, please refer to </span></font><a href="https://github.com/oria/gridx/wiki/Gridx-Model">this article</a><font face="宋体"><span style="font-size: 14px; line-height: 21px;">.</span></font></p><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Gridx users usually don't need to use these model functions directly. Gridx modules will wrap them into more user-friendly APIs or UI behaviors. For example, when you want to get a row in Gridx, you can just do the following:</span></font></p><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">var row = grid.row(0);</span></font></p></blockquote><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">This row object contains almost everything you need about a row. Same for columns and cells:</span></font></p><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">grid.column(3).moveTo(0);</span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">grid.cell(0, 1).select();</span></font></p></blockquote><div> </div><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;"><b>Conclusion</b></span></font></p><p class="p0" style="margin-bottom:0pt; margin-top:0pt; "><font face="宋体"><span style="font-size: 14px; line-height: 21px;">Two major design topics, module system and data model, are covered here to explain why Gridx is designed in this way. There're always better ideas on grid design, so I'm just starting a topic here and really welcome anyone to join this discussion.</span></font></p><div><br /></div><p>
</p>
I've written an article introducing basics of Gridx here . If you've never heard of Gridx, you might need that one instead of this one. You can also visit Gridx's website or the GitHub site for more details. In this article, I'd like to share some...2210961urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-02e8c98b-66e0-4907-807e-d660d9fcfd24Dojox.Gfx Recipe #1 : Composite Shapes, the basicsPatrickRuzand270002HRN2activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2012-10-09T13:01:52-04:002012-10-09T13:02:42-04:00
The dojox.gfx API provides a set of basic graphic shapes that cover all the 2D shape types spectrum. While this set of shapes allows building all kind of drawings made of individual shapes, complex graphical representations requires a different structure in order to be handled as one unique graphic object. It is particularly true when building business applications
that use specific symbology. The answer to this use case is composite 2D shape.
<h1>Composing 2D shapes : the Group shape<o:p /></h1>
<p>The idea behind composite shape is to group individual shapes into one logical shape that becomes the graphical object the user interacts with. So instead of handling several 2D primitives individually, there is only one logical shape.</p>
<p>In gfx, it is described by the dojox/gfx/Group class. This class inherits from the dojox/gfx/shape.Shape class (which makes it a standard shape), and mixes the Container and Creator implementations. In other words, this is a true graphic object that handles child shapes that can be dynamically created from, added to or removed from it.</p>
<h2>Creating a composite shape</h2>
<p>Let’s take a concrete example of a simple composite shape. In this example, we want to build a symbol like this one:</p><wbr />
<br /><wbr />
<a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/symbol.png
" target="_blank"><wbr /><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/symbol.png" style=" display:block;text-align: center; margin: 0pt auto; position:relative;" /></a><wbr />
<p>
The symbol is made of a round rectangle in the background, a picture on the left, and four text lines. The first step is to create the Group instance that is going to contain the shapes mentioned previously:</p>
<pre>require([&quot;dojo/ready&quot;, &quot;dojox/gfx&quot;, function(ready, gfx){
ready(function(){
gfx.createSurface(&quot;canvas&quot;, 300,300).whenLoaded(this, function(surface){
var symbol = surface.createGroup();
});
});
}]);
</pre>
<p>If you run the application now, you may notice there is nothing displayed on the surface. The reason is that a Group shape has no graphical representation by itself. It is a pure logical container that handles a list of children. Its graphical representation is actually the one of its children.</p>
<p>So the next step is to populate the symbol with the child shapes (aka the picture and the texts). Let’s create the shape:</p>
<pre>[…]
var symbol = surface.createGroup();
var bg = symbol.createRect({x:0, y:0, width:170, height:110, r:6})
.setFill(&quot;#F5F5DC&quot;)
.setStroke({color:&quot;#EED5B7&quot;, width:3});
</pre>
<p>You may have noticed that the rectangle is created directly from the symbol and not from the surface, as the symbol is. It is because the gfx API also provides the shape creation methods on the Group class itself. So, when invoking the Group.createRect() method, the background Rect is added to the Group. From this point, the rect is defined as a child of symbol, and symbol defined as the parent (aka the container ) of bg. Running the application at this point displays the new rectangle, that is, the symbol content.</p>
<p>Note that in case a Group instance must be populated with a shape that has already been created (that is, already exists in the gfx scene), the Group class also exposes the Group.add() method, which takes as parameter a Shape instance to be added to the group. Similarly, it also exposes the Group.remove() and Group.clear() methods to remove a specific shape or all its children.</p>
<p>Following the same methodology, the remaining text shapes are created. Here is the full initialization code:</p>
<pre>var symbol = surface.createGroup();
symbol.createRect({x:0, y:0, width:230, height:110, r:6})
.setFill(&quot;#F5F5DC&quot;)
.setStroke({color:&quot;#EED5B7&quot;, width:3});
symbol.createImage({x:8, y:8, width:55, height:60, src:'male.png'});
symbol.createText({x:67, y:25, text:'John Smith'})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'13pt'});
symbol.createText({x:67, y:45, text:'Software Engineer'})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'10pt'});
symbol.createText({x:67, y:65, text:'Phone: 6060-842'})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'08pt'});
symbol.createText({x:67, y:80, text:'Email: jsmith@mycompany.com'})
.setFill(&quot;black&quot;)
.setFont({family:'sans-serif', size:'08pt'});
</pre>
<h2>Order of drawing, aka z-order</h2>
<p>In the previous example, you may notice that the background rectangle is the first shape we created and added to the group. The reason we did it this way is because we want the other shapes (the image and the texts) to be drawn on top of the background. Indeed, a Group draws its children in the order they have been added, which means the first shape in the children list is drawn first, then the second in the list, and so on and so forth, to the last one, which is drawn on top of all shapes. In other words, the first shape in the list lies at the back of the drawing, and the last one at the front of the drawing. This drawing order is usually called the z-order (‘z’ being the name of an imaginary axis going from the screen to the user eye).</p>
<a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/zorder.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/zorder.png" style=" display:block; margin: 0pt auto; position:relative;" /></a>
<p>
In case the z-order needs to be changed dynamically after the creation of the shapes, the Gfx API provides the following methods :
</p><ul>
<li>Shape.moveToFront(): this method moves the shape to the end of its parent’s list, or in other words, to the front of the drawing (will be drawn on top of the other children).</li>
<li>Shape.moveToBack(): this method moves the shape to the start of its parent’s list, or in other words, to the back of the drawing (will be drawn below the other children).</li>
</ul>
<p>Note: In 1.9, the z-order API will be extended to allow specifying the position of a child shape relative to another child. As of this writing, the proposed API is:</p>
<ul>
<li>Shape.moveBefore(refShape): moves the shape before the specified reference shape.</li>
<li>Shape.moveAfter(refShape): moves the shape after the specified reference shape.</li>
<li>Group.insertBefore(newShape, refShape): insert the new shape before the specified reference shape.</li>
<li>Group.insertAfter(newShape, refShape): insert the new shape after the specified reference shape.</li>
</ul>
<p>The corresponding dojo ticket that describes this proposal can be found here: <a href="http://bugs.dojotoolkit.org/ticket/15296">http://bugs.dojotoolkit.org/ticket/15296 </a>. Feel free to comment the proposed API on the ticket. You can test it locally by applying the patch attached to the ticket on a trunk or 1.8 src install. Also, bear in mind that as of this writing, it is still a proposal and no commitment can be made for its availability in the next release.
</p>
<h1>Dealing with transforms</h1>
As stated previously, one of the benefits of the composite approach is the ability to handle a set of shapes as only one logical shape. This is particularly useful when one needs to handle interactions at the group level (more on this later) or change the overall position of the group. For example, in the latter case, instead of changing the position of every symbol shapes, one would only have to translate the Group shape.
<h2>Transform propagation</h2>
<p>An easy and quick way to see how a container transform impacts the children shapes is to use the dojox/gfx/Moveable class. The purpose of this class is to easily enable moving a shape using a mouse drag interaction. The underlying implementation consists of applying a translation transform to the dragged shape. So, taking the previous example, let’s add the corresponding module to the list of the require and make our Group instance a Moveable:</p>
<pre>require([…, &quot;dojox/gfx/Moveable&quot;],
function(…, Moveable){
…
new Moveable(symbol);
</pre>
<p>From this point, dragging any of one of the symbol shapes (the image, text, etc.) moves the whole symbol. Or, in term of API, the translation transform set on the group while dragging it impacts all the children shapes.</p>
<p>So, what’s happening ? As we saw, a Group is a shape, and as such, may have its own transform, as all shapes in gfx do. Also, remember that a shape transform is a matrix that converts the shape coordinates/geometry into its parent coordinate system. For example, a rect with the {x:10,y:0,width:100,height:100} geometry and a translation transform of {dx:10, dy:20} will be considered (i.e. displayed) at the position {x:20,y:20} in the parent surface.</p><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/transform.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/transform.png" style=" display:block; margin: 0pt auto; position:relative;" /></a>
<p>So, similarly, setting a transform on a Group has the very same meaning: the “geometry” of the group (i.e.the set of its children geometry) is converted by the group transform to its parent coordinate system.</p>
<p>Note that transforms are cumulative. If a shape that has its own transform and is added to a Group which itself has a transform too, both combined together. So, for example, if a rect has a translation transform {dx:5} and the group has a translation transform {dx:10}, the shape will be translated by a delta-x of 15 in the surface.</p><p>
</p><h2>Coordinates conversion from container to container</h2>
<p>A usual pattern when using Group is to have nested containers (containers inside containers). When such nested hierarchy exists, it is often required to compute coordinates of nested children into the coordinate system of another container. Gfx does not provide anything built-in for this, but the required API to implement it is there.</p>
<p>The following method computes the transform from the shape coordinate system, taking into account the shape transform, to the specified container coordinate system.</p>
<pre>function transformToContainer(shape, /*dojox/gfx/Group*/container){
// summary:
// Gets the transform from the shape coordinate system (i.e
// taking into account the shape transform)
// to the specified container coordinate system.
// container: dojox/gfx/Group:
// the target container.
if(!container){
return null;
}
if(container === shape){
return matrix.identity;
}
var t1 = shape._getRealMatrix() || matrix.identity;
var t2 = container._getRealMatrix() || matrix.identity;
return matrix.multiply(matrix.invert(t2), t1);
}
</pre>
<p>
A brief explanation for the sake of completeness: the code first gets the two transforms that goes up to the surface for each container (the _getRealMatrix calls), and then computes the new matrix that first applies the shape-to-surface transform (t1) then the surface-to-container transform (which is the invert of t2, the container-to-surface transform).</p>
<p>With this method, it is easy to express a coordinate in the coordinate system of another container. For example, still using the previous sample, assume we want to draw a line whose one extremity is attached to the picture, right in the center of the face, and we want the line end sticks to this connection point when the symbol is dragged. Let’s say the center of the face in the image corresponds to the {x:37, y:33} point (let’s call it ‘facePt’) in the image coordinate system (that is, {0,0} being the top left corner of the image). Also, the line is not part of the symbol but is added to another independent container (let’s call it ‘link’). So, in term of coordinates, it means the {x2,y2} property (i.e. the line end point) of the Line shape must be computed in the coordinate system of the ‘link’ container (because this container is the Line parent) from ‘facePt’ whose coordinates are expressed in the Image shape coordinate system. In other words, we need to convert ‘facePt’ from the image coordinate system to the link coordinate system.</p><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/coordsys.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/coordsys.png" style=" display:block; margin: 0pt auto; position:relative;" /></a>
<p>Here is the code of the method that computes the point coordinates:</p>
<pre>var facePt = {x:37,y:33};
var computeLineEnd = function(){
var t = transformToContainer(symbol.img, link);
var pt = matrix.multiplyPoint(t, facePt);
line.setShape({x2:pt.x, y2:pt.y});
};
</pre>
<p>Since the attach point of the line must sticks to the “face” when dragging the symbol, the point location must me recomputed on each move. To do this, a onMoved handler is set on the Moveable instance to get notified of the move:</p>
<pre>var angle = 0;
new Moveable(symbol).onMoved = function(){
// rotate the image to illustrate the transform chain
angle +=10;
symbol.img.setTransform(matrix.rotategAt(angle,37,53));
// now computes the new position
computeLineEnd();
};
// make the link moveable too
new Moveable(link).onMoved = computeLineEnd;
</pre>
<p>Run the application and drag the symbol and/or the link: the attach point of the line sticks to the “face” of the symbol.</p><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/drag.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/drag.png" style=" display:block; margin: 0pt auto; position:relative;" /></a>
<h2>Interaction</h2>
<p>As with any Gfx shapes, the Group shape supports input events handling via the Shape.connect() API. This method allows registering event handlers on a Group shape in order to be notified of any events that occurred on one of the Group children. </p>
<p>For example, let’s add some interactions to the previous sample. We would like the user be able to change the color of the symbol background rectangle on a mouse click, wherever the click occurs in the symbol. We could register an event handler on the rect shape itself, but in this case, clicking on the image or the text would not notify the handler. So instead, the handler is registered on the symbol, so that it receives the event notifications from `all its children.</p>
<pre>symbol.connect(&quot;onclick&quot;, function(evt){
symbol.children[0].setFill(&quot;blue&quot;);
});
</pre>
<p>Note that the parameter received in the event handler is the native DOM event. In some cases, you may also need to have a reference to the gfx shape that fired the event. To this purpose, the 1.7 release introduced a new expando property on the event (named “gfxTarget”) that references the target gfx shape. To illustrate its use, let’s change the previous “onclick” event handler implementation, so that when the user clicks on the symbol, it highlights the symbol child shape that has been clicked. The highlight is done via a new Rect shape that is added to the symbol, and whose geometry is set to the target shape bounding box.</p>
<pre>var ghost;
symbol.connect(&quot;onclick&quot;, function(e){
var s = e.gfxTarget,
// Text have no bbox. In this case, get the background shape bbox
bbox = s.getBoundingBox() || symbol.children[0].getBoundingBox();
bbox = matrix.multiplyRectangle(s.getTransform() || matrix.identity, bbox);
if(!ghost){
ghost = symbol.createRect().setStroke({width:2,style:&quot;Dash&quot;,color:&quot;black&quot;});
}
ghost.setShape(bbox);
});
</pre>
<h1>Conclusion</h1><div>
When building business applications that require rich visualization, the composite approach and its Gfx implementation via the Group class offer an efficient answer to implement complex graphical representation. In this first recipe, we presented the basics to define a shape container by means of the Group class. We explained how to deal with deep nested container hierarchy to build complex graphic symbol and handle coordinates conversion as well as how to control the rendering via the children z-order. Finally, we illustrated how to handle user interactions at the symbol level. In the next recipe, we will learn how to go one step further in representing business data with rich graphic symbol, creating our own Group subclass and implementing the symbol behavior according to the data logic.
</div><div> </div><div>The sample source code used for this article is freely available <a href="https://github.com/pruzand/dojox-gfx-samples">here</a>. </div>
The dojox.gfx API provides a set of basic graphic shapes that cover all the 2D shape types spectrum. While this set of shapes allows building all kind of drawings made of individual shapes, complex graphical representations requires a different structure in...007577urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-d4933334-c3b0-4d4e-a4cf-f3b46309ee3eCreating Bullet Graphs in Dojo with the gauges packageChristopheJolif2700024FBYactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2012-08-23T10:29:14-04:002012-09-11T04:09:25-04:00
<p>A while ago I wrote <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/elixir/entry/creating_bullet_graph_in_flex_with_ibm_ilog_elixir_gauges_1?">a series of blog posts</a> about a data visualization component called the Bullet Graph that I had developed on top of IBM ILOG Elixir gauges framework. This component is basically trying to mix
some of the bar graph and horizontal gauge concepts to take the best of
both worlds and convey data information in a more efficient way. The
component was first described by <a href="http://www.perceptualedge.com/">Stephen Few</a> in its <i>Information Dashboard Design</i> book. You will find more information on the bullet graph on its <a href="http://en.wikipedia.org/wiki/Bullet_graph">Wikipedia page</a> or in its <a href="http://www.perceptualedge.com/articles/misc/Bullet_Graph_Design_Spec.pdf">specification</a>. In this new blog post I will show how you can easily create the same bullet graph component using an open web technologies stack and in particular the new gauges framework from Dojo 1.8 release which are also provided as part of WebSphere Application Server 8 and Web 2.0 and Mobile Feature Pack 1.1.0.2. This will allow you to easily deploy this component either on regular Web application or in mobile applications base on Dojo Mobile.</p><div> </div>
<p>In the end, what we want to achieve is something that looks like this
example of bullet graph available on Wikipedia (with explanations in
red):</p>
<br />
<a href="http://upload.wikimedia.org/wikipedia/en/e/e1/Bullet_graph_labeled.png" target="_blank"><img alt="image" src="http://upload.wikimedia.org/wikipedia/en/e/e1/Bullet_graph_labeled.png" style="margin: 0pt auto; display: block; text-align: center; position: relative;" /></a>
<br />
<p>The first step in order to achieve this is to create a Dojo module &amp; the corresponding class that will represent our bullet graph. As we already described a bullet graph is a particular kind of horizontal gauge that's why the BulletGraph class inherits from the dojox/dgauges/RectangularGauge class that represents horizontal &amp; vertical gauges in Dojo. In order to extend it you need to create a BulletGraph.js file with the following content:</p><div> </div><pre>define([&quot;dojo/_base/declare&quot;, &quot;dojox/dgauges/RectangularGauge&quot;], function(declare, RectangularGauge){ <br /> // the AMD module declares a new (empty) BulletGraph class inheriting from RectangularGauge and returns it<br /> return declare(&quot;samples.BulletGraph&quot;, RectangularGauge, {<br /> });<br />});</pre><div> </div><div>We then need to define the core properties that defines the model of a bullet graph: <br /></div><ul><li>the performance measure which is represented by the gauge value property</li><li>the comparative measure which is represented by the gauge target property<br /></li><li>the qualitative ranges bad, satisfactory and good which are bound by the gauge minimum value (hardcoded to 0 for the bullet graph) as well as the low, medium and high threshold properties.</li></ul><p>We also need to define a logical scaler that represents the limits of the bullet graph and extents from the minimum value (0) to the maximum one (high threshold property). There are several kind of scalers like logarithmic or linear ones and you can define your own scalers. For the bullet graph a simple linear scaler is used.<br /></p>
<div>The core properties are usually defined and assigned as fields of the class while the scaler is instantiate in the constructor as follows:<br /></div><div><pre>define([&quot;dojo/_base/declare&quot;, &quot;dojox/dgauges/RectangularGauge&quot;], function(declare, RectangularGauge){ <br /> // the AMD module declares a new (empty) BulletGraph class inheriting from RectangularGauge and returns it<br /> return declare(&quot;samples.BulletGraph&quot;, RectangularGauge, {<br /> value: 50,<br /> target: 80,<br /> low: 50,<br /> medium: 75,<br /> high: 100,<br /><br /> constructor: function(){<br /> var scaler = new LinearScaler({<br /> // minimum is set to 0 which is the default<br /> // maximum is set to the high threshold <br /> maximum: this.high<br /> });<br /> }<br /> });<br />});<br /></pre></div><div>Now that the core logical values of the bullet graph are defined we need to create the graphical objects of the gauge that will be in charge of rendering each of these values. There are several kind of graphical elements and indicators that can be put in a gauge. For the bullet graph we will use:</div><div><ul><li>dojox/dgauges/RectangularScale: a graphical element in charge of rendering the scaler of a gauge</li><li>dojox/dgauges/RectangularRangeIndicator: a graphical indicator in charge of rendering a range of values from a minimum to a maximum value on a scale. This is typically represented by a rectangle or a triangular ramp.<br /></li><li>dojox/dgauges/RectangularValueIndicator: a graphical indicator in charge of rendering a single value on the gauge. This is typically represented by a thumb or a marker.</li></ul><p>Once a graphical element has been instantiated it can be added to the gauge using the GaugeBase (from which RectangularGauge inherits) addElement method. In the following code extracted from the gauge constructor we are creating the RectangularScale connected to the previously created scaler and adding it to the gauge giving it the &quot;scale&quot; name:<br /></p><div><pre>this._scale = new RectangularScale({<br /> scaler: scaler,<br /> labelPosition: &quot;trailing&quot;,<br /> paddingTop: 30,<br /> paddingBottom: 23,<br />});<br />this.addElement(&quot;scale&quot;, this._scale);<br /></pre><div> The paddingTop and Bottom properties allows to specify how far from the top and bottom of the gauge content box the scale will be positioned. The labelPosition property allows to specify where the labels of the scale will be rendered. For an horizontal scale the &quot;trailing&quot; position means that the label will be rendered below the scale. The default would have lead to render them above the scale which is not corresponding to what a bullet graph should look like.</div><div> </div></div></div>
<div>Similarly once a graphical indicator has been created it can be added to a scale use the ScaleBase addIndicator method. The following code is creating and adding to the scale the five indicators. They correspond to the five core properties of a bullet graph: the high, medium and low thresholds for performance measurement, the
measure itself which represents the value of the bullet graph and the
target or comparative measure.</div><div> <br /><pre>var high = new RectangularRangeIndicator({<br /> start: 0,<br /> value: this.high,<br /> paddingTop: 0<br />});<br />this._scale.addIndicator(&quot;high&quot;, high);<br /><br />var medium = new RectangularRangeIndicator({<br /> start: 0,<br /> value: this.medium,<br /> paddingTop: 0<br />});<br />this._scale.addIndicator(&quot;medium&quot;, medium);<br /><br />var low = new RectangularRangeIndicator({<br /> start: 0,<br /> value: this.low,<br /> interactionMode: &quot;none&quot;,<br /> paddingTop: 0,<br />});<br />this._scale.addIndicator(&quot;low&quot;, low);<br /><br />var measure = new RectangularRangeIndicator({<br /> start: 0,<br /> value: this.value,<br /> paddingTop: 10<br />});<br />this._scale.addIndicator(&quot;measure&quot;, measure);<br /><br />var target = new RectangularValueIndicator({<br /> value: this.target<br />});<br />this._scale.addIndicator(&quot;target&quot;, target);</pre><div>As for the scale the padding properties allow to specify where the various indicators must be laid out. Each indicator value is initialized with the initial value of the corresponding core property of the bullet graph.</div><div> </div></div>A shown in the following example, you can now use the bullet graph in a Dojo application by creating a sample HTML page that imports Dojo and instantiate the bullet graph with custom values for the various core properties using the Dojo parser:<pre>&lt;html&gt;<br />&lt;head&gt;<br /> &lt;title&gt;Bullet Graph&lt;/title&gt;<br /> &lt;style type=&quot;text/css&quot;&gt;<br /> @import &quot;../dojo/resources/dojo.css&quot;;<br /> @import &quot;../dijit/themes/claro/claro.css&quot;;<br /> &lt;/style&gt;<br /> &lt;script type=&quot;text/javascript&quot; src=&quot;../dojo/dojo.js&quot; data-dojo-config=&quot;parseOnLoad: true, async: true&quot;&gt;&lt;/script&gt;<br /> &lt;script type=&quot;text/javascript&quot;&gt;require([&quot;dojo/parser&quot;], function(parser){});<br />&lt;/head&gt;<br />&lt;body class=&quot;claro&quot;&gt;<br /> &lt;div data-dojo-type=&quot;dijit/layout/BorderContainer&quot; data-dojo-props=&quot;gutters: true, liveSplitters: true&quot; id=&quot;borderContainer&quot;&gt;<br /> &lt;div data-dojo-type=&quot;dijit/layout/ContentPane&quot; style=&quot;background-color: #fafafa;&quot; data-dojo-props=&quot;splitter: true, region: 'center'&quot;&gt;<br /> &lt;div id=&quot;g&quot; data-dojo-type=&quot;samples/BulletGraph&quot; style=&quot;height:60px&quot; data-dojo-props=&quot;value: 135, target: 130, low: 100, medium: 125, high: 150&quot;&gt;&lt;/div&gt;<br /> &lt;/div&gt;<br /> &lt;/div&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;</pre>
<div> </div><p>Doing so you'll get the following rendering:</p> <a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/bulletgraph1.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/bulletgraph1.png" style=" width:400px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <br />
<br />
<p>As you can see even if all the core properties we are interested in
are present and rendered a the correct position on the scale, this is still not really looking like a bullet graph as we have not worked on the customization of the various indicators including their colors, thickness and other rendering properties.</p><div>In the following code several types of properties are added to the indicators to match the exact bullet graph expected rendering: </div><div><ul><li>interactionMode: by default an indicator can be edited using mouse gestures or touch gestures. To prevent this behavior you should put interactionMode to &quot;none&quot;. This is what we are doing for the bullet graph as it is not supposed to be editable as used just to monitor existing values.<br /></li><li>fill: the fill object (color, gradient...) used to draw the indicator in dojox/gfx. Here we are levarage dojox/color to compute different intensities of a single hue (black here) to comply with bullet graph specification.<br /></li><li>stroke: this defines the outline of the indicator, if no outline needs to be drawn like in the bullet graph you should put it to null</li><li>startThickness &amp; endThickness: the tickness of the indicator at its initial and last position. To get a rectangular indicator they must be set to a single value, to get a ramp indicator you can use different values.</li><li>indicatorShapeFunc: this allows to override the way an indicator is drawn by providing a function that returns a dojox/gfx shape for the indicator. We need that for the target indicator in our case. By default an arrow is drawn where we want a vertical bar.<br /></li></ul></div><div><pre>var high = new RectangularRangeIndicator({<br /> // ...<br /> interactionMode: &quot;none&quot;,<br /> fill: color.fromHsv(0, 0, 90),<br /> stroke: null,<br /> startThickness: 30,<br /> endThickness: 30<br />});<br /><br />var medium = new RectangularRangeIndicator({<br /> // ...<br /> interactionMode: &quot;none&quot;,<br /> fill: color.fromHsv(0, 0, 60),<br /> stroke: null,<br /> startThickness: 30,<br /> endThickness: 30<br />});<br /><br />var low = new RectangularRangeIndicator({<br /> // ...<br /> interactionMode: &quot;none&quot;,<br /> fill: color.fromHsv(0, 0, 40),<br /> stroke: null,<br /> startThickness: 30,<br /> endThickness: 30<br />});<br /><br />var measure = new RectangularRangeIndicator({<br /> // ...<br /> interactionMode: &quot;none&quot;,<br /> fill: [0, 0, 0],<br /> stroke: null,<br /> startThickness: 10,<br /> endThickness: 10<br />});<br /><br />var target = new RectangularValueIndicator({<br /> // ...<br /> interactionMode: &quot;none&quot;, <br /> indicatorShapeFunc: function(group){<br /> return group.createLine({ x1: 0, y1: 0, x2: 0, y2: 16 }).setStroke({ color: &quot;black&quot;, width: 3 }).setFill([250, 0, 0]);<br /> }<br />}); </pre><div> </div><div>With these modifications we get the expected rendering behavior of a bullet graph:<br /><br /><a '="" href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/bulletgraph2.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/bulletgraph2.png" style=" width:400px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <br /></div></div><div> We could stop here and have a working bullet graph. That said if at runtime you modify one of the gauge properties dynamically after its creation this would not be reflected in drawing. Indeed all the gauge elements and indicators were created and set in the gauge constructor and not updating later.</div><div> </div><div>Fortunately this is relatively easy to enable this. You first have to enable the gauge refresh mechanism for the core properties you want to be dynamically set at runtime, for that you add the following code in the constructor:</div><div><pre>this.addInvalidatingProperties([&quot;target&quot;, &quot;value&quot;, &quot;low&quot;, &quot;medium&quot;, &quot;high&quot;]);</pre><div>This will make sure a refreshRendering method is called when one of these core properties value is changed. You can then define the refreshRendering method to re-bind the new values to the gauge elements and indicators as follows:<br /><br /><br /><pre>refreshRendering: function(){<br /> this.inherited(arguments);<br /><br /> this._scale.scaler.set(&quot;maximum&quot;, this.high);<br /> this._scale.getIndicator(&quot;target&quot;).set(&quot;value&quot;, this.target);<br /> this._scale.getIndicator(&quot;measure&quot;).set(&quot;value&quot;, this.value);<br /> this._scale.getIndicator(&quot;low&quot;).set(&quot;value&quot;, this.low);<br /> this._scale.getIndicator(&quot;medium&quot;).set(&quot;value&quot;, this.medium);<br /> this._scale.getIndicator(&quot;high&quot;).set(&quot;value&quot;, this.high);<br />}</pre><div> </div>With this blog post we have seen how easy it is to create your own custom gauge using the dojox/dgauges package from Dojo. In a future blog post we will see how to use several instances of this gauge and automatically bind them to your data using the dojox/mvc package.</div></div><div><br /> </div>
A while ago I wrote a series of blog posts about a data visualization component called the Bullet Graph that I had developed on top of IBM ILOG Elixir gauges framework. This component is basically trying to mix
some of the bar graph and horizontal gauge...008041urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-9f58dbb9-1aa4-463a-bc26-415dd9e82f62Combining Columns with Dojo ChartingChristopheJolif2700024FBYactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2012-07-04T11:08:39-04:002012-07-04T11:11:42-04:00
<div>When displaying a column chart you might want the user to be able to choose whether he wants the chart data to be combined into a single column per data item (stacked columns) or not (clustered columns). This is pretty easy in Dojo to create either a combined plot (using dojox/charting/plot2d/StackedColumns) or a clustered one (using dojox/charting/plot2d/ClusteredColumns). The purpose of this short blog post is to show you how to allow the user of your application to navigate between these two modes.<br /></div><div> </div><div>For that the application listens to the dojo double tap event (from dojox/gesture/tap) in order to be able on a mobile devices to react to a double tap action and on a desktop to the double click action. In the callback for this event we will trigger the action that will navigate from one mode to the other. That way each time the user double tap the chart he will move from the combined mode to the clustered one and conversely.</div><div> </div><div><pre>require([&quot;dojox/charting/Chart&quot;, &quot;dojo/on&quot;, &quot;dojox/gesture/tap&quot;, <br /> &quot;dojox/charting/plot2d/StackedColumns&quot;, &quot;dojox/charting/plot2d/ClusteredColumn&quot;], <br /> function(Chart, on, tap, StackedColumns, ClusteredColumns){<br /> // build our chart...<br /> var chart = ...;<br /> // consider we start in clustered mode<br /> var clustered = true;<br /> var switchClustering = function(){ <span class="k"><br /> if</span><span class="p">(</span><span class="nx">clustered</span><span class="p">){</span><span class="nx"><br /> chart</span><span class="p">.</span><span class="nx">addAxis</span><span class="p">(</span><span class="s2">&quot;y&quot;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">vertical</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">fixLower</span><span class="o">:</span> <span class="s2">&quot;major&quot;</span><span class="p">,</span> <span class="nx">fixUpper</span><span class="o">:</span> <span class="s2">&quot;major&quot;</span><span class="p">,</span> <span class="nx"><br /> includeZero</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">majorTickStep</span><span class="o">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">max</span><span class="o">:</span> <span class="mi">30000</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&quot;Revenues&quot;</span> <span class="p">});<br /> chart.</span><span class="nx">addPlot</span><span class="p">(</span><span class="s2">&quot;default&quot;</span><span class="p">,</span> <span class="p">{</span><span class="nx">type</span><span class="o">:</span> <span class="nx">StackedColumns</span><span class="p">,</span> <span class="nx">gap</span><span class="o">:</span> <span class="mi">5</span><span class="p">,</span> <span class="nx">maxBarSize</span><span class="o">:</span> <span class="mi">35</span><span class="p">,</span> <span class="nx"><br /> vAxis</span><span class="o">:</span> <span class="s2">&quot;y&quot;</span><span class="p">,</span> <span class="nx">animate</span><span class="o">:</span> <span class="kc">true</span><span class="p">});<br /> </span><span class="nx">chart</span><span class="p">.</span><span class="nx">render</span><span class="p">();<br /> </span><span class="nx">clustered</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span><span class="p"><br /> }</span><span class="k">else</span><span class="p">{<br /> </span><span class="nx">chart</span><span class="p">.</span><span class="nx">addAxis</span><span class="p">(</span><span class="s2">&quot;y&quot;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">vertical</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">fixLower</span><span class="o">:</span> <span class="s2">&quot;major&quot;</span><span class="p">,</span> <span class="nx">fixUpper</span><span class="o">:</span> <span class="s2">&quot;major&quot;</span><span class="p">,</span> <br /> <span class="nx">includeZero</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">majorTickStep</span><span class="o">:</span> <span class="mi">1000</span><span class="p">,</span> <span class="nx">max</span><span class="o">:</span> <span class="mi">20000</span><span class="p">,</span> <span class="nx">title</span><span class="o">:</span> <span class="s2">&quot;Revenues&quot;</span> <span class="p">});<br /> chart.</span><span class="nx">addPlot</span><span class="p">(</span><span class="s2">&quot;default&quot;</span><span class="p">,</span> <span class="p">{</span><span class="nx">type</span><span class="o">:</span> <span class="nx">ClusteredColumns</span><span class="p">,</span> <span class="nx">gap</span><span class="o">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nx">maxBarSize</span><span class="o">:</span> <span class="mi">35</span><span class="p">,</span> <br /> <span class="nx">vAxis</span><span class="o">:</span> <span class="s2">&quot;y&quot;</span><span class="p">,</span> <span class="nx">animate</span><span class="o">:</span> <span class="kc">true</span><span class="p">});<br /> </span><span class="nx"> chart</span><span class="p">.</span><span class="nx">render</span><span class="p">();<br /> </span><span class="nx">clustered</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span><span class="p"><br /> }<br /> };<br /><span class="nx"> on</span><span class="p">(</span><span class="nx">dom</span><span class="p">.</span><span class="nx">byId</span><span class="p">(</span><span class="s2">&quot;chart&quot;</span><span class="p">),</span> <span class="nx">tap</span><span class="p">.</span><span class="nx">doubletap</span><span class="p">,</span> <span class="nx">switchClustering</span><span class="p">);</span><br />});</span></pre><div> </div></div><div>As you can see in the example in addition to changing the plot type we also change the configuration of the axis as the scale of the data is different when combining them or not. Finally we are calling the chart render method to make sure the chart is correctly updated following these changes.<br /><br /><br /><div>You can give <a href="http://cjolif.github.com/dojo-samples/charting-combine/combine.html">a try</a> to the example online or grab the full code source of this blog post on github at <a href="https://github.com/cjolif/dojo-samples/tree/master/charting-combine">https://github.com/cjolif/dojo-samples/tree/master/charting-combine</a>. The code has been tested against the current Dojo 1.7 release and the <a href="http://download.dojotoolkit.org/release-1.8.0b1/">1.8 beta</a>. If you download the beta you will also be able to checkout charting enhancements like automatic drop of superfluous labels on axis, consistent support of drop shadows or ability to dynamically style data points using a styling function.<br /></div></div><div> </div><div> <br /></div><div> </div>
When displaying a column chart you might want the user to be able to choose whether he wants the chart data to be combined into a single column per data item (stacked columns) or not (clustered columns). This is pretty easy in Dojo to create either a combined...005265urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-83c0b4d6-8d5b-49d8-93a8-33112aa44800Announcing beta for the next generation of development toolskennas060001VDYXactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-12-21T23:33:05-05:002011-12-21T23:33:05-05:00<div>Rational Application Developer V8.5 Beta and WebSphere Application Server Developer Tools for Eclipse v8.5 Beta have been announced on <a href="http://ibm.co/uLO4QM">WASdev</a>.</div><div><br /></div><div><b>IBM® WebSphere Application Server Developer Tools for Eclipse V8.5 Beta</b></div><div>IBM® WebSphere Application Server Developer Tools for Eclipse (Beta) provides a development environment for building and deploying Java EE and OSGi applications. The workbench provides support for WebSphere Application Server V8.5 Beta, V8.5 Beta Liberty Profile, V8.0, and V7.0. It contains tools for managing and publishing to a local or remote server, and for controlling incremental publishing. Web development tools help developers create Web 2.0 and mobile applications. Included is a Rich Page Editor, a multi-tabbed editor that makes it easy to edit HTML files, add Dojo widgets to HTML pages, and create and edit web pages for mobile devices.</div><div>When combined with Eclipse SDK and Eclipse Web Tools Platform, WebSphere® Application Server Developer Tools for Eclipse (Beta) provides a lightweight environment for developing Java EE applications.</div><div><br /></div><div><b>IBM® Rational Application Developer V8.5 Beta</b></div><div>Rational® Application Developer V8.5 Beta provides an award-winning development environment for building applications that run on WebSphere Application Server V8.5. The workbench contains wizards and editors helping you build standards-compliant, business-critical Java EE, Web 2.0, and Service Oriented Architecture applications. Integration with Rational Team Concert provides a team-based environment that helps developers share information and work collaboratively. Code quality tools help teams find and correct problems early before they escalate into expensive problems.</div><div><br /></div>
Rational Application Developer V8.5 Beta and WebSphere Application Server Developer Tools for Eclipse v8.5 Beta have been announced on WASdev . IBM® WebSphere Application Server Developer Tools for Eclipse V8.5 Beta IBM® WebSphere Application Server Developer...004732urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-2edccf18-3971-4e24-baa4-5e8f1f4c022fDojo Diagrammer 1.1.1: benchmarking figuresAdrianVasiliu270002HS2Bactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-12-21T12:52:07-05:002011-12-22T04:37:14-05:00
<div> </div><div>In this post, you can find benchmarking figures for Dojo Diagrammer 1.1.1 using Dojo 1.7.0 and covering various graph layout and other graph/diagram operations. Before presenting the numbers, here is some introductory information and analysis:<br /></div><div> </div><div>General remarks:<br /><ul><li>All benchmarks figures shown in this post have been obtained by running benchmarking test programs on the following configuration: </li><ul><li>Client part: Lenovo ThinkPad T9400 Intel Core 2 Duo 2.53 GHz, Windows 7 Professional SP1</li><li>Server part (server-side layout): VM Linux CentOS 2.4Ghz, Tomcat 7.0.21.</li></ul><li>We take various approaches to increase the reliability of the benchmarks: running once for filling browser's cache and measuring at a second run (thus systematically measuring the performance when all code is in the cache, rather than measuring a mix of situations), and averaging the duration over a number of successive runs. </li><li>Despite that, it remains hard to measure the performance in a reliable, stable manner. This might be due to various factors, such as the non-deterministic execution of garbage collection in the JavaScript engine, variations of computer's or network's speed, caching policy and so on. The variation of network speed and of server's response time are in particular responsible for the variability of server-side benchmark results. </li><li>Therefore, these benchmarking figures should be <span style="color: rgb(34, 34, 34); font-family: arial, sans-serif; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 16px; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); font-size: small; display: inline !important; float: none; ">cautiously </span>interpreted. They are more precise than a simple order of magnitude sizing, but they are not to be taken as very precise numbers.<br /></li></ul></div><div>The performance varies depending on <b>browsers</b> (more exactly on the speed of their JavaScript and rendering engines):<br /></div><div><ul><li>Chrome and Firefox are the fastest. Over the last years, the performance comparison between Chrome and Firefox evolved. Until some time ago, Chrome used to be faster than Firefox for most Dojo Diagrammer operations, however the latest releases (Chrome 16 and Firefox 8) are roughly equally fast.</li><li>Internet Explorer is, by far, the slowest. Here, we measured IE8; the situation is worse for IE6, and significantly better for IE9. The rendering mode used for Dojo widgets matters a lot. VML rendering is known to have dramatic performance problems, while SVG and Canvas rendering modes are preferable. <br /></li></ul></div><div>Finally, remarks about the performance of the graph layout algorithms of Dojo Diagrammer:</div><div><ul><li>In terms of speed, how the various <b>graph layout algorithms</b> compare?</li><ul><li>The fastest algorithms are TreeLayout and GridLayout. </li><li>The medium speed category includes HierarchicalLayout. Its performance mostly depends on the &quot;link density&quot;, that is on the ratio obtained by dividing the number of links by the number of nodes. We can also include in this medium category the link layout algorithms: ShortLinkLayout and LongLinkLayout (the first being usually faster than the second).</li><li>The slow category includes ForceDirectedLayout.</li></ul><li>Among the two solutions offered by Dojo Diagrammer for laying out the graphs - <b>client-side</b> and <b>server-side layout</b> - which one is the fastest? There is no simple answer. Factors to be considered: </li><ul><li>Per se, the execution of the layout should be faster on the server, because in general Java code runs faster than JavaScript.</li><li>However, this speed difference depends on various factors such as hardware and software (mainly browser) used on the client, software and hardware on the server, server response time and so on.</li><li>Moreover, for laying out the graph on the server, Dojo Diagrammer needs to send to the server a JSON representation of the client-side graph, then it receives it back from the server with updated node location and link shape information, which then needs to be applied on the client-side graph. This takes an amount of time which might be smaller or higher than the speed gain due to the faster execution on the server.</li><li>The overall speed comparison is therefore highly dependent on the numerous factors mentioned above. In practice, from the performance standpoint, the server-side solution should be useful especially when using slow algorithms such as ForceDirectedLayout. Also, it can improve the performance for browsers with low JavaScript/Dojo performance such as the old versions of Internet Explorer, however, even if the layout execution happens on the server, the browser still has to deal with the creation and display of the client-side graph. <br /><br /></li></ul></ul></div><div>And now, here are the figures (durations are in seconds; tests that took more than 30 sec. are marked as having timed out):</div><div> </div>
<div>
<table _moz_resizing="true" border="1" cellpadding="4" cellspacing="0" class="ShadedHead"><colgroup align="left" span="1">
<col span="1" />
<col span="1" />
<col span="1" />
<col span="1" />
<col span="1" />
<col span="1" />
<col span="1" /></colgroup><thead>
<tr bgcolor="#BFD6E7">
<th colspan="1" rowspan="1" valign="top">Test ID</th>
<th colspan="1" rowspan="1" valign="top">Description</th>
<th colspan="1" rowspan="1" valign="top">Nodes</th>
<th colspan="1" rowspan="1" valign="top">Links</th>
<th colspan="1" rowspan="1" valign="top">Google Chrome 16.0</th>
<th colspan="1" rowspan="1" valign="top">Mozilla Firefox 8.0</th>
<th colspan="1" rowspan="1" valign="top">Microsoft Internet Explorer 8.0</th>
</tr>
</thead><tbody valign="top">
<tr>
<td colspan="1" rowspan="1">CreateDiagram10</td>
<td colspan="1" rowspan="1">Creates a diagram (widget) using a tree data set with 10
nodes</td>
<td colspan="1" rowspan="1">10</td>
<td colspan="1" rowspan="1">9</td>
<td colspan="1" rowspan="1">0.0</td>
<td colspan="1" rowspan="1">0.1</td>
<td colspan="1" rowspan="1">0.4</td>
</tr>
<tr>
<td colspan="1" rowspan="1">CreateDiagram100</td>
<td colspan="1" rowspan="1">Creates a diagram (widget) using a tree data set with 100
nodes</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">99</td>
<td colspan="1" rowspan="1">0.2 </td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">3.7</td>
</tr>
<tr>
<td colspan="1" rowspan="1">CreateDiagram1000</td>
<td colspan="1" rowspan="1">Creates a diagram (widget) using a tree data set with 1000
nodes</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">2.8</td>
<td colspan="1" rowspan="1">3.5<br /></td>
<td colspan="1" rowspan="1"><span class="bold">Timed out</span><br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">CreateGraph10</td>
<td colspan="1" rowspan="1">Creates a graph (GFX) using a tree data set with 10 nodes</td>
<td colspan="1" rowspan="1">10</td>
<td colspan="1" rowspan="1">9</td>
<td colspan="1" rowspan="1">0.0</td>
<td colspan="1" rowspan="1">0.0</td>
<td colspan="1" rowspan="1">0.2 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">CreateGraph100</td>
<td colspan="1" rowspan="1">Creates a graph (GFX) using a tree data set with 100 nodes</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">99</td>
<td colspan="1" rowspan="1">0.1 </td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">2.6 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">CreateGraph1000</td>
<td colspan="1" rowspan="1">Creates a graph (GFX) using a tree data set with 1000 nodes</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">1.3</td>
<td colspan="1" rowspan="1">2.4</td>
<td colspan="1" rowspan="1"><span class="bold">Timed out</span><br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">MoveTest1000a</td>
<td colspan="1" rowspan="1">Moves one node 10000 times</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">1.4 </td>
<td colspan="1" rowspan="1">2.0 </td>
<td colspan="1" rowspan="1"><span class="bold">19</span>.0<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">MoveTest1000b</td>
<td colspan="1" rowspan="1">Moves 1000 nodes, 10 times each</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">0.8 </td>
<td colspan="1" rowspan="1">1.3 </td>
<td colspan="1" rowspan="1"><span class="bold">29.2</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">TreeTree1000</td>
<td colspan="1" rowspan="1">Tree layout using a tree data set with 1000 nodes</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">1.0 </td>
<td colspan="1" rowspan="1">1.7 </td>
<td colspan="1" rowspan="1"><span class="bold">16.1</span><br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">TreeTree1000Server</td>
<td colspan="1" rowspan="1">Tree layout using a tree data set with 1000 nodes</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">1.1 </td>
<td colspan="1" rowspan="1">1.8 </td>
<td colspan="1" rowspan="1"><span class="bold">Timed out</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy2c</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, after layout,
with one node moved out of place</td>
<td colspan="1" rowspan="1">19</td>
<td colspan="1" rowspan="1">25</td>
<td colspan="1" rowspan="1">0.0 </td>
<td colspan="1" rowspan="1">0.1 </td>
<td colspan="1" rowspan="1">0.3 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy2cServer</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, after layout,
with one node moved out of place</td>
<td colspan="1" rowspan="1">19</td>
<td colspan="1" rowspan="1">25</td>
<td colspan="1" rowspan="1">0.1 </td>
<td colspan="1" rowspan="1">0.1</td>
<td colspan="1" rowspan="1">0.4</td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy4a</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, starting
with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">162</td>
<td colspan="1" rowspan="1">221</td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">1.0<br /></td>
<td colspan="1" rowspan="1">3.9</td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy4aServer</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, starting
with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">162</td>
<td colspan="1" rowspan="1">221</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">5.4 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy4b</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, starting
with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">162</td>
<td colspan="1" rowspan="1">221</td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">1.0 </td>
<td colspan="1" rowspan="1"><span class="bold">4.0</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy4bServer</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, starting
with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">162</td>
<td colspan="1" rowspan="1">221</td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">2.2 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy4c</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, after layout,
with one node moved out of place</td>
<td colspan="1" rowspan="1">162</td>
<td colspan="1" rowspan="1">221</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">0.9 </td>
<td colspan="1" rowspan="1">3.3</td>
</tr>
<tr>
<td colspan="1" rowspan="1">Hierarchy4cServer</td>
<td colspan="1" rowspan="1">Hierarchical layout of a hierarchical data set, after layout,
with one node moved out of place</td>
<td colspan="1" rowspan="1">162</td>
<td colspan="1" rowspan="1">221</td>
<td colspan="1" rowspan="1">0.4</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">5.2<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">HierTree1000</td>
<td colspan="1" rowspan="1">Hierarchical layout using a tree data set with 1000 nodes</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">1.6<br /></td>
<td colspan="1" rowspan="1">3.9 </td>
<td colspan="1" rowspan="1"><span class="bold">22.0</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">HierTree1000Server</td>
<td colspan="1" rowspan="1">Hierarchical layout using a tree data set with 1000 nodes</td>
<td colspan="1" rowspan="1">1000</td>
<td colspan="1" rowspan="1">999</td>
<td colspan="1" rowspan="1">1.2 </td>
<td colspan="1" rowspan="1">1.7<br /></td>
<td colspan="1" rowspan="1"><span class="bold">Timed out</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10a1</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a grid data set,
starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.3</td>
<td colspan="1" rowspan="1">0.3<br /></td>
<td colspan="1" rowspan="1">3.7<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10a1Server</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a grid data set,
starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.4</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">5.6 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10a2</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a grid data
set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">0.4<br /></td>
<td colspan="1" rowspan="1">4.5<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10a2Server</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a grid data
set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">0.8 </td>
<td colspan="1" rowspan="1">2.0 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10a3</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a grid data
set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">1.2</td>
<td colspan="1" rowspan="1">0.9<br /></td>
<td colspan="1" rowspan="1"><span class="bold">13.3</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10a3Server</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a grid data
set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">0.6</td>
<td colspan="1" rowspan="1">2.3 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10b1</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a grid data set,
starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.9 </td>
<td colspan="1" rowspan="1">0.7 </td>
<td colspan="1" rowspan="1">9.4<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10b1Server</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a grid data set,
starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.5</td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">3.1<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10b2</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a grid data
set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.5</td>
<td colspan="1" rowspan="1">0.4<br /></td>
<td colspan="1" rowspan="1">4.5<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10b2Server</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a grid data
set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">0.4</td>
<td colspan="1" rowspan="1">2.6<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10b3</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a grid data
set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">1.2 </td>
<td colspan="1" rowspan="1">1.0<br /></td>
<td colspan="1" rowspan="1">14.6<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10b3Server</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a grid data
set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">2.9<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10c1</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a grid data set,
after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">5.3<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10c1Server</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a grid data set,
after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.3</td>
<td colspan="1" rowspan="1">0.4</td>
<td colspan="1" rowspan="1">3.7<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10c2</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a grid data
set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">0.3</td>
<td colspan="1" rowspan="1">4.4<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10c2Server</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a grid data
set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">0.3 </td>
<td colspan="1" rowspan="1">3.9<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10c3</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a grid data
set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">1.1<br /></td>
<td colspan="1" rowspan="1">0.9<br /></td>
<td colspan="1" rowspan="1">13.4<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDGrid10x10c3Server</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a grid data
set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">100</td>
<td colspan="1" rowspan="1">180</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">3.2<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4a1</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a Sierpinski data
set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.7 </td>
<td colspan="1" rowspan="1">0.5<br /></td>
<td colspan="1" rowspan="1">22.7</td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4a1Server</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a Sierpinski data
set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">0.7<br /></td>
<td colspan="1" rowspan="1">6.5 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4a2</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a Sierpinski
data set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.3 </td>
<td colspan="1" rowspan="1">0.8<br /></td>
<td colspan="1" rowspan="1"><span class="bold">Timed out</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4a2Server</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a Sierpinski
data set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.5</td>
<td colspan="1" rowspan="1">0.9 </td>
<td colspan="1" rowspan="1">5.5 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4a3</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a Sierpinski
data set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.8 </td>
<td colspan="1" rowspan="1">1.3 </td>
<td colspan="1" rowspan="1"><span class="bold">Timed out</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4a3Server</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a Sierpinski
data set, starting with all nodes laid out (using the creation algorithm)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">0.7<br /></td>
<td colspan="1" rowspan="1">5.8<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4b1</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a Sierpinski data
set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.4 </td>
<td colspan="1" rowspan="1">0.8<br /></td>
<td colspan="1" rowspan="1"><span class="bold">20.0</span><br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4b1Server</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a Sierpinski data
set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.7 </td>
<td colspan="1" rowspan="1">0.9 </td>
<td colspan="1" rowspan="1">3.1</td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4b2</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a Sierpinski
data set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.2 </td>
<td colspan="1" rowspan="1">0.7<br /></td>
<td colspan="1" rowspan="1"><span class="bold">15.3</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4b2Server</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a Sierpinski
data set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">3.1 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4b3</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a Sierpinski
data set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.8 </td>
<td colspan="1" rowspan="1">1.2 </td>
<td colspan="1" rowspan="1"><span class="bold">15.6</span><br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4b3Server</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a Sierpinski
data set, starting with all nodes at (0,0)</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">0.7</td>
<td colspan="1" rowspan="1">2.5 </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4c1</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a Sierpinski data
set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.5</td>
<td colspan="1" rowspan="1">0.4<br /></td>
<td colspan="1" rowspan="1">6.4</td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4c1Server</td>
<td colspan="1" rowspan="1">Force-directed layout (incremental) of a Sierpinski data
set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.4 </td>
<td colspan="1" rowspan="1">0.5 </td>
<td colspan="1" rowspan="1">7.1<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4c2</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a Sierpinski
data set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.3<br /></td>
<td colspan="1" rowspan="1">0.8<br /></td>
<td colspan="1" rowspan="1"><span class="bold">15.1</span> </td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4c2Server</td>
<td colspan="1" rowspan="1">Force-directed layout (non-incremental) of a Sierpinski
data set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.4</td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">4.7<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4c3</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a Sierpinski
data set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">1.7 </td>
<td colspan="1" rowspan="1">1.4 </td>
<td colspan="1" rowspan="1">17.1<br /></td>
</tr>
<tr>
<td colspan="1" rowspan="1">FDSierpinski4c3Server</td>
<td colspan="1" rowspan="1">Force-directed layout (fast multilevel) of a Sierpinski
data set, after layout, with one node moved out of place</td>
<td colspan="1" rowspan="1">123</td>
<td colspan="1" rowspan="1">243</td>
<td colspan="1" rowspan="1">0.6 </td>
<td colspan="1" rowspan="1">0.9 </td>
<td colspan="1" rowspan="1">5.0<br /></td>
</tr>
</tbody></table>
</div>
<div> </div><div><b>Data sets used in benchmark figures </b><br /><br />The following images show the types of graphs used in the performance tests. <br /><br />Sierpinski fractal graph, used for force-directed layout tests:</div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/sierpinski_default.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/sierpinski_default.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <br /></div><div>Grid graph, used for force-directed layout tests:<br /><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/grid_default.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/grid_default.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <br />Tree graph, used for tree layout and other tests:</div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/tree_default.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/tree_default.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a> </div><div>Hierarchical graph, used for hierarchical layout tests:<br /><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/hierarchy_default.png
" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/hierarchy_default.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <br /> </div><div> </div>
In this post, you can find benchmarking figures for Dojo Diagrammer 1.1.1 using Dojo 1.7.0 and covering various graph layout and other graph/diagram operations. Before presenting the numbers, here is some introductory information and analysis: General...005017urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-0bf28444-f26a-4b45-b50e-884b3778bd3bFeature Pack for Web 2.0 and Mobile fix pack 1.1.0.1 is now available.marknesb270004X39Vactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-12-16T10:48:36-05:002011-12-16T11:18:41-05:00
<div>IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile V1.1 fixpack 1 (1.1.0.1) is now available for download. Fix pack 1 contains multiple updates including:</div><div> </div><div><ul><li>Ajax runtime has been updated to include the GA version of Dojo 1.7. </li><li>All samples and applications, including the showcase sample, have been updated to include the GA version of Dojo 1.7.</li><li>IBM Dojo Diagrammer has been updated to work with the GA version Dojo 1.7. </li><li>A new Map Conversion service in included in fix pack 1.1.0.1. This service provides a server-side JAX-RS resource that supports conversion from cartographic ESRI shapefiles to a JSON representation of the map, suitable for display by a Dojo dojox.geo.charting widget</li><li>The Web 2.0 and Mobile Feature Pack is now supported on WebSphere Application Server Community Edition 3.0.</li></ul></div><div> </div><div><a href="http://www-01.ibm.com/support/docview.wss?uid=swg27023799">A list of fixes is available here. </a></div><div> </div><div> </div><div><span style="font-weight: bold;">WebSphere Application Server V6.1, V7.0, and V8.0 Feature Pack for Web 2.0 and Mobile users: <br /><br /></span><a href="http://www-01.ibm.com/support/docview.wss?uid=swg24031700">Download information is available here.</a><br /><span style="font-weight: bold;"><br /></span>Note: No download is required if you are using the WebSphere Application Server V8 Feature Pack for Web 2.0 and Mobile live repository through Installation Manager. Installation Manager should have the live repository http://www.software.ibm.com/software/repositorymanager/com.ibm.websphere.WEB2MOBILE.v11 selected in the preferences panel .In Installation Manager, select update, choose the package group you want to update, and you can upgrade or install directly to 1.1.0.1.<span style="font-weight: bold;"><span style="font-weight: bold;"><br /></span></span><br />For the latest WebSphere Application Server Feature Pack for Web 2.0 and Mobile fix pack information, see the <a href="https://www-304.ibm.com/support/docview.wss?uid=swg27004980">WebSphere Application Server recommended fix page.</a><span style="font-weight: bold;"><span style="font-weight: bold;"><br /><br /><span style="font-weight: bold;">WebSphere Application Server Community Edition V2.1 and V3.0 Feature Pack for Web 2.0 and Mobile users:<br /><br /></span></span></span><a href="http://www-01.ibm.com/software/webservers/appserv/was/featurepacks/web20-mobile/">Web 2.0 and Mobile Feature Pack download site.</a><span style="font-weight: bold;"><span style="font-weight: bold;"><span style="font-weight: bold;"><br /><br /></span></span></span>WebSphere Application Server Community edition users will need to download a new installation package and repeated the installation process to move to version 1.1.0.1.<span style="font-weight: bold;"><span style="font-weight: bold;"><span style="font-weight: bold;"><br /><br /></span></span><br /></span></div>
IBM WebSphere Application Server Feature Pack for Web 2.0 and Mobile V1.1 fixpack 1 (1.1.0.1) is now available for download. Fix pack 1 contains multiple updates including: Ajax runtime has been updated to include the GA version of Dojo 1.7. All samples...215161urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-7aae095a-1522-41fe-915e-4b0a08df6ee6Loading jQuery with Dojo 1.7 AMD loaderChristopheJolif2700024FBYactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-12-15T22:59:15-05:002011-12-20T05:34:39-05:00
<div>
Since 1.6 and even further with 1.7 Dojo has a brand new AMD loader. Among the numerous features
of the AMD loader like its ability to load various resources asynchronously, one of the great
advantage is that AMD is a common approach followed by several JavaScript libraries.
That basically means that you can use a shared approach in your web application to load and use
various libraries getting a far better interoperability between the libraries on the
market and avoiding potential clashes between them.
</div><div> </div>
In this blog post I'll show you how to load the jQuery 1.7.x library using the Dojo AMD loader and
use these two libraries in a similar fashion in a single application.
<br /><div>
Before diving into the technical details of this, if you are not familiar with AMD I encourage you
to read this <a href="https://github.com/amdjs/amdjs-api/wiki/AMD">introduction to AMD</a>.<br /></div><div> </div><div>
Let's first see how you load Dojo 1.7 and some of its modules using AMD.
</div><div> </div>
First you obviously need to make Dojo and its loader available to you JavaScript environment.
<br /><div> </div><div>
For that you include the following tag in your web page:
</div>
<div> </div><pre>&lt;script src=&quot;https://ajax.googleapis.com/ajax/libs/dojo/1.7.0/dojo/dojo.js&quot; <br /> type=&quot;text/javascript&quot;&gt;
</pre>
<br />
Here in order to simplify the example I have referenced Dojo on the Google CDN, however nothing
prevents you from hosting your own Dojo version and referencing it.<br /><div> </div><div>Once this is done any subsequent script tag in your web page will have access to the Dojo loader.
</div><div> </div><div>
In order to load several Dojo modules you just need to leverage the require function. In the following example
we are loading the Dojo query module and the DOM manipulation module as well as using them to modify the visibility of a DOM node:
</div>
<div> </div><pre>&lt;script type=&quot;text/javascript&quot;&gt;
require([&quot;dojo/query&quot;, &quot;dojo/NodeList-dom&quot;], function($){
$(&quot;#output&quot;).style(&quot;visibility&quot;, &quot;visible&quot;);
});
&lt;/script&gt;
</pre>
<br />
The first parameter of require is the array of modules we want to load, the second one being a
callback function called once all the modules have been loaded. Each parameter of the function
corresponds to a reference to each loaded module. You can give whatever name to the reference
you want to and no global reference is supposed to be created allowing several modules to
be loaded without any of theme stepping on each other. Note that in this example we aliased
the query module of Dojo to the usual $ alias of jQuery and we will see in the next example
that it does not cause issues.<br /><div> </div><div>
In order to load jQuery in addition to Dojo modules we need to tell the Dojo AMD loader where
to find the jQuery module. One more time we can either configure it to find it locally or on a CDN.
To be consistent with where we loaded Dojo let's load jQuery from Google CDN as well.
</div><div> </div><div>For that we first need to decorate the script tag loading Dojo with the correct configuration on where
to find jQuery. This is done using the package property in the data-dojo-config attribute as follows:
</div>
<div> </div><pre>&lt;script type=&quot;text/javascript&quot;
src=&quot;https://ajax.googleapis.com/ajax/libs/dojo/1.7.0/dojo/dojo.js&quot;
data-dojo-config=&quot;async: true, packages: [
{ name: 'jquery', location: 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1', <br /> main: 'jquery' }
]&quot;&gt;
&lt;/script&gt;
</pre>
<br />
The second step is to modify our source code to add the actual loading of jQuery through the require
function:
<br />
<div> </div><pre>&lt;script type=&quot;text/javascript&quot;&gt;
define.amd.jQuery = true;
require([&quot;jquery&quot;, &quot;dojo/query&quot;, &quot;dojo/NodeList-dom&quot;], function(jquery, $){
$(&quot;output&quot;).style(&quot;visibility&quot;, &quot;visible&quot;);
jquery(&quot;#output&quot;).css(&quot;visibility&quot;, &quot;hidden&quot;);
});
&lt;/script&gt;
</pre>
<br />
As you can see we now have dojo/query module aliased to $ and jQuery module aliased to jquery without
any conflict.
<br /><div> </div><div>
Note that we had to set the define.amd.jQuery flag to true in order for Dojo loader to be able
to load jQuery. Indeed there is specific protection in jQuery code that prevents jQuery from being
loaded by loaders that do not support the loading of multiple releases of jQuery in a single page.
However as in our case we control the web page and we know we will only load one we can set that
flag to override that protection. Note that if you need to load both Dojo and several jQuery versions
on the same page you can, for example, use a 3rd party alternate AMD loader like RequireJS.
</div><div> </div><div>
Obviously in a real life application you won't probably load the two libraries to achieve the same
task (i.e. doing DOM queries and manipulations). So you will be loading different modules.
The following example shows you how you can use jQuery to query your DOM and use Dojo to
display a chart widge both being loaded using Dojo AMD loader:
</div>
<div> </div><pre>&lt;script type=&quot;text/javascript&quot;&gt;
define.amd.jQuery = true;
require([&quot;jquery&quot;, &quot;dojox/charting/Chart&quot;, &quot;dojox/charting/axis2d/Default&quot;,
&quot;dojox/charting/plot2d/Default&quot;, &quot;dojox/charting/plot2d/Columns&quot;, &quot;dojox/charting/Theme&quot;,
&quot;dojox/charting/plot2d/Grid&quot;, &quot;dojox/charting/StoreSeries&quot;, &quot;dojo/store/Memory&quot;],
function($, Chart, Axis, Line, Columns, Theme, Grid, StoreSeries, Memory){
// ...
$(document).ready(function(){
$(&quot;p&quot;).click(function(){
var chart = new Chart(&quot;chart&quot;).
setTheme(theme).
addAxis(&quot;x&quot;, { majorTickStep: 1, minorTicks: false, labelFunc: monthLabel, <br /> minorLabels: false}).
addAxis(&quot;ry&quot;, { vertical: true, fixLower: &quot;major&quot;, fixUpper: &quot;major&quot;, includeZero: true, <br /> majorTickStep: 10000, max: 30000, title: &quot;Revenues&quot; }).
addAxis(&quot;py&quot;, { vertical: true, fixLower: &quot;major&quot;, fixUpper: &quot;major&quot;, includeZero: true, <br /> leftBottom: false, majorTickStep: 100, max: 300, title: &quot;Profit&quot; }).
addPlot(&quot;profit&quot;, {type: Line, vAxis: &quot;py&quot;, tension: &quot;X&quot;, markers: true, <br /> stroke: {color:&quot;yellow&quot;}, fill: &quot;yellow&quot;, animate: true}).
addPlot(&quot;revenues&quot;, {type: Columns, gap: 5, vAxis: &quot;ry&quot;, <br /> stroke: {color:&quot;white&quot;}, fill: &quot;#2a6ead&quot;, animate: true}).
addPlot(&quot;grid&quot;, { type: Grid, vMajorLines: false, vAxis: &quot;ry&quot;}).
addSeries(&quot;data1&quot;, new StoreSeries(store, {}, function(item){
return item.revenues;
}), { plot: &quot;revenues&quot;}).
addSeries(&quot;data2&quot;, new StoreSeries(store, {}, function(item){
return item.profit;
}), {plot: &quot;profit&quot;});
chart.render();
});
});
&lt;/script&gt;
</pre><div> </div>
You can find the full example source code on github at <a href="https://github.com/cjolif/dojo-samples/tree/master/jquery-dojo-amd">https://github.com/cjolif/dojo-samples/tree/master/jquery-dojo-amd</a>.
<br /><div> </div><div>
Finally if you are interested in knowing more about Dojo AMD loader you can find a detailed
documentation in <a href="http://livedocs.dojotoolkit.org/loader/amd">Dojo reference guide</a>.
</div>
Since 1.6 and even further with 1.7 Dojo has a brand new AMD loader. Among the numerous features
of the AMD loader like its ability to load various resources asynchronously, one of the great
advantage is that AMD is a common approach followed by several...0232264urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-1e9603c3-92e6-418a-9e5f-3f71a9e3e6cfDrill Down with Dojo ChartingChristopheJolif2700024FBYactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-12-06T10:51:22-05:002012-07-04T10:56:23-04:00
<p>This is always nice to display data through a chart, however getting a single level of data insight
through a chart might not be enough.</p>
<p>For example you might want to display worldwide monthly sales data for a company using a column chart
to show revenues overlaid by a line chart to show profit.</p><p>
</p><p>This would give the following result:</p><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/revenues.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/revenues.png" style="width: 400px; display: block; margin: 0pt auto; text-align: center; position: relative;" /></a> <br /></div>
<p>However doing this will not give you insights on why the data of particular month are good or bad.
In other words you won't be able to know from which region of the word the bad or good sales are
coming.</p>
<p>One technique to solve this is to use a stacked chart in order instead of displaying aggregated
worldwide data to display several series of date, one column for each word region.</p>
<p>The problem is that it might distract you from the top level picture by putting emphasis on the
split by region.</p>
<p>Another solution that overcome this problem is to keep the principal chart as-is and only display
the region data on demand when the user wants to drill down on the data, for example by clicking
on a given month column.</p>
<p>In the Dojo charting example available <a href="http://cjolif.github.com/dojo-samples/charting-drilldown/drilldown.html">here</a> you can click on a column and get a drill down on the data
using a pie chart that displays the data per region. Let's now see how to achieve this in Dojo.</p>
<p>We will start from the following JSON data:</p>
<p>
</p><pre>var sales = [<br /> { month: &quot;Jan&quot;, revenues: 12435, profit: 53, America:40, Europe:35, Asia:25},<br /> { month: &quot;Feb&quot;, revenues: 11425, profit: 67, America:35, Europe:35, Asia:30},<br /> { month: &quot;Mar&quot;, revenues: 13534, profit: 130, America:45, Europe:25, Asia:30},<br /> { month: &quot;Apr&quot;, revenues: 12001, profit: 54, America:60, Europe:20, Asia:20},<br /> { month: &quot;May&quot;, revenues: 14334, profit: 140, America:40, Europe:30, Asia:30},<br /> { month: &quot;Jun&quot;, revenues: 12400, profit: 121, America:60, Europe:20, Asia:20},<br /> { month: &quot;Jul&quot;, revenues: 12212, profit: 50, America:40, Europe:30, Asia:30},<br /> { month: &quot;Aug&quot;, revenues: 14424, profit: 101, America:40, Europe:35, Asia:25},<br /> { month: &quot;Nov&quot;, revenues: 14134, profit: 72, America:35, Europe:35, Asia:30},<br /> { month: &quot;Oct&quot;, revenues: 13242, profit: 85, America:45, Europe:25, Asia:30},<br /> { month: &quot;Nov&quot;, revenues: 16312, profit: 264, America:60, Europe:20, Asia:20},<br /> { month: &quot;Dec&quot;, revenues: 19132, profit: 124, America:50, Europe:25, Asia:25}<br />];<br /></pre>
<p>The column chart will show the revenues on monthly bases and the line chart the profit. By default
the per-continent data won't be shown and will appear only on drill down in the pie chart.</p><p>
</p><p>The first step is obviously to create the HTML of the Dojo application that will contain the div
placeholders for both the overall and drill down chart as follows:</p>
<p>
</p><pre>&lt;div id=&quot;chart1&quot; style=&quot;width:1280px;height:800px&quot;&gt;&lt;/div&gt;<br />&lt;div id=&quot;overlay&quot; style=&quot;position:relative;top:-720px;left:90px;width:240px;visibility:hidden&quot; onclick=&quot;setStyle('overlay', 'visibility', 'hidden')&quot;&gt;<br /> &lt;div id=&quot;chart2&quot; style=&quot;width:240px;height:240px&quot;&gt;&lt;/div&gt;<br />&lt;/div&gt;<br /></pre>
<p>As you can see in order to get the overlaying effect of the demo the second chart is placed an
overlay div that is hidden by default and will be shown only on demand. Also when the user
click on the overlay, it is back to hidden mode.</p>
<p>The second step is to create a chart with the overall column &amp; lines view in the chart1 placeholder.
This could be done in the markup, but we will here use JavaScript code to achieve this.</p>
<p>
</p><pre>var setStyle;<br />require([&quot;dojo/_base/declare&quot;, &quot;dojo/dom-style&quot;, &quot;dojo/ready&quot;, &quot;dojox/charting/Chart&quot;,<br /> &quot;dojo/store/Memory&quot;, &quot;dojox/charting/StoreSeries&quot;, <br /> &quot;dojox/charting/action2d/PlotAction&quot;,<br /> &quot;dojox/charting/axis2d/Default&quot;, &quot;dojox/charting/plot2d/Columns&quot;, &quot;dojox/charting/plot2d/Lines&quot;,<br /> &quot;dojox/charting/plot2d/Grid&quot;], <br /> function(declare, domStyle, ready, Chart, Memory, StoreSeries, PlotAction, Default, Columns, Lines, Grid){<br /><br /> setStyle = domStyle.set;<br /><br /> ready(function() {<br /> var store = new Memory({ data: sales });<br /> var monthLabel = function(index){<br /> if(index&gt;12 || index&lt;1){<br /> return &quot;&quot;;<br /> }<br /> return sales[index-1].month;<br /> };<br /> var chart1 = new Chart(&quot;chart1&quot;).<br /> addAxis(&quot;x&quot;, { majorTickStep: 1, minorTicks: false, labelFunc: monthLabel, minorLabels: false}).<br /> addAxis(&quot;ry&quot;, { vertical: true, fixLower: &quot;major&quot;, fixUpper: &quot;major&quot;, includeZero: true, <br /> majorTickStep: 10000, max: 30000, title: &quot;Revenues&quot; }).<br /> addAxis(&quot;py&quot;, { vertical: true, fixLower: &quot;major&quot;, fixUpper: &quot;major&quot;, includeZero: true, <br /> leftBottom: false, majorTickStep: 100, max: 300, title: &quot;Profit&quot; }).<br /> addPlot(&quot;profit&quot;, {type: &quot;Default&quot;, vAxis: &quot;py&quot;, tension: &quot;X&quot;, markers: true, stroke: {color:&quot;yellow&quot;}, <br /> fill: &quot;yellow&quot;, animate: true}).<br /> addPlot(&quot;revenues&quot;, {type: &quot;Columns&quot;, gap: 5, vAxis: &quot;ry&quot;, stroke: {color:&quot;white&quot;}, fill: &quot;#2a6ead&quot;, animate: true}).<br /> addPlot(&quot;grid&quot;, { type: &quot;Grid&quot;, vMajorLines: false, vAxis: &quot;ry&quot;}).<br /> addSeries(&quot;data1&quot;, new StoreSeries(store, {}, function(item){<br /> return item.revenues;<br /> }), { plot: &quot;revenues&quot;}).<br /> addSeries(&quot;data2&quot;, new StoreSeries(store, {}, function(item){<br /> return item.profit;<br /> }), {plot: &quot;profit&quot;});<br /> chart1.render();<br /> });<br />});<br /></pre>
<p>As you can see above we are mapping the first data store series which extracts data from the revenues property of
the items on the profit line (default) plot and the second data store series which extracts data from the profit
profit property of the items on the revenues columns plot.</p>
<p>The third step is to create the pie chart so that is available when we want to make it visible. At creation time
we associate dummy empty data with the pie chart series. The real data will be filled in only when the user drills
down.</p>
<p>
</p><pre>var chart2 = new Chart(&quot;chart2&quot;, { fill: null, margins: {t:0, l:0, b:0, r:0} }).<br /> addPlot(&quot;regions&quot;, {type: &quot;Pie&quot;, radius: 48, stroke: &quot;white&quot;}).<br /> addSeries(&quot;data&quot;, [], {plot: &quot;regions&quot;});<br /></pre>
<p>The final step is to put everything together by making sure to react when the user is clicking on a column in order
to drill down in into the per-region revenue. For that we need to construct a custom plot action as follows:</p>
<p>
</p><pre>var PieAction = declare(PlotAction, {<br /> constructor: function(chart, plot, callback){<br /> this.callback = callback;<br /> this.connect();<br /> },<br /> process: function(o){<br /> if(o.shape &amp;&amp; o.type == &quot;onclick&quot; || o.type == &quot;onmouseover&quot;){<br /> this.callback(o.index, o.type == &quot;onclick&quot;);<br /> };<br /> }<br />});<br /></pre>
<p>The custom constructor is taking a callback that will be called by the process method of the action. The process method
trigger the callback either on the onlick or onmouseover HTML events. The first argument of the callback is the index of
data item that was clicked. If onclick was at the origin of the call the second argument of the callback will be true.
We use that second argument to either force the creation of the pie chart (if true) or just update it if it exists. Indeed
when hovering the columns we just want to update drill down data not force the pie chart creation if it is not yet visible.</p>
<p>We now need to register that action onto the plot and give it a callback. This can very easily be done that way:</p>
<p>
</p><pre>new PieAction(chart1, &quot;revenues&quot;, function(index, forceVisible){<br /> // show up a PieChart with the month split by world region<br /> chart2.updateSeries(&quot;data&quot;, [<br /> { y: sales[index].America, text: &quot;America&quot; },<br /> { y: sales[index].Europe, text: &quot;Europe&quot; },<br /> { y: sales[index].Asia, text: &quot;Asia&quot; }<br /> ]);<br /> // in case it was hidden and we clicked<br /> if(forceVisible){<br /> domStyle.set(&quot;overlay&quot;, &quot;visibility&quot;, &quot;visible&quot;);<br /> }<br /> chart2.render();<br />});<br /></pre>
<p>As you can see when the callback is called we reach the pie chart and update its data series with an extract from the
original data for the index that the user has select.</p>
<p>Optionally if the second parameter (forceVisible) is true, then the pie chart overlay is made visible so that it appears the first time to give the following visual result:</p><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/final.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/final.png" style="width: 400px; display: block; margin: 0pt auto; text-align: center; position: relative;" /></a> <br /></div>
<div>You can give it <a href="http://cjolif.github.com/dojo-samples/charting-drilldown/drilldown.html">a try</a> or grab the full code source of this blog post on github at <a href="https://github.com/cjolif/dojo-samples/tree/master/charting-drilldown">https://github.com/cjolif/dojo-samples/tree/master/charting-drilldown</a></div><p>As we have seen in the article the plot actions on the Dojo charts are quite powerful and can be leveraged
to extend existing charts with new features such as drill down ability. So waiting for us the enhance the toolkit don't stop
at the feature list and experience how to easily build new features that you are missing. </p><div><br /></div>
This is always nice to display data through a chart, however getting a single level of data insight
through a chart might not be enough. For example you might want to display worldwide monthly sales data for a company using a column chart
to show revenues...119458urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-5b385564-4202-4438-bd72-7dea4a3f3a13Using the new dojox.mvc support available in Dojo 1.7edchat110000SX2Qactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-11-16T09:20:24-05:002012-02-17T11:37:51-05:00
The <b>dojox.mvc</b> project is new in Dojo 1.7. It focuses on View (Desktop UI or Mobile UI) to Model data binding concerns on the
client, thereby on easing development of data-rich applications and
accelerating the authoring of applications to Create, Read, Update, and
Delete data using a set of Dojo-based patterns. <b>dojox.mvc</b> deals with data binding concerns for a page, it does not deal with application level concerns, see <a href="http://livedocs.dojotoolkit.org/dojox/app">dojox.app</a> for application level controllers. <br /><div><div><p>The support includes a first-class client side data model (<b>dojox.mvc.StatefulModel</b>) that
wraps any data structure(s) that may be relevant for a view, a section of a view, or a widget. <b>dojox.mvc.StatefulModel</b> is based on <a href="http://livedocs.dojotoolkit.org/dojo/Stateful">dojo.Stateful</a>, and uses Promises as defined in dojo.Deferred. <b><br /></b></p><p><b>dojox.mvc</b> has the following properties:</p>
<ul><li>It enables widgets (desktop and mobile) to &quot;bind&quot; to data
within the model. A bind creates a bi-directional update mechanism
between the bound view and the underlying data.</li><li>The data model is &quot;live&quot; data i.e. it maintains any updates driven by the view on the underlying data.</li><li>The
data model issues updates to portions of the view if the data they bind
to is updated in the model. For example, if two widgets are bound to the
same part of a data model, updating the value of one in the view will
cause the data model to issue an update to the other containing the new
value.</li><li>The data model internally creates a tree of dojo.Stateful
objects that matches the input, which is effectively a plain JavaScript
object i.e. &quot;pure data&quot;. This tree allows widgets to bind to any node within the data model. Typically, widgets
with simple values bind to leaf nodes of the StatefulModel, whereas
containers bind to internal nodes of the StatefulModel.</li><li>Each of the dojo.Stateful nodes in the model may store data as well
as associated &quot;meta-data&quot;, which includes things such as whether the
data is required or read-only etc. This meta-data differs from that
maintained by an individual widget because it is
maintained by the datamodel and may therefore be affected by
datamodel-level constraints that span multiple widgets or even additional
criteria such as server-side computations.</li><li>When the model is
backed by a dojo.store or dojo.data query, the client-side updates can
be persisted once the client is ready to &quot;submit&quot; the changes (which may
include both value changes or structural changes - adds/deletes). The
datamodel allows control over when the underlying data is persisted i.e.
this can be more incremental or batched per application needs. For more information about the dojox.mvc.StatefulModel look here: <a href="http://livedocs.dojotoolkit.org/dojox/mvc/StatefulModel">dojox.mvc.StatefulModel</a><br /></li></ul>The <b>dojox.mvc</b> support also includes the following:<br /><ul><li>A simple data binding layer (<a href="http://livedocs.dojotoolkit.org/dojox/mvc/Bind">dojox.mvc.Bind</a>) </li><li>A Data binding mixin that allows widgets or arbitrary view components to bind to locations in the StatefulModel (<a href="http://livedocs.dojotoolkit.org/dojox/mvc/_DataBindingMixin">dojox.mvc._DataBindingMixin</a>)</li><li>A number of new data binding controls and containers, including: </li><ul><li>Group, an MVC container for hierarchical data (<a href="http://livedocs.dojotoolkit.org/dojox/mvc/Group">dojox.mvc.Group</a>), </li><li>Repeat, an MVC container for repeating data i.e. arrays (<a href="http://livedocs.dojotoolkit.org/dojox/mvc/Repeat">dojox.mvc.Repeat</a>)</li><li>Output, an MVC widget for data-bound output (<a href="http://livedocs.dojotoolkit.org/dojox/mvc/Output">dojox.mvc.Output</a>)</li><li>Generate, a simple example of UI generation from a supplied data model (<a href="http://livedocs.dojotoolkit.org/dojox/mvc/Generate">dojox.mvc.Generate</a>)</li><li>Samples for a number of data-rich patterns that can be built using the above</li></ul></ul></div>
<br /><br />The best way to learn about the dojox.mvc support is to look at some examples to see it
in action. So next we will look at a few of the many examples/tests included in the
dojox/mvc project.<br /><br /><h2>Examples:<br /></h2><ul><li><span style="font-weight: bold;">How to bind an output field to a TextBox value:</span></li></ul><br /><div style="margin-left: 40px;">Often it may be necessary to display back to the user what had previously been entered into a form, for example to have the user validate the input. The following code shows how a <span style="font-weight: bold;">dojox.mvc.Output</span> field can be bound to a dijit.form.TextBox via a StatefulModel node in order to keep the output field synchronized with the value in the TextBox.<br /><br />The javascript code below creates the dojox.mvc.StatefulModel directly from a javascript object, it could have used a DataStore:<br />
<div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">// The dojox.mvc.StatefulModel class creates a data model instance<br />// where each leaf within the data model is decorated with dojo.Stateful<br />// properties that widgets can bind to and watch for their changes.<br />model = mvc.newStatefulModel({ data : { &quot;First&quot; : &quot;John&quot;, &quot;Last&quot; : &quot;Doe&quot;, <br /> &quot;Email&quot; : &quot;jdoe@example.com&quot; }});<br />// the StatefulModel created above is initialized with <br />// model.First set to &quot;John&quot;, model.Last set to &quot;Doe&quot; and <br />// model.Email set to &quot;jdoe@example.com&quot;</pre></div>
<br />The html snippet below contains the markup for the TextBox and the Output fields which are bound together via the <span style="font-style: italic;">ref</span> attribute which is set to <span style="font-style: italic;">model.First<i>,</i> so the TextBox will start out with the value of &quot;<i>John</i>&quot;, and the Output field with &quot;<i>(first name is: John)</i>&quot;</span>: And if the TextBox value is changed, the update will be reflected in the Output field.<br /><br /><div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;"> <br />&lt;label class=&quot;cell&quot; for=&quot;firstnameInput&quot;&gt;First:&lt;/label&gt;<br />&lt;input class=&quot;cell&quot; id=&quot;firstnameInput&quot; data-dojo-type=&quot;dijit.form.TextBox&quot; <br /> data-dojo-props=&quot;<b>ref: model.First</b>&quot;&gt;&lt;/input&gt;<br />&lt;!-- Content in output below will always be in sync with value of textbox above --&gt;<br />&lt;span data-dojo-type=&quot;dojox.mvc.Output&quot; data-dojo-props=&quot;<b>ref: model.First</b>&quot;&gt;<br /><b>(first name is: ${this.value})</b><br />&lt;/span&gt;</pre></div>Click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01/dojotoolkit/dojox/mvc/tests/test_mvc_input-output-simple.html">here</a> to see the live test. Click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/test_mvc_input-output-simple.html">here</a> to see the source code. <span style="text-decoration: underline;"><br /><br /></span></div></div><div> </div><div><ul><li><span style="font-weight: bold;">How to use an MVC Group to be able to reuse a part of a form:</span></li></ul>
<div style="margin-left: 40px;">The following example will show how to use a <span style="font-weight: bold;">dojox.mvc.Group</span> to swap between a &quot;Ship To&quot; address and a &quot;Bill To&quot; address on a form. The StatefulModel is updated if either address is changed, so changes to either address will not be lost when swapping back and forth.<br />The javascript code below creates the dojox.mvc.StatefulModel directly from a javascript object, and the setRef function below is called when the &quot;Ship To&quot; button is pressed, using this function call: <i>setRef('addrGroup', model.ShipTo);</i> and when the &quot;Bill To button is pressed, it is called like this: <i>setRef('addrGroup', model.BillTo);</i>
The setRef function will update the ref for the dojox.mvc.Group, which
will cause all of the fields inside the Group to be updated with
appropriate values from the StatefulModel.<div>
<pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">// Initial data
var order = {
&quot;Serial&quot; : &quot;360324&quot;,
&quot;First&quot; : &quot;John&quot;,
&quot;Last&quot; : &quot;Doe&quot;,
&quot;Email&quot; : &quot;jdoe@example.com&quot;,
&quot;ShipTo&quot; : {
&quot;Street&quot; : &quot;123 Valley Rd&quot;,
&quot;City&quot; : &quot;Katonah&quot;,
&quot;State&quot; : &quot;NY&quot;,
&quot;Zip&quot; : &quot;10536&quot;
},
&quot;BillTo&quot; : {
&quot;Street&quot; : &quot;17 Skyline Dr&quot;,
&quot;City&quot; : &quot;Hawthorne&quot;,
&quot;State&quot; : &quot;NY&quot;,
&quot;Zip&quot; : &quot;10532&quot;
}
};
// The dojox.mvc.StatefulModel class creates a data model <br />// instance where each leaf within the data model is <br />// decorated with dojo.Stateful properties that widgets <br />// can bind to and watch for their changes.
model = mvc.newStatefulModel({ data : order });
// the StatefulModel created above is initialized with
// model.First set to &quot;John&quot;, model.Last set to &quot;Doe&quot; and <br />// model.Email set to &quot;jdoe@example.com&quot;
});
// setRef is call when the &quot;Ship To&quot; or &quot;Bill To&quot; button <br />// is pressed.
function setRef(id, addrRef) {
var widget = dijit.byId(id);
widget.set(&quot;ref&quot;, addrRef);
}</pre></div>The html snippet below contains the markup for the Group which is bound to <b>model.ShipTo</b> initially, and will be bound to <b>model.BillTo</b> when the &quot;Bill To&quot; button is pressed:
<div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">&lt;div class=&quot;row&quot; id=&quot;addrGroup&quot; data-dojo-type=&quot;<b>dojox.mvc.Group</b>&quot; <br /> data-dojo-props=&quot;<b>ref: 'model.ShipTo</b>'&quot;&gt;<br /> &lt;div class=&quot;row&quot;&gt;<br /> &lt;label class=&quot;cell&quot; for=&quot;streetInput&quot;&gt;Street:&lt;/label&gt;<br /> &lt;input class=&quot;cell&quot; id=&quot;streetInput&quot; data-dojo-type=&quot;dijit.form.TextBox&quot; <br /> data-dojo-props=&quot;<b>ref: 'Street'</b>&quot;/&gt;<br /> &lt;/div&gt;<br /> &lt;div class=&quot;row&quot;&gt;<br /> &lt;label class=&quot;cell&quot; for=&quot;cityInput&quot;&gt;City:&lt;/label&gt;<br /> &lt;input class=&quot;cell&quot; id=&quot;cityInput&quot; data-dojo-type=&quot;dijit.form.TextBox&quot; <br /> data-dojo-props=&quot;<b>ref: 'City'</b>&quot;/&gt;<br /> &lt;/div&gt;<br /> &lt;div class=&quot;row&quot;&gt;<br /> &lt;label class=&quot;cell&quot; for=&quot;stateInput&quot;&gt;State:&lt;/label&gt;<br /> &lt;input class=&quot;cell&quot; id=&quot;stateInput&quot; data-dojo-type=&quot;dijit.form.TextBox&quot; <br /> data-dojo-props=&quot;<b>ref: 'State'</b>&quot;/&gt;<br /> &lt;/div&gt;<br /> &lt;div class=&quot;row&quot;&gt;<br /> &lt;label class=&quot;cell&quot; for=&quot;zipInput&quot;&gt;Zipcode:&lt;/label&gt;<br /> &lt;input class=&quot;cell&quot; id=&quot;zipInput&quot; data-dojo-type=&quot;dijit.form.TextBox&quot; <br /> data-dojo-props=&quot;<b>ref: 'Zip'</b>&quot;/&gt;<br /> &lt;/div&gt;<br />&lt;/div&gt;
</pre></div>
Click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01/dojotoolkit/dojox/mvc/tests/test_mvc_shipto-billto-simple.html">here</a> to see the live test. Click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/test_mvc_shipto-billto-simple.html">here</a> to see the source code. <span style="text-decoration: underline;"><br /></span>In the live test, notice how the address changes when the &quot;Bill to&quot; or &quot;Ship To&quot; buttons are pressed, and changes made to either will be be reflected in the StatefulModel, so any updates remain when the address is switched back and forth, unless the &quot;Reset&quot; button is pressed which will reset the StatefulModel back to it's original values.<br /><br /><br /></div><ul><li><span style="font-weight: bold;">How to use MVC Repeat to display a list of items from a model, allowing an item to be selected to show and update it's details:</span></li></ul></div><div><div style="margin-left: 40px;">The following example will show how to setup a &quot;Master Detail&quot; pattern using a <span style="font-weight: bold;">dojox.mvc.Repeat</span> with a dataStore.<br />The javascript code below creates an ItemFileWriteStore, a DataStore and a StatefulModel, it uses a promise to wait for the data to be loaded before parsing the page. The setDetailsContext function is called with the selected index when the Details button is pressed for on one of the rows.<br />
<div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">var writeStore = new ItemFileWriteStore(<br /> {url: kernel.moduleUrl(&quot;dojox.mvc.tests._data&quot;, <br /> &quot;mvcRepeatData.json&quot;)});
var modelPromise = mvc.newStatefulModel(<br /> {store: new DataStore({store: writeStore})});
modelPromise.then(function(model){
results = model;
parser.parse();
});
// called to change from the selected item
function setDetailsContext(index) {
selectedIndex = index;
var widget = dijit.byId(&quot;detailsGroup&quot;);
widget.set(&quot;ref&quot;, index);
};</pre></div>
The html snippet below contains the markup for the Repeat which is used to create the Master portion of the Master Detail pattern, the Group is repeated for each entry in the model.
<div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">&lt;div id=&quot;repeatId&quot; data-dojo-type=&quot;<b>dojox.mvc.Repeat</b>&quot; <br /> data-dojo-props=&quot;<b>ref: 'results'</b>&quot;&gt;<br /> &lt;div class=&quot;row&quot; data-dojo-type=&quot;dojox.mvc.Group&quot; <br /> data-dojo-props=&quot;<b>ref: '${this.index}</b>'&quot;&gt;<br /> &lt;label class=&quot;cell&quot; for=&quot;nameInput${this.index}&quot;&gt;Name:&lt;/label&gt;<br /> &lt;input class=&quot;cell&quot; data-dojo-type=&quot;dijit.form.TextBox&quot; <br /> id=&quot;nameInput${this.index}&quot; <br /> data-dojo-props=&quot;<b>ref: 'First'</b>&quot;&gt;&lt;/input&gt;<br /> &lt;button type=&quot;button&quot; data-dojo-type=&quot;dijit.form.Button&quot; <br /> data-dojo-props=&quot;onClick: <br /> function(){<b>setDetailsContext('${this.index}');</b>}&quot;&gt;Details&lt;/button&gt;<br /> &lt;/div&gt;<br />&lt;/div&gt;
</pre></div>
Click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01dojotoolkit/dojox/mvc/tests/test_mvc_search-results-repeat-store.html">here</a> to see the live test. Click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/test_mvc_search-results-repeat-store.html">here</a> to see the source code. <span style="text-decoration: underline;"><br /></span>In the live test, notice how the &quot;Details&quot; portion of the pattern is updated when the Details button is pressed on one of the rows, also notice that if the First Name field is updated the update will be reflected in both sections.<br /><br />
</div><div> </div><div><div>
<ul><li><span style="font-weight: bold;">How to use the MVC support with widgets created programmatically: </span><span style="font-weight: bold;"><span style="font-weight: bold;"> </span></span></li></ul></div><div><div style="margin-left: 40px;">All of the previous examples have used declarative creation via the html, dojox.mvc can also be used with widgets created programmatically. The following example will show how to use dojox.mvc support with many different types of dijit widgets with both programmatic and declarative creation.<br />The javascript code below programmatically creates a FilteringSelect, a TextBox and an mvc.Output all bound together by the ref: filmodel.number.<br />
<div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">// create the filtering select, textbox, output and button
var filsel = new FilteringSelect({
store: store,
ref: filmodel.number // bind to model.number
}, document.getElementById('filsel'));
var filtext = new TextBox({
ref: filmodel.number
}, document.getElementById('filtext'));
var filoutput = new mvc.Output({
ref: filmodel.number
}, document.getElementById('filoutput'));
filoutput.startup();
var filreset = new Button({
onClick: function(){filmodel.reset();},
id: &quot;filReset&quot;,
label: &quot;Reset&quot;
}, document.getElementById('filreset'));
filreset.startup();
filtext.startup();
filsel.startup();</pre></div>
Click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01/dojotoolkit/dojox/mvc/tests/test_mvc_form-kitchensink.html">here</a> to see the live test. Click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/test_mvc_form-kitchensink.html">here</a> to see the source code. <span style="text-decoration: underline;"><br /></span>In the live test, notice how the TextBox value and the Output values are updated when the widget is updated.<br /><br /></div></div><div> </div><div><ul><li><span style="font-weight: bold;">How to use the MVC support to update metadata for things like Validity, Relevance (enable/disable), Read-only, and Required.</span></li></ul>
</div>
<div><div style="margin-left: 40px;">The <span style="font-weight: bold;">dojox.mvc.Bind</span> support makes it easy to use Metadata for Validity, Relevance (enable/disable), Read-only, and Required. It also helps by supporting Validation in the Model instead of only on the control itself. <br />The javascript code below shows how to use dojox.mvc.Bind to have changes in a value for one widget effect the Validity, Relevance, Read-only, and Required attributes of another widget.<br />
<div><pre style="border: 1px solid gray; background: none repeat scroll 0% 0% rgb(238, 238, 238); margin-left: 20px; padding: 5px;">model = mvc.newStatefulModel({ data :
{ &quot;First&quot; : &quot;John&quot;,
&quot;Last&quot; : &quot;Doe&quot;,
&quot;Email&quot; : &quot;jdoe@example.com&quot;,
&quot;Num&quot; : 3
}
});
function testFirstRelevance(newValue) {
//newValue = model.First.value;
if ( newValue !== &quot;0&quot; ) return true;
else return false;
}
function testFirstReadOnly(newValue) {
if ( newValue !== &quot;2&quot; &amp;&amp; <br /> newValue !== &quot;3&quot;) return false;
else return true;
}
function testNumValid(newValue) {
if ( newValue !== 1 &amp;&amp; newValue !== &quot;1&quot; &amp;&amp; <br /> newValue !== &quot;3&quot;) return true;
else return false;
}
function testRequired(newValue) {
if ( newValue !== 4 &amp;&amp; <br /> newValue !== &quot;4&quot;) return false;
else return true;
}
function testLastRelevance(newValue) {
//newValue = model.First.value;
if ( newValue !== &quot;0&quot; ) return true;
else return false;
}
function testLastValid(newValue) {
if ( newValue !== &quot;1&quot; ) return true;
else return false;
}
function init(){
// bind tests, &quot;read-only&quot; etc.
mvc.bind(model.First, &quot;value&quot;, model.First, <br /> &quot;readOnly&quot;, testFirstReadOnly, true);
mvc.bind(model.First, &quot;value&quot;, model.First, <br /> &quot;relevant&quot;, testFirstRelevance, true);
mvc.bind(model.First, &quot;value&quot;, model.First, <br /> &quot;valid&quot;, testNumValid, true);
mvc.bind(model.Num, &quot;value&quot;, model.Num, <br /> &quot;valid&quot;, testNumValid, true);
// bind tests bind Last value to Email attributes
mvc.bind(model.Last, &quot;value&quot;, model.Email, <br /> &quot;relevant&quot;, testFirstRelevance, true);
mvc.bind(model.Last, &quot;value&quot;, model.Email, <br /> &quot;valid&quot;, testNumValid, true);
mvc.bind(model.Last, &quot;value&quot;, model.Email, <br /> &quot;readOnly&quot;, testFirstReadOnly, true);
mvc.bind(model.Last, &quot;value&quot;, model.Email, <br /> &quot;required&quot;, testRequired, true);
mvc.bind(model.Last, &quot;value&quot;, model.Email, &quot;value&quot;);
}
ready(init);</pre></div><br />
Click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01/dojotoolkit/dojox/mvc/tests/test_mvc_bindings-simple.html">here</a> to see the live test. Click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/test_mvc_bindings-simple.html">here</a> to see the source code. <span style="text-decoration: underline;"><br /></span>In the live test, follow the instructions on the page to see how the fields will be set to be Required, Disabled, Read-only, and Invalid.</div></div></div></div><div><br /><ul><li><span style="font-weight: bold;">How to use the MVC support for event driven model calculations.</span></li></ul><div style="margin-left: 40px;">Within a data model we want to support a convenient <i>reactive</i>
programming model.
Whenever an end-user changes a data element bound to a widget,
we need to detect that
change and derive dependent data values elsewhere in the model.
Likewise, when model
values change, either as a result of such a user-initiated
action or perhaps from
an asychronous back-end event, we need to respond in order to
recompute other
dependent values in the model and eventually have those results
flow back to updates in
widgets bound to the model. This example will show a sample
Loan Application which will use dojox.mvc.Bind to keep track of changes
to expenses and income, and it will set a field to invalid if the
housing percent is over 33%.</div><div><div> </div><div style="margin-left: 40px;">To run the Sample Loan Application, click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01/dojotoolkit/dojox/mvc/tests/test_mvc_loan-stateful.html">here</a>.<br />To see the sample loan application form html source click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/test_mvc_loan-stateful.html">here</a>.<br />To see the sample loan application form javascript code for the model click <a href="http://svn.dojotoolkit.org/src/branches/1.7/dojox/mvc/tests/models/LoanWizardModel.js">here</a>.</div> .</div>
<ul><li><span style="font-weight: bold;">How to use the MVC support in a dojox.mobile application.</span></li></ul></div><div style="margin-left: 40px;"><div>This Mobile MVC demo contains three different Mobile MVC examples; a Simple Data Binding example, a Repeat Data Binding example, and a Simple Form Generate example.<br /></div><div>To see the Mobile MVC Demo html source click <a href="http://svn.dojotoolkit.org/src/branches/1.7/demos/mobileMvc/demo.html">here</a>.<br />To see the Mobile MVC Demo javascript code for the model click <a href="http://svn.dojotoolkit.org/src/branches/1.7/demos/mobileMvc/src.js">here</a>.<br />To run the Mobile MVC Demo, click <a href="http://archive.dojotoolkit.org/dojo-2011-10-01/dojotoolkit/demos/mobileMvc/demo.html">here</a> or you can reach the demo from your mobile device with the barcode below.<br /></div></div>
<div style="margin-left: 40px;"><div><img alt="qrcode" src="http://qrcode.kaywa.com/img.php?s=5&amp;d=http%3A%2F%2Farchive.dojotoolkit.org%2Fdojo-2011-10-01%2Fdojotoolkit%2Fdemos%2FmobileMvc%2Fdemo.html" /></div><div> </div></div>
The dojox.mvc project is new in Dojo 1.7. It focuses on View (Desktop UI or Mobile UI) to Model data binding concerns on the
client, thereby on easing development of data-rich applications and
accelerating the authoring of applications to Create, Read,...0116703urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-3c1aab1c-5855-48d0-935c-c6a621cdb5b1Dynamic Page Loading for PhoneGapbcurtis120000J28Pactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-11-14T23:16:07-05:002011-11-14T23:20:56-05:00
<h1>Introduction</h1><br />
Mobile PhoneGap applications vary in complexity and size from simple, one-screen applications to large, multi-screen apps. To simplify development of applications that have more than one screen, JavaScript frameworks such as Dojo, jQuery or jQTouch can be used. They provide a way to display one screen while hiding the remaining screens. This is accomplished by using a single HTML file containing div elements for each screen. The current screen is made visible by setting it's css style properties. As the application grows to include more screens and function, the HTML file also grows in size. Since mobile devices have limited resources, larger HTML files load and run slower - particularly upon application launch.<br /> <br />
To improve application performance, dynamic page loading can be used. For an application that uses dynamic page loading, the app launches and loads only the first screen. Based upon user interaction a new screen can be displayed by retrieving the new HTML content and replacing the current screen with it.<br /> <br />
This blog post will describe how a PhoneGap mobile application can be structured to utilize dynamic page loading. Source code is provided for readers who want to build and run the application.<br /> <br />
<h1>Dynamic Page Loading</h1><br />
The main HTML page for our application contains a div element to display dynamic content. It also loads the JavaScript code used by our application. Referring to the HTML code below, the <span style="font-style: italic;">container</span> div is where we will place our content.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;body id=&quot;body&quot; onload=&quot;init();&quot;&gt;
&lt;h3 id=&quot;header&quot;&gt;PhoneGap Dynamic Pages Demo&lt;/h3&gt;
&lt;div id='container'&gt;&lt;/div&gt;
&lt;/body&gt;
</pre><br /> <br />
The content is retrieved using XHR (also referred to as Ajax) from the same location that our HTML page was loaded.<br /> <br />
PhoneGap applications typically load their main HTML page from the device; thus, XHR can access both local files or server files. However, if the main HTML page is loaded from a server, then only XHR requests to that server are allowed. This is a security restriction to prevent cross-domain security concerns. <br /> <br />
The <span style="font-style: italic;">loadPage()</span> function makes an XHR call to load the specified url. Once the content is available, the <span style="font-style: italic;">container</span> div is updated by setting it's innerHTML property.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">/**
* Load page into url
*
* @param url The url to load
*/
function loadPage(url) {
var xmlhttp = new XMLHttpRequest();
// Callback function when XMLHttpRequest is ready
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState === 4){
if (xmlhttp.status === 200) {
document.getElementById('container').innerHTML = xmlhttp.responseText;
}
}
};
xmlhttp.open(&quot;GET&quot;, url , true);
xmlhttp.send();
}
</pre><br /> <br />
The final step is to specify the initial content to be displayed. This is done inside the <span style="font-style: italic;">init()</span> function, which is run after the HTML page is loaded (defined by the <span style="font-style: italic;">onload</span> property of the body element).<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">/**
* Function called when page has finished loading.
*/
function init() {
// Load first page into container
loadPage(&quot;screen1.html&quot;);
}
</pre><br /> <br />
<h1>Application Screens</h1><br />
With our dynamic loading function completed, it's time to write the screens that comprise our application. There is an HTML file for each screen in the app. Navigation between screens is accomplished by calling <span style="font-style: italic;">loadPage()</span> from within a screen, or using global navigation links from outside the <span style="font-style: italic;">container</span> div as shown below.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;body id=&quot;body&quot; onload=&quot;init();&quot;&gt;
&lt;h3 id=&quot;header&quot;&gt;PhoneGap Dynamic Pages Demo&lt;/h3&gt;
&lt;div id='container'&gt;&lt;/div&gt;
&lt;a href=&quot;screen1.html&quot;&gt;Screen 1&lt;/a&gt;
&lt;a href=&quot;screen2.html&quot;&gt;Screen 2&lt;/a&gt;
&lt;/body&gt;
</pre><br /> <br />
To navigate from within a screen, <span style="font-style: italic;">loadPage()</span> can be called from an event handler. For example, <span style="font-weight: bold;">screen1</span> can load <span style="font-weight: bold;">screen2</span> using the <span style="font-style: italic;">onclick</span> button event.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;h3&gt;Screen1.html&lt;/h3&gt;
This is screen1.html.&lt;br&gt;
&lt;button type=&quot;button&quot; onclick=&quot;loadPage('screen2.html')&quot;&gt;Load screen 2&lt;/button&gt;
</pre><br /> <br />
You don't need to specify the head or body tags in the screen files, since the content is HTML and will be set to the container's innerHTML.<br /> <br />
<h1>Running JavaScript</h1><br />
When setting innerHTML with content, none of the embedded script tags are run - only the HTML content is rendered. To execute JavaScript when a new screen is loaded the JavaScript function must already exist. It can be included as part of the main HTML file for an application, or be located in a script file that is loaded by the main HTML file.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;phonegap-1.1.0.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot; src=&quot;screen3.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
/**
* Load page into url
*
* @param url The url to load
* @param onleave The function to call before leaving
* @param onenter The function to call after loading
*/
function loadPage(url, onleave, onenter) {
// If onleave function specified
if (onleave) {
onleave();
}
var xmlhttp = new XMLHttpRequest();
// Callback function when XMLHttpRequest is ready
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState === 4){
if (xmlhttp.status === 200) {
document.getElementById('container').innerHTML = xmlhttp.responseText;
// If onenter function specified
if (onenter) {
onenter();
}
}
else {
document.getElementById('container').innerHTML = &quot;Error loading page &quot; + url;
}
}
};
xmlhttp.open(&quot;GET&quot;, url , true);
xmlhttp.send();
}
&lt;/script&gt;
</pre><br /> <br />
Referring to the code snippet above, the PhoneGap library is loaded, since the application will use PhoneGap APIs. In particular, we will call the compass API in <span style="font-weight: bold;">screen3.html</span>.<br /> <br />
Several JavaScript functions are used by <span style="font-weight: bold;">screen3.html</span> to access the compass API, so we include them by loading <span style="font-weight: bold;">screen3.js</span>.<br /> <br />
We have modified <span style="font-style: italic;">loadPage()</span> to include two additional parameters. The <span style="font-style: italic;">onleave</span> parameter is a function that is called when leaving the current screen, and the <span style="font-style: italic;">onenter</span> function is called after the new screen is loaded.<br /> <br />
We add a new button to <span style="font-weight: bold;">screen1</span> that loads <span style="font-weight: bold;">screen3</span>, and runs the <span style="font-style: italic;">deviceInfo()</span> function necessary to initialize the <span style="font-weight: bold;">screen3</span> contents. This is accomplished by setting the <span style="font-style: italic;">onenter</span> parameter.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;h3&gt;Screen1.html&lt;/h3&gt;
This is screen1.html.&lt;br&gt;
&lt;button type=&quot;button&quot; onclick=&quot;loadPage('screen2.html')&quot;&gt;Load screen 2&lt;/button&gt;
&lt;button type=&quot;button&quot; onclick=&quot;loadPage('screen3.html', null, deviceInfo);&quot;&gt;Load screen 3&lt;/button&gt;
</pre><br /> <br />
As shown below, <span style="font-weight: bold;">screen3</span> calls functions defined in <span style="font-weight: bold;">screen3.js</span> to obtain the current compass heading and display it.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;h3&gt;Screen3.html&lt;/h3&gt;
This page calls PhoneGap for retrieving device information.
&lt;div class=&quot;info&quot;&gt;
Platform: &lt;span id=&quot;platform&quot;&gt; &lt;/span&gt;&lt;br&gt;
Version: &lt;span id=&quot;version&quot;&gt; &lt;/span&gt;&lt;br&gt;
UUID: &lt;span id=&quot;uuid&quot;&gt; &lt;/span&gt;&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
You can press one of the buttons below to get the compass reading.
&lt;div class=&quot;info&quot;&gt;
Heading: &lt;span id=&quot;compassHeading&quot;&gt; &lt;/span&gt;&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;button type=&quot;button&quot; onclick=&quot;getCompass();&quot;&gt;Get Compass&lt;/button&gt;
&lt;button type=&quot;button&quot; onclick=&quot;watchCompass();&quot;&gt;Begin Watch Compass&lt;/button&gt;
&lt;button type=&quot;button&quot; onclick=&quot;stopWatchCompass();&quot;&gt;Stop Watch Compass&lt;/button&gt;
&lt;br&gt;
&lt;button type=&quot;button&quot; onclick=&quot;loadPage('screen1.html');&quot;&gt;Load screen 1&lt;/button&gt;
&lt;button type=&quot;button&quot; onclick=&quot;loadPage('screen2.html');&quot;&gt;Load screen 2&lt;/button&gt;
</pre><br /> <br />
There are several helper functions defined in <span style="font-weight: bold;">screen3.js</span> that invoke the compass APIs directly.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">var deviceInfo = function(){
document.getElementById(&quot;platform&quot;).innerHTML = device.platform;
document.getElementById(&quot;version&quot;).innerHTML = device.version;
document.getElementById(&quot;uuid&quot;).innerHTML = device.uuid;
}
var getCompass = function() {
...
}
var watchCompass = function() {
...
}
var stopWatchCompass = function() {
...
}
</pre><br /> <br />
When <span style="font-weight: bold;">screen3.html</span> is passed to <span style="font-style: italic;">loadPage('screen3.html', null, deviceInfo)</span>, the <span style="font-style: italic;">deviceInfo()</span> function is called immediately after the file is loaded. This ensures that the HTML elements defined in <span style="font-weight: bold;">screen3.html</span> exist before attempting to fill in the device information determined by PhoneGap.<br /> <br />
<h1>Transitions between Screens</h1><br />
JavaScript toolkits such as Dojo have transitions or animations when navigating from one screen to another. This feature can be easily added to our example by using the css <span style="font-style: italic;">webkitTransition</span> property. Instead of replacing the <span style="font-style: italic;">container</span> div with our new screen, two divs can be used as HTML containers between which we animate.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;body id=&quot;body&quot; onload=&quot;init();&quot;&gt;
&lt;h3 id=&quot;header&quot;&gt;PhoneGap Dynamic Pages Demo&lt;/h3&gt;
&lt;div id=&quot;container&quot;&gt;
&lt;div id=&quot;div0&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;div1&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
</pre><br /> <br />
The following code slides both <span style="font-style: italic;">div0</span> and <span style="font-style: italic;">div1</span> left at the same rate, with <span style="font-style: italic;">div0</span> sliding off the screen to the left and <span style="font-style: italic;">div1</span> sliding onto the screen from the right.<br /> <br />
<pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">// Slide div0 out and div1 in from left
div1.innerHTML = content;
div0.style.webkitTransition = &quot;left 0.5s ease-in&quot;;
div1.style.webkitTransition = &quot;left 0.5s ease-in&quot;;
div0.style.left = &quot;-100%&quot;;
div1.style.left = &quot;0px&quot;;
// Clear transition so div0 can be moved to the right of div1
setTimeout(function() {
div0.style.webkitTransition = null;
div1.style.webkitTransition = null;
div0.style.left = &quot;100%&quot;;
}, 500);
</pre><br /> <br />
Once the transition has completed after 0.5 seconds, we reposition <span style="font-style: italic;">div0</span> at <span style="font-style: italic;">left=100%</span> so that it can be used by the next screen to slide in from the right. With the transitions specified above, all screens will slide from right to left. You can reverse this by specifying a different direction in the <span style="font-style: italic;">webkitTransition</span> property.<br /> <br />
<h1>Summary</h1><br />
This blog post showed how to write mobile applications that use dynamic page loading to load each screen of an app individually, instead of loading all screens at once inside a single HTML file. The result is a shorter startup time for large, complex mobile applications, and simplified development and management of individual screens.<br /> <br />
<h1>Code</h1><br />
The source code for this blog post is available as a PhoneGap Android project at <a href="https://github.com/brycecurtis/articles/tree/master/DynamicPages">https://github.com/brycecurtis/articles/tree/master/DynamicPages</a>.<br /> <br />
The <span style="font-weight: bold;">index.html</span> file has links for two examples. The first one, <span style="font-weight: bold;">simple.html</span>, demonstrates how to do a simple replace of content in a container div. The second file, <span style="font-weight: bold;">transition.html</span>, demonstrates how to use two divs to animate the transition between screens.<br /> <br />
<h1>Links</h1><br />
<div>
<span style="font-weight: bold;">PhoneGap</span>
<ul>
<li>Web site: <a href="http://www.phonegap.com">http://www.phonegap.com</a></li>
<li>Documentation API: <a href="http://docs.phonegap.com">http://docs.phonegap.com</a></li>
<li>Download: <a href="http://www.phonegap.com/download">http://www.phonegap.com/download</a></li>
<li>Wiki: <a href="http://wiki.phonegap.com">http://wiki.phonegap.com</a></li>
<li>Forums: <ul>
<li>User group: <a href="http://groups.google.com/group/phonegap">http://groups.google.com/group/phonegap</a></li>
<li>Developer group: <a href="http://groups.google.com/group/phonegap-dev">http://groups.google.com/group/phonegap-dev</a></li>
</ul>
</li><li>Source code: <a href="https://github.com/callback">https://github.com/callback</a></li>
</ul></div>
<br /> <br />
<div><span style="font-weight: bold;">Articles</span>
<ul>
<li><a href="https://www.ibm.com/developerworks/web/library/wa-mobappdev1/">Mobile application development, Part 1: PhoneGap and Dojo Mobile on Android</a></li>
<li><a href="http://simonmacdonald.blogspot.com/2011/10/changes-in-phonegap-android-110.html">Changes in PhoneGap Android 1.1.0</a></li>
</ul></div>
Introduction Mobile PhoneGap applications vary in complexity and size from simple, one-screen applications to large, multi-screen apps. To simplify development of applications that have more than one screen, JavaScript frameworks such as Dojo, jQuery or...1180795urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-0ae26960-b2de-4271-bb91-b0af2876a0b0A Look at Dojo Diagrammer Features through the Business Process DemoEric Durocher270002J655activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-11-01T12:28:43-04:002011-11-02T12:26:23-04:00
<div>The IBM ILOG Dojo Diagrammer component included in the Web 2.0 and Mobile Feature Pack offers many options to create diagrams that are really customized for your application. Of course all these options are described in the documentation, but it is not obvious to get an overview of all the features. So, in this article, we will take a look at one of the demos shipped with the component, and explain how it uses some interesting Dojo Diagrammer features.<br /></div><div> </div><div>The example that we will look at is the <i>Business Process Diagram</i> demo. You can run the demo directly from the <a href="http://bit.ly/web2mobile_demos">showcase sample</a> - click <i>Business Process Diagram </i>on the top-right (you can also see what it looks like in the following screen shot). The demo basically shows a simple business process diagram organized into horizontal swim lanes:<br /><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/bpmdemo.JPG" target="_blank"><img alt="image" height="331" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/bpmdemo.JPG" style="display: block; margin: 1em 1em 0pt 0pt; position: relative;" width="746" /></a> </div><div> We will focus on the following aspects of the demo:</div><div><ul><li>Loading data from a Dojo data store</li><li>Creating the different types of nodes</li><li>Creating the different types of links</li><li>Link decorations</li><li>Creating swim lanes</li><li>Synchronizing selections of the tree widget and the diagram</li></ul><div> </div><div> </div><h2><br />Loading data from a Dojo data store</h2><br /><br /><div> </div><div>The HTML page contains a Dojo Tree widget on the left, and a Diagram widget on the right. Dojo Diagrammer supports the standard Dojo data store APIs, so both widgets can read their data from the same Dojo data store that loads a simple JSON file. Here is a short extract of this JSON file:<br /><br /><div style="font-family:courier; font-size:small">{<br /> identifier : 'id',<br /> label: 'label',<br /> items: [<br /> {<br /> type: 'HPool',<br /> id: 'hPool1',<br /> label: 'Doctor\'s Office',<br /> x: 20,<br /> y: 204,<br /> width: 1490,<br /> height: 238,<br /> children:[<br /> {_reference: 'hLane1'},<br /> {_reference: 'hLane2'}<br /> ]<br /> },<br /> ...<br /> {<br /> type: 'Task',<br /> id: 'task1',<br /> label: 'Receive Doctor Request',<br /> x: 127,<br /> y: 52,<br /> successors:[<br /> {_reference: 'task2'}<br /> ]<br /> },<br /> ...</div><br /><br />Here is the markup used to create the data store and the Diagram widget:</div><div> </div><div style="font-family:courier; font-size:small"> &lt;div data-dojo-type=&quot;dojo.data.ItemFileReadStore&quot; url=&quot;./data/doctor.json&quot; jsId=&quot;diagramStore&quot;&gt;<br /> &lt;/div&gt;<br /> &lt;div id=&quot;diagram&quot; class=&quot;diagram&quot; data-dojo-type='ibm_ilog.diagram.widget.Diagram' <br /> nodesStore=&quot;diagramStore&quot; <br /> nodesQuery=&quot;{id:'*'}&quot; <br /> nodesQueryOptions=&quot;{deep: true}&quot;<br /> successorsBinding=&quot;successors&quot;<br /> childBinding=&quot;children&quot;<br /> ...<br /> &gt;<br /> &lt;/div&gt;</div><br /><br />The <b>nodesStore</b>, <b>nodesQuery </b>and <b>nodesQueryOptions </b>attributes tell the Diagram what to load. The <b>successorsBinding </b>attribute specifies that the &quot;successors&quot; property of the data items define the links between the nodes. The <b>childBinding </b>attribute defines the hierarchical structure of the diagram, that is, what are the children of a swim lane.<br /><br />The tree widget uses the same data store through a ForestTreeModel:<br /><br /><div style="font-family:courier; font-size:small"> &lt;div data-dojo-type=&quot;dijit.tree.ForestStoreModel&quot; jsId=&quot;treeModel&quot; store=&quot;diagramStore&quot; query=&quot;{type:'HPool'}&quot; childrenAttrs=&quot;children&quot; rootLabel=&quot;Doctor Visit&quot; rootId=&quot;processRoot&quot;&gt;<br /> &lt;/div&gt;<br /> &lt;div data-dojo-type=&quot;dijit.Tree&quot; id=&quot;mytree&quot; model=&quot;treeModel&quot;<br /> openOnClick=&quot;true&quot; class=&quot;tree&quot;<br /> showRoot=&quot;false&quot; persist=&quot;false&quot;&gt;&lt;/div&gt;<br /> &lt;/div&gt;</div><br /><br /><br /><br /><div> </div><h2>Creating different types of nodes</h2><div> </div></div><div> </div><div>The nodes in the demo have a custom look that resembles the BPMN notation. In Dojo Diagrammer, the appearance of nodes is controlled by a <b>template </b>(a JavaScript object that defines the graphic elements of the node). Furthermore, the different types of nodes (events and tasks) look differently. To do this, we use the <b>nodeTemplateFunction </b>attribute of the Diagram widget. The value of the attribute is a function that is called for each node, and that will return the appropriate template depending on the data item:</div><div> </div><div><div style="font-family:courier; font-size:small"> &lt;div id=&quot;diagram&quot; class=&quot;diagram&quot; data-dojo-type='ibm_ilog.diagram.widget.Diagram' <br /> ... <br /> nodeTemplateFunction=&quot;getNodeTemplate&quot;<br /> ...<br /> &gt;<br /> &lt;/div&gt;</div></div><div> </div><div><div style="font-family:courier; font-size:small"> function getNodeTemplate(item, diagram){<br /> var templateName = diagram.nodesStore.getValue(item, &quot;type&quot;) + &quot;Template&quot;;<br /> return window[templateName];<br /> };</div><div> </div></div><div> </div><div>The different templates are contained in separate JavaScript files that have previously been loaded by &lt;script&gt; tags in the page. Will will not dive into templates too much this time but, just to give an idea, here is the beginning of the template for &quot;StartEvent&quot; nodes:<br /></div><div> </div><div><div style="font-family:courier; font-size:small"> var StartEventTemplate = {<br /> layout: {type: 'ibm_ilog.diagram.gfxlayout.StackLayout'},<br /> children: [{<br /> dojoAttachPoint: 'baseShape',<br /> transform: {dx:1, dy:1}, halign: 'center',<br /> shape: {<br /> r: 14,<br /> cy: 0,<br /> cx: 0,<br /> type: 'circle'<br /> },<br /> stroke: {<br /> type: 'stroke',<br /> color: '#71924B',<br /> width: 2<br /> },<br /> ... <br /></div></div><div> </div><div><div> </div><div> </div><h2>Creating different types of links</h2><div> </div><br />Just like nodes, the appearance of links is defined by templates. In our BPM demo, we have two types of links: <br /><ul><li>links between tasks use the default template: a solid polyline and a triangular arrow at the end point.</li><li>&quot;message&quot; links have a dotted polyline, and have two custom shapes: a circle at the start point and an open arrow at the end point.</li></ul>We could use the same technique as for nodes, that is, use different templates through a linkTemplateFunction, but instead, we use an alternate way to customize our links: the <b>onLinkCreated </b>attribute of the Diagram widget. This attribute is set to a function that will be called for each created link in the diagram. This is where we customize our message links: set the dotted stroke, show the small circle, etc.<br /><br />Note that there are also corresponding methods for customizing nodes and subgraphs (<b>onNodeCreated </b>and <b>onSubgraphCreated</b>).<br /><br /><div> </div><div> </div><h2>Link decorations</h2><div> </div><h2><br /><br /></h2>Links typically have a main path and one or two arrows, but they can also have decorations placed at specified positions along the path. For example, in the BPM demo, if you choose the second process (use the combo box and select <i>Provider Process</i>), you will see that some links have a &quot;yes&quot; or &quot;no&quot; label:<br /><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/linklabels.JPG" target="_blank"><img _moz_resizing="true" alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/linklabels.JPG" style=" display:block; margin: 1em 1em 0pt 0pt; position:relative;" /></a> <br />Link decorations are simply elements of the link template. They are marked with a &quot;isDecoration&quot; property, and have optional position parameters that tell the link where to place them on the path (start, middle or end point, on the left or on the right, etc). Decorations can be simple texts as in this example, or any other graphical elements (icon, circles, etc).<br /><div> </div><div> </div><h2><br />Creating swim lanes</h2><div> </div></div><div> </div><div>Business processes are a very common application of diagramming, and they often use &quot;swim lanes&quot;, that is, containers that span the width (or height) of the diagram. By default, when the data store contains hierarchical data, Dojo Diagrammer will create Subgraph objects. Swim lanes are a special type of Subgraph: the main difference is that they have a different look (for example, horizontal swim lanes have their title displayed vertically on the left). So you have to tell the Diagram widget to create SwimLane objects instead of Subgraph objects. To do this, you set the <b>createSubgraphFunction</b> attribute of the Diagram widget. This function will be called to create the containers in the diagram when the data is hierarchical. </div><div> </div><div>As usual, the same type of attribute is available for nodes and links. For example, you could create instances of your own subclass of Node or Link by setting the <b>createNodeFunction</b> or <b>createLinkFunction</b>.. <br /></div><div> </div><div><div> </div><div> </div><h2>Synchronizing selections</h2><div> </div></div><div> </div><div>We looked mainly at how the diagram looks like for now. Of course Dojo Diagrammer also lets you define how it behaves when you interact with it. For example, you can select nodes by clicking them with the left button. Selecting a node will change its background color (again this can be customized), and it also fires an event that you can listen to. In the demo, this is used to synchronize the selection of the Diagram with the selection of the Tree widget on the left (this makes sense since the Tree represents the same data as the Diagram).</div><div> </div><div>Selection is handled in the Diagram widget by a separate object obtained by calling getSelection(). You can then listen to selection changes on this object like this:<br /></div><div> </div><div><div style="font-family:courier; font-size:small"> dojo.connect(diagram.getSelection(), &quot;onSelectionChanged&quot;, this, function (added, removed) {<br /> if (added.length&gt;0 || removed.length&gt;0) {<br /> // added contains the newly selected items,<br />
// removed contains the newly deselected items.<br /> ...<br />
}<br /> });</div><div> </div></div><div> </div><div>Now the other way round: when you select in the tree, it will also select in the diagram. For this, we listen to the Tree widget's selection changes and we select the corresponding item in the diagram. Again, this is done through the auxiliary selection object, by calling its add method:</div><div> <div style="font-family:courier; font-size:small"> diagram.getSelection().add(item,true);<br /></div></div><div> </div><div> </div><div>This concludes our tour of the BPM demo and the overview of some of the Dojo Diagrammer features used. Feel free to comment this post if you would like to see other features covered!</div><div> </div><div> </div><div> </div><div> </div><div> </div>
The IBM ILOG Dojo Diagrammer component included in the Web 2.0 and Mobile Feature Pack offers many options to create diagrams that are really customized for your application. Of course all these options are described in the documentation, but it is not...006994urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-2f9fb865-4813-4a9e-baf6-51c4ea94368bBring JSF and Dojo Together With DojoServerFacescurtiss_howard110000C7PFactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-10-18T07:32:09-04:002011-10-28T00:03:36-04:00<h2>Background</h2><div> </div><div>In a very simplified view, web applications consist of a customer-facing user interface (UI) and server-side code that executes business logic. The UI part of the puzzle is solved with a widget library, which is a set of controls and actions that a customer interacts with. In recent years there has been a great surge in activity in this area, and today there are dozens of widget libraries from which to choose. But the second part of the puzzle -- the server side -- has largely been neglected, with developers being directed to &quot;roll their own&quot; solutions for server-side code execution in response to client-side events, validation, and value conversion, to name a few things.</div><div> </div><div>In this article, we will introduce <a href="http://dojoserverfaces.org">DojoServerFaces</a>, a Java Server Faces (JSF) widget library that combines the server-side power of JSF and Java EE with the client-side richness and accessibility of the popular <a href="http://dojotoolkit.org/">Dojo</a> widget library.</div><div> </div><h2>What is DojoServerFaces?</h2><div> </div><div>DojoServerFaces, or DSF, is a JSF widget library that wraps standard Dojo widgets and attaches the necessary plumbing to have those widgets interact with the JSF 2.0 server-side model. This means that all the Dojo widgets you know and love -- and even your own custom widgets -- can easily leverage the power of JSF 2.0, including server-side validation, value conversion, AJAX, managed beans, and more. </div><div> </div><h2>Getting Started</h2><div> </div><div>In order to use DSF, you must first download the library from its <a href="http://github.com">Github</a> open source repository. You can either build DSF from source using <a href="http://maven.apache.org">Maven</a> or you can download a prebuilt binary from <a href="https://github.com/downloads/kennas/DojoServerFaces/DojoServerFaces-beta-0.0.1.zip">https://github.com/downloads/kennas/DojoServerFaces/DojoServerFaces-beta-0.0.1.zip</a>. For this example, we will use the prebuilt binaries. They need to be extracted from the downloaded ZIP file and added to a new web project's WEB-INF/lib directory. Also note that, for the purposes of this article, we will be using WebSphere Application Server version 8, though any application server with support for JSF 2.0 and Bean Validation should work.</div><div> </div><div>For any DojoServerFaces application, keep in mind that you must provide a copy of Dojo and refer to it using a web.xml context parameter: </div><div> </div><div><pre style="border: 1px solid gray; background: #EEEEEE; margin-left: 50px; padding: 5px;">&lt;context-param&gt; &lt;param-name&gt;dojoserverfaces.dojo.location&lt;/param-name&gt; &lt;param-value&gt;/dojotoolkit&lt;/param-value&gt;&lt;/context-param&gt;</pre></div><div><div> </div><div>In this case, the use of a single slash before &quot;dojotoolkit&quot; indicates that we will bundle Dojo in our web application under the directory &quot;dojotoolkit&quot;. A double slash, or &quot;//dojotoolkit&quot; would indicate that Dojo will be deployed in a separate web application with context root &quot;/dojotoolkit&quot;.</div><div> </div><div><h2>Writing a Simple DojoServerFaces Application</h2></div></div><div> </div><div>Let's consider a very simple application: one that asks for a value between a certain range and validates it. In either pure JSF or pure Dojo we can very easily create this application, but each approach has its own strengths and weaknesses.</div><div> </div><div>With a pure JSF approach, very little code is needed and we can easily manage the value on the server side using a managed bean coupled with Bean Validation to easily validate that the submitted value is not invalid by the time it reaches the server. However, unless we want to write a set of custom widgets, the default JSF widgets aren't going to look very pretty and certainly won't have rich client-side interaction, such as prompts and warning messages.</div><div> </div><div>If we use a pure Dojo approach, again we find that very little code is needed. In addition, our widgets will look very nice and have rich client-side interaction and built-in accessibility features. But what about the server side? We'll have to write backend code that validates our value (remember, you can never trust a value submitted by a client, even if your widget library validates it!) and keeps track of it based on our scoping requirements. While not a daunting task for this simple application, in a larger application the amount of work necessary is considerable.</div><div> </div><div>With DojoServerFaces, we can get the best of both worlds: the server-side richness of JSF coupled with the client-side richness of Dojo. To begin, let's create the managed bean that will hold our value:</div><div> </div><pre style="border: 1px solid gray; background: #EEEEEE; margin-left: 50px; padding: 5px;"><div>@ManagedBean(name=&quot;dsf&quot;)@SessionScopedpublic class DSFManagedBean { @Min(value=1, message=&quot;Value must be at least 1&quot;) @Max(value=100, message=&quot;Value cannot be greater than 100&quot;) private int value = 1; public int getValue () { return this.value; } public void setValue (int value) { this.value = value; }}</div></pre><div> </div><div>Note the power that JSF 2.0 and Bean Validation gives us -- with a few simple annotations we can have our bean persist throughout the user's session and automatically perform validation on our input values. Rolling our own solution would be considerably more work. Now let's finish with our JSF 2.0 Facelet code to complete the user interface:</div><div> </div><pre style="border: 1px solid gray; background: #EEEEEE; margin-left: 50px; padding: 5px;">&lt;html xmlns=&quot;http://www.w3.org/1999/xhtml&quot; xmlns:dsf=&quot;http://www.dojoserverfaces.org/library/standard&quot; xmlns:f=&quot;http://java.sun.com/jsf/core&quot; xmlns:h=&quot;http://java.sun.com/jsf/html&quot;&gt; &lt;h:head&gt; &lt;title&gt;DojoServerFaces Example&lt;/title&gt; &lt;/h:head&gt; &lt;h:body&gt; &lt;h:form id=&quot;form&quot;&gt; &lt;h:outputText id=&quot;prompt&quot; value=&quot;Enter a value between 1 and 100 (current value is #{dsf.value}):&quot; /&gt; &lt;br /&gt;&lt;br /&gt; &lt;dsf:textBox id=&quot;input&quot; value=&quot;#{dsf.value}&quot; /&gt; &lt;dsf:button label=&quot;Enter&quot;&gt; &lt;f:ajax event=&quot;click&quot; execute=&quot;input&quot; render=&quot;prompt messages&quot; /&gt; &lt;/dsf:button&gt; &lt;br /&gt;&lt;br /&gt; &lt;h:message id=&quot;messages&quot; for=&quot;input&quot; style=&quot;color: #FF0000; font-weight: bold&quot; /&gt; &lt;/h:form&gt; &lt;/h:body&gt;&lt;/html&gt;</pre><div> </div><div>Let's take a look at what's happening here. We have a prompt widget that displays a prompt message and the current bean value (using the bean expression #{dsf.value}) and a DSF textbox which both displays and updates the current bean value. We also have a button that, when clicked (i.e., the &quot;click&quot; event occurs), will use JSF 2.0's AJAX capabilities to &quot;execute&quot; or update our textbox and re-render the prompt and a standard JSF message widget. If an invalid value is given, the JSF message widget will contain the error message. Otherwise, the prompt will contain the new value. Here's a look at this page in action:</div><div> </div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/shot1a.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/shot1a.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <div> </div><div>Note that DSF has automatically included all the typical Dojo boilerplate (Javascript, CSS files, and import statements) and as a result, we have a boring yet visually appealing (at least, more so than normal) JSF application. But we want to leverage more of Dojo's client-side functionality, so let's make a change to our text box:</div><div> </div><pre style="border: 1px solid gray; background: #EEEEEE; margin-left: 50px; padding: 5px;">&lt;dsf:numberTextBox id=&quot;input&quot; value=&quot;#{dsf.value}&quot; required=&quot;true&quot; promptMessage=&quot;Enter value&quot; min=&quot;1&quot; max=&quot;100&quot; /&gt;</pre><div> </div><div>By using a DSF numberTextBox, we can leverage the functionality of the Dojo numberTextBox, including value ranges, prompt messages, and warnings when values are missing or invalid:</div><div> </div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/shot2a.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/shot2a.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a><div> </div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/shot2.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/shot2.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <div> </div><div> </div><h2>Conclusion</h2><div> </div><div>With this simple example, we can begin to see the usefulness in using DSF to write web applications. By using DSF, we can fill the gaps that exist between pure JSF development and pure Dojo (or any other client-side widget library) development. Future articles will explore more advanced topics, such as integrating custom Dojo widgets with DSF and doing mobile application development with DSF.</div><div> </div><div><h2>Links</h2><div> </div><div><ul><li>Java Server Faces (JSF): <a href="http://www.javaserverfaces.org/">http://www.javaserverfaces.org/</a></li><li>Dojo Toolkit: <a href="http://dojotoolkit.org" /><a href="http://dojotoolkit.org/">http://dojotoolkit.org/</a></li><li>DojoServerFaces: <a href="http://www.dojoserverfaces.org/">http://www.dojoserverfaces.org/</a></li><li>DojoServerFaces tag reference: <a href="http://www.dojoserverfaces.org/html/toc.html">http://www.dojoserverfaces.org/html/toc.html</a></li></ul></div></div><div></div>Background In a very simplified view, web applications consist of a customer-facing user interface (UI) and server-side code that executes business logic. The UI part of the puzzle is solved with a widget library, which is a set of controls and actions that...018807urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-7cbc9b1d-1805-4cd5-b3a7-5613f816dc62Upload a Picture using PhoneGap on Androidbcurtis120000J28Pactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-10-11T00:00:13-04:002011-10-28T00:03:24-04:00<h1>Introduction</h1><br />Nearly every mobile device available today has a camera. Given their easy use and instant availability, cameras are increasingly being used to share photos with friends and family, document activities and even deposit checks. Paramount to sharing or saving photos is uploading the photo to a server so it can be used by a web service. Utilizing mobile hybrid technologies, <span style="font-weight: bold;">PhoneGap</span> provides mobile applications access to device features and capabilities using only standards-based web technologies HTML and JavaScript - instead of requiring the application be written using native device code.<br />&nbsp;<br />This blog post will demonstrate how the <span style="font-weight: bold;">PhoneGap</span> API can be used to write a simple Android application that either takes a picture with the camera, or selects a photo from the photo album, and uploads it to a web server. Figure 1 shows the application that will be developed. Source code is provided for readers who want to build and run the application.<br />&nbsp;<br /><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/screen1.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/screen1.png" style=" width:250px; display:block; margin: 0 auto;text-align: center; position:relative; border: 1px solid gray;" /></a><br />&nbsp;</div><div style="text-align: center;"><span style="font-weight: bold;">Figure 1:</span> Picture upload application.</div>&nbsp;<br /> <h1>Getting the Picture</h1><br />One of the many features included in PhoneGap's API is the <span style="font-weight: bold;">Camera</span> object. The <span style="font-weight: bold;">Camera</span> includes a <span style="font-style: italic;">getPicture</span>() method which takes a picture using the camera or retrieves a photo from the device's photo album. It can be called from JavaScript as follows:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">navigator.camera.getPicture( cameraSuccess, cameraError, [ cameraOptions ] );</pre>&nbsp;<br />The <span style="font-style: italic;">cameraOptions</span> object contains several keys. The most interesting key is <span style="font-style: italic;">sourceType</span>, which identifies the source of the photo. The valid <span style="font-style: italic;">sourceType</span> values are:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">Camera.PictureSourceType.PHOTOLIBRARY</li>Camera.PictureSourceType.CAMERA <-- default value</li>Camera.PictureSourceType.SAVEDPHOTOALBUM</li></pre>&nbsp;<br />Some platforms like Android have only one album or library, so <span style="font-style: italic;">PHOTOLIBRARY</span> and <span style="font-style: italic;">SAVEDPHOTOALBUM</span> both display the photo album chooser.<br />&nbsp;<br />The <span style="font-style: italic;">cameraSuccess</span> function is called with the URI of the photo when a picture is taken or a photo is selected from the album. The <span style="font-style: italic;">src</span> attribute of an &lt;img&gt; tag can be set to this URI to display the photo in an HTML page. The URI can also be used to identify the photo to upload to a server.<br />&nbsp;<br />First we start with an HTML page that includes an &lt;img&gt; tag and several buttons for obtaining and uploading a picture:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;!-- Camera --&gt;&lt;div&gt; &lt;h3&gt;Camera:&lt;/h3&gt; &lt;b&gt;Status:&lt;/b&gt; &lt;span id=&quot;camera_status&quot;&gt;&lt;/span&gt;&lt;br&gt; &lt;b&gt;Image:&lt;/b&gt; &lt;img style=&quot;width:120px;visibility:hidden;display:none;&quot; id=&quot;camera_image&quot; src=&quot;&quot; /&gt;&lt;/div&gt;&lt;!-- Actions --&gt;&lt;div&gt; &lt;input type=&quot;button&quot; onclick=&quot;takePicture();&quot; value=&quot;Take Picture&quot; /&gt;&lt;br/&gt; &lt;input type=&quot;button&quot; onclick=&quot;selectPicture();&quot; value=&quot;Select Picture from Library&quot; /&gt;&lt;br/&gt; &lt;input type=&quot;button&quot; onclick=&quot;uploadPicture();&quot; value=&quot;Upload Picture&quot; /&gt;&lt;/div&gt;</pre>&nbsp;<br />The camera &lt;div&gt; displays the status and selected photo to be uploaded.<br />&nbsp;<br />If the <b>Take Picture</b> button is pressed, the following <span style="font-style: italic;">takePicture</span>() function is called.<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">/** * Take picture with camera */function takePicture() { navigator.camera.getPicture( function(uri) { var img = document.getElementById('camera_image'); img.style.visibility = "visible"; img.style.display = "block"; img.src = uri; document.getElementById('camera_status').innerHTML = "Success"; }, function(e) { console.log("Error getting picture: " + e); document.getElementById('camera_status').innerHTML = "Error getting picture."; }, { quality: 50, destinationType: navigator.camera.DestinationType.FILE_URI});};</pre>&nbsp;<br /> When a picture has been successfully taken, the success callback <span style="font-style: italic;">function(uri) </span>is called. This function sets the <span style="font-style: italic;">src</span> attribute of the &lt;img&gt; tag to the URI passed in.<br />&nbsp;<br />The <span style="font-style: italic;">selectPicture</span>() function is identical to the <span style="font-style: italic;">takePicture</span>() function except for the <span style="font-style: italic;">sourceType</span> parameter. Thus, instead of displaying the camera, the photo album will be displayed.<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">/** * Select picture from library */function selectPicture() { navigator.camera.getPicture( ... { quality: 50, destinationType: navigator.camera.DestinationType.FILE_URI, sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY});};</pre>&nbsp;<br />For more information about the <span style="font-style: italic;">navigator.camera.getPicture</span>() method see the <a href="http://docs.phonegap.com/en/1.1.0/phonegap_camera_camera.md.html#camera.getPicture">PhoneGap Camera API</a> documentation.<br />&nbsp;<br /><h1>Uploading the Picture</h1><br /><span style="font-weight: bold;">PhoneGap</span> also includes a File API that has a <span style="font-weight: bold;">FileTransfer</span> object. <span style="font-weight: bold;">FileTransfer</span> can be used to upload a file represented by a local URI to a web server using HTTP or HTTPS multi-part POST. A file can be uploaded by calling:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">var ft = new FileTransfer();ft.upload(uri, serverUrl, successCallback, errorCallback, options);</pre>&nbsp;<br />The last parameter is the <span style="font-style: italic;">options</span> object which consists of the following keys:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">fileKey: The name of the form element. (default="file")fileName: The file name you want the file to be saved as on the server. (default="image.jpg")mimeType: The mime type of the data you are uploading. (default="image/jpeg")params: A set of optional key/value pairs to be passed along in the HTTP request.</pre>&nbsp;<br />The URI of the picture returned by the <span style="font-style: italic;">takePicture</span>() or <span style="font-style: italic;">selectPicture</span>() functions can be used by <span style="font-weight: bold;">FileTransfer</span> to upload the picture to a server. Referring to the HTML code above, clicking on the <b>Upload Picture</b> button calls the <span style="font-style: italic;">uploadPicture</span>() function. This function uploads the selected photo to the server:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">/** * Upload current picture */function uploadPicture() { // Get URI of picture to upload var img = document.getElementById('camera_image'); var imageURI = img.src; if (!imageURI || (img.style.display == "none")) { document.getElementById('camera_status').innerHTML = "Take picture or select picture from library first."; return; } // Verify server has been entered server = document.getElementById('serverUrl').value; if (server) { // Specify transfer options var options = new FileUploadOptions(); options.fileKey="file"; options.fileName=imageURI.substr(imageURI.lastIndexOf('/')+1); options.mimeType="image/jpeg"; options.chunkedMode = false; // Transfer picture to server var ft = new FileTransfer(); ft.upload(imageURI, server, function(r) { document.getElementById('camera_status').innerHTML = "Upload successful: "+ r.bytesSent+" bytes uploaded."; }, function(error) { document.getElementById('camera_status').innerHTML = "Upload failed: Code = "+ error.code; }, options); }}</pre>&nbsp;<br />For more information about the <span style="font-weight: bold;">FileTransfer</span> object see the <a href="http://docs.phonegap.com/en/1.1.0/phonegap_file_file.md.html#FileTransfer">PhoneGap File API</a> documentation.<br />&nbsp;<br /><h1>Picture Server</h1><br />To save or share the picture, it needs to be uploaded to a server. Most servers accept HTTP multi-part POST requests. Using an Apache web server with PHP enabled, a simple PHP application can be used to receive a photo and save it on the server.<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">&lt;?php// Directory where uploaded images are saved$dirname = "/tmp/phonegap/uploads"; // If uploading fileif ($_FILES) { print_r($_FILES); mkdir ($dirname, 0777, true); move_uploaded_file($_FILES["file"]["tmp_name"],$dirname."/".$_FILES["file"]["name"]);}?&gt;</pre>&nbsp;<br />The <span style="font-style: italic;">$dirname</span> variable is set to a directory that can be written to by the web server. When an upload request is received, the <span style="font-style: italic;">$_FILES</span> variable contains the details of the uploaded file and it's temporary location. This temporary file is copied into <span style="font-style: italic;">$dirname</span>.<br />&nbsp;<br /><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/screen2.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/screen2.png" style=" width:250px; display:block; margin: 0 auto;text-align: center; position:relative; border: 1px solid gray;" /></a><br />&nbsp;</div><div style="text-align: center;"><span style="font-weight: bold;">Figure 2:</span> Selecting a photo and uploading to server.<br /></div>&nbsp;<br />Before uploading a photo to the server for our application, the server URL input text field must specify where <span style="font-style: italic;">upload.php</span> is located.<br />&nbsp;<br /><h1>Viewing Pictures</h1><br />Photos saved on the server can be retrieved using PHP code that generates HTML for displaying on the device. Clicking the <b>View Uploaded Pictures</b> button will call the following <span style="font-style: italic;">viewUploadedPictures</span>() function.<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">/** * View pictures uploaded to the server */function viewUploadedPictures() { // Get server URL server = document.getElementById('serverUrl').value; if (server) { // Get HTML that lists all pictures on server using XHR var xmlhttp = new XMLHttpRequest(); // Callback function when XMLHttpRequest is ready xmlhttp.onreadystatechange=function(){ if(xmlhttp.readyState === 4){ // HTML is returned, which has pictures to display if (xmlhttp.status === 200) { document.getElementById('server_images').innerHTML = xmlhttp.responseText; } // If error else { document.getElementById('server_images').innerHTML = "Error retrieving pictures from server."; } } }; xmlhttp.open("GET", server , true); xmlhttp.send(); } }</pre>&nbsp;<br />An XMLHttpRequest object, also know as XHR or Ajax, is used to call the web server. A successful result from the server is indicated by a status of 200. Any other status indicates an error.<br />&nbsp;<br />The PHP script on the server displays the file name and shows the photo by generating an HTML response using the following code:<br />&nbsp;<br /><pre style="border: 1px solid gray; background: #EEFFEE; margin-left: 50px; padding: 5px;">// If displaying imageselse { $baseURI = "http://".$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT']. $_SERVER['REQUEST_URI']; $images = scandir($dirname); $ignore = Array(".", ".."); if ($images) { foreach($images as $curimg){ if (!in_array($curimg, $ignore)) { echo "Image: ".$curimg."&lt;br&gt;"; echo "&lt;img src='".$baseURI."?image=".$curimg."&rnd=".uniqid()."'&gt;&lt;br&gt;"; } } } else { echo "No images on server"; }}</pre>&nbsp;<br />Since the successful response is already formatted as HTML, it can be rendered directly by setting innerHTML of the <span style="font-style: italic;">server_images</span> &lt;div&gt; tag as shown in Figure 3.<br />&nbsp;<br /><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/screen3.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/screen3.png" style=" width:250px; display:block; margin: 0 auto;text-align: center; position:relative; border: 1px solid gray;" /></a><br />&nbsp;</div><div style="text-align: center;"><span style=" font-weight: bold;">Figure 3:</span> Viewing uploaded pictures on server.</div>&nbsp;<br /><h1>Summary</h1><br />This blog post showed that device features not normally available to mobile web applications can be easily accessed from JavaScript using <span style="font-weight: bold;">PhoneGap</span>. We discussed how to use the PhoneGap APIs to take a picture or select a photo from the device's album, upload it to a PHP application on the web server, and display the photos saved on the web server - all without having to write any native device code.<br />&nbsp;<br /><h1>Code</h1><br />The source code for this blog post is available at <a href="https://github.com/brycecurtis/articles/tree/master/CameraUpload">https://github.com/brycecurtis/articles/tree/master/CameraUpload</a><br />&nbsp;<br /><h1>Links</h1><br /><div><span style="font-weight: bold;">PhoneGap</span><ul><li>Web site: <a href="http://www.phonegap.com">http://www.phonegap.com</a></li><li>Documentation API: <a href="http://docs.phonegap.com">http://docs.phonegap.com</a></li><li>Download: <a href="http://www.phonegap.com/download">http://www.phonegap.com/download</a></li><li>Wiki: <a href="http://wiki.phonegap.com">http://wiki.phonegap.com</a></li><li>Forums: <ul><li>User group: <a href="http://groups.google.com/group/phonegap">http://groups.google.com/group/phonegap</a></li><li>Developer group: <a href="http://groups.google.com/group/phonegap-dev">http://groups.google.com/group/phonegap-dev</a></li></ul><li>Source code: <a href="https://github.com/phonegap">https://github.com/phonegap</a></li></ul></div><br />&nbsp;<br /><div><span style="font-weight: bold;">Articles</span><ul><li><a href="https://www.ibm.com/developerworks/web/library/wa-mobappdev1/">Mobile application development, Part 1: PhoneGap and Dojo Mobile on Android</a></li><li><a href="http://simonmacdonald.blogspot.com/2011/10/changes-in-phonegap-android-110.html">Changes in PhoneGap Android 1.1.0</a></li></ul></div> Introduction Nearly every mobile device available today has a camera. Given their easy use and instant availability, cameras are increasingly being used to share photos with friends and family, document activities and even deposit checks. Paramount to...2967565urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-eae9d393-9c81-4e00-bcb8-e0c63792434dInteractive Diagrams on Mobile Devices - Important ParametersAdrianVasiliu270002HS2Bactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-10-05T12:55:32-04:002011-11-02T12:28:56-04:00
<p>In a previous blog post (<a href="http://ibm.co/diagramBlog2">Displaying attractive diagrams: quick hints for choosing a graph layout algorithm from Dojo Diagrammer</a>), I've provided hints about how to choose a graph layout algorithm among those available in Dojo Diagrammer. </p><p>Complementary information about how to configure the parameters of these algorithms can be found in the following developerWorks article: <a href="http://ibm.co/pNlOZp">The parameters that matter most for creating interactive diagrams on mobile devices. (Algorithm tips and performance hints for using IBM ILOG Dojo Diagrammer)</a>.</p><p>Hope this helps! (and, if you have questions, don't hesitate to ask)</p>
In a previous blog post ( Displaying attractive diagrams: quick hints for choosing a graph layout algorithm from Dojo Diagrammer ), I've provided hints about how to choose a graph layout algorithm among those available in Dojo Diagrammer. Complementary...002339urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-e1a95102-0f94-4c0c-af8f-2a7414dcab4fDojo testing, revisitedhaysmark060001GVK9activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-10-04T06:00:00-04:002011-10-28T00:03:03-04:00<b>Summary</b>: The <a href="http://www-01.ibm.com/software/webservers/appserv/was/featurepacks/web20-mobile/">IBM WebSphere Feature Pack for Web 2.0 and Mobile</a> provides the IBM Dojo Toolkit (IDT), an Ajax framework built on open-source technology. IDT developers internally test the IDT widgets using the Dojo Objective Harness (DOH), which ships with IDT. In this post, I describe DOH test patterns the IDT team has developed, explore how DOH testing has evolved internally since its introduction to the IDT testing process, and discuss future directions.<br /><h1>Background</h1>For the uninitiated, DOH is a functional test framework for testing HTML and JavaScript code. Tests are written in HTML and JavaScript and execute in any IDT-supported browser; this format makes it easy for developers already working with in languages to verify their code. DOH shares much of the JUnit style:<br /><ul><li>Provides setUp/runTest/tearDown interface to construct tests</li><li>Tests arbitrary JavaScript code</li><li>Provides assert methods to manually verify the resulting data state is correct</li></ul>DOH also provides the doh.robot API, which enables tests to interact with the Web page under test using native input events.<br /><br />For more basic information and examples using DOH, refer to our DOH tutorial on developerWorks:<br /><a href="http://www.ibm.com/developerworks/web/library/wa-aj-doh/index.html">http://www.ibm.com/developerworks/web/library/wa-aj-doh/index.html</a><br /><br />For information and examples using the doh.robot API, refer to the Dojo Reference Guide:<br /><a href="http://dojotoolkit.org/reference-guide/util/dohrobot.html">http://dojotoolkit.org/reference-guide/util/dohrobot.html</a><br /><h1>Common test patterns</h1>In this section, I enumerate some common test patterns I see in IDT and describe how they have evolved. I often advise developers to examine our test cases for examples of how to use IDT and how to test IDT-based code; I summarize what I've noticed here.<br /><br /><h2>Visual inspection</h2>Prior to IDT 1.1, we didn't have a test framework like DOH at all! In those days, we primarily focused on making the widgets look and behave the same on ALL browsers (looking at you IE6!); functional correctness was of secondary concern and was just assumed. While today we place greater emphasis on verifying functional correctness, we still run visual inspections to make sure our widgets still flow with the page.<br /><br /><div>Visual inspection tests are all over Dojo; they are HTML pages with names that start with test_, like test_Slider.html. When you load a visual inspection test, it creates instances of the widget under test (such as Slider in test_Slider) with various parameters applied.</div><div> </div><h3>Evolution</h3>Our visual inspection tests have evolved over time. As we add new features to widgets, we add new test instances exercising those features. And while some of the instances we test still remain in the form they were first created in way back in Dojo 0.4, the way we test them has changed. In particular, all of our Dijit visual inspection tests take GET parameters to change the page's CSS theme, locale, and text direction (to test right-to-left languages). We reuse these tests to construct our GUI tests.<br /><br /><div>Not all of our testing revolves around inspection of the GUI; sometimes we just like to make sure our JavaScript logic works. The next section describes a common pattern we use to verify our JavaScript.</div><div> </div><h2>Functional Testing</h2>To test JavaScript code that runs independently of any particular HTML document, we use the Functional Testing pattern. This pattern most closely resembles JUnit testing and characterizes IDT's internal testing of the core dojo package. These tests are written almost entirely as JavaScript files with no supporting HTML. The Functional Testing pattern consists of two steps:<br /><ol><li>Perform computation</li><li>Assert computation matches expected value</li></ol><br />For example, we use the Functional Testing pattern to test our JSON deserialization:<br /><br /><pre>doh.register(&quot;tests.json&quot;, [<br /> // Computation step: parse JSON<br /> // Assert step: verify value is correct (doh.t is shorthand for doh.assertTrue from JUnit)<br /> function simpleString(t){ t.is(&quot;bar&quot;, JSON.parse('{&quot;foo&quot;:&quot;bar&quot;}').foo)},</pre><br />When HTML is called for, it is simple enough to inline DOH into a Web page, as with the dojo.cookie test:<br /><br /><pre>&lt;script type=&quot;text/javascript&quot;&gt;<br /> require([&quot;dojo&quot;, &quot;doh&quot;, &quot;dojo/cookie&quot;, &quot;dojo/domReady!&quot;], function(dojo, doh){<br /> doh.register([<br /> {<br /> name: &quot;basicSet&quot;,<br /> runTest: function(t){<br /> ...</pre><div> </div><h3>Evolution</h3><div>Our Functional Testing for our core Dojo has not significantly changed since its inception. As we develop more complicated widgets in other areas, we try to separate DOH from the test page so we can still manually inspect them without waiting on DOH. The next pattern utilizes this approach.</div><div> </div><h2>Robot-Sequence-Assert</h2>To test the functional correctness of our GUI code, each of our test cases typically undergoes several steps:<br /><ol><li>Create the widget to test</li><li>Use the doh.robot API to interact with the widget</li><li>Wait for the robot to finish</li><li>Assert that the resulting state is what we expected</li></ol><div>This pattern characterizes IDT's internal testing of the dijit package. We typically instantiate the widgets we want to test by loading the corresponding visual inspection test into DOH, like this:<br /><pre>&lt;script type=&quot;text/javascript&quot;&gt;<br /> dojo.require(&quot;dojo.window&quot;);<br /> dojo.require(&quot;dijit.robotx&quot;);<br /> dojo.addOnLoad(function(){<br /> doh.robot.initRobot('../test_Slider.html');</pre><div> </div>With this separation, our DOH test cases usually start with step #2. For example, our dijit Slider tests reuse this pattern:</div><div> </div><pre>{<br /> name: &quot;slider1&quot;,<br /> timeout: 5000,<br /> runTest: function(){<br /> // Robot step: get slider and interact with it<br /> var d = new doh.Deferred();<br /> var slider = dijit.byId(&quot;slider1&quot;);<br /> slider.set('value', 10);<br /> dojo.window.scrollIntoView(slider.domNode);<br /> doh.robot.mouseMoveAt(slider.focusNode, 1, 0);<br /> doh.robot.mousePress({left: true}, 500);<br /> // drag to 20% marker<br /> var marker = dojo.query(&quot;div[style*='20%']&quot;, dojo.byId('dijit_form_HorizontalRule_0'))[0];<br /> doh.robot.mouseMoveAt(marker, 500, 0);<br /> doh.robot.mouseRelease({left: true}, 500);<br /> // Sequence step: wait for robot to finish interacting<br /> doh.robot.sequence(d.getTestCallback(function(){<br /> var value = slider.get('value');<br /> // Assert step: verify state is correct (doh.t is shorthand for doh.assertTrue)<br /> doh.t(value &gt;= 19 &amp;&amp; value &lt;= 21, &quot;Expected 20-ish, got &quot;+value);<br /> }), 1000);<br /> return d;<br /> }<br />},</pre><div> </div><h3>Evolution</h3><div>As we started writing lots of tests like this, we noticed that we ended up copying and pasting a lot of the same test code; we often needed to drag Sliders, open ComboBoxes, etc. Thus, over time we refactored our test code to use a parameterized test function. For example, in ComboBox we found we were writing a lot of tests to browse the menu and select an item, so we generalized that class of tests into a generating function this:</div><pre>// this function arrows through the list of choices until it can verify the one we specified was in the UI<br />var robot_a11ySelectValue = function(combo, text, value, expectedText){<br /> if(!value) value = text;<br /> if(!expectedText) expectedText = text;<br /> var d = new doh.Deferred();<br /><br /> // setup watch() on &quot;value&quot;, &quot;displayedValue&quot;, &quot;item&quot;<br /> var dv, v, i; <br /> var h1 = combo.watch(&quot;displayedValue&quot;, function(name, oldVal, newVal){ dv = newVal; });<br /> var h2 = combo.watch(&quot;value&quot;, function(name, oldVal, newVal){ v = newVal; });<br /> var h3 = combo.watch(&quot;item&quot;, function(name, oldVal, newVal){ i = newVal; });<br /><br /> combo.focusNode.focus();<br /> combo.itemError = false;<br /> doh.robot.sequence(function(){ combo.set(&quot;value&quot;, null); }, 1000);<br /> // Robot step: cycle through choices<br /> doh.robot.mouseMoveAt(combo.focusNode, 0); // get cursor out of the way so that wiggling doesn't mess up test<br /> doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500);<br /> var repeat = function(){<br /> var node = findMenuItem(combo, text);<br /> var isMoreChoices = node == combo.dropDown.nextButton;<br /> var selected = combo.dropDown.getHighlightedOption() || combo.dropDown.domNode.firstChild;<br /> while(selected != node){<br /> doh.robot.keyPress(dojo.keys.DOWN_ARROW, 300);<br /> selected = selected.nextSibling;<br /> }<br /> doh.robot.keyPress(dojo.keys.ENTER, 500);<br /> if(isMoreChoices){<br /> // can go faster since the data will have loaded by now<br /> doh.robot.sequence(repeat, 1000);<br /> }else{<br /> // Sequence step: wait for robot to select item in menu<br /> doh.robot.sequence(d.getTestCallback(function(){<br /> // Assert step: verify value changed<br /> doh.is(value, combo.get(&quot;value&quot;), &quot;combo.get(value)&quot;);<br /> doh.is(expectedText, combo.focusNode.value, &quot;expectedText&quot;);<br /> doh.f(combo._opened, &quot;not opened&quot;);<br /> doh.f(combo.itemError, &quot;no itemError&quot;);<br /> <br /> // watch() tests<br /> doh.is(expectedText, dv, &quot;watch of displayedValue&quot;);<br /> doh.is(value, v, &quot;watch of value&quot;);<br /> doh.is(value, isComboBox ? i[combo.searchAttr] : combo.store.getIdentity(i), &quot;watch of item&quot;);<br /> h1.unwatch();<br /> h2.unwatch();<br /> h3.unwatch();<br /> }), 500);<br /> }<br /><br /> };<br /> // first time, wait for the data to come in<br /> doh.robot.sequence(repeat, 3000);<br /> return d;<br />};</pre><div>Also, a lot of people ask me about capture-playback tools, like those found in dojox.robot, to automatically write doh.robot tests. While at first we used them extensively, in practice we stopped using the automation to write tests for several reasons. First of all, test maintenance became difficult as we rewrote our widgets' templates; widgets' insides both moved around the screen and moved with respect to the DOM, so our tests became obsolete as soon as we made even minor changes to the templates. Second, we still had to manually write assert conditions. While other playback tools can verify the screen looks the same as before, it is inherently very difficult to verify the correctness of a widget in IDT just by its appearance because a) different browsers and OSes will render components differently no matter how hard you try and b) the widget's appearance doesn't reflect the entire data state. These factors led us to just manually write DOH tests for our GUI testing and as a result we achieved cleaner and more maintainable tests.<br /></div><h2>Performance testing</h2><div>A few performance-sensitive pieces of code in IDT, like the graphics package, have performance tests associated with them. The owners periodically run the tests on their machines to prevent performance regressions. We first constructed these tests identically to Functional Tests. For example, one of our oldest performances tests comes from CsvStore:</div><div> </div><pre>doh.register(&quot;dojox.data.tests.performance.CsvStore&quot;,[<br /> {<br /> name: &quot;Initial Parse and First Fetch&quot;,<br /> testType: &quot;perf&quot;,<br /> trialDuration: 100,<br /> trialDelay: 50,<br /> trialIterations: 50,<br /> runTest: function(){<br /> store = new dojox.data.CsvStore({<br /> data: dojox.data.tests.performance.CsvStore.getData(2000)<br /> });<br /> var def = new doh.Deferred();<br /> var complete = function(items, request){<br /> try{<br /> doh.assertEqual(2000, items.length);<br /> def.callback(true);<br /> }catch(e){<br /> def.errback(e);<br /> }<br /> };<br /> var err = function(error, request){<br /> def.errback(error);<br /> };<br /> store.fetch({onComplete: complete, onError: err});<br /> return def;<br /> }<br /> },</pre><div>As you can see, the test shares a lot of the same semantics as Functional Tests; it performs a computation (a datastore query in this case) , then verifies the results are correct. Our DOH performance testing harness runs this computation some multiple of 50 times (trialIterations), the magic multiple defined during calibration until the combined runs exceed 100ms (trialDuration).<br /></div><h3>Evolution</h3><div>Over time however, we realized we could much more efficiently construct performance tests by separating the functional and performance aspects of the tests; we ran performance tests many times to get a good sample, so it made sense to strip out the redundant assertions. The dojox.gfx tests offer new examples of how we write performance tests:</div><pre> {<br /> name: &quot;Path (lineTo)&quot;,<br /> testType: &quot;perf&quot;,<br /> trialDuration: 100,<br /> trialDelay: 50,<br /> trialIterations: 50,<br /> setUp: function() {<br /> createSurface();<br /> },<br /> tearDown: function(){<br /> destroySurface();<br /> },<br /> runTest: function(){<br /> surface.clear();<br /> var path = surface.createPath();<br /> path.moveTo(100,100);<br /> path.lineTo(200, 200);<br /> path.lineTo(150, 200);<br /> path.lineTo(150, 100);<br /> path.setStroke({color: &quot;black&quot;});<br /> }<br /> },</pre><h1>Future work</h1><div>With IDT 1.7, we are introducing new mobile widgets and with it mobile testing. We created a DOH mobileRunner to display visual tests and DOH Functional Test results on mobile devices. We are prototyping some new mobile testing technologies to re-use our existing doh.robot APIs in a mobile environment. Unfortunately, the way we interact with mobile devices is fundamentally different than the way we interact with desktops; this change will require testers to re-think their favorite desktop tests. For now, we use a mix of manual mobile verification and desktop doh.robot tests to test mobile widgets.<br /><br />Also, I personally am finding that the JavaScript world is still lacking static analysis tools like Control Flow Graphs, Data Flow Graphs, and coverage metrics beyond just line coverage. While these features would be outside the capabilities of a framework like DOH, I would still like to see more research in this area in the future. Although we do static analysis manually for IDT, I think having automated static analysis is important for driving IDT users' testing efforts toward a reachable goal.</div><div> </div><div>We continue to evaluate the progress of external open-source testing frameworks, notably Selenium with its 2.0 release, looking for areas to improve or revamp our DOH technologies. Now that we have established a large and maturing regression test bucket in IDT, we will continue to address the needs of developers looking to test their IDT code by writing IDT code through our DOH framework.<br /></div>Summary : The IBM WebSphere Feature Pack for Web 2.0 and Mobile provides the IBM Dojo Toolkit (IDT), an Ajax framework built on open-source technology. IDT developers internally test the IDT widgets using the Dojo Objective Harness (DOH), which ships with IDT....004961urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-e5622695-f196-4e4c-84f7-fcd0f278f2e5Dojo Diagrammer: link layouts for nested graphs decryptedAdrianVasiliu270002HS2Bactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-09-27T13:15:42-04:002011-11-02T12:30:50-04:00
<div>As mentioned in previous blog posts (for instance <a href="http://ibm.co/diagramBlog2">Displaying attractive diagrams: quick hints for choosing a graph layout algorithm from Dojo Diagrammer</a>), Dojo Diagrammer provides a comprehensive set of layout algorithms for laying out nodes and links in graphs. In the present post I will provide more in-depth information that may help for configuring the link layout in a tricky case of graphs: nested graphs. Nested graphs are graphs where nodes contain another graph, as opposed to flat graphs which do not contain other graphs. An example of nested graph: </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/nestedgraph.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/nestedgraph.png" style=" width:200px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <p>In Dojo Diagrammer, there are several ways to get the links laid out. Link layouts can be performed by:<br /></p><ul><li>Node layout algorithms that also have extensive built-in link layout capabilities: HierarchicalLayout, TreeLayout. These provide various link styles and many configuration options.</li><li>Node layout algorithms that only support a straight link style: CircularLayout, ForceDirectedLayout. These provide support for self-links (links connecting a node to itself) and multi-links (several links that connect the same pair of nodes).</li><li>Specialized link layout algorithms: ShortLinkLayout and LongLinkLayout. These can be used in conjunction with a node layout. </li></ul><p>All these algorithms can be applied to flat or nested graphs. The case of nested graph is the trickiest, mostly because of two reasons: <br /></p><ol><li>The layout algorithms being applied locally to each subgraph in a bottom-to-top traversal of the graph hierarchy, the result is optimal locally (for each subgraph) but is suboptimal globally (for the entire nested graph).</li><li>Most node layout algorithms (TreeLayout, CircularLayout, ForceDirectedLayout) are not able to lay out intergraph links, that is links that interconnect nodes from different subgraphs. (The reason being the one mentioned above: the layout being computed individually for each subgraph, it cannot treat links that traverse the boundaries of subgraphs.) <br /></li></ol><p>To deal with intergraph links, the node layouts such as TreeLayout, CircularLayout, and ForceDirectedLayout need to be combined with a link layout (ShortLinkLayout or LongLinkLayout) configured such that it only lays out the intergraph links, while the regular links are laid out by the node layout: </p><p /><pre>graph.setNodeLayout(nodeLayout); // Tree, Circular, ForceDirected <br />graph.setLinkLayout(linkLayout); // ShortLinkLayout or LongLinkLayout <br />// Configure it to handle the intergraph links: <br />linkLayout.setInterGraphLinksMode(true); <br />// and to only handle intergraph links, not the regular links: <br />linkLayout.setCombinedInterGraphLinksMode(false); </pre> <br /><div>Remains the happy case of HierarchicalLayout. This node layout is the only one that is able to handle intergraph links by itself, and to optimize the layout for the entire nested graph. That is, for laying out nested graphs using Hierarchical, the configuration is much simpler: you only need to set the Hierarchical as node layout. No link layout is needed. </div><p>This feature of HierarchicalLayout is enabled by default. It can be disabled (but in this case do not forget to combine it with a link layout for intergraph links in nested graphs): <br /></p><p /><pre>hierarchicalLayout.setRecursiveLayoutMode(false); </pre><p><i>Note for users of Dojo Diagrammer 1.0: this feature is new in Dojo Diagrammer 1.1.</i><br />The picture below shows a nested graph laid out with this feature of HierarchicalLayout disabled, plus LongLinkLayout configured for the intergraph links:</p></div><div> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/nestedgraphNonRecMode.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/nestedgraphNonRecMode.png" style=" width:400px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a> </div><div><div>For comparison, here is the same graph laid out using only HierarchicalLayout with the recursive mode enabled (which is the default mode):</div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/nestedgraphRecMode.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/nestedgraphRecMode.png" style=" width:400px; display:block; margin: 0 auto;text-align: center; position:relative;" /></a> <span lang="EN-US" style="mso-ansi-language:EN-US">Besides the simplification of the layout configuration (no need to combine the node layout with a link layout), the recursive mode of HierarchicalLayout provides a better quality. As you can see by comparing the two screenshots above, the number of link crossings is smaller, and the links are laid out in a more uniform manner, pointing from left to right (the direction of the hierarchical flow). This is the benefit of the global optimization of the layout for the entire nested graphs.</span></div><div> </div><div>In summary, the rules of thumb for laying out nested graphs:<br /><ol><li>Use HierarchicalLayout in its default recursive mode whenever your graph data has hierarchical structure.</li><li>Otherwise, combine a node layout with a link layout for intergraph links.<br /></li></ol></div></div>
As mentioned in previous blog posts (for instance Displaying attractive diagrams: quick hints for choosing a graph layout algorithm from Dojo Diagrammer ), Dojo Diagrammer provides a comprehensive set of layout algorithms for laying out nodes and links in...004011urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-d1e032cf-a940-4620-a7bd-58bc703398b8Developing hybrid applications for Android? Try mobile web tools in Rational Application Developer with Android Eclipse SDKJim Zhang120000QGNCactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-09-21T16:57:59-04:002011-10-27T23:58:30-04:00<div><p>Using web technologies to develop native mobile applications has become a popular solution when writing applications for multiple smartphone platforms. Such applications are typically labeled as &quot;hybrid&quot;, referring to the mix of native code and web code running inside the application. As far as development tools are concerned, different options exist for different platforms. In this post I focus on a method for developing in the hybrid model for the Android platform because of the easy integration between the tools for native development on the Eclipse platform. More specifically the Android Development Tools (ADT) plug-ins for Eclipse will be used for native Android development, Rational Application Developer (RAD) tools for web development. Lastly the Phonegap JavaScript library will be used for native Android SDK to JavaScript integration. Both RAD and ADT are Eclipse based and can be installed into, and live happily together in, the same Eclipse platform. Such integration also exists for BlackBerry development. We might revisit that in a possible future post.</p><p>Note that the information provided here regarding using Rational Application Developer for hybrid development is not an officially supported scenario. In RAD 8.0.3 ifix1, only purely web-based mobile solutions are officially supported. However, as you will find out below, the existing tools do actually lend itself nicely to the hybrid development needs.</p><p>First let's see what it takes to get the necessary stuff installed. You should start by installing Rational Application Developer 8.0.3 ifix1. If you are going to stay in this hybrid development mode for awhile, consider reducing the installed features to the minimum required for this task, to reduce the UI clutter and improve performance. In the Installation Manager's &quot;Install Packages&quot; panel, click the root entry twice to clear all selections (first click selects everything, second click clears everything), and then select only the &quot;Web development tools&quot; -&gt; &quot;Ajax, Dojo toolkit and HTML development tools&quot; feature. This gives you all the tools that are needed, including all the web resource editors (HTML, JavaScript, CSS, JSON), a true WYSIWYG editor (<a href="http://www.ibm.com/developerworks/wikis/download/attachments/140051369/RichPageEditor.swf?version=1">Rich Page Editor beta</a>), the <a href="http://www.ibm.com/developerworks/wikis/download/attachments/140051369/mobile_browser_simulator.swf?version=4">Mobile Browser Simulator</a>, and a fast testing server (Ajax Test Server). Next, follow <a href="http://developer.android.com/sdk/installing.html">the steps to install the Android SDK and the Eclipse tools</a> for Android native development, including the emulator. Make sure to go through &quot;Step 4. Adding platforms and other components&quot; so that the necessary Android platform will be installed to compile the native application against.</p><p>At the end of the install, you will be prompted to restart RAD. Click &quot;Restart Later&quot; to close the dialog. You should manually shut down RAD and restart it because an additional step must be preformed before RAD can be successfully started after the Android tools are installed. This additional step involves making the necessary changes to launch RAD with a Sun Java 1.6 JDK. The Android SDK requires a Sun-specific JCE provider in order to add the digital signature to the .apk during the build. This class is not included in IBM JDKs that come with RAD installs. To get around this problem RAD must be configured to launch with a Sun JDK. Replace the parameters in the eclipse.ini file, in the same folder as eclipse.exe, with the following:</p></div><blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><div>-startup</div></div><div><div>plugins/org.eclipse.equinox.launcher_1.1.1.R36x_v20101122_1400.jar</div></div><div><div>--launcher.library</div></div><div><div>plugins/org.eclipse.equinox.launcher.gtk.linux.x86_1.1.2.R36x_v20101019_1345</div></div><div><div>-showsplash</div></div><div><div>org.eclipse.platform</div></div><div><div>--launcher.XXMaxPermSize</div></div><div><div>256m</div></div><div><div>--launcher.defaultAction</div></div><div><div>openFile</div></div><div><div>-vmargs</div></div><div><div>-Dosgi.requiredJavaVersion=1.6</div></div><div><div>-Xms40m</div></div><div><div>-Xmx1024m</div></div></blockquote><div><p>Now you are ready to write some code! At a high level three types of code are typically developed for a hybrid solution. First, the bulk of the application should be written in web code, HTML/JavaScript/CSS, which is the beauty of the hybrid approach. This covers most of the UI and business logic. In order to invoke device services, such as the camera or the contact list, a JavaScript-to-Java bridge library, in this case, <a href="http://www.phonegap.com/">PhoneGap</a>, is needed. Second, there might be situations that require the writing of native code, Java for Android and BlackBerry, Objective C for iOS. For instance, if you need to have an overlaying frame on the camera viewfinder screen, you would write your own native control that extends from the native camera control class. Last but not least, there might be device specific services that are not directly supported by the PhoneGap API. In order to invoke the services from JavaScript, you need to write PhoneGap extensions, which is a fairly simple task and requires a minimum amount of JavaScript and native coding.</p><p>My suggestion is that you break this process into three steps:</p><p>1. Develop the web code using the full browser as the target environment. The environment in the full browser is almost identical to the embedded browser that gets packaged in the hybrid application, with some minor differences mainly in page load events. For instance, in full browsers dojo code is kicked off by the dojo.ready() event. When running in the native application, the code must be changed to listen to the DOM &quot;deviceready&quot; event which is fired by PhoneGap when it's fully loaded. More than 95% of the code should work the same way. The advantage of developing as much as possible in this environment is that with a fast and light-weight server, such as the Ajax Test Server, changes can easily be made and then quickly reloaded in the browser for instant testing. This saves the time of waiting for the build and sitting around until the emulator catches up. With the Mobile Browser Simulator you can get a sense of the look-and-feel of your UI in the target phone's specific form factor and in different orientations. In addition, using the Rich Page Editor (beta) you can easily put the UI together with markup. RAD also provides one-click publishing of your web projects onto the target server.</p><p>2. Develop the native code and PhoneGap code in the Android project. There's no need to go into details about native development here. For the PhoneGap part, the typical tasks are listed in the <a href="http://www.phonegap.com/start#android">Getting Started</a> guide. What about the web code? I suggest for this phase you keep the web code in its original web project, separate from the Android project, especially if the project contains a lot of code, taking into account any dependent libraries such as Dojo. Doing this saves time by avoiding lengthy builds. </p><p>And there's a specific reason for doing this if Dojo is used. If Dojo itself is put inside the Android project, it will not work due to a problem with the Android build tool. The problem is that all folders and files starting with &quot;_&quot; are not included in the .apk (<a href="http://code.google.com/p/android/issues/detail?id=5343">http://code.google.com/p/android/issues/detail?id=5343</a>). A work around is to produce custom layers so that the code is consolidated into larger js files. Performing custom Dojo builds will occur in step 3 after fully testing the application. As such, keeping the web resources in their separate web projects and loading them from the web server (using http:// URLs instead of file:// URLs) will make Dojo work without custom builds.</p><p>In order to get the web resources loaded into the native Android Application, built using the PhoneGap tools, you can use the PhoneGap API like this, in the Application class that extends DroidGap:</p></div><blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><div>super.loadUrl(&quot;http://&lt;link to the deployed web resources&gt;&quot;);</div></div></blockquote><div><p>Note that the link can't be &quot;localhost&quot; because on the device emulator that maps to the device itself instead of the development machine that is running the emulator and webserver. Use the IP address for the development machine instead.</p><p>3. Move the necessary code into the Android project. Once the code is well tested both in the Mobile Browser Simulator (in step 1) and the emulator (in step 2), the web resources, including any libraries such as Dojo, can be moved into the Android project. If Dojo is used, a custom build needs to be produced for three reasons: 1) reduce size, 2) better performance, and 3) getting around the excluded resources problem (see step 2). This custom build can be created by using the Dojo Custom Build wizard provided in RAD. Make sure to thoroughly test the consolidated layers by continuing to load them with the http:// URL. After the custom layers have been well tested the web resources can be moved into the Android project.</p><p>Create a folder &quot;www&quot; inside the &quot;assets&quot; folder which should have been created by the Android project wizard. Then start moving the web resources into that folder. Because of the custom layers (large js files consolidating multiple dojo modules), most of the Dojo source files can be left behind. You should never copy the entire Dojo source tree into the Android project for the reasons already mentioned.</p><p>Finally, to start loading the web resources inside the Android package, change the Android application class to something like the following:</p></div><blockquote class="webkit-indent-blockquote" style="margin: 0 0 0 40px; border: none; padding: 0px;"><div><div>super.loadUrl(&quot;file://android_asset/www/&lt;link to the target file&gt;&quot;);</div></div></blockquote><div><p>From this point on, all coding and testing is done exclusively in the Android development environment. Most of the web development tools are not supported in Android projects, except for the CSS editor and JSON editor which can continue to be used.</p><p>Although the current release of RAD does not claim support for hybrid development, it does offer value if a process such as the one described here is leveraged. From searching the Internet many have already started using Android Development Tools with Rational Application developer to develop in the hybrid model. Some have run into problems, and I hope this article will provide a method for solving these problems. Happy coding!</p></div>Using web technologies to develop native mobile applications has become a popular solution when writing applications for multiple smartphone platforms. Such applications are typically labeled as &quot;hybrid&quot;, referring to the mix of native code and web...019536urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-3c955164-3ff7-4107-b13b-f73f132d8a07Enable HTML5 Offline Web Application with Dojo 1.7stephen_zhang0600008A90activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-09-08T02:08:47-04:002011-10-27T23:58:20-04:00<div><span style="font-weight: bold;">Background</span></div><div> </div><div>HTML5 offline web application is a wonderful technology that let web applications work without network connection, which is especially useful for mobile web applications since wireless network is not as stable as wire network is. <br /></div>According to <a href="http://www.w3.org/TR/html5/offline.html">W3C specification</a> of offline web application, developers needs to provide a manifest file if they want to enable the offline cache capability. The manifest file lists all the resources that will be offline cached and needs to be referenced in HTML page like this. <br />&lt;html manifest=”cache.appcache”&gt;<br />The cache.appcache needs to be served as “text/cache-manifest” content-type and the browser will fetch all of the resource files in the manifest during the first load or whenever the content of manifest is updated.<br /><div>When the network is unavailable, the browser will detect cache error when trying to communicate with server for the latest manifest and then fetch the resources on the manifest list from local offline cache.</div><div> </div><div><span style="font-weight: bold;">Problem<br /><br /></span>However, if you are developing a web application with offline capability in the coming Dojo 1.7 (either from scratch or migrated from previous version), you might meet with XHR errors when loading the application in offline mode in Safari mobile on iOS (or maybe in other mobile browsers as well). <br />That is because of:<br />1. By default in Dojo 1.7, the loader is not in true asynchronous mode and will use XHR to load resources<br />2. The XHR loading and offline cache manifest detection are in parallel in Safari mobile, and JavaScript codes are loaded and being executed during the manifest detection.<br />3. In Safari mobile, the XHR sent before manifest detection is completed will not fall back to offline cache. So the request is sent to network, and thus will fail in loading. That's why you get XHR errors even if the target resources are defined in offline cache manifest and preserved in local.<br /><br />According to above reasons, the default module loading of Dojo 1.7 will fail the loading of offline web application in Safari mobile. You can see mysterious XHR errors in debugging console.<br />The following test HTML page demonstrates the problem. Before trying it, please make sure you have correct MIME type setting for “.appcache” file extension name on web server. Please refer to this <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/entry/offline_web_applications_and_caching_of_web_resources65?lang=en">blog post</a> for more information.<br /><span style="font-weight: bold;"><hr style="width: 100%; height: 2px;" /><span style="font-weight: bold;">&lt;!DOCTYPE html&gt;<br />&lt;html manifest=&quot;cache.appcache&quot;&gt;&lt;!-- this is a cache mainfest file, defined by w3c --&gt;<br /> &lt;head&gt;<br /> &lt;title&gt;Test&lt;/title&gt;<br /> &lt;script&gt;<br /> // check the HTML5 app offline cache status<br /> var cache = window.applicationCache;<br /> cache.onchecking = function(){ console.log(&quot;cache checking....&quot;);};<br /> cache.onerror = function(){ console.log(&quot;cache error&quot;);};<br /> cache.onnoupdate = function(){ console.log(&quot;cache no update&quot;);};<br /> cache.ondownloading = function(){ console.log(&quot;cache download&quot;);};<br /> cache.onobsolete = function(){ console.log(&quot;cache obsolete&quot;);};<br /> cache.oncached = function(){ console.log(&quot;cache already cached&quot;);};<br /> &lt;/script&gt;<br /> &lt;script type=&quot;text/javascript&quot; src=&quot;dojo/dojo.js&quot; djConfig=&quot;parseOnLoad:false&quot;&gt;&lt;/script&gt;<br /> &lt;/head&gt;<br /> &lt;body&gt;<br /> &lt;/body&gt;<br />&lt;/html&gt;<br /></span><hr style="width: 100%; height: 2px;" /><span style="font-weight: bold;" /></span></div>The content of cache.appcache manifest file is very simple. You can get the latest build version from http://archive.dojotoolkit.org. By September 1st, the nightly build can still re-produce the problem.<br /><br /><div>Below is the content of “cache.appcache” manifest file which just includes some basic resource files.<hr style="width: 100%; height: 2px;" /></div><div><span style="font-weight: bold;">CACHE MANIFEST</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;">CACHE:</span><br style="font-weight: bold;" /><span style="font-weight: bold;">test.html</span><br style="font-weight: bold;" /><span style="font-weight: bold;">dojo/dojo.js</span><br style="font-weight: bold;" /><span style="font-weight: bold;">dojo/selector/acme.js</span><br style="font-weight: bold;" /><br style="font-weight: bold;" /><span style="font-weight: bold;">NETWORK:</span><br style="font-weight: bold;" /><span style="font-weight: bold;">*</span><hr style="width: 100%; height: 2px;" /></div><div> </div><div>Here is the steps for trying the test in offline mode. <br /></div>First, let us load the test page in Safari mobile. The author is using an iPhone with iOS 4.3.1. This step allow the browser to load the web resources and cache them in offline cache.<br /><div>Then, disconnect the network and load it again. Note that you'd better kill Safari process to avoid false positive. Check the debugging console.</div><div><div align="center"><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/errorconsole.png" target="_blank"><img alt="image" height="413" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/errorconsole.png" style="display: block; margin: 0pt auto; text-align: center; position: relative;" width="275" /></a>Figure 1<br /><br /></div><div align="left"> As you can see in Figure 1, the XHR error happens before cache “checking” event is popped up, which means an XHR is sent before manifest detection is completed. That XHR is sent when dojo.js is being executed and it will fetch acme.js. <br /></div><br />However, the same test case will not fail on the desktop browser like Chrome. Below is a network log captured in Chrome desktop. It might be because the Chrome browser organize the resource loading after the manifest detection is over or it is much faster for desktop browser to finish the manifest detection than the mobile browsers do. Anyway, the network log in Figure 1 proves that an XHR request for acme.js is sent when executing dojo.js, which fail in Safari mobile.</div><div> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/chromeconsole.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/chromeconsole.png" style=" display:block; margin: 0 auto;text-align: center; position:relative;" /></a></div><div align="center">Figure 2 <br /></div><div> </div><div><b>Solution</b></div><div> </div>So how can we we enable HTML5 offline web application with Dojo 1.7? <br /><br />The solution is to use asynchronous module loading and honor the web browser in loading resources. Asynchronous module loading will insert &lt;script&gt; tags in HTML document to load modules, instead of sending XHR. That means that the loader delegate the resource loading to web browser and Safari mobile will take the responsibility of loading the content in &lt;script&gt; tag. In this way, Safari mobile can decide the loading sequence, load resources after the manifest detection is over, and prevent any loading conflict. <br /><br />To enable asynchronous module loading, you have to set “async:true” in djConfig, like below:<br />&lt;script src=”dojo/dojo.js” djConfig=”asyn:true”&gt;&lt;/script&gt;<br />And you need to follow AMD and use “require()”to load all other Dojo modules or JavaScript files instead of manually coding the &lt;script&gt; tag directly into HTML. For example, if I need to load my own src.js (src.js needs to use AMD way to define the module) which is under the same directory with index.html. I can use the this piece of code right after &lt;script&gt; tag which load dojo.js<br />&lt;script&gt;<br /> require([“./src.js”], function (moduleName){<br /> //call back to execute after src.js is loaded<br /> });<br />&lt;/script&gt;<br /><br />After the above changes are applied to the code, the test case can now work on safari mobile in offline mode.<br /><div>Here is the screen capture of the safari mobile log.</div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/successconsole.png" target="_blank"><img alt="image" height="411" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/successconsole.png" style="display: block; margin: 0pt auto; text-align: center; position: relative;" width="275" /></a></div><div align="center">Figure 3 <br /></div><div>Note: the cache error log on Line 9 in the screen capture (Figure 3) means the browser cannot get any update information in the checking of manifest file from the server, it will then force to use the resources in the local offline cache. It is not the error thrown by the browser and is different from the JavaScript error in Figure 1.</div><div> </div><div><b>Resources</b></div><div><ul><li>Please refer to http://www.sitepen.com/blog/2010/11/04/requirejsamd-module-forms/ about AMD loading.</li><li>Please refer to http://trac.dojotoolkit.org/ticket/13663 for more detail.</li></ul></div><div> </div><div><b>Author<br /></b>Mingzhe (Archer) Huang mzhuang@cn.ibm.com<br />Yuanpeng (Stephen) Zhang zhangyp@cn.ibm.com<br /></div>Background HTML5 offline web application is a wonderful technology that let web applications work without network connection, which is especially useful for mobile web applications since wireless network is not as stable as wire network is. According to W3C...009455urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-b715a850-7096-427e-88f2-ad4d39fa44e5[Article] Create interactive diagrams for web pages with IBM ILOG Dojo Diagrammer and Rational Application DeveloperEric Durocher270002J655activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-09-06T13:38:15-04:002011-10-27T23:58:09-04:00<div> </div><div>In <a href="http://ibm.co/diagramBlog">this previous post</a> I introduced the new IBM ILOG Dojo Diagrammer component included in the WebSphere Application Server Feature Pack for Web 2.0 &amp; Mobile v1.1.</div><br /><div> </div><div>In <a href="http://ibm.co/pb4jqT">this new developerWorks article</a>, Emmanuel Tissandier and I give an end-to-end description on how to use Dojo Diagrammer and Rational Application Developer to create a web application that displays an organization chart from data retrieved from the server through a REST service. The step-by-step article explains how to create both parts of the web application: the server-side REST service and a client-side page based on the Dojo Diagrammer widget.<br /><br />Here is the final application described in the article:<br /><br /><img alt="" src="file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/moz-screenshot.png" /><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/orgchart-article.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/orgchart-article.png" style="width: 400px; display: block; margin: 0pt auto; text-align: center; position: relative;" /></a> <br /><br />We think this article shows well how the new WAS Feature Pack for Web 2.0 &amp; Mobile visualization components (along with Rational Application Developer Web Tooling) allow you to easily create innovative web applications.</div><div> </div> In this previous post I introduced the new IBM ILOG Dojo Diagrammer component included in the WebSphere Application Server Feature Pack for Web 2.0 &amp; Mobile v1.1. In this new developerWorks article , Emmanuel Tissandier and I give an end-to-end...003855urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-d7f6e5c9-6d79-4b66-b1b2-76a74dece121List Mobile UI Design Patternsjlentz0600004DK1activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-09-05T21:17:49-04:002011-10-27T23:57:57-04:00In my article, <a href="http://ibm.co/wa-interface">User interface design for the mobile web</a>, I recommended UI design patterns as a resource for designing applications. In a series of posts,I'll survey UI design patterns for mobile devices starting with the list family of designs. But first, I'll say a few words about UI design patterns. I like <a href="http://designinginterfaces.com/">Jennifer Tidwell’s</a> definition of a pattern:<blockquote> Patterns capture a common structure, without being too concrete on the details, which gives you flexibility to be creative</blockquote><p>Unlike programming design patterns, UI design patterns define the externals of an application - the user interactions and visual structure of the interface. UI design patterns intentionally omit details about the implementation so that the designer can concentrate on the user experience. UI design patterns are often grouped into collections (&quot;libraries&quot;) that are organized by user goals, task or use of specific UI elements.</p><p>I'm covering list patterns first because of their pervasiveness in mobile applications. In a sample of 100 popular mobile web applications, I found almost 40% used lists as the central organizing UI feature.</p><p>Lists are widely used because they are flexible. They are used for navigation, layout, and option selection. Furthermore, because they are taller than wide, they are well-suited for portrait mode presentation on phones.</p><p>The following are some of the most common elemental list patterns. Most of these can be combined to create hybrid patterns.</p><h3>Navigation</h3><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/SimpleNavigation1.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/SimpleNavigation1.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Single Window Drill-Down:</b> A simple list with a &quot;&gt;&quot; symbol on the right edge of each item is used to direct the user forward through a hierarchy of options. Because it is bad form to take a user somewhere without supplying a way out (other than exiting the application!), navigation lists also must supply a way for the user to navigate backwards.<p>This usually isn't much of an issue for native applications on Android devices because this platform provides a built-in, hardware &quot;Back&quot; button. However, if you are developing a mobile web application that will display on multiple device types, you will usually need to define a software Back button. The Back button usually appears in a page title or heading bar. If the parent page doesn't have a title, use the label &quot;Back&quot; for this button. When the parent page has a title, you should use it as the label for the Back button.</p><p><b>Split View Navigation:</b> The Split View layout is a useful option for organizing a hierarchy of pages on tablets. The left-hand pane consists of a navigation list and the right-hand view displays the contents of the selected item in the list. Because the navigation list is always visible, users can quickly flip through options while remaining visually oriented to the overall navigation structure. Subsequent navigation appears in the right-hand pane. The Settings application on the iPad is a good example of Split View Navigation.</p><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/SplitViewNavigation1.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/SplitViewNavigation1.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <br /></div><p>Split View and Single Window Drill-Down Navigation differ in a few details. In split view, the Back button can be omitted in the first level down from the home page because the user can navigate out of this page by selecting another option in the navigation pane. At lower levels, a Back button should be supplied labeled with the title of the parent view. In addition, because the destination page and navigation list are visible together, the &quot;&gt;&quot; icons are unnecessary and can be removed to create a simpler, cleaner appearance.</p><p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/SearchResultList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/SearchResultList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Search List:</b> Search Lists provide navigation through large, non-hierarchical sets of pages. The search field is used to generate a list of results. If the items in the search result represent links to pages, &quot;&gt;&quot; icons may be provided to indicate that there is additional content to be seen. <br /></p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><h3>Organization</h3>Lists provide a linear structure for options or values. Vertical lists work well with structured textual information.<p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/RichList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/RichList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Rich Lists:</b> Good user interface design presents information in a hierarchy that allows the user to quickly scan through a page to determine which items to attend to in order to reach his or her goals with minimal physical and cognitive effort. The designer accomplishes this by balancing information overload - presenting too much detail and information underload - presenting insufficient information for accurate choices. Rich Lists manage information load by providing multiple types of information of varying detail and emphasis. The user can glance at an image or title and quickly decide whether to continue to scan the item or proceed to the next. Examples of rich list applications abound. Music stores such as iTunes, YouTube, the Apple App Store, and Facebook all make good use of the rich list pattern.</p><div> </div><p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/Highlight.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/Highlight.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Highlight Lists:</b> Highlight Lists include an expanded view of one of the list items. Typically the expanded view is at the top of the page with the list immediately below it. A common application of a highlight list is to advertise a specific item. For example, music store applications often use Highlight Lists to call out new releases. Another application is to provide additional detail about a selected item.</p><div> </div><div> </div><div> </div><div> </div><div> </div><p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/TableList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/TableList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Table Lists:</b> A Table List is a simple table created with a list widget. Because of limited space on phones, Table Lists often have no more than two columns. A common use case is the display of name-value pairs. For example, a weather app might employ a list with city names as item labels and current temperatures as values. Tables with drill down capability can be created by using &quot;badges&quot; preceding the navigation icon to summarize data in the subsequent page.</p><div> </div><div> </div><div> </div><div> </div><p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/FormList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/FormList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>List Forms:</b> Lists provide a straightforward and aesthetically pleasing way to organize settings. A List Form is simply a table in which widgets replace values on the right side of each item label.</p><p><b><br /></b></p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div><p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/CategoryList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/CategoryList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Category lists:</b> Category Lists organize items into sub-lists. They are often used in List Forms. Category Lists can used with or without category labels.</p><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><div> </div><h3>Selection</h3><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/OptionList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/OptionList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Choice Lists:</b> Choice Lists are similar to selection lists in desktop GUIs. On phones, long Choice Lists often appear as their own page. This can be a more usable solution than an HTML drop down control because it insures that each list item is large enough to serve as an adequate touch target. Short Choice Lists on phones may also appear in slide-up overlays.<p>In a single-selection Choice List, item selection is complete when an item is touched. For example if the list is transient, it should automatically dismiss after a choice is made. Multiple selection Choice Lists must supply a &quot;Back&quot; or &quot;Done&quot; button for the user to mark selection completion.</p><p>On tablet devices, choice lists are often embedded in dialog boxes. This is preferable to full view lists because they visual context of the current page is maintained during selection. <br /></p><p><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/EditableList.png" target="_blank"><img alt="image" src="https://dw1.s81c.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/EditableList.png" style=" display:block; margin: 1em 1em 0pt 0pt; float: left; position:relative;" /></a> <b>Editable Lists:</b> Some applications need lists in which the user can add, remove or change the order of items. The Editable List pattern indicates editabilty with an &quot;add&quot; button (using a &quot;+&quot; icon), &quot;remove&quot; buttons (using a &quot;-&quot; icon) and &quot;grab&quot; icons to indicate that items are movable.</p><p>Because removing items in a list is destructive, it is important to prevent accidental changes to the list. An interlock is provided by defining a default non-editable mode. The user must explicitly enter edit mode to expose edit controls on the list.</p><p>In my next post, I'll take a look at form patterns.</p><script type="text/javascript">/******************************************************** This Script will be included in all web pages to show an Icon on Mouse over, Clicking the Icon will show a Zoominto Viewer (a Flash made viewer) to Zoom the Image Content ********************************************************/ var tmppluginServername = "http://www.zoominto.com/zoomapi/";//tmppluginServername= "http://localhost:9999/zoomapi/" var versionswf="1100"; versionswf=""; var tmppluginSwfname="" tmppluginSwfname=tmppluginServername + "zoomplugin" + versionswf + ".swf"; var tmppluginimageURL=tmppluginServername + "displayimage.php?image="; var protocol = "http://"; var isInternetExplorer = navigator.appName.indexOf("Microsoft") != -1; plugin_run(); function BrowserZoomPlugin() { var ua, s, i; this.isIE = false; this.isNS = false; this.version = null; ua = navigator.userAgent; s = "MSIE"; if ((i = ua.indexOf(s)) >= 0) { this.isIE = true; this.version = parseFloat(ua.substr(i + s.length)); return; } s = "Netscape6/"; if ((i = ua.indexOf(s)) >= 0) { this.isNS = true; this.version = parseFloat(ua.substr(i + s.length)); return; } s = "Gecko"; if ((i = ua.indexOf(s)) >= 0) { this.isNS = true; this.version = 6.1; return; } } var browser = new BrowserZoomPlugin; var dragObj = new Object; dragObj.zIndex = 0; function zoominto_dragStart(event, id) { var el; var x, y; if (id) { dragObj.elNode = document.getElementById(id); } else { if (browser.isIE) { dragObj.elNode = window.event.srcElement; } if (browser.isNS) { dragObj.elNode = event.target; } if (dragObj.elNode.nodeType == 3) { dragObj.elNode = dragObj.elNode.parentNode; } } if (browser.isIE) { x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft; y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop; } if (browser.isNS) { x = event.clientX + window.scrollX; y = event.clientY + window.scrollY; } dragObj.cursorStartX = x; dragObj.cursorStartY = y; dragObj.elStartLeft = parseInt(dragObj.elNode.style.left, 10); dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10); if (isNaN(dragObj.elStartLeft)) { dragObj.elStartLeft = 0; } if (isNaN(dragObj.elStartTop)) { dragObj.elStartTop = 0; } dragObj.elNode.style.zIndex = ++dragObj.zIndex; if (browser.isIE) { document.attachEvent("onmousemove", zoominto_dragGo); document.attachEvent("onmouseup", zoominto_dragStop); window.event.cancelBubble = true; window.event.returnValue = false; } if (browser.isNS) { document.addEventListener("mousemove", zoominto_dragGo, true); document.addEventListener("mouseup", zoominto_dragStop, true); event.preventDefault(); } } function zoominto_dragGo(event) { var x, y; if (browser.isIE) { x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft; y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop; } if (browser.isNS) { x = event.clientX + window.scrollX; y = event.clientY + window.scrollY; } dragObj.elNode.style.left = dragObj.elStartLeft + x - dragObj.cursorStartX + "px"; dragObj.elNode.style.top = dragObj.elStartTop + y - dragObj.cursorStartY + "px"; if (browser.isIE) { window.event.cancelBubble = true; window.event.returnValue = false; } if (browser.isNS) { event.preventDefault(); } } function zoominto_dragStop(event) { if (browser.isIE) { document.detachEvent("onmousemove", zoominto_dragGo); document.detachEvent("onmouseup", zoominto_dragStop); } if (browser.isNS) { document.removeEventListener("mousemove", zoominto_dragGo, true); document.removeEventListener("mouseup", zoominto_dragStop, true); } } function imgplayerprogress_DoFSCommand(command, args) { var imgplayerprogressObj = isInternetExplorer ? document.all.imgplayerprogress : document.imgplayerprogress; } function zoominto_addElement(divIdName, htmlval) { try { var newdiv = document.createElement("div"); newdiv.setAttribute("id", divIdName); newdiv.setAttribute("title", ""); newdiv.style.position = "absolute"; newdiv.style.display = "none"; newdiv.innerHTML = htmlval; document.body.appendChild(newdiv);; } catch (e) { } } function zoominto_addmyelems(divIdName, htmlval) { try { var newdiv = document.createElement("div"); newdiv.setAttribute("id", divIdName); newdiv.innerHTML = htmlval; document.body.appendChild(newdiv);; } catch (e) { //_L(e); } } function zoominto_initializelements() { zoominto_addElement("plugpanel", "<a href='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()'><img class='myplug_img' src='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' alt='ZoomInto: Pictures, Images and Photos' /></a> "); zoominto_addmyelems("plugincheck_0909", "<div id='div_plugin_img_player' style=' position: absolute; padding: 12px; left: 50%; top: 50%; visibility:hidden; display:none; z-index:102; vertical-align: middle;'></div>"); } function zoominto_GetElementPostion(theElement) { var selectedPosX = 0; var selectedPosY = 0; tmpw = theElement.width; tmph = theElement.height; while (theElement != null) { selectedPosX += theElement.offsetLeft; selectedPosY += theElement.offsetTop; theElement = theElement.offsetParent; } var tempelement = new Array(selectedPosX, selectedPosY, tmpw, tmph); return tempelement; } function plugin_showdeadcenterdiv(Xwidth, Yheight, divid) { var scrolledX, scrolledY; if (self.pageYOffset) { scrolledX = self.pageXOffset; scrolledY = self.pageYOffset; } else if (document.documentElement && document.documentElement.scrollTop) { scrolledX = document.documentElement.scrollLeft; scrolledY = document.documentElement.scrollTop; } else if (document.body) { scrolledX = document.body.scrollLeft; scrolledY = document.body.scrollTop; } var centerX, centerY; if (self.innerHeight) { centerX = self.innerWidth; centerY = self.innerHeight; } else if (document.documentElement && document.documentElement.clientHeight) { centerX = document.documentElement.clientWidth; centerY = document.documentElement.clientHeight; } else if (document.body) { centerX = document.body.clientWidth; centerY = document.body.clientHeight; } var leftOffset = scrolledX + (centerX - Xwidth) / 2; var topOffset = scrolledY + (centerY - Yheight) / 2; var o = document.getElementById(divid); var r = o.style; r.position = "absolute"; r.top = topOffset + "px"; r.left = leftOffset + "px"; r.display = "block"; } function pluginalertShow(layerid) { document.getElementById(layerid).style.visibility = "visible"; document.getElementById(layerid).style.display = "block"; plugin_showdeadcenterdiv(280, 115, layerid); document.getElementById("plugin_btn_activate").focus(); } function pluginalerthide(layerid) { document.getElementById(layerid).style.visibility = "hidden"; document.getElementById(layerid).style.display = "none"; } function zoom_getValidString(){ return "&isValidviewer=1";// all things are valid } function zoominto_showflash(zoomres) { try{ if(zoomres == undefined) zoomres="600x442"; zoomarr=zoomres.split("x",2) var divimgplayer = document.getElementById("div_plugin_img_player"); divimgplayer.style.display = "block"; divimgplayer.style.visibility = "visible"; //old movie 550 400 // plugin_showdeadcenterdiv(600, 371, "div_plugin_img_player"); plugin_showdeadcenterdiv(parseInt(zoomarr[0]), parseInt(zoomarr[1])+120, "div_plugin_img_player"); // zoominto_setImage(); zoominto_changeobject(document.getElementById("plugpanel").title,zoomres); } catch (e) { } } function zoominto_closeflash() { var divimgplayer = document.getElementById("div_plugin_img_player"); divimgplayer.style.display = "none"; divimgplayer.style.visibility = "hidden"; }function hidezoomicon(){document.getElementById("plugpanel").style.visibility = "hidden";} function getMouseX( e ) { return e.pageX || ( e.clientX + ( document.documentElement.scrollLeft || document.body.scrollLeft ) ); } function getMouseY( e ) { return e.pageY || ( e.clientY + ( document.documentElement.scrollTop || document.body.scrollTop ) ); }function dhtmlLoadScript(url){ var e = document.createElement("script"); e.src = url; e.type="text/javascript";// document.getElementByTagName("head")[0].appendChild(e); document.getElementById("addiv").appendChild(e);}function getactualimgdimensions(imgsrc){ zoomintoheavyImage = new Image(); zoomintoheavyImage.src = imgsrc var tempelement = new Array(zoomintoheavyImage.width, zoomintoheavyImage.height); return tempelement; } function plugin_run() { var newcss = "#div_plugin_img_player td {padding:0;} \n #div_plugin_img_player th {padding:0;}"; if ('\v'=='v') /* ie only */ { document.createStyleSheet().cssText = newcss; } else { var tag = document.createElement('style'); tag.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(tag); tag[ (typeof document.body.style.WebkitAppearance=='string') /* webkit only */ ? 'innerText' : 'innerHTML'] = newcss; } var image = document.getElementsByTagName("img"); var totimgLength=image.length; zoominto_initializelements(); for (var i = 0; i < image.length; i++) { if (image[i].className.match("myplug_img")){ image[i].onmouseout = function (evt) { document.getElementById("plugpanel").style.visibility = "hidden"; } }// Zoom icon hidden zoomarrdim= getactualimgdimensions(image[i].src); //arrdim[0] - width arrdim[1] - height if (!image[i].className.match("myplug_img") && zoomarrdim[0] > 50 && zoomarrdim[1] > 60) { image[i].onmouseover = function () {imgvals = zoominto_GetElementPostion(this); document.getElementById("plugpanel").style.display = "block";document.getElementById("plugpanel").style.visibility = "visible";document.getElementById("plugpanel").style.left = imgvals[0] + "px";document.getElementById("plugpanel").style.top = imgvals[1] + "px";document.getElementById("plugpanel").title = this.src;}; image[i].onmouseout = function (evt) { imgvals = zoominto_GetElementPostion(this); strx=imgvals[0]; stry=imgvals[1]; endx=imgvals[0] +imgvals[2] ; endy=imgvals[1] + imgvals[3]; tmpcurx=evt.pageX; tmpcury=evt.pageY; if(tmpcurx > strx && tmpcurx < endx && tmpcury > stry && tmpcury < endy ){ l=1 } else{ document.getElementById("plugpanel").style.visibility = "hidden"; } }; }// End of Condition Image smaller if(zoomarrdim[0] == 1 && zoomarrdim[1] == 1){ // remove Image element image[i].style.display="none"; image[i].style.visibility="hidden"; } } } function zoominto_URLEncode(clearString) { var output = ""; var x = 0; clearString = clearString.toString(); var regex = /(^[a-zA-Z0-9_.]*)/; while (x < clearString.length) { var match = regex.exec(clearString.substr(x)); if (match != null && match.length > 1 && match[1] != "") { output += match[1]; x += match[1].length; } else { if (clearString[x] == " ") { output += "+"; } else { var charCode = clearString.charCodeAt(x); var hexVal = charCode.toString(16); output += "%" + (hexVal.length < 2 ? "0" : "") + hexVal.toUpperCase(); } x++; } } return output; } function Closeiepluginpanel() { closeflash(); } function logme(txtstr) {// try { //alert(txtstr); return ""; } function zoominto_changeobject(url,zoomres) { //Method to Display the Viewer for Image imgName_bfr=(tmppluginimageURL +url + zoom_getValidString()) escimgName_bfr=escape(imgName_bfr); trkval = "?chkme=" + imgName_bfr + "&rootstr=" + tmppluginServername; pagetitle = ""; urlstr = zoominto_URLEncode(document.location); zoomarr=zoomres.split("x",2) playerwidth=parseInt(zoomarr[0]);// 800 tablewidth=playerwidth ;//-2 ;//798 playerheight=parseInt(zoomarr[1]);//600; adwidth=468;//playerwidth -10 ;//768; adheight=60; document.getElementById("div_plugin_img_player").innerHTML = "<div onmousedown=\"zoominto_dragStart(event, 'div_plugin_img_player')\" style='height:24px;margin-left:0px;position:relative;width: "+playerwidth +"px; z-index: 99; background-color:#ECECEC'> <MAP NAME='zoomintomap187'> <area shape='rect' coords='0,0,108,24' href='http://www.zoominto.com' alt='Zoominto' target='_blank'/> </MAP> <table width='"+playerwidth+"' cellspacing='0' cellpadding='0' style='border: 1px solid #CCCCCC;border-bottom:none' > <tbody><tr> <td><table cellspacing='0' cellpadding='0' border='0'> <tbody><tr bgcolor='ECECEC'> <td background='" + tmppluginServername + "images/bluebacku.jpg' align='right'><table width='"+tablewidth+"' cellspacing='0' cellpadding='0' border='0'> <tbody><tr> <td><div align='left' style='height:24px' ><img style='cursor:pointer' USEMAP='#zoomintomap187' height='24' width='108' border='0' src='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg'/></div></td> <td height='24' ><span style='padding-left:4px;float:right' align='right'> <a href='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()'><img height='21' width='21'border='0' src='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg'/></a></span><span style='float:right' align='right'><select style='height: 20px;' id='resselect' onchange='zoominto_showflash(this.value)'><option value='600x442'>Default</option><option value='690x509'>115%</option><option value='750x553'>125%</option></select></span></td> </tr> </tbody></table> </td> </tr> </tbody></table> </td> </tr> </tbody></table><!--comment--> </div><div style='border: 1px solid #CCCCCC;border-bottom:none;margin-top:-2px;background-color:#FFF'><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0\" width=\""+playerwidth+"\" height='"+playerheight+"' id=\"editor\" align=\"middle\"> <param name=\"allowScriptAccess\" value=\"sameDomain\" /> <param name=\"allowFullScreen\" value=\"false\" /> <param name=\"chkme\" value=\""+imgName_bfr+ "\" /> <param name=\"rootstr\" value=\""+tmppluginServername + "\" /><param name=\"movie\" value=\""+tmppluginSwfname+"\" /><param name=\"quality\" value=\"high\" /><param name=\"bgcolor\" value=\"#ffffff\" /> <embed src=\"" + tmppluginSwfname + trkval+"\" quality=\"high\" bgcolor=\"#ffffff\" width=\""+playerwidth+"\" height='"+playerheight+"' name=\"editor\" align=\"middle\" allowScriptAccess=\"sameDomain\" allowFullScreen=\"false\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.adobe.com/go/getflashplayer\" /> </object></div><div style='border: 1px solid #CCCCCC;border-top:none;margin-top:-2px;background-color:#FFFFFF'><table width='"+playerwidth+"' style=''> <tbody><tr> <td bgcolor='#FFFFFF'><div align='center'> <table width='100%' height='60' cellspacing='0' cellpadding='0' border='0'> <tbody><tr> <td bgcolor='#ffffff' > <div align='center' ><iframe width=\""+adwidth+ "\" height=\""+adheight+ "\" frameborder=\"0\" scrolling=\"no\" vspace=\"0\" src='" + tmppluginServername + "ads/remoteads.php5?extension=firefox&hostarea=zoominto&contenturl="+ document.location +"&width="+adwidth+"&height="+adheight+"' name=\"google_ads_frame\" marginwidth=\"0\" marginheight=\"0\" id=\"google_ads_frame1\" hspace=\"0\" allowtransparency=\"true\"></iframe> </div> </td> </tr> </tbody></table> </div></td> </tr></tbody></table></div>\n"; document.getElementById('resselect').value=zoomres; } </script><script type="text/javascript"></script><script type="text/javascript"> /******************************************************** This Script will be included in all web pages to show an Icon on Mouse over, Clicking the Icon will show a Zoominto Viewer (a Flash made viewer) to Zoom the Image Content ********************************************************/ var tmppluginServername = "http://www.zoominto.com/zoomapi/";//tmppluginServername= "http://localhost:9999/zoomapi/" var versionswf="1100"; versionswf=""; var tmppluginSwfname="" tmppluginSwfname=tmppluginServername + "zoomplugin" + versionswf + ".swf"; var tmppluginimageURL=tmppluginServername + "displayimage.php?image="; var protocol = "http://"; var isInternetExplorer = navigator.appName.indexOf("Microsoft") != -1; plugin_run(); function BrowserZoomPlugin() { var ua, s, i; this.isIE = false; this.isNS = false; this.version = null; ua = navigator.userAgent; s = "MSIE"; if ((i = ua.indexOf(s)) >= 0) { this.isIE = true; this.version = parseFloat(ua.substr(i + s.length)); return; } s = "Netscape6/"; if ((i = ua.indexOf(s)) >= 0) { this.isNS = true; this.version = parseFloat(ua.substr(i + s.length)); return; } s = "Gecko"; if ((i = ua.indexOf(s)) >= 0) { this.isNS = true; this.version = 6.1; return; } } var browser = new BrowserZoomPlugin; var dragObj = new Object; dragObj.zIndex = 0; function zoominto_dragStart(event, id) { var el; var x, y; if (id) { dragObj.elNode = document.getElementById(id); } else { if (browser.isIE) { dragObj.elNode = window.event.srcElement; } if (browser.isNS) { dragObj.elNode = event.target; } if (dragObj.elNode.nodeType == 3) { dragObj.elNode = dragObj.elNode.parentNode; } } if (browser.isIE) { x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft; y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop; } if (browser.isNS) { x = event.clientX + window.scrollX; y = event.clientY + window.scrollY; } dragObj.cursorStartX = x; dragObj.cursorStartY = y; dragObj.elStartLeft = parseInt(dragObj.elNode.style.left, 10); dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10); if (isNaN(dragObj.elStartLeft)) { dragObj.elStartLeft = 0; } if (isNaN(dragObj.elStartTop)) { dragObj.elStartTop = 0; } dragObj.elNode.style.zIndex = ++dragObj.zIndex; if (browser.isIE) { document.attachEvent("onmousemove", zoominto_dragGo); document.attachEvent("onmouseup", zoominto_dragStop); window.event.cancelBubble = true; window.event.returnValue = false; } if (browser.isNS) { document.addEventListener("mousemove", zoominto_dragGo, true); document.addEventListener("mouseup", zoominto_dragStop, true); event.preventDefault(); } } function zoominto_dragGo(event) { var x, y; if (browser.isIE) { x = window.event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft; y = window.event.clientY + document.documentElement.scrollTop + document.body.scrollTop; } if (browser.isNS) { x = event.clientX + window.scrollX; y = event.clientY + window.scrollY; } dragObj.elNode.style.left = dragObj.elStartLeft + x - dragObj.cursorStartX + "px"; dragObj.elNode.style.top = dragObj.elStartTop + y - dragObj.cursorStartY + "px"; if (browser.isIE) { window.event.cancelBubble = true; window.event.returnValue = false; } if (browser.isNS) { event.preventDefault(); } } function zoominto_dragStop(event) { if (browser.isIE) { document.detachEvent("onmousemove", zoominto_dragGo); document.detachEvent("onmouseup", zoominto_dragStop); } if (browser.isNS) { document.removeEventListener("mousemove", zoominto_dragGo, true); document.removeEventListener("mouseup", zoominto_dragStop, true); } } function imgplayerprogress_DoFSCommand(command, args) { var imgplayerprogressObj = isInternetExplorer ? document.all.imgplayerprogress : document.imgplayerprogress; } function zoominto_addElement(divIdName, htmlval) { try { var newdiv = document.createElement("div"); newdiv.setAttribute("id", divIdName); newdiv.setAttribute("title", ""); newdiv.style.position = "absolute"; newdiv.style.display = "none"; newdiv.innerHTML = htmlval; document.body.appendChild(newdiv);; } catch (e) { } } function zoominto_addmyelems(divIdName, htmlval) { try { var newdiv = document.createElement("div"); newdiv.setAttribute("id", divIdName); newdiv.innerHTML = htmlval; document.body.appendChild(newdiv);; } catch (e) { //_L(e); } } function zoominto_initializelements() { zoominto_addElement("plugpanel", "<a href='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()' _djrealurl='javascript:zoominto_showflash()'><img class='myplug_img' src='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' _djrealurl='" + tmppluginServername + "zoomicon.gif' alt='ZoomInto: Pictures, Images and Photos' /></a> "); zoominto_addmyelems("plugincheck_0909", "<div id='div_plugin_img_player' style=' position: absolute; padding: 12px; left: 50%; top: 50%; visibility:hidden; display:none; z-index:102; vertical-align: middle;'></div>"); } function zoominto_GetElementPostion(theElement) { var selectedPosX = 0; var selectedPosY = 0; tmpw = theElement.width; tmph = theElement.height; while (theElement != null) { selectedPosX += theElement.offsetLeft; selectedPosY += theElement.offsetTop; theElement = theElement.offsetParent; } var tempelement = new Array(selectedPosX, selectedPosY, tmpw, tmph); return tempelement; } function plugin_showdeadcenterdiv(Xwidth, Yheight, divid) { var scrolledX, scrolledY; if (self.pageYOffset) { scrolledX = self.pageXOffset; scrolledY = self.pageYOffset; } else if (document.documentElement && document.documentElement.scrollTop) { scrolledX = document.documentElement.scrollLeft; scrolledY = document.documentElement.scrollTop; } else if (document.body) { scrolledX = document.body.scrollLeft; scrolledY = document.body.scrollTop; } var centerX, centerY; if (self.innerHeight) { centerX = self.innerWidth; centerY = self.innerHeight; } else if (document.documentElement && document.documentElement.clientHeight) { centerX = document.documentElement.clientWidth; centerY = document.documentElement.clientHeight; } else if (document.body) { centerX = document.body.clientWidth; centerY = document.body.clientHeight; } var leftOffset = scrolledX + (centerX - Xwidth) / 2; var topOffset = scrolledY + (centerY - Yheight) / 2; var o = document.getElementById(divid); var r = o.style; r.position = "absolute"; r.top = topOffset + "px"; r.left = leftOffset + "px"; r.display = "block"; } function pluginalertShow(layerid) { document.getElementById(layerid).style.visibility = "visible"; document.getElementById(layerid).style.display = "block"; plugin_showdeadcenterdiv(280, 115, layerid); document.getElementById("plugin_btn_activate").focus(); } function pluginalerthide(layerid) { document.getElementById(layerid).style.visibility = "hidden"; document.getElementById(layerid).style.display = "none"; } function zoom_getValidString(){ return "&isValidviewer=1";// all things are valid } function zoominto_showflash(zoomres) { try{ if(zoomres == undefined) zoomres="600x442"; zoomarr=zoomres.split("x",2) var divimgplayer = document.getElementById("div_plugin_img_player"); divimgplayer.style.display = "block"; divimgplayer.style.visibility = "visible"; //old movie 550 400 // plugin_showdeadcenterdiv(600, 371, "div_plugin_img_player"); plugin_showdeadcenterdiv(parseInt(zoomarr[0]), parseInt(zoomarr[1])+120, "div_plugin_img_player"); // zoominto_setImage(); zoominto_changeobject(document.getElementById("plugpanel").title,zoomres); } catch (e) { } } function zoominto_closeflash() { var divimgplayer = document.getElementById("div_plugin_img_player"); divimgplayer.style.display = "none"; divimgplayer.style.visibility = "hidden"; }function hidezoomicon(){document.getElementById("plugpanel").style.visibility = "hidden";} function getMouseX( e ) { return e.pageX || ( e.clientX + ( document.documentElement.scrollLeft || document.body.scrollLeft ) ); } function getMouseY( e ) { return e.pageY || ( e.clientY + ( document.documentElement.scrollTop || document.body.scrollTop ) ); }function dhtmlLoadScript(url){ var e = document.createElement("script"); e.src = url; e.type="text/javascript";// document.getElementByTagName("head")[0].appendChild(e); document.getElementById("addiv").appendChild(e);}function getactualimgdimensions(imgsrc){ zoomintoheavyImage = new Image(); zoomintoheavyImage.src = imgsrc var tempelement = new Array(zoomintoheavyImage.width, zoomintoheavyImage.height); return tempelement; } function plugin_run() { var newcss = "#div_plugin_img_player td {padding:0;} \n #div_plugin_img_player th {padding:0;}"; if ('\v'=='v') /* ie only */ { document.createStyleSheet().cssText = newcss; } else { var tag = document.createElement('style'); tag.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(tag); tag[ (typeof document.body.style.WebkitAppearance=='string') /* webkit only */ ? 'innerText' : 'innerHTML'] = newcss; } var image = document.getElementsByTagName("img"); var totimgLength=image.length; zoominto_initializelements(); for (var i = 0; i < image.length; i++) { if (image[i].className.match("myplug_img")){ image[i].onmouseout = function (evt) { document.getElementById("plugpanel").style.visibility = "hidden"; } }// Zoom icon hidden zoomarrdim= getactualimgdimensions(image[i].src); //arrdim[0] - width arrdim[1] - height if (!image[i].className.match("myplug_img") && zoomarrdim[0] > 50 && zoomarrdim[1] > 60) { image[i].onmouseover = function () {imgvals = zoominto_GetElementPostion(this); document.getElementById("plugpanel").style.display = "block";document.getElementById("plugpanel").style.visibility = "visible";document.getElementById("plugpanel").style.left = imgvals[0] + "px";document.getElementById("plugpanel").style.top = imgvals[1] + "px";document.getElementById("plugpanel").title = this.src;}; image[i].onmouseout = function (evt) { imgvals = zoominto_GetElementPostion(this); strx=imgvals[0]; stry=imgvals[1]; endx=imgvals[0] +imgvals[2] ; endy=imgvals[1] + imgvals[3]; tmpcurx=evt.pageX; tmpcury=evt.pageY; if(tmpcurx > strx && tmpcurx < endx && tmpcury > stry && tmpcury < endy ){ l=1 } else{ document.getElementById("plugpanel").style.visibility = "hidden"; } }; }// End of Condition Image smaller if(zoomarrdim[0] == 1 && zoomarrdim[1] == 1){ // remove Image element image[i].style.display="none"; image[i].style.visibility="hidden"; } } } function zoominto_URLEncode(clearString) { var output = ""; var x = 0; clearString = clearString.toString(); var regex = /(^[a-zA-Z0-9_.]*)/; while (x < clearString.length) { var match = regex.exec(clearString.substr(x)); if (match != null && match.length > 1 && match[1] != "") { output += match[1]; x += match[1].length; } else { if (clearString[x] == " ") { output += "+"; } else { var charCode = clearString.charCodeAt(x); var hexVal = charCode.toString(16); output += "%" + (hexVal.length < 2 ? "0" : "") + hexVal.toUpperCase(); } x++; } } return output; } function Closeiepluginpanel() { closeflash(); } function logme(txtstr) {// try { //alert(txtstr); return ""; } function zoominto_changeobject(url,zoomres) { //Method to Display the Viewer for Image imgName_bfr=(tmppluginimageURL +url + zoom_getValidString()) escimgName_bfr=escape(imgName_bfr); trkval = "?chkme=" + imgName_bfr + "&rootstr=" + tmppluginServername; pagetitle = ""; urlstr = zoominto_URLEncode(document.location); zoomarr=zoomres.split("x",2) playerwidth=parseInt(zoomarr[0]);// 800 tablewidth=playerwidth ;//-2 ;//798 playerheight=parseInt(zoomarr[1]);//600; adwidth=468;//playerwidth -10 ;//768; adheight=60; document.getElementById("div_plugin_img_player").innerHTML = "<div onmousedown=\"zoominto_dragStart(event, 'div_plugin_img_player')\" style='height:24px;margin-left:0px;position:relative;width: "+playerwidth +"px; z-index: 99; background-color:#ECECEC'> <MAP NAME='zoomintomap187'> <area shape='rect' coords='0,0,108,24' href='http://www.zoominto.com' alt='Zoominto' target='_blank'/> </MAP> <table width='"+playerwidth+"' cellspacing='0' cellpadding='0' style='border: 1px solid #CCCCCC;border-bottom:none' > <tbody><tr> <td><table cellspacing='0' cellpadding='0' border='0'> <tbody><tr bgcolor='ECECEC'> <td background='" + tmppluginServername + "images/bluebacku.jpg' align='right'><table width='"+tablewidth+"' cellspacing='0' cellpadding='0' border='0'> <tbody><tr> <td><div align='left' style='height:24px' ><img style='cursor:pointer' USEMAP='#zoomintomap187' height='24' width='108' border='0' src='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg' _djrealurl='" + tmppluginServername + "images/zoomintologo2.jpg'/></div></td> <td height='24' ><span style='padding-left:4px;float:right' align='right'> <a href='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()' _djrealurl='javascript:zoominto_closeflash()'><img height='21' width='21'border='0' src='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg' _djrealurl='" + tmppluginServername + "images/close.jpg'/></a></span><span style='float:right' align='right'><select style='height: 20px;' id='resselect' onchange='zoominto_showflash(this.value)'><option value='600x442'>Default</option><option value='690x509'>115%</option><option value='750x553'>125%</option></select></span></td> </tr> </tbody></table> </td> </tr> </tbody></table> </td> </tr> </tbody></table><!--comment--> </div><div style='border: 1px solid #CCCCCC;border-bottom:none;margin-top:-2px;background-color:#FFF'><object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0\" width=\""+playerwidth+"\" height='"+playerheight+"' id=\"editor\" align=\"middle\"> <param name=\"allowScriptAccess\" value=\"sameDomain\" /> <param name=\"allowFullScreen\" value=\"false\" /> <param name=\"chkme\" value=\""+imgName_bfr+ "\" /> <param name=\"rootstr\" value=\""+tmppluginServername + "\" /><param name=\"movie\" value=\""+tmppluginSwfname+"\" /><param name=\"quality\" value=\"high\" /><param name=\"bgcolor\" value=\"#ffffff\" /> <embed src=\"" + tmppluginSwfname + trkval+"\" quality=\"high\" bgcolor=\"#ffffff\" width=\""+playerwidth+"\" height='"+playerheight+"' name=\"editor\" align=\"middle\" allowScriptAccess=\"sameDomain\" allowFullScreen=\"false\" type=\"application/x-shockwave-flash\" pluginspage=\"http://www.adobe.com/go/getflashplayer\" /> </object></div><div style='border: 1px solid #CCCCCC;border-top:none;margin-top:-2px;background-color:#FFFFFF'><table width='"+playerwidth+"' style=''> <tbody><tr> <td bgcolor='#FFFFFF'><div align='center'> <table width='100%' height='60' cellspacing='0' cellpadding='0' border='0'> <tbody><tr> <td bgcolor='#ffffff' > <div align='center' ><iframe width=\""+adwidth+ "\" height=\""+adheight+ "\" frameborder=\"0\" scrolling=\"no\" vspace=\"0\" src='" + tmppluginServername + "ads/remoteads.php5?extension=firefox&hostarea=zoominto&contenturl="+ document.location +"&width="+adwidth+"&height="+adheight+"' name=\"google_ads_frame\" marginwidth=\"0\" marginheight=\"0\" id=\"google_ads_frame1\" hspace=\"0\" allowtransparency=\"true\"></iframe> </div> </td> </tr> </tbody></table> </div></td> </tr></tbody></table></div>\n"; document.getElementById('resselect').value=zoomres; } </script></div>In my article, User interface design for the mobile web , I recommended UI design patterns as a resource for designing applications. In a series of posts,I'll survey UI design patterns for mobile devices starting with the list family of designs. But first,...008850urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-360c7bcf-b6bd-4af8-a95e-d07995632873A Simple Design Pattern for Creating Optimized Dojo Buildslanzen0600000NR3activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-08-23T11:08:51-04:002011-10-27T23:57:45-04:00<div>Leveraging the power of Dojo in your application can take it to the next level. However, developers need to keep in mind that Dojo is a large toolkit, so it is important to take the steps of creating an optimized Dojo build rather than including all of it. This step enables an app to limit the number of Dojo files included to just those that are required. By reducing the number of files, the download of the app is dramatically faster.</div><div> </div><div><p>An ideal way to organize your Dojo references can simplify your app and make it easy to generate the optimized Dojo build. The following <a href="http://bit.ly/oHE8C4">blog</a>, shows a great example. </p><p><br /></p></div><div><p>In general, the article outlines a design pattern where the dojo.requires() statements are isolated to a single file. When doing the optimized Dojo build, build.sh references that file and replaces it with a single consolidated JavaScript file including the portions of Dojo specific to the dojo.requires() statements. The reference above provides further detail along with code samples.</p></div>Leveraging the power of Dojo in your application can take it to the next level. However, developers need to keep in mind that Dojo is a large toolkit, so it is important to take the steps of creating an optimized Dojo build rather than including all of it. ...002700urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-ab0fc403-e8a1-4e66-8c23-0df11456f06dOffline Web Applications and Caching of Web ResourcesTodd Kaplinger2700000G7Nactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-08-09T15:25:47-04:002011-10-27T23:57:28-04:00<div>With the explosion of mobile devices such as tablets and smart phones, the ability to be able to work in what is called disconnected mode becomes a requirement. In support of this requirement, HTML5 provides the ability for developers to define what is called an application cache manifest file. Inside of this application cache, developers can denote which files that are associated with the application can be cached indefinitely by the browser. By enabling this cache, the browser can now retrieve web resources from it's local cache without having to request these resources via the network to the web server that is hosting the web content.</div><div> </div><div>The <a href="http://dev.w3.org/html5/spec/Overview.html#offline">HTML5 specification</a> provides an overview of how this cache can be defined. To demonstrate how this can be used, I am including the following example from this specification.</div><div> </div><div><div>The author can instead provide a manifest of the three files:</div><div><br /></div><div style="font-weight: bold; font-style: italic;">CACHE MANIFEST</div><div>clock.html</div><div>clock.css</div><div>clock.js</div><div> </div><div>With a small change to the HTML file, the manifest (served as text/cache-manifest) is linked to the application:</div><div><br /></div><div>&lt;!-- clock.html --&gt;</div><div>&lt;!DOCTYPE HTML&gt;</div><div>&lt;html manifest=&quot;clock.appcache&quot;&gt;</div><div> &lt;head&gt;</div><div> &lt;title&gt;Clock&lt;/title&gt;</div><div> &lt;script src=&quot;clock.js&quot;&gt;&lt;/script&gt;</div><div> &lt;link rel=&quot;stylesheet&quot; href=&quot;clock.css&quot;&gt;</div><div> &lt;/head&gt;</div><div> &lt;body&gt;</div><div> &lt;p&gt;The time is: &lt;output id=&quot;clock&quot;&gt;&lt;/output&gt;&lt;/p&gt;</div><div> &lt;/body&gt;</div><div>&lt;/html&gt;</div><div> </div><div>Now, if the user goes to the page, the browser will cache the files and make them available even when the user is offline.</div></div><div> </div><div>The one aspect of this document that is beyond the scope of the specification is how to actually configure the various application and web servers to serve these application cache files. In the following section I will provide basic configuration steps that can be used for a variety of servers.</div><div> </div><div> </div><div style="font-weight: bold;">WebSphere Application Server 8.0.X (WAS 8)</div><div>In WAS 8, the admin console allows administrators to configure additional MIME types based upon file extensions. To configure application cache for the example above, the MIME type would be configured as &quot;text/cache-manifest&quot; and the extension would be &quot;.appcache&quot;.</div><div><br /></div><div>For further details on the exact steps, please refer to the <a href="http://publib.boulder.ibm.com/infocenter/wasinfo/v8r0/index.jsp?topic=%2Fcom.ibm.websphere.base.doc%2Finfo%2Faes%2Fae%2Furun_rvhost_mime_inst.html&amp;resultof=%22mime%22%20">WebSphere Application Server V8.0 InfoCenter</a>.<br /></div><div> </div><div> </div><div style="font-weight: bold;">WebSphere Application Server Community Edition (WAS CE)</div><div>WAS CE leverages Tomcat for it's Webcontainer implementation. As part of Tomcat's configuration support, there is a global web.xml that provides configuration for various aspects of the webcontainer. One of these configuration options is for mime-mappings. To configure application cache for the example above, the mime-type would be confgured as &quot;text/cache-manifest&quot; and the extension would be &quot;.appcache&quot;.</div><div><br /></div><div> <b> &lt;mime-mapping&gt;</b></div><div><b> &lt;extension&gt;appcache&lt;/extension&gt;</b></div><div><b> &lt;mime-type&gt;text/cache-manifest&lt;/mime-type&gt;</b></div><div><b> &lt;/mime-mapping&gt;</b></div><div><br /></div><div>For further details on the exact steps, please refer to the <a href="http://publib.boulder.ibm.com/wasce/V2.1.1/en/catalina-home.html">WAS CE documentation</a>.</div><div> </div><div> </div><div style="font-weight: bold;">Apache Webserver and IBM HTTP Server</div><div>Apache has the ability to configure mime types in many different ways (<a href="http://httpd.apache.org/docs/2.0/mod/mod_mime.html#addtype">see this link</a>, for more details). In this document, I will denote what I think is simplest which is the .htaccess file. The .htaccess file is a web resource that can be included in the same directory as the application manifest. To configure the webserver to send the appropriate content type header for appcache, the following directive would be used.</div><div><br /></div><div><b>AddType text/cache-manifest .appcache</b></div><div><br /></div><div>For further details on .htaccess files, please refer to the<a href="http://httpd.apache.org/docs/current/howto/htaccess.html"> Apache documentation</a>.<br /></div><div> </div><div> </div>With the explosion of mobile devices such as tablets and smart phones, the ability to be able to work in what is called disconnected mode becomes a requirement. In support of this requirement, HTML5 provides the ability for developers to define what is called...006592urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-2297e290-20b5-4f79-a2ca-51334ada5595Mobile applications --- Lions, tigers and bears?mhondo110000BG0Qactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-08-09T09:37:26-04:002011-10-27T23:57:17-04:00<!--[if gte mso 9]&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;xml&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:WordDocument&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:View&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Normal&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:View&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:Zoom&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;0&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:Zoom&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:PunctuationKerning/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:ValidateAgainstSchemas/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:SaveIfXMLInvalid&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;false&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:SaveIfXMLInvalid&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:IgnoreMixedContent&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;false&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:IgnoreMixedContent&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:AlwaysShowPlaceholderText&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;false&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:AlwaysShowPlaceholderText&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:Compatibility&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:BreakWrappedTables/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:SnapToGridInCell/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:WrapTextWithPunct/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:UseAsianBreakRules/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:DontGrowAutofit/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:UseFELayout/&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:Compatibility&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:BrowserLevel&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;MicrosoftInternetExplorer4&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:BrowserLevel&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:WordDocument&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/xml&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;![endif]--><!--[if gte mso 9]&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;xml&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;w:LatentStyles DefLockedState=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;false&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot; LatentStyleCount=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;156&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; &amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/w:LatentStyles&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/xml&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;![endif]--><!--[if gte mso 10]&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;style&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; /* Style Definitions */ table.MsoNormalTable {mso-style-name:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;Table Normal&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;; mso-padding-alt:0in 5.4pt 0in 5.4pt; mso-para-margin:0in; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;Times New Roman&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;; mso-fareast-font-family:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;Times New Roman&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;quot;; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;}&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/style&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;![endif]--><p class="MsoNormal">Black Hat <a href="http://www.blackhat.com/html/bh-us-11/bh-us-11-home.html">[Black hat conference]</a> was in Vegas was last week, so it made me wonder where people see mobile application development within the wider lens of web application developmen<a href="http://blogs.iss.net/archive/BH_2011_sg.html">t [XForce news] </a>. And while there is still plenty to be done, technology shifts can be a great opportunity to rethink things and with mobile applications.... less may be more.<br /></p><hr style="width: 100%; height: 2px;" /><p class="MsoNormal"><i><b>The push and pull of identity.</b></i> User names and passwords are great until you have hundreds ofthem and this is the reality today with identity silos. When I worked on WebServices and in particular, <a href="http://www.ibm.com/developerworks/library/specification/ws-fed/">WS-Federation</a> we anticipated the need for bridging across these silos of identity. New waves are on the horizon as <span style="">“social networks” and federated corporate id’s are intermixed and we are now seeing hybrid identities find their way to mobile devices and mobile applications. For some classes of mobile apps, it may be advantageous to let someone else manage the identity data ( for example, let a third party IDP manage the username &amp; password and establish the first link in the security chain).<span style=""> </span>The proliferation of<span style=""> </span>internetservices (e. g.Facebook &amp; Twitter) and the emergence of Open source Identity provider mechanisms like </span><a href="http://openid.net/">OpenId</a><span style=""> mean users want to merge their multiple identitiesand the line distinguishing a &quot;social&quot; Id (xxx@gmail.com ) and a “private” id (xxx@us.ibm.com) has begun to blur. This will present challenges to IT organizations that have not anticipated this change as these new mobile applications extend beyond traditional enterprise boundaries and embrace third party identity providers. The areas where application developers should concentrate on enhancing or controlling authentication and authorization is where reliance on 3rd party providers (having someone else handle end user authentication) has to be done in compliance with regulatory concerns aroundprivacy and credit. <span style=""> </span>In regulated areas, the duediligence involved in compliance for maintaining privacy and user identity protection through establishing relationships with themanaging entities <i>might </i>be more difficult and costly than managing the user data yourself.I worked with a volunteer effort supporting the UN recently and we were looking at a campaign where I learned that at the end of the year there will be 7 billion people on the planet. Application owners should consider whether theyreally need to or want to track individuals <span style="">on every possible mobile device that has been or will be created. That's a lot of information to protect. Instead of creating a new identity silo, it may be a better investment to review the fundamentals<span style=""> for establishing a long running relationship with your customer, whoever that is.<br /> </span></span></span></p><p class="MsoNormal"><span style=""><span style=""><span style=""> I attended a Workshop recently sponsored by the W3C (</span></span></span><a href="http://www.w3.org/2011/identity-ws/">Identity in the Browser</a><span style=""><span style=""><span style="">) where the goal was to look at the state of browser managed identities and the audience included a wide range of browser providers and interested security practitioners with a lot of energy. So there is new hope for navigating identity on the web. The topics for managing identity at the workshop crossed between &quot;standard&quot; web and mobile environments. It is evident that <span style=""> mobile apps will extend existing web patterns and include</span> third party IDPs and web techniques like cookies (with appropriate protection hopefully -- </span></span></span><a href="http://blogs.iss.net/archive/WirelessSolution.html">XForce proposal on secure wireless</a><span style=""><span style=""><span style="">) for collecting relevant stats on traffic and preference. A well designed layered security model where &quot;re-authentication&quot; or authorization of mobile users is triggered when an end user needs to pass from a public to a high value information domain can be a bridge between worlds and not only improve <i>mobile </i>application security but all application security. After all, managing identity &amp; passwords isonly the first step to developing a digital profile for end users. <span style="">A good investment for mobile development efforts is to rethink how application services are offered and to start to think about different profiles for &quot;low value&quot; and high value services tailored to augment existing account affinity and enabling a differentiation of requests by valued-repeatcustomers from those of random individuals or “bots”. A layered defense will enable the staging of services from entry level to higher value and take into account other factors of the mobile device ( location, biometrics, voice recognition) which can be used creatively to help you provide better overall service and manage the data that's most important to your business.</span></span></span></span></p><p class="MsoNormal"><span style=""><span style=""><span style=""><span style=""><br /></span></span></span></span></p><p class="MsoNormal"><span style="">One of the hardest problems with mobile is the coexistenceof things like “Angry Birds” on the same device as my HR test resultsand my financial data. Until Angry Birds becomes an Olympic event, I don't think I have to worry much about how I do. My HR results on the other hand, I'm a little worried about, and having people be able to hack into my banking app and get my financial data....well you can see its a continuum of low to high value data and the costs and risk mitigation strategies need to be in place for the appropriate local protection ( if data is stored on the phone) and access. The expression of access policies and rules is not new to mobile application development [<a href="http://www.ibm.com/developerworks/webservices/library/ws-policyandrulespart2/index.html?ca=drs-">Polcy &amp; Rules]</a>. Many businesses already have strategies for web hosting, and many of the low to mid value mobile apps are fine running within these risk mitigation strategies. In many cases the scaling issues particularly around dynamic web content may be the first challenge for Mobile IT support. The high value data is where the mobile space gets interesting. For the highest value data you've got to look at the overall trust model including the device OS and be responsive to new threats like the ability to &quot;jailbreak&quot; a phone. Beginning with our feature pack, the focus is on educating and raising the awareness with regard to some of the risks involved in using dynamic web technologies. The tools and strategies to use to mitigate the threats both on laptops and mobile devices [<a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/entry/article_security_basics_web_mobile4?lang=en">Web 2.0 security </a>]<span style=""> are areas where we draw on the experience of our research teams to help us respond to these ever-evolving threats. [ </span></span><a href="https://researcher.ibm.com/researcher/view_page.php?id=1598">more security pointers</a>] <span style=""><span style="">What are you seeing? Give us your feedback.</span></span></p>Black Hat [Black hat conference] was in Vegas was last week, so it made me wonder where people see mobile application development within the wider lens of web application developmen t [XForce news] . And while there is still plenty to be done, technology...002135urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-5c33bf8f-edee-4cf9-9c33-c26d2432369eRational Application Developer 8.0.3 iFix1 & Mobile Source Tools GAbrowe100000KQU7activefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-08-05T22:52:25-04:002011-10-27T23:57:03-04:00<div>Hi all, </div><div> </div><div>On July 22, <b>Rational Application Developer released Version</b> <b>8.0.3 Interim Fix 1</b>. Source tools for mobile web application development and the Mobile Browser Simulator, formerly available as beta-level functionality, are updated and supported. Support for Dojo 1.3.2, 1.4.1, 1.5, and 1.7 Beta1 is provided. Come over to the <a href="http://www.ibm.com/developerworks/wikis/display/rad/Home">Rational Application Developer developerWorks wiki </a>to check it out as well as our latest demos.<br /></div><div> </div><div>Cheers<br />Billy<br /> </div>Hi all, On July 22, Rational Application Developer released Version 8.0.3 Interim Fix 1 . Source tools for mobile web application development and the Mobile Browser Simulator, formerly available as beta-level functionality, are updated and supported....002035urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-1e190db9-1c2b-4a19-8fb8-df9552a3103cDojo Mobile 1.7 New Widgetsonoat120000EBPFactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-08-02T11:55:59-04:002011-10-27T23:56:50-04:00<div>Dojo Mobile takes an important role in <a href="http://ibm.co/Web20Mobile">Web 2.0 and Mobile Feature Pack</a> to provide a powerful mobile application development environment. It has a lot of lightweight UI widgets, which smooth over browser differences not only for mobile devices such as iPhone, Android and BlackBerry but also for desktop browsers through the compatibility module. The existing widgets in Dojo Mobile 1.6 are already introduced in various ways, for example, in a good developerWorks article &quot;<a href="http://www.ibm.com/developerworks/library/wa-dojomobile/">Get started with Dojo Mobile 1.6</a>&quot; referred to in <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/entry/get_started_with_dojo_mobile_1_61?lang=en">the previous post</a>. In this post, I'd like to further expand on the new widgets that are part the <a href="http://ibm.co/Web20Mobile">Web 2.0 and Mobile Feature Pack</a> and which will also be part of the Dojo Mobile 1.7 upcoming release.</div><div> </div><div> </div><div style="font-weight: bold;">SwapView (formerly FlippableView) </div><div>SwapView widget, formerly FlippableView widget but has been renamed, provides the capability to switch pages by swiping a view left and right. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_iPhone-SwapView.html">SwapView Sample here </a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SwapView1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SwapView1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SwapView2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SwapView2.jpg" style="width: 250px;" /></a></div><div> </div><div> </div><div style="font-weight: bold;">PageIndicator </div><div>PageIndicator widget can be used with SwapView widgets to indicate which page you are on now. </div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_PageIndicator1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_PageIndicator1.jpg" style="width: 250px;" /></a></div><div> </div><div>You can create, for example, the following sample slideshow application by using SwapView and PageIndicator widgets. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_iPhone-SwapView-slideshow.html">SwapView + PageIndicator Sample here</a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_PageIndicator2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_PageIndicator2.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_PageIndicator3.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_PageIndicator3.jpg" style="width: 250px;" /></a> </div><div> </div><div> </div><div><span style="font-weight: bold;">Carousel </span></div><div>Carousel widget displays images in a carousel style based on SwapView and PageIndicator widgets. It can use <a href="http://docs.dojocampus.org/dojo/data/api">Dojo Data API</a> to load image list data. The following sample has two Carousel widgets, and when you select an image in the upper carousel, the lower one loads image list depending on the selection and displays them dynamically through <a href="http://docs.dojocampus.org/dojo/data/api">Dojo Data API</a>. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_Carousel.html">Carousel Sample here </a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Carousel1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Carousel1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Carousel2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Carousel2.jpg" style="width: 250px;" /></a> </div><div> </div><div> </div><div><span style="font-weight: bold;">EdgeToEdgeDataList/RoundRectDataList</span></div><div>EdgeToEdgeDataList and RoundRectDataList widgets are subclasses of simple EdgeToEdgeList and RoundRectangleList widgets respectively., They have the ability to use <a href="http://docs.dojocampus.org/dojo/data/api">Dojo Data API</a> to populate its data. You can easily switch data store and add/remove items as the following sample shows. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_EdgeToEdgeDataList.html">EdgeToEdgeDataList Sample</a> </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_RoundRectDataList.html">RoundRectDataList Sample</a> </div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_RoundRectDataList1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_RoundRectDataList1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_RoundRectDataList2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_RoundRectDataList2.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_RoundRectDataList3.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_RoundRectDataList3.jpg" style="width: 250px;" /></a> </div><div> </div><div> </div><div><span style="font-weight: bold;">Form Controls </span></div><div>Dojo Mobile 1.7 has a lot of HTML form control widgets such as Button, CheckBox, ToggleButton, RationButton, Slider, ComboBox, TextArea and ExpandingTextArea widgets. All widgets support iPhone, Android and BlackBerry theme, and provide helpful functions such as ExpandingTextArea's auto height sizing, TextBox's text length validation, etc. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_FormControls.html?theme=iPhone">iPhone Theme Sample</a> </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_FormControls.html?theme=Android">Android Theme Sample</a> </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_FormControls.html?theme=BlackBerry">BlackBerry Theme Sample</a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_FormControls1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_FormControls1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_FormControls2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_FormControls2.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_FormControls3.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_FormControls3.jpg" style="width: 250px;" /></a> </div><div> </div><div> </div><div><span style="font-weight: bold;">SpinWheel (SpinWheelDatePicker/SpinWheelTimePicker) </span></div><div>SpinWheel widget shows spinning wheels for users to select data. For the most popular usage, there are two sub widgets: one is SpinWheelDataPicker widget to show the date picker and the other is SpinWheeTimePicker widget to show the time picker in a spinning wheel style. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_SpinWheelDatePicker.html">SpinWheelDatePicker Sample </a></div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_SpinWheelTimePicker.html">SpinWheelTimePicker Sample </a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SpilWheelDatePicker1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SpilWheelDatePicker1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SpinWheelTimePicker1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SpinWheelTimePicker1.jpg" style="width: 175px;" /></a></div><div> </div><div>You can also customize each wheel in SpinWheel by leveraging SpinWheelSlot widget. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_SpinWheel-custom.html">Custom SpinWheel Sample </a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SpinWheelCustom1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_SpinWheelCustom1.jpg" style="width: 250px;" /></a> </div><div> </div><div> </div><div style="font-weight: bold;">Tooltip </div><div>You can show tooltip on any controls with this Tooltip widget. It can display not only simple text but also richer content such as a SpinWheel widget as shown below. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_Tooltip.html">Tooltip Sample here </a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Tooltip1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Tooltip1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Tooltip2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Tooltip2.jpg" style="width: 250px;" /></a> </div><div> </div><div> </div><div><span style="font-weight: bold;">Overlay</span></div><div>Overlay widget shows/hides a dialog at the bottom of screen with slide-in/out animation. It can contains any controls such as a calendar, SpinWheel widget, etc. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_Overlay.html">Overlay Sample here </a></div><div> </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Overlay1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Overlay1.jpg" style="width: 250px;" /></a> <a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Overlay2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Overlay2.jpg" style="width: 250px;" /></a></div><div> </div><div> </div><div><span style="font-weight: bold;">Opener</span></div><div>Opener widget shows a dialog in an appropriate way depending on the screen size. </div><div><a href="http://download.dojotoolkit.org/release-1.7.0b2/dojo-release-1.7.0b2/dojox/mobile/tests/test_Opener-Calendar-async.html">Opener Sample here </a></div><div> </div><div>In phone size, it shows a dialog with Overlay widget as shown below. </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Opener1.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Opener1.jpg" style="width: 250px;" /></a> </div><div> </div><div>And in tablet size or desktop screen size, it shows a dialog with Tooltip widget as shown below. </div><div><a href="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Opener2.jpg" target="_blank"><img alt="image" src="https://www.ibm.com/developerworks/mydeveloperworks/blogs/94e7fded-7162-445e-8ceb-97a2140866a9/resource/BLOGS_UPLOADED_IMAGES/DM17_Opener2.jpg" style="width: 330px;" /></a> </div><div> </div><div> </div><div>I have just discussed new widgets, but there are a lot of other new features in <a href="http://ibm.co/Web20Mobile">Web 2.0 and Mobile Feature Pack</a> and Dojo Mobile 1.7 such as new BlackBerry theme, new transition effects (cover, reveal, zoom in/out, swirl, etc), many widget enhancements, and so on. All these new widgets and other new features are available on Web 2.0 and Mobile Feature Pack as well as Dojo Mobile 1.7 which will be released soon, so please give it a try and enjoy it! </div><div> </div><div> </div>Dojo Mobile takes an important role in Web 2.0 and Mobile Feature Pack to provide a powerful mobile application development environment. It has a lot of lightweight UI widgets, which smooth over browser differences not only for mobile devices such as iPhone,...0015476urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00urn:lsid:ibm.com:blogs:entry-c32d932a-822e-4f58-8e9e-21139c4801b9Overview of the WebSphere Application Server Web 2.0 & Mobile Feature Pack: Webminar Replay Now AvailableLizetErnand060001J3SFactivefalse94e7fded-7162-445e-8ceb-97a2140866a9Comment Entriesapplication/atom+xml;type=entryLikestrue2011-08-01T14:51:25-04:002011-10-27T23:56:39-04:00Thank you to all who attended thewebminar last week titled “Overview of the WebSphere Application Server Web 2.0 and Mobile Feature Pack“ , hosted by the Global WebSphere Community (GWC) . If you submitted aquestion during the webcast, please check the GWC's <a href="http://bit.ly/pWgbvM">WebSphereand Mobile forum</a> over the following weeks for the answers to yourquestion as well as new conversations.<p style="margin-bottom: 0in"> </p><p style="margin-bottom: 0in">For those that didn't get a chance toattend, but would like to <a href="http://bit.ly/pDGWeJ">view thereplay</a>, bookmark the webcast page so you can view the replay and<a href="http://bit.ly/p6cqSC">download the presentation</a> at your convenience.</p><div> </div><div><object width="480" height="445"><param name="movie" value="http://www.onesite.com/resources/flash/tofPlayer.swf"><param name="allowScriptAccess" value="always"><param name="allowNetworking" value="all"><param name="allowFullScreen" value="true"><param name="flashvars" value="autoPlay=0&useRating=0&paddedWidth=480&paddedHeight=445&videoWidth=480&videoHeight=360&videoID=1446319&loginURL=http://websphereusergroup.org&signupURL=http://websphereusergroup.org/signup&content_id=1368555969"><embed src="http://www.onesite.com/resources/flash/tofPlayer.swf" width="480" height="445" flashvars="autoPlay=0&useRating=0&paddedWidth=480&paddedHeight=445&videoWidth=480&videoHeight=360&videoID=1446319&loginURL=http://websphereusergroup.org&signupURL=http://websphereusergroup.org/signup&content_id=1368555969" type="application/x-shockwave-flash" allowScriptAccess="always" allowNetworking="all" allowFullScreen="true" /></object> </div>Thank you to all who attended thewebminar last week titled “Overview of the WebSphere Application Server Web 2.0 and Mobile Feature Pack“ , hosted by the Global WebSphere Community (GWC) . If you submitted aquestion during the webcast, please check the GWC's...001996urn:lsid:ibm.com:blogs:entries-44651415-c964-480a-ab4c-c81690a1f28bWeb 2.0 and Mobile Development Community2014-12-11T00:08:06-05:00