Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Debugging Dojo Applications (2/10/2010)

11,376 views

Published on

This was the presentation I gave at dojo.connect on February 10, 2010.

  • Be the first to comment

Debugging Dojo Applications (2/10/2010)

  1. 1. Debugging Dojo Applications dojo.connect February 10, 2010 Chris Barber CB1, INC. http://www.cb1inc.com/
  2. 2. About Me ● Chris Barber ● Open source hacker ● Software consultant ● JavaScript, C++, PHP ● Dojo committer ● http://www.cb1inc.com/ ● http://twitter.com/cb1kenobi ● http://slideshare.net/cb1kenobi dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  3. 3. All Software Has Bugs dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  4. 4. Fixing Bugs Sucks dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  5. 5. Things You Could Be Doing Instead of Fixing Bugs ● Sleeping ● Playing Bioshock 2 ● Reading a book ● Shopping for new shoes ● Working out ● Getting your picture ● Enjoying a frosty brew taken at those little booths at the mall ● Mowing the lawn ● Shoveling snow ● Watching a movie ● Actually, this is worse ● Bowling that fixing bugs ● Doing laundry ● Selling junk on eBay ● Attending this ● Buying junk on eBay presentation! dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  6. 6. Debugging Dojo & JavaScript dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  7. 7. Types of Bugs ● Syntax errors ● Runtime errors ● Logic errors ● Performance issues ● Bugs that other people committed into svn ● Because our code is flawless dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  8. 8. Old School Debugging dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  9. 9. Old School Debugging <form> <textarea name="output"></textarea> </form> <script type="text/javascript"> alert("If you're reading this, it doesn't work."); document.write("x should be 10, but for some reason it's " + x); document.forms[0].output.value += "Loading external resource...n"; document.forms[0].output.value += "Still loading...n"; document.forms[0].output.value += "Hello?n"; </script> dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  10. 10. Old School Debugging ● Write some code, reload, test, write some code, reload test, write some code, reload, test ● But if you write too much code ● Comment out / remove huge chunks of code until things work again ● View source ● What exactly did the server send? var quote_of_the_day = "Who said "debugging" ain't easy?"; dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  11. 11. Old School Debugging ● Custom error handling awesomeness window.onerror = function(msg, url, lineno){ alert("Thou art detects an error:n" + "Message: " + msg + "n" + "URL: " + url + "n" + "Line #: " + lineno); }; function foo(){ var bar = "fail"; dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  12. 12. Old School Debugging ● JavaScript console ● Netscape 4, Mozilla, Firefox ● Internet Explorer ● Generally sucked ● Line # rarely correct ● Crappy error messages – "Object required" dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  13. 13. Old School Debugging ● Microsoft Script ● Microsoft Script Editor Debugger ● Uses Visual Studio ● Steaming pile ● It's on your Office 2003 CDs dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  14. 14. Old School Debugging ● Venkman ● Component of the Mozilla Suite ● Also a Firefox extension ● Sloooooooooooow – Provided it didn't crash dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  15. 15. Not So Old School ● try/catch/throw ● New in ECMAScript 3! – Published Dec 1999 ● Implemented by IE6/NS6 try { if (something) { throw new Error("oh snap!"); } } catch (e) { alert(e); } ● Catches that suppressed errors ● All over Dojo code base ● If something is not working, check if it's being silently being caught dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  16. 16. Advanced Tactics ● Proxy server ● Packet sniffing dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  17. 17. JavaScript Bugs dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  18. 18. JavaScript Bugs ● Syntax errors ● Things that the cause the parser to hate life function foo() { foo(1 2); dojo..isFunction(foo); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  19. 19. JavaScript Bugs ● Runtime Bugs ● Undefined variables & methods ● Unexpected results foo(); // foo() undefined var i = 10 + x; // x undefined function bar() { return arguments.join(','); } function baz(y) { return y * y; } alert(baz(2)); // undefined! dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  20. 20. JavaScript Bugs ● Variable scopes ● Watch out for reserved ● Local vs global words! ● Naming ● Rhino (used by Dojo's build tool) is very picky ● Case sentivity break, case, catch, continue, default, delete, do, else, finally, for, ● Try to use unique function, if, in, instanceof, new, names to avoid return, switch, this, throw, try, accidentally overwriting typeof, var, void, while, with abstract, boolean, byte, char, class, ● Use meaningful names const, debugger, double, enum, export, – Unless you need a little extends, final, float, goto, implements, import, int, interface, more job security long, native, package, private, protected, public, short, static, super, synchronized, throws, transient, volatile dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  21. 21. JavaScript Bugs ● Functions ● Forgetting commas between arguments ● Forgetting braces ● Misspelling funtcion ● Avoid resuing function names function foo(){ alert("hello"); } foo(); // alerts "world" function foo(){ alert("world"); } foo(); // alerts "world" dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  22. 22. JSON Bugs ● Missing commas ● Hanging commas ● Confusing } and ] ● Not quoting names that contain special chars var i = { var k = [ firstName: "Chris" { lastName: "Barber" "class": "JavaScript", }; "course #": 101 }, var j = { { firstName: "Chris", "class": "C++", lastName: "Barber", "course #": 102 }; }; dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  23. 23. JavaScript Engine Limits ● Max string length ● Implementation dependent ● Max integer value ● Really, really huge ● Max z-index ● 2,147,483,647 ● ... dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  24. 24. dojo dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  25. 25. djConfig.isDebug ● Only useful in browsers that don't have a console ● IE6/7 ● Firefox without Firebug ● Old versions of Opera/Safari dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  26. 26. djConfig.isDebug ● Automatically loads dojo._firebug.firebug ● Limited functionality ● ReCSS, console, DOM viewer, Object viewer dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  27. 27. dojo.declare ● Used to define objects ● Careful what you initialize ● Numbers, booleans, & strings are OK ● Arrays & objects are a bad idea – Use constructor() or postCreate() (if a dijit) dojo.declare("MyObj", null, { foo: [ "Hello" ], bar: null, constructor: function() { this.bar = ["baz"]; } }); var obj1 = new MyObj; var obj2 = new MyObj; console.debug(obj1.foo); // outputs ["Hello"] console.debug(obj2.foo); // outputs ["Hello"] obj1.foo.push("world!"); console.debug(obj1.foo); // outputs ["Hello","world!"] console.debug(obj2.foo); // outputs ["Hello","world!"] obj1.bar.push("zap"); console.debug(obj1.bar); // outputs ["baz","zap"] console.debug(obj2.bar); // outputs ["baz"] dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  28. 28. dojo.declare ● Extending objects ● Invoking an inherited object's method dojo.declare("MyObj", nullj, { foo: function(){ alert("Hi from MyObj!"); } }); dojo.declare("MyNewObj", MyObj, { foo: function(){ alert("Hi from MyNewObj"); this.inherited(arguments); } }); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  29. 29. dojo.data ● fetchItemByIdentity() ● Make sure IDs are unique! dojo.require("dojo.data.ItemFileReadStore"); var s = new dojo.data.ItemFileReadStore({ data: { identifier: "id", items: [ { id: "/foo", name: "foo", children: [ { id: "/foo/bar", name: "bar" } ] } ] } }); s.fetchItemByIdentity({ identity: "/foo", onItem: function(item){ console.info(item); }, onError: function(item){ console.error(item); } }); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  30. 30. Manipulating the DOM ● Wait until onload ● dojo.addOnLoad() or dojo.ready() (new in 1.4) ● If you can't wait, use document.write() dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  31. 31. dojo.parser ● djConfig.parseOnLoad does not autoload dojo.parser ● Must dojo.require("dojo.parser") ● Can fire the parser manually ● dojo.parser.parse() dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  32. 32. dojo.require() ● Build system is a little too smart ● Uses regex to find requires if(condition){ dojo.require("my.module"); } if(condition){ dojo["require"]("my.module"); } dojo.requireIf("my.module", condition); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  33. 33. Closures & dojo.hitch() // assume we're in an object's function... dojo.forEach([1, 2, 3], this.foo); // Error! var _t = this; dojo.forEach([1, 2, 3], function(n){ _t.foo(n) }); dojo.forEach([1, 2, 3], dojo.hitch(this, "foo")); // does the same thing internally as dojo.hitch() dojo.forEach([1, 2, 3], "foo", this); // dojo.connect does the same thing dojo.connect(myButton, "onclick", this, "foo"); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  34. 34. file:/// issues ● Firefox 3 security causes issues ● about:config ● security.fileuri.strict_origin_policy = false – http://kb.mozillazine.org/Security.fileuri.strict_origin_policy dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  35. 35. dojo.deferred ● Use errbacks ● dojo.xhr() returns a deferred function getData(i, n){ return dojo.xhrPost({ url: "/fetchData.php", postData: { id: i, name: n }, handleAs: "json" }); } var deferred = getData(123, "foo"); deferred.addCallback(function(data){ console.log("Whoo!"); }); deferred.addErrback(function(error){ console.error("Oh noes!"); console.error(error); }); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  36. 36. dijit dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  37. 37. Widget Params ● Param must be defined before mixin <div dojoType="MyObj" param1="abc" param2="def" param3="ghi"></div> <script type="text/javascript"> dojo.require("dijit._Widget"); dojo.declare("MyObj", dijit._Widget, { param1: "", constructor: function(){ this.param2 = ""; }, postCreate: function(){ console.debug(this.param1); // abc console.debug(this.param2); // def console.debug(this.param3); // undefined } }); </script> dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  38. 38. Template Errors ● 404 File not found ● No root node ● Invalid variables ${foo} ● Variables must be defined before buildRendering() – member variable – initialize in constructor() – initialize in postMixInProperties() dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  39. 39. Template Errors ● Say you want to speed up development ● Do a Dojo build of dojo, dijit, code that is good to go ● Your namespace is "foo" and you have a custom dialog that extends dijit.Dialog ● If using Dojo 1.3.2 or earlier, you MUST define templateString:null before your templatePath ● Otherwise the dijit.Dialog's templateString overrides your templatePath! dojo.declare("foo.MyDialog", dijit.Dialog, { templateString: null, templatePath: dojo.moduleUrl("foo", "MyDialog.html") }); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  40. 40. Template Errors ● Not an issue with 1.4 and newer ● Just use templateString ● Use dojo.cache! ● You can still use dojo.moduleUrl() dojo.declare("foo.MyDialog", dijit.Dialog, { templateString: dojo.cache("foo", "MyDialog.html") }); // After build, template is inlined! Awesome dojo.declare("foo.MyDialog", dijit.Dialog, { templateString: dojo.cache("foo", "MyDialog.html", "<div>Hi from my dialog!</div>") }); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  41. 41. Template Errors ● Build system uses regex to inline templates ● Don't get cute (function($){ $.declare("foo.MyDialog", dijit.Dialog, { templateString: $.cache("foo", "MyDialog.html") // NO! }); })(dojo); (function($){ $.declare("foo.MyDialog", dijit.Dialog, { templateString: dojo.cache("foo", "MyDialog.html") // OK! }); })(dojo); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  42. 42. Templates Caching Issues ● When not using a build, templates are loaded via XHR ● Templates may be cached by the browser ● Definitely in Firefox ● Disable cache or empty cache after template changes dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  43. 43. postCreate() vs startup() ● postCreate() ● Template created ● Can create new DOM nodes ● Not every widget's postCreate() has been called yet! ● startup() ● Called after a widget and its children have been created and added to the page ● All parent/child widgets created, postCreate() has been called ● Now you can start talking to other widgets :) dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  44. 44. Dijit Themes ● Live coding mistake #253 ● Forgetting to include the dijit theme's css file <link rel="stylesheet" href="/path/to/dijit/themes/tundra/tundra.css" type="text/css"/> dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  45. 45. Dijit Themes ● Live coding mistake #254 ● Forgetting to add the theme name to the body class <body class="tundra"> ... </body> dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  46. 46. Dijit Themes ● Only use what you need! ● Example: you only need a dijit.Calendar // mytundra.css @import url("/path/to/dijit/themes/dijit.css"); @import url("/path/to/dijit/themes/tundra/Common.css"); @import url("/path/to/dijit/themes/tundra/form/Common.css"); @import url("/path/to/dijit/themes/tundra/Calendar.css"); ● Build system inlines only those files! <link rel="stylesheet" href="/path/to/mybuild/mytundra.css" type="text/css"/> dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  47. 47. The Build System dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  48. 48. Build Profiles ● Define the prefixes you need ● Build system copies each namespace ● Don't need to specify "dojo" prefix dependencies = { stripConsole: "normal", version: "1.4.1", cssOptimize: "comments", copyTests: false, optimize: "shrinksafe.keepLines", layerOptimize: "shrinksafe.keepLines", layers: [ { name: "dojo.js", dependencies: [ "dijit.Dialog", "dijit.layout.BorderContainer", "dijit.layout.ContentPane" ] } ], prefixes: [ [ "dijit", "../dijit" ] ] } dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  49. 49. ShrinkSafe ● optimize & layerOptimize ● shrinksafe – Awesome for production! ● shrinksafe.keepLines – Awesome for debugging! ● dojo.js & dojo.js.uncompressed.js ● Use the uncompressed version for debugging dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  50. 50. Other Tricks! dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  51. 51. dojox.analytics ● Use it to send info back to the server ● Dojo startup info ● Window information ● mouseover sampling ● idle activity ● console.* messages <script type="text/javascript" src="/path/to/dojo.js" djConfig="sendMethod:'script', sendInterval:5000, analyticsUrl:'http://example.com/dojox/analytics/logger/dojoxAnalytics.php'" ></script> <script type="text/javascript"> dojo.require("dojox.analytics"); dojo.require("dojox.analytics.plugins.consoleMessages"); dojo.require("dojox.analytics.plugins.dojo"); dojo.require("dojox.analytics.plugins.idle"); dojo.require("dojox.analytics.plugins.mouseClick"); dojo.require("dojox.analytics.plugins.mouseOver"); dojo.require("dojox.analytics.plugins.window"); </script> dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  52. 52. CSS Tricks ● Disable a style/property by changing the name to something invalid ● x.myStyle{padding:10px;} ● Styling dojo.dnd.Avatars ● Start a drag, then disable JavaScript ● Use Firebug/ReCSS to finish styling ● Empty cache or disable cache to refresh background images dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  53. 53. Missing Images ● Create images and be notified if the image is 404 ● dojox.image.Lightbox uses this technique dojo.create("img", { id: "myimage", src: "/path/to/my/image.jpg", onerror: function(){ dojo.create("img", { id: "missingimage", src: "/path/to/missing/image.jpg" }, dojo.body()); } }, dojo.body()); // note: this code is untested... don't shoot me // if it doesn't work :) dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  54. 54. Performance If it's slow, it's a bug dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  55. 55. Performance ● Use a Dojo build ● Only include the dijit css files you need ● Cache values that won't change ● getViewport(), marginBox(), etc ● Destroy non-visible tab content ● Combine XHR payloads ● Use a profiler ● Write better code dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  56. 56. Performance ● Combine images into sprites ● Additional sprite for repeat-x & repeat-y ● Enable gzip ● Use a CDN ● Or multiple subdomains ● Put scripts at the bottom of the page ● Read Steve Souder's books! ● http://stevesouders.com/ dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  57. 57. Testing dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  58. 58. Test Cases ● Write simple test cases ● Individual html files to test a specific feature ● DOH: Dojo Objective Harness ● Little chunks of code to test stuff dojo.require("doh.runner"); dojo.addOnLoad(function(){ doh.register("myTest", [ function myFunction(){ var foo = []; foo.push(6); foo.push(1); foo.push(2); doh.is(foo.indexOf(1), 2); } ]); doh.run(); }); dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  59. 59. Testing Multiple Browsers ● Use a bunch of old computers ● Or join the modern web development revolution and use a virtual machine ● Virtual Box ● VMware Workstation ● VMware Fusion ● Parallels ● IE6/IE7/IE8 ● FF2/FF3 ● Safari 3/4 ● Opera 9/10 dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  60. 60. Testing Multiple Browsers ● Microsoft Virtual PC Disk Images ● http://tinyurl.com/osoomm Disk Image Expires Size IE6 + XP SP3 April 1, 2010 753.8MB IE7 + XP SP3 April 1, 2010 813.1MB IE8 + XP SP3 April 1, 2010 812.9MB IE7 + Vista 120 days after first run 700.0MB, 700.0MB, 590.5MB IE8 + Vista 120 days after first run 50.6MB, 700.0MB, 700.0MB, 687.9MB dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  61. 61. Tools dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  62. 62. Firefox Extensions ● Lori – Life-of-request info (page load times) ● Tamper Data ● Dated, but can still be useful ● Web Developer Toolbar ● Firebug ● Firecookie ● Yslow ● Many, many, many, many more... dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  63. 63. Firebug ● http://www.getfirebug.com/ ● console.log(), console.debug(), console.info(), console.warn(), console.error() ● Use "," instead of "+": console.debug("x =", x); ● Console ● Debugger ● Profiler ● more... dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  64. 64. Firebug Lite ● http://getfirebug.com/firebuglite ● Bookmarklet ● More featureful than Dojo's FBL dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  65. 65. Webkit Web Inspector ● Chrome/Safari/Epiphany/Titanium ● Enable in Safari from command line: – defaults write com.apple.Safari IncludeDebugMenu 1 ● Console ● Debugger ● Profiler ● more... dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  66. 66. IE Developer Toolbar ● IE6/IE7 ● DOM viewer ● Modify CSS ● Clear cache dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  67. 67. Internet Explorer 8 ● Integrated into IE8! ● Console ● Debugger dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  68. 68. IDEs With JavaScript Debuggers ● Netbeans ● http://netbeans.org/kb/67/web/js-debugger-ug.html ● Firefox & IE ● Aptana Studio ● http://www.aptana.org/studio ● Eclipse + Fireclipse ● http://www.almaden.ibm.com/u/bartonjj/fireclipse ● Still active? dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  69. 69. dair ● Dojo Extensions for Adobe AIR ● http://o.sitepen.com/labs/dair/ ● Debug console ● Command line logger ● File logger dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  70. 70. Resources dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  71. 71. Getting Help ● API Docs ● http://api.dojotoolkit.org/ ● http://docs.dojocampus.org/ ● IRC ● #dojo on irc.freenode.net ● dojo-interest Mailing List ● http://mail.dojotoolkit.org/mailman/listinfo/dojo-interest ● Dojo's source code dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  72. 72. Getting Help CB1, INC http://www.cb1inc.com/ Dojo Consulting Web Applications dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  73. 73. Thanks! Questions? http://slideshare.net/cb1kenobi http://twitter.com/cb1kenobi dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/

×