SlideShare a Scribd company logo
Dojo and Adobe AIR Nikolai Onken, CEO uxebu Ltd. nikolai@uxebu.com - http://twitter.com/nonken
Me Nikolai Onken uxebu Ltd. Dojo community evangelist, committer DojoCampus.org Do you know this? Browser fights Sleeples nights Endless cursing about IE6 Wrestling with the floats Welcome
Adobe AIR Introduction Benefits Dojo Toolkit Introduction Benefits Hands on xRay, from web to AIR What are we going to do today?
The  ideal  world Existing Application Define functionality for AIR Deliver
The  real  world Existing Application Rewrite, disassemble, cleanup Test, bugfix Deliver? Define functionality for AIR
Is there a solution? Dojo Toolkit Module based Ready for AIR Feature rich Deliver Fast Adobe AIR
Features Webkit Offline apps SQL Lite Flash, e.g. sockets, etc. DND Access to filesystem Adobe AIR AIR Runtime
The core Client-side developers toolbox Helper functions OO The UI library Layout Forms Widgets Dojos Las Vegas  (c) Tom Trenka Charting Grid Cometd The Dojo Toolkit Dojo Dijit Dojox
What do we have? Performance reports online Data API Charting API What do we want? Performance reports live and offline Interactivity Saveable sessions Screenshots, multimedia features Hands on - the analysis
Adobe AIR Sockets File storage SQL Lite DND Autoupdate Screenshots Dojo dojo.declare dojo.publish/subscribe dojox.grid dojox.charting dijit.layout i18n themeing Hands on - ingredients
Adobe AIR introduction
Connecting to sockets this.socket = new air.Socket("localhost", 56789); // add listeners for success and failurethis.eventConnect = this.socket.addEventListener(air.Event.CONNECT,  dojo.hitch(this, "connected"));this.eventIO = this.socket.addEventListener(air.IOErrorEvent.IO_ERROR,  dojo.hitch(this, "disconnect")); connected: function(){ // on socket connection, connect to data event and handle data this.eventData = this.socket.addEventListener(air.ProgressEvent.SOCKET_DATA,  dojo.hitch(this, "handleData")); } } Sockets (Socket.js)
Accessing local files this.docsDir = air.File.desktopDirectory.resolvePath("xRay session.xry");this.docsDir.browseForSave(this.messages.saveAs); this.docsDir.addEventListener(air.Event.SELECT, dojo.hitch(this, "_writeData", formData)); writeData: function(formData, e){ // on select we write data this.docsDir.removeEventListener(air.Event.SELECT, dojo.hitch(this, "_writeData", formData));// create new file objectvar file = air.File(e.target); // save to streamvar stream = new air.FileStream();stream.open( file, air.FileMode.WRITE );stream.writeMultiByte( dojo.toJson(this._createData(formData)), air.File.systemCharset );stream.close(); } File storage (DataStore.js - 165)
Working with SQLLite // connect to SQLLitethis.conn = new air.SQLConnection();  // event handlingthis.conn.addEventListener(air.SQLEvent.OPEN, function(e){d.callback(e);});  this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);});  // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile);  this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);});  // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile);  this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);});  // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile);  this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);});  // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile);  this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);});  // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile);  SQL Lite (DataStore.js - 97)
AIR and DND dojo.connect(this.dropContainer, "dragenter", this, function(e){e.dataTransfer.effectAllowed = "copy";e.preventDefault(); }); dojo.connect(this.dropContainer, "dragover", this, function(e){e.preventDefault();}); dojo.connect(this.dropContainer, "dragleave", this, function(e){e.preventDefault();}); dojo.connect(this.dropContainer, "drop", this, function(e){ e.preventDefault(); }); }); }); }); }); }); }); }); DND (DropTarget.js)
Keeping your application up to date this.updReq = new air.URLRequest(this.updateUrl);this.updStream = new air.URLStream();this.updData = new air.ByteArray(); this.updStream.addEventListener(air.Event.COMPLETE, dojo.hitch(this, function(){this.updStream.readBytes(this.updData, 0, this.updStream.bytesAvailable); var data = dojo.fromJson(this.updData);})); this.updStream.load(this.updReq); // run the update var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); Autoupdate
Taking pictures // write image datavar rect = dojo.coords(dojo.byId("mainGraphContent")); var bmp = new air.BitmapData( rect.w - 2, rect.h - 2 );var matrix = new air.Matrix();matrix.translate( 0 - ( rect.x + 1 ), 0 - ( rect.y + 1 ) ); bmp.draw( window.htmlLoader, matrix ); this.png = runtime.com.adobe.images.PNGEncoder.encode( bmp ); Screenshots
The Dojo Toolkit introduction
Creating classes dojo.declare(“uxebu.chart”,  null,  { constructor: function(){ this.size = {h: 200, w: 500}; }, init: function(){ this.render(); } render: function(size){ console.log(size); } }); >>> var chart = new uxebu.chart(); >>> chart.init(); [Object] dojo.declare
Event handling dojo.connect(node, “onclick”, function(e){ console.log(e.target); }) dojo.connect(obj, “method”, function(e){ console.log(e); }); Decoupling modules function loadChart(time){ dojo.publish(“uxebu/load”, [time]); } dojo.subscribe(“uxebu/load”, function(time){ console.dir(time); }); publish/subscribe, connect/disconnect
JavaScript // create grid to display datathis.sessionStore = new dojo.data.ItemFileWriteStore({id: "sessionStore", data: {identifier: 'id', items: []}});var layout = [[{name: this.messages.gridId, field: 'id', width: "5%"},{name: this.messages.gridAuthor, field: 'author', width: "25%"},{name: this.messages.gridComment, field: 'comment', width: "auto"},{name: this.messages.gridCreated, field: 'created', width: "25%"},{name: this.messages.gridEdit, field: 'edit', width: "110px"}]]; this.grid = new xray.Grid({title: "History", store: this.sessionStore, structure: layout, rowsPerPage: 10, id: "sessionGrid"}, dojo.doc.createElement("div")); dijit.byId("mainTabs").addChild(this.grid);this.grid.startup(); HTML function loadChart(time){ dojo.publish(“uxebu/load”, [time]); } dojo.subscribe(“uxebu/load”, function(time){ console.dir(time); }); }); }); dojox.grid
JavaScript this.chart = new dojox.charting.Chart2D("mainGraphContent"); this.chart.setTheme(dojox.charting.themes.ET.greys); this.chart.addAxis("x", { fixLower:"minor", natural:true, min:1,  max:this.CHART_WIDTH, majorTickStep: 10, minorTickStep: 1 }); this.chart.addAxis("y", {vertical: true, min: 0, max: 500, majorTickStep: 50, minorTickStep: 10}); this.chart.addPlot("default", {type: "Lines"}); this.chart.addSeries("memory", s[this.MEM_SERIES], {stroke:{width:2, color:"#73d9ef"}}); this.chart.render(); dojox.charting
HTML <div dojoType=”dijit.layout.BorderContainer” style=”width: 100%; height: 100%”> <div dojoType=”dijit.layout.TabContainer” region=”center” style=”width: 75%”> <div dojoType=”dijit.layout.ContentPane” title=”Main”> Content </div> </div> <div dojoType=”dijit.layout.TabContainer” region=”right”> <div dojoType=”dijit.layout.ContentPane” title=”Stats”> Content </div> </div> </div> dijit.layout
Dojo Themes Tundra, Soria, Nihilo a11y!!! Customisation is simple! .dijitBorderContainer { background: #333; } .dijitTabContainer { border: none; } Themeing
Internationalisation Build in Fallback mechanism i18n
See xRay in action
Thank you! http://www.uxebu.com nikolai@uxebu.com - http://twitter.com/nonken

More Related Content

Dojo and Adobe AIR

  • 1. Dojo and Adobe AIR Nikolai Onken, CEO uxebu Ltd. nikolai@uxebu.com - http://twitter.com/nonken
  • 2. Me Nikolai Onken uxebu Ltd. Dojo community evangelist, committer DojoCampus.org Do you know this? Browser fights Sleeples nights Endless cursing about IE6 Wrestling with the floats Welcome
  • 3. Adobe AIR Introduction Benefits Dojo Toolkit Introduction Benefits Hands on xRay, from web to AIR What are we going to do today?
  • 4. The ideal world Existing Application Define functionality for AIR Deliver
  • 5. The real world Existing Application Rewrite, disassemble, cleanup Test, bugfix Deliver? Define functionality for AIR
  • 6. Is there a solution? Dojo Toolkit Module based Ready for AIR Feature rich Deliver Fast Adobe AIR
  • 7. Features Webkit Offline apps SQL Lite Flash, e.g. sockets, etc. DND Access to filesystem Adobe AIR AIR Runtime
  • 8. The core Client-side developers toolbox Helper functions OO The UI library Layout Forms Widgets Dojos Las Vegas (c) Tom Trenka Charting Grid Cometd The Dojo Toolkit Dojo Dijit Dojox
  • 9. What do we have? Performance reports online Data API Charting API What do we want? Performance reports live and offline Interactivity Saveable sessions Screenshots, multimedia features Hands on - the analysis
  • 10. Adobe AIR Sockets File storage SQL Lite DND Autoupdate Screenshots Dojo dojo.declare dojo.publish/subscribe dojox.grid dojox.charting dijit.layout i18n themeing Hands on - ingredients
  • 12. Connecting to sockets this.socket = new air.Socket(&quot;localhost&quot;, 56789); // add listeners for success and failurethis.eventConnect = this.socket.addEventListener(air.Event.CONNECT, dojo.hitch(this, &quot;connected&quot;));this.eventIO = this.socket.addEventListener(air.IOErrorEvent.IO_ERROR, dojo.hitch(this, &quot;disconnect&quot;)); connected: function(){ // on socket connection, connect to data event and handle data this.eventData = this.socket.addEventListener(air.ProgressEvent.SOCKET_DATA, dojo.hitch(this, &quot;handleData&quot;)); } } Sockets (Socket.js)
  • 13. Accessing local files this.docsDir = air.File.desktopDirectory.resolvePath(&quot;xRay session.xry&quot;);this.docsDir.browseForSave(this.messages.saveAs); this.docsDir.addEventListener(air.Event.SELECT, dojo.hitch(this, &quot;_writeData&quot;, formData)); writeData: function(formData, e){ // on select we write data this.docsDir.removeEventListener(air.Event.SELECT, dojo.hitch(this, &quot;_writeData&quot;, formData));// create new file objectvar file = air.File(e.target); // save to streamvar stream = new air.FileStream();stream.open( file, air.FileMode.WRITE );stream.writeMultiByte( dojo.toJson(this._createData(formData)), air.File.systemCharset );stream.close(); } File storage (DataStore.js - 165)
  • 14. Working with SQLLite // connect to SQLLitethis.conn = new air.SQLConnection(); // event handlingthis.conn.addEventListener(air.SQLEvent.OPEN, function(e){d.callback(e);}); this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);}); // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile); this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);}); // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile); this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);}); // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile); this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);}); // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile); this.conn.addEventListener(air.SQLErrorEvent.ERROR, function(e){d.errback(e);}); // reference to db file, if not available it will be created automaticallyvar dbFile = air.File.applicationStorageDirectory.resolvePath(this.sqlLiteDb); this.conn.openAsync(dbFile); SQL Lite (DataStore.js - 97)
  • 15. AIR and DND dojo.connect(this.dropContainer, &quot;dragenter&quot;, this, function(e){e.dataTransfer.effectAllowed = &quot;copy&quot;;e.preventDefault(); }); dojo.connect(this.dropContainer, &quot;dragover&quot;, this, function(e){e.preventDefault();}); dojo.connect(this.dropContainer, &quot;dragleave&quot;, this, function(e){e.preventDefault();}); dojo.connect(this.dropContainer, &quot;drop&quot;, this, function(e){ e.preventDefault(); }); }); }); }); }); }); }); }); DND (DropTarget.js)
  • 16. Keeping your application up to date this.updReq = new air.URLRequest(this.updateUrl);this.updStream = new air.URLStream();this.updData = new air.ByteArray(); this.updStream.addEventListener(air.Event.COMPLETE, dojo.hitch(this, function(){this.updStream.readBytes(this.updData, 0, this.updStream.bytesAvailable); var data = dojo.fromJson(this.updData);})); this.updStream.load(this.updReq); // run the update var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); var updater = new air.Updater();var version = this.updateVersion;updater.update(this.file, this.updateVersion); Autoupdate
  • 17. Taking pictures // write image datavar rect = dojo.coords(dojo.byId(&quot;mainGraphContent&quot;)); var bmp = new air.BitmapData( rect.w - 2, rect.h - 2 );var matrix = new air.Matrix();matrix.translate( 0 - ( rect.x + 1 ), 0 - ( rect.y + 1 ) ); bmp.draw( window.htmlLoader, matrix ); this.png = runtime.com.adobe.images.PNGEncoder.encode( bmp ); Screenshots
  • 18. The Dojo Toolkit introduction
  • 19. Creating classes dojo.declare(“uxebu.chart”, null, { constructor: function(){ this.size = {h: 200, w: 500}; }, init: function(){ this.render(); } render: function(size){ console.log(size); } }); >>> var chart = new uxebu.chart(); >>> chart.init(); [Object] dojo.declare
  • 20. Event handling dojo.connect(node, “onclick”, function(e){ console.log(e.target); }) dojo.connect(obj, “method”, function(e){ console.log(e); }); Decoupling modules function loadChart(time){ dojo.publish(“uxebu/load”, [time]); } dojo.subscribe(“uxebu/load”, function(time){ console.dir(time); }); publish/subscribe, connect/disconnect
  • 21. JavaScript // create grid to display datathis.sessionStore = new dojo.data.ItemFileWriteStore({id: &quot;sessionStore&quot;, data: {identifier: 'id', items: []}});var layout = [[{name: this.messages.gridId, field: 'id', width: &quot;5%&quot;},{name: this.messages.gridAuthor, field: 'author', width: &quot;25%&quot;},{name: this.messages.gridComment, field: 'comment', width: &quot;auto&quot;},{name: this.messages.gridCreated, field: 'created', width: &quot;25%&quot;},{name: this.messages.gridEdit, field: 'edit', width: &quot;110px&quot;}]]; this.grid = new xray.Grid({title: &quot;History&quot;, store: this.sessionStore, structure: layout, rowsPerPage: 10, id: &quot;sessionGrid&quot;}, dojo.doc.createElement(&quot;div&quot;)); dijit.byId(&quot;mainTabs&quot;).addChild(this.grid);this.grid.startup(); HTML function loadChart(time){ dojo.publish(“uxebu/load”, [time]); } dojo.subscribe(“uxebu/load”, function(time){ console.dir(time); }); }); }); dojox.grid
  • 22. JavaScript this.chart = new dojox.charting.Chart2D(&quot;mainGraphContent&quot;); this.chart.setTheme(dojox.charting.themes.ET.greys); this.chart.addAxis(&quot;x&quot;, { fixLower:&quot;minor&quot;, natural:true, min:1, max:this.CHART_WIDTH, majorTickStep: 10, minorTickStep: 1 }); this.chart.addAxis(&quot;y&quot;, {vertical: true, min: 0, max: 500, majorTickStep: 50, minorTickStep: 10}); this.chart.addPlot(&quot;default&quot;, {type: &quot;Lines&quot;}); this.chart.addSeries(&quot;memory&quot;, s[this.MEM_SERIES], {stroke:{width:2, color:&quot;#73d9ef&quot;}}); this.chart.render(); dojox.charting
  • 23. HTML <div dojoType=”dijit.layout.BorderContainer” style=”width: 100%; height: 100%”> <div dojoType=”dijit.layout.TabContainer” region=”center” style=”width: 75%”> <div dojoType=”dijit.layout.ContentPane” title=”Main”> Content </div> </div> <div dojoType=”dijit.layout.TabContainer” region=”right”> <div dojoType=”dijit.layout.ContentPane” title=”Stats”> Content </div> </div> </div> dijit.layout
  • 24. Dojo Themes Tundra, Soria, Nihilo a11y!!! Customisation is simple! .dijitBorderContainer { background: #333; } .dijitTabContainer { border: none; } Themeing
  • 25. Internationalisation Build in Fallback mechanism i18n
  • 26. See xRay in action
  • 27. Thank you! http://www.uxebu.com nikolai@uxebu.com - http://twitter.com/nonken