SlideShare a Scribd company logo
1 of 73
Download to read offline
Debugging Dojo Applications
           dojo.connect
        February 10, 2010
            Chris Barber
             CB1, INC.
      http://www.cb1inc.com/
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/
All Software Has Bugs



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
Fixing Bugs Sucks



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
Debugging Dojo
         & JavaScript



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
Old School Debugging



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
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/
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/
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/
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/
Advanced Tactics
●   Proxy server
●   Packet sniffing




         dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
JavaScript Bugs



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
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/
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/
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/
dojo



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
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/
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/
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/
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/
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/
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/
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/
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/
dijit



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
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/
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/
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/
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/
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/
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/
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/
The Build System



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
Other Tricks!



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
Performance
             If it's slow, it's a bug




dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
Testing



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
Tools



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
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/
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/
IE Developer Toolbar
●   IE6/IE7
●   DOM viewer
●   Modify CSS
●   Clear cache




        dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
Internet Explorer 8
●   Integrated into IE8!
●   Console
●   Debugger




         dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
Resources



dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
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/
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/
Thanks!

                 Questions?
          http://slideshare.net/cb1kenobi
           http://twitter.com/cb1kenobi




dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/

More Related Content

Similar to Debugging Dojo Applications (2/10/2010)

AOT-compilation of JavaScript with V8
AOT-compilation of JavaScript with V8AOT-compilation of JavaScript with V8
AOT-compilation of JavaScript with V8Phil Eaton
 
Testing web APIs
Testing web APIsTesting web APIs
Testing web APIsFDConf
 
How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)
How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)
How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)어형 이
 
JavaScript From Hell - CONFidence 2.0 2009
JavaScript From Hell - CONFidence 2.0 2009JavaScript From Hell - CONFidence 2.0 2009
JavaScript From Hell - CONFidence 2.0 2009Mario Heiderich
 
ECMAScript 6: A Better JavaScript for the Ambient Computing Era
ECMAScript 6: A Better JavaScript for the Ambient Computing EraECMAScript 6: A Better JavaScript for the Ambient Computing Era
ECMAScript 6: A Better JavaScript for the Ambient Computing EraAllen Wirfs-Brock
 
Creating a Responsive Website From Scratch
Creating a Responsive Website From ScratchCreating a Responsive Website From Scratch
Creating a Responsive Website From ScratchCorky Brown
 
Simple Pure Java
Simple Pure JavaSimple Pure Java
Simple Pure JavaAnton Keks
 
Developing cross platform desktop application with Ruby
Developing cross platform desktop application with RubyDeveloping cross platform desktop application with Ruby
Developing cross platform desktop application with RubyAnis Ahmad
 
Browser Internals for JS Devs: WebU Toronto 2016 by Alex Blom
Browser Internals for JS Devs: WebU Toronto 2016 by Alex BlomBrowser Internals for JS Devs: WebU Toronto 2016 by Alex Blom
Browser Internals for JS Devs: WebU Toronto 2016 by Alex BlomAlex Blom
 
Design and Evolution of cyber-dojo
Design and Evolution of cyber-dojoDesign and Evolution of cyber-dojo
Design and Evolution of cyber-dojoJon Jagger
 
Using Grails to Power your Electric Car
Using Grails to Power your Electric CarUsing Grails to Power your Electric Car
Using Grails to Power your Electric CarGR8Conf
 
用 IBDesignable 作 UI
用 IBDesignable 作 UI用 IBDesignable 作 UI
用 IBDesignable 作 UITsungyu Yu
 
Why I Love TorqueBox (And Why You Will Too)
Why I Love TorqueBox (And Why You Will Too)Why I Love TorqueBox (And Why You Will Too)
Why I Love TorqueBox (And Why You Will Too)benbrowning
 
BeagleBoard Workshop ESC Boston 2011
BeagleBoard Workshop ESC Boston 2011BeagleBoard Workshop ESC Boston 2011
BeagleBoard Workshop ESC Boston 2011Opersys inc.
 

Similar to Debugging Dojo Applications (2/10/2010) (20)

AOT-compilation of JavaScript with V8
AOT-compilation of JavaScript with V8AOT-compilation of JavaScript with V8
AOT-compilation of JavaScript with V8
 
JS class slides (2016)
JS class slides (2016)JS class slides (2016)
JS class slides (2016)
 
12 tricks to avoid hackers breaks your CI / CD
12 tricks to avoid hackers breaks your  CI / CD12 tricks to avoid hackers breaks your  CI / CD
12 tricks to avoid hackers breaks your CI / CD
 
JS Class 2016
JS Class 2016JS Class 2016
JS Class 2016
 
Java vs. C/C++
Java vs. C/C++Java vs. C/C++
Java vs. C/C++
 
Ruby Kaigi 2008 LT
Ruby Kaigi 2008 LTRuby Kaigi 2008 LT
Ruby Kaigi 2008 LT
 
Testing web APIs
Testing web APIsTesting web APIs
Testing web APIs
 
How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)
How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)
How to debug the pod which is hard to debug (디버그 하기 어려운 POD 디버그 하기)
 
JavaScript From Hell - CONFidence 2.0 2009
JavaScript From Hell - CONFidence 2.0 2009JavaScript From Hell - CONFidence 2.0 2009
JavaScript From Hell - CONFidence 2.0 2009
 
ECMAScript 6: A Better JavaScript for the Ambient Computing Era
ECMAScript 6: A Better JavaScript for the Ambient Computing EraECMAScript 6: A Better JavaScript for the Ambient Computing Era
ECMAScript 6: A Better JavaScript for the Ambient Computing Era
 
Creating a Responsive Website From Scratch
Creating a Responsive Website From ScratchCreating a Responsive Website From Scratch
Creating a Responsive Website From Scratch
 
Simple Pure Java
Simple Pure JavaSimple Pure Java
Simple Pure Java
 
Developing cross platform desktop application with Ruby
Developing cross platform desktop application with RubyDeveloping cross platform desktop application with Ruby
Developing cross platform desktop application with Ruby
 
Browser Internals for JS Devs: WebU Toronto 2016 by Alex Blom
Browser Internals for JS Devs: WebU Toronto 2016 by Alex BlomBrowser Internals for JS Devs: WebU Toronto 2016 by Alex Blom
Browser Internals for JS Devs: WebU Toronto 2016 by Alex Blom
 
Design and Evolution of cyber-dojo
Design and Evolution of cyber-dojoDesign and Evolution of cyber-dojo
Design and Evolution of cyber-dojo
 
Sensible scaling
Sensible scalingSensible scaling
Sensible scaling
 
Using Grails to Power your Electric Car
Using Grails to Power your Electric CarUsing Grails to Power your Electric Car
Using Grails to Power your Electric Car
 
用 IBDesignable 作 UI
用 IBDesignable 作 UI用 IBDesignable 作 UI
用 IBDesignable 作 UI
 
Why I Love TorqueBox (And Why You Will Too)
Why I Love TorqueBox (And Why You Will Too)Why I Love TorqueBox (And Why You Will Too)
Why I Love TorqueBox (And Why You Will Too)
 
BeagleBoard Workshop ESC Boston 2011
BeagleBoard Workshop ESC Boston 2011BeagleBoard Workshop ESC Boston 2011
BeagleBoard Workshop ESC Boston 2011
 

More from Chris Barber

Remote IP Power Switches
Remote IP Power SwitchesRemote IP Power Switches
Remote IP Power SwitchesChris Barber
 
Node.js/io.js Native C++ Addons
Node.js/io.js Native C++ AddonsNode.js/io.js Native C++ Addons
Node.js/io.js Native C++ AddonsChris Barber
 
Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013
Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013
Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013Chris Barber
 
Exploring the Titanium CLI - Codestrong 2012
Exploring the Titanium CLI - Codestrong 2012Exploring the Titanium CLI - Codestrong 2012
Exploring the Titanium CLI - Codestrong 2012Chris Barber
 
Intro to PECL/mysqlnd_ms (4/7/2011)
Intro to PECL/mysqlnd_ms (4/7/2011)Intro to PECL/mysqlnd_ms (4/7/2011)
Intro to PECL/mysqlnd_ms (4/7/2011)Chris Barber
 
Cassandra - Say Goodbye to the Relational Database (5-6-2010)
Cassandra - Say Goodbye to the Relational Database (5-6-2010)Cassandra - Say Goodbye to the Relational Database (5-6-2010)
Cassandra - Say Goodbye to the Relational Database (5-6-2010)Chris Barber
 
Titanium Powered Desktop & Mobile Apps (11/21/2009)
Titanium Powered Desktop & Mobile Apps (11/21/2009)Titanium Powered Desktop & Mobile Apps (11/21/2009)
Titanium Powered Desktop & Mobile Apps (11/21/2009)Chris Barber
 
High Availability With DRBD & Heartbeat
High Availability With DRBD & HeartbeatHigh Availability With DRBD & Heartbeat
High Availability With DRBD & HeartbeatChris Barber
 
2008 MySQL Conference Recap
2008 MySQL Conference Recap2008 MySQL Conference Recap
2008 MySQL Conference RecapChris Barber
 
Memcached And MySQL
Memcached And MySQLMemcached And MySQL
Memcached And MySQLChris Barber
 

More from Chris Barber (10)

Remote IP Power Switches
Remote IP Power SwitchesRemote IP Power Switches
Remote IP Power Switches
 
Node.js/io.js Native C++ Addons
Node.js/io.js Native C++ AddonsNode.js/io.js Native C++ Addons
Node.js/io.js Native C++ Addons
 
Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013
Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013
Titanium 3.2 CLI - TiAppCamp2 - 11/2/2013
 
Exploring the Titanium CLI - Codestrong 2012
Exploring the Titanium CLI - Codestrong 2012Exploring the Titanium CLI - Codestrong 2012
Exploring the Titanium CLI - Codestrong 2012
 
Intro to PECL/mysqlnd_ms (4/7/2011)
Intro to PECL/mysqlnd_ms (4/7/2011)Intro to PECL/mysqlnd_ms (4/7/2011)
Intro to PECL/mysqlnd_ms (4/7/2011)
 
Cassandra - Say Goodbye to the Relational Database (5-6-2010)
Cassandra - Say Goodbye to the Relational Database (5-6-2010)Cassandra - Say Goodbye to the Relational Database (5-6-2010)
Cassandra - Say Goodbye to the Relational Database (5-6-2010)
 
Titanium Powered Desktop & Mobile Apps (11/21/2009)
Titanium Powered Desktop & Mobile Apps (11/21/2009)Titanium Powered Desktop & Mobile Apps (11/21/2009)
Titanium Powered Desktop & Mobile Apps (11/21/2009)
 
High Availability With DRBD & Heartbeat
High Availability With DRBD & HeartbeatHigh Availability With DRBD & Heartbeat
High Availability With DRBD & Heartbeat
 
2008 MySQL Conference Recap
2008 MySQL Conference Recap2008 MySQL Conference Recap
2008 MySQL Conference Recap
 
Memcached And MySQL
Memcached And MySQLMemcached And MySQL
Memcached And MySQL
 

Recently uploaded

Automation Ops Series: Session 3 - Solutions management
Automation Ops Series: Session 3 - Solutions managementAutomation Ops Series: Session 3 - Solutions management
Automation Ops Series: Session 3 - Solutions managementDianaGray10
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Kaya Weers
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkPixlogix Infotech
 
Transport in Open Pits______SM_MI10415MI
Transport in Open Pits______SM_MI10415MITransport in Open Pits______SM_MI10415MI
Transport in Open Pits______SM_MI10415MIRomil Mishra
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Software Security in the Real World w/Kelsey Hightower
Software Security in the Real World w/Kelsey HightowerSoftware Security in the Real World w/Kelsey Hightower
Software Security in the Real World w/Kelsey HightowerAnchore
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsYoss Cohen
 
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...amber724300
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesManik S Magar
 
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...BookNet Canada
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...Karmanjay Verma
 
Dublin_mulesoft_meetup_API_specifications.pptx
Dublin_mulesoft_meetup_API_specifications.pptxDublin_mulesoft_meetup_API_specifications.pptx
Dublin_mulesoft_meetup_API_specifications.pptxKunal Gupta
 
Introduction-to-Wazuh-and-its-integration.pptx
Introduction-to-Wazuh-and-its-integration.pptxIntroduction-to-Wazuh-and-its-integration.pptx
Introduction-to-Wazuh-and-its-integration.pptxmprakaash5
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
Women in Automation 2024: Career session - explore career paths in automation
Women in Automation 2024: Career session - explore career paths in automationWomen in Automation 2024: Career session - explore career paths in automation
Women in Automation 2024: Career session - explore career paths in automationDianaGray10
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...itnewsafrica
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 

Recently uploaded (20)

Automation Ops Series: Session 3 - Solutions management
Automation Ops Series: Session 3 - Solutions managementAutomation Ops Series: Session 3 - Solutions management
Automation Ops Series: Session 3 - Solutions management
 
Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)Design pattern talk by Kaya Weers - 2024 (v2)
Design pattern talk by Kaya Weers - 2024 (v2)
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App Framework
 
Transport in Open Pits______SM_MI10415MI
Transport in Open Pits______SM_MI10415MITransport in Open Pits______SM_MI10415MI
Transport in Open Pits______SM_MI10415MI
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Software Security in the Real World w/Kelsey Hightower
Software Security in the Real World w/Kelsey HightowerSoftware Security in the Real World w/Kelsey Hightower
Software Security in the Real World w/Kelsey Hightower
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platforms
 
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
JET Technology Labs White Paper for Virtualized Security and Encryption Techn...
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
 
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
Transcript: New from BookNet Canada for 2024: BNC SalesData and LibraryData -...
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
 
Dublin_mulesoft_meetup_API_specifications.pptx
Dublin_mulesoft_meetup_API_specifications.pptxDublin_mulesoft_meetup_API_specifications.pptx
Dublin_mulesoft_meetup_API_specifications.pptx
 
Introduction-to-Wazuh-and-its-integration.pptx
Introduction-to-Wazuh-and-its-integration.pptxIntroduction-to-Wazuh-and-its-integration.pptx
Introduction-to-Wazuh-and-its-integration.pptx
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
Women in Automation 2024: Career session - explore career paths in automation
Women in Automation 2024: Career session - explore career paths in automationWomen in Automation 2024: Career session - explore career paths in automation
Women in Automation 2024: Career session - explore career paths in automation
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 

Debugging Dojo Applications (2/10/2010)

  • 1. Debugging Dojo Applications dojo.connect February 10, 2010 Chris Barber CB1, INC. http://www.cb1inc.com/
  • 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. All Software Has Bugs dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 4. Fixing Bugs Sucks dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. Debugging Dojo & JavaScript dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. Old School Debugging dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. 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. 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. 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. 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. Advanced Tactics ● Proxy server ● Packet sniffing dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 17. JavaScript Bugs dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. 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. 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. 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. dojo dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. dijit dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. The Build System dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. Other Tricks! dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. Performance If it's slow, it's a bug dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. Testing dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. Tools dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. 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. 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. 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. Internet Explorer 8 ● Integrated into IE8! ● Console ● Debugger dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. Resources dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/
  • 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. 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. Thanks! Questions? http://slideshare.net/cb1kenobi http://twitter.com/cb1kenobi dojo.connect | 2.10.2010 | Chris Barber | CB1, INC. | http://www.cb1inc.com/