SlideShare a Scribd company logo
1 of 48
Debugging with Firebug and Web Inspector Beyond the Basics By Steven Roussey
Agenda About Me Past, Present, Future Design Patterns Debugging Patterns Debugging Performance Exploring Further Q & A
About Me Name: Steven Roussey Firebug Working Group W3C Invited Expert Illuminations Sites: Network54 AppCenter Studybeat and others… twitter.com/sroussey www.sroussey.com
Past, Present, Future alert()
Past, Present, Future alert() console.log()
Past, Present, Future alert() console.log() JavaScript Debuggers MS Script Debugger for IE4 Venkman Firebug! Web Inspector MS IE Developer Tools Opera Dragonfly MS Visual Studio Mozilla Dev Tools
Past, Present, Future alert() console.log() JavaScript Debuggers MS Script Debugger for IE4 Venkman Firebug! Web Inspector MS IE Developer Tools Opera Dragonfly MS Visual Studio Mozilla Dev Tools Tools for Design Patterns Illuminations for Developers
Past, Present, Future HTML  vs. HTML5
Past, Present, Future An end to progressive enhancement Moving business logic to the client Mobile is accelerating the trend
Past, Present, Future An end to progressive enhancement Abstractions over DOM-ness Top-Down over Bottom-Up Moving business logic to the client Mobile is accelerating the trend
Past, Present, Future An end to progressive enhancement Abstractions over DOM-ness Top-Down over Bottom-Up Moving business logic to the client localStorage needs processing, templating, syncing with server / cloud Mobile is accelerating the trend
Past, Present, Future An end to progressive enhancement Abstractions over DOM-ness Top-Down over Bottom-Up Moving business logic to the client localStorage needs processing, templating, syncing with server / cloud Mobile is accelerating the trend Offline Latency kills
Past, Present, Future Toolkits jQuery MooTools ExtCore Frameworks ExtJS Desktop / Sencha Touch Dojo Toolkit SproutCore qooXdoo / Unity
Tooling for Design Patterns Tools like Firebug show what they know: DOM JavaScript Objects, etc. CSS Script Files
Tooling for Design Patterns Tools like Firebug show what they know: DOM JavaScript Objects, etc. CSS Script Files But now there is a disconnect
Tooling for Design Patterns Example: Inspecting Events Firebug extension: Eventbug
Tooling for Design Patterns Eventbug with plain JS Eventbug with a toolkit
Tooling for Design Patterns Example: Inspecting Events Firebug extension: Eventbug Fails to give useful information, everything links to the same toolkit bind handler
Tooling for Design Patterns Design Patterns Singletons, Sets, Composites, etc. Model – View – Controller Model Associations – HasMany / BelongsTo
Debugging Patterns Inspecting Log Streaming Divide & Conquer / Binary Search Test and Assertion Driven
Inspecting
Inspecting
Inspecting with FireQuery
Inspecting with Illuminations
Log Streaming Watch what is happening flow by Great for training your intuition Great for spotting odd behavior Great for spotting errors that otherwise get caught and hidden from the user
Log Streaming console.log/info/warn/error/exception/assert Use the different levels and filter in the console console.group/groupCollapsed/groupEnd Bundle many together console.dir/dirxml/table Formatted data to the console console.count() and “Log calls to …” Following the flow console.profile/profileEnd & time/timeEnd http://getfirebug.com/wiki/index.php/Console_API
Divide & Conquer Tracking the bugger down Break half way though and check Interate But first…
Basics Review Don’t try and use minified version of JS libs. Use “debug” versions. (YUI/ExtJS4) Try {} catch(e){} pros and cons JSLint/ IDE checking Trailing comma in IE return{ some: “thing” } Yes, speling and caSe matter
JavaScript Comparisons No block level scope Function scopes and hoisting Careful with with{} Closures
JavaScript Comparisons Rarely a need for explicit true (!!) === does not perform type conversion null/undefined are different
JavaScript No block level scope Function scopes and hoisting Careful with with(){}
JavaScript Closures Variable binding Variable shadowing Closure scopes
JavaScript var links = document.getElementByTagName(‘a’), len = links.length; for (var i = 0; i < len; i++) {    links[i].onclick= function() {        alert(i+1);    }; }
JavaScript var links = document.getElementsByTagName('a'), len = links.length; for (var i = 0; i < len; i++) {     links[i].onclick = (function(i) { return function() {     alert(i);     return false;     };    })(i); }
JavaScript varLIB = { helper:function(){console.log("helped");} }; function test() {     with (LIB){ var helper = 5;         console.log(helper);      } } test(); // 5  console.log(LIB.helper); // also 5, not a function
JavaScript varLIB = { helper:function(){console.log("helped");} }; function test() {     with (LIB){         function helper(){console.log("good");};         // ...         helper();      } } test(); // helped  LIB.helper(); // helped, not good…
Breakpoints debugger; // unique comment to search for After an error, from the console With conditions Disabled breakpoints Break-On-Next (BON) Error XHR Cookie DOM Mutation
OK, I’m Broken, Now What? Watches Scope and closure chains are viewable Call Stacks Function chains are viewable Command line in the current scope
Illuminations I'm really excited to see tools which try to help the developer piece together a coherent story about the state of their web apps, instead of just burying them in a pile of data. I think the natural next step is to follow this evolution from straight data to... interpreted data, data that anticipates the questions web developers are trying to answer. I think Illuminations really nails that when it comes to web toolkit users. —Johnathan Nightingale, Director of Firefox Development, Mozilla
Illuminations Object NamingIt presents the framework objects in a smarter way. It recognizes what they are and shows the whole name, like "Ext.DataView" instead of "Object" in the console and the other Firebug panels. And instead of random properties being appended, it looks for a ID-ish and a Value-ish property to show. This gives you an idea what you are looking at when you are debugging. See the example without and with Illumination:
Illuminations Element HighlightingNow, when you hover the mouse over a Widget like Ext.DataView, it will highlight the component on the page. In the case where the coder didn't give a descriptive itemId or name, hovering over it does the trick -- it shows you exactly what that object is all about. This Components and Elements, and it even works for Ext Composites -- it highlights all of its nodes on the page!
Illuminations Contextual MenuWhen you right click on an element in Firefox, Firebug adds an "Inspect Element" menu item to open Firebug and bring you to that element in the HTML panel. Illumination does the same sort of thing, but tries to find the best match: ideally some sort of UI widget, otherwise an Ext Element.
Illuminations Illumination PanelThere is a new panel added to Firebug called Illuminations, and when you use the contextual menu above, this is where it brings you. The Illumination panel is the place to inspect ExtJS objects: Widgets (or Views), Data (stores, records, fields), and Elements (Ext.Element and its brethren). These views show the hierarchical structure of your code:
Illuminations
Debugging Performance Inside your code console.time() console.profile() Web Inspector Audits Tools for Production YSlow (Firefox / Firebug) PageSpeed (Firefox / Firebug) SpeedTracer (Chrome) DynaTrace (IE)
You Are the Best Debugger Stop, and read the code Maybe even read the manual Walk away (or sleep on it) Get a second pair of eyes Some of the simplest things can be spotted by someone that has not been staring at for the last eight hours. Create a test case of a problem and explanation – many times this process is enough
Exploring Further http://getfirebug.com/ http://code.google.com/chrome/devtools/ http://getfirebug.com/downloads#extensions http://code.google.com/webtoolkit/speedtracer/ http://www.illuminations-for-developers.com/
Question & Answer http://www.sroussey.com/ http://www.twitter.com/sroussey http://www.illuminations-for-developers.com/

More Related Content

What's hot

Automating Accessibility: WordCamp Minneapolis 2015
Automating Accessibility: WordCamp Minneapolis 2015Automating Accessibility: WordCamp Minneapolis 2015
Automating Accessibility: WordCamp Minneapolis 2015Joseph Dolson
 
Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...
Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...
Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...Infinum
 
What are accessible names and why should you care?
What are accessible names and why should you care?What are accessible names and why should you care?
What are accessible names and why should you care?Russ Weakley
 
Creating Windows-based Applications Part-I
Creating Windows-based Applications Part-ICreating Windows-based Applications Part-I
Creating Windows-based Applications Part-IUmar Farooq
 
Baawjsajq109
Baawjsajq109Baawjsajq109
Baawjsajq109Thinkful
 
Web app-la-jan-2
Web app-la-jan-2Web app-la-jan-2
Web app-la-jan-2Thinkful
 

What's hot (9)

Robotium Tutorial
Robotium TutorialRobotium Tutorial
Robotium Tutorial
 
Automating Accessibility: WordCamp Minneapolis 2015
Automating Accessibility: WordCamp Minneapolis 2015Automating Accessibility: WordCamp Minneapolis 2015
Automating Accessibility: WordCamp Minneapolis 2015
 
Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...
Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...
Android Meetup Slovenia #5 - Don't go crashing my heart by Zeljko Plesac, Inf...
 
What are accessible names and why should you care?
What are accessible names and why should you care?What are accessible names and why should you care?
What are accessible names and why should you care?
 
Creating Windows-based Applications Part-I
Creating Windows-based Applications Part-ICreating Windows-based Applications Part-I
Creating Windows-based Applications Part-I
 
Deck 6-456 (1)
Deck 6-456 (1)Deck 6-456 (1)
Deck 6-456 (1)
 
Baawjsajq109
Baawjsajq109Baawjsajq109
Baawjsajq109
 
Delphi magic!
Delphi magic!Delphi magic!
Delphi magic!
 
Web app-la-jan-2
Web app-la-jan-2Web app-la-jan-2
Web app-la-jan-2
 

Similar to Beyond the Basics, Debugging with Firebug and Web Inspector

Secrets of the web inspector
Secrets of the web inspectorSecrets of the web inspector
Secrets of the web inspectorCarly Ho
 
AtlasCamp 2011 - Five Strategies to Accelerate Plugin Development
AtlasCamp 2011 - Five Strategies to Accelerate Plugin DevelopmentAtlasCamp 2011 - Five Strategies to Accelerate Plugin Development
AtlasCamp 2011 - Five Strategies to Accelerate Plugin Developmentmrdon
 
Understanding Framework Architecture using Eclipse
Understanding Framework Architecture using EclipseUnderstanding Framework Architecture using Eclipse
Understanding Framework Architecture using Eclipseanshunjain
 
BDD with SpecFlow and Selenium
BDD with SpecFlow and SeleniumBDD with SpecFlow and Selenium
BDD with SpecFlow and SeleniumLiraz Shay
 
A tale of two proxies
A tale of two proxiesA tale of two proxies
A tale of two proxiesSensePost
 
Ef Poco And Unit Testing
Ef Poco And Unit TestingEf Poco And Unit Testing
Ef Poco And Unit TestingJames Phillips
 
Designing A Project Using Java Programming
Designing A Project Using Java ProgrammingDesigning A Project Using Java Programming
Designing A Project Using Java ProgrammingKaty Allen
 
C# Security Testing and Debugging
C# Security Testing and DebuggingC# Security Testing and Debugging
C# Security Testing and DebuggingRich Helton
 
War of the Machines: PVS-Studio vs. TensorFlow
War of the Machines: PVS-Studio vs. TensorFlowWar of the Machines: PVS-Studio vs. TensorFlow
War of the Machines: PVS-Studio vs. TensorFlowPVS-Studio
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsJeff Durta
 
Pragmatic Parallels: Java and JavaScript
Pragmatic Parallels: Java and JavaScriptPragmatic Parallels: Java and JavaScript
Pragmatic Parallels: Java and JavaScriptdavejohnson
 
Behavior & Specification Driven Development in PHP - #OpenWest
Behavior & Specification Driven Development in PHP - #OpenWestBehavior & Specification Driven Development in PHP - #OpenWest
Behavior & Specification Driven Development in PHP - #OpenWestJoshua Warren
 
Joomla Day Austin Part 4
Joomla Day Austin Part 4Joomla Day Austin Part 4
Joomla Day Austin Part 4Kyle Ledbetter
 
What's New for AJAX Developers in IE8 Beta1?
What's New for AJAX Developers in IE8 Beta1?What's New for AJAX Developers in IE8 Beta1?
What's New for AJAX Developers in IE8 Beta1?Janakiram MSV
 
The State of Front-end At CrowdTwist
The State of Front-end At CrowdTwistThe State of Front-end At CrowdTwist
The State of Front-end At CrowdTwistMark Fayngersh
 
Firebug: Javascript Development Made Easier
Firebug: Javascript Development Made EasierFirebug: Javascript Development Made Easier
Firebug: Javascript Development Made EasierBinny V A
 
Workshop - The Little Pattern That Could.pdf
Workshop - The Little Pattern That Could.pdfWorkshop - The Little Pattern That Could.pdf
Workshop - The Little Pattern That Could.pdfTobiasGoeschel
 
Web driver selenium simplified
Web driver selenium simplifiedWeb driver selenium simplified
Web driver selenium simplifiedVikas Singh
 
Professional JavaScript: AntiPatterns
Professional JavaScript: AntiPatternsProfessional JavaScript: AntiPatterns
Professional JavaScript: AntiPatternsMike Wilcox
 

Similar to Beyond the Basics, Debugging with Firebug and Web Inspector (20)

Secrets of the web inspector
Secrets of the web inspectorSecrets of the web inspector
Secrets of the web inspector
 
AtlasCamp 2011 - Five Strategies to Accelerate Plugin Development
AtlasCamp 2011 - Five Strategies to Accelerate Plugin DevelopmentAtlasCamp 2011 - Five Strategies to Accelerate Plugin Development
AtlasCamp 2011 - Five Strategies to Accelerate Plugin Development
 
Understanding Framework Architecture using Eclipse
Understanding Framework Architecture using EclipseUnderstanding Framework Architecture using Eclipse
Understanding Framework Architecture using Eclipse
 
BDD with SpecFlow and Selenium
BDD with SpecFlow and SeleniumBDD with SpecFlow and Selenium
BDD with SpecFlow and Selenium
 
A tale of two proxies
A tale of two proxiesA tale of two proxies
A tale of two proxies
 
Ef Poco And Unit Testing
Ef Poco And Unit TestingEf Poco And Unit Testing
Ef Poco And Unit Testing
 
Designing A Project Using Java Programming
Designing A Project Using Java ProgrammingDesigning A Project Using Java Programming
Designing A Project Using Java Programming
 
C# Security Testing and Debugging
C# Security Testing and DebuggingC# Security Testing and Debugging
C# Security Testing and Debugging
 
War of the Machines: PVS-Studio vs. TensorFlow
War of the Machines: PVS-Studio vs. TensorFlowWar of the Machines: PVS-Studio vs. TensorFlow
War of the Machines: PVS-Studio vs. TensorFlow
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
Pragmatic Parallels: Java and JavaScript
Pragmatic Parallels: Java and JavaScriptPragmatic Parallels: Java and JavaScript
Pragmatic Parallels: Java and JavaScript
 
Behavior & Specification Driven Development in PHP - #OpenWest
Behavior & Specification Driven Development in PHP - #OpenWestBehavior & Specification Driven Development in PHP - #OpenWest
Behavior & Specification Driven Development in PHP - #OpenWest
 
Joomla Day Austin Part 4
Joomla Day Austin Part 4Joomla Day Austin Part 4
Joomla Day Austin Part 4
 
Drupal 7 ci and testing
Drupal 7 ci and testingDrupal 7 ci and testing
Drupal 7 ci and testing
 
What's New for AJAX Developers in IE8 Beta1?
What's New for AJAX Developers in IE8 Beta1?What's New for AJAX Developers in IE8 Beta1?
What's New for AJAX Developers in IE8 Beta1?
 
The State of Front-end At CrowdTwist
The State of Front-end At CrowdTwistThe State of Front-end At CrowdTwist
The State of Front-end At CrowdTwist
 
Firebug: Javascript Development Made Easier
Firebug: Javascript Development Made EasierFirebug: Javascript Development Made Easier
Firebug: Javascript Development Made Easier
 
Workshop - The Little Pattern That Could.pdf
Workshop - The Little Pattern That Could.pdfWorkshop - The Little Pattern That Could.pdf
Workshop - The Little Pattern That Could.pdf
 
Web driver selenium simplified
Web driver selenium simplifiedWeb driver selenium simplified
Web driver selenium simplified
 
Professional JavaScript: AntiPatterns
Professional JavaScript: AntiPatternsProfessional JavaScript: AntiPatterns
Professional JavaScript: AntiPatterns
 

Beyond the Basics, Debugging with Firebug and Web Inspector

  • 1. Debugging with Firebug and Web Inspector Beyond the Basics By Steven Roussey
  • 2. Agenda About Me Past, Present, Future Design Patterns Debugging Patterns Debugging Performance Exploring Further Q & A
  • 3. About Me Name: Steven Roussey Firebug Working Group W3C Invited Expert Illuminations Sites: Network54 AppCenter Studybeat and others… twitter.com/sroussey www.sroussey.com
  • 5. Past, Present, Future alert() console.log()
  • 6. Past, Present, Future alert() console.log() JavaScript Debuggers MS Script Debugger for IE4 Venkman Firebug! Web Inspector MS IE Developer Tools Opera Dragonfly MS Visual Studio Mozilla Dev Tools
  • 7. Past, Present, Future alert() console.log() JavaScript Debuggers MS Script Debugger for IE4 Venkman Firebug! Web Inspector MS IE Developer Tools Opera Dragonfly MS Visual Studio Mozilla Dev Tools Tools for Design Patterns Illuminations for Developers
  • 8. Past, Present, Future HTML vs. HTML5
  • 9. Past, Present, Future An end to progressive enhancement Moving business logic to the client Mobile is accelerating the trend
  • 10. Past, Present, Future An end to progressive enhancement Abstractions over DOM-ness Top-Down over Bottom-Up Moving business logic to the client Mobile is accelerating the trend
  • 11. Past, Present, Future An end to progressive enhancement Abstractions over DOM-ness Top-Down over Bottom-Up Moving business logic to the client localStorage needs processing, templating, syncing with server / cloud Mobile is accelerating the trend
  • 12. Past, Present, Future An end to progressive enhancement Abstractions over DOM-ness Top-Down over Bottom-Up Moving business logic to the client localStorage needs processing, templating, syncing with server / cloud Mobile is accelerating the trend Offline Latency kills
  • 13. Past, Present, Future Toolkits jQuery MooTools ExtCore Frameworks ExtJS Desktop / Sencha Touch Dojo Toolkit SproutCore qooXdoo / Unity
  • 14. Tooling for Design Patterns Tools like Firebug show what they know: DOM JavaScript Objects, etc. CSS Script Files
  • 15. Tooling for Design Patterns Tools like Firebug show what they know: DOM JavaScript Objects, etc. CSS Script Files But now there is a disconnect
  • 16. Tooling for Design Patterns Example: Inspecting Events Firebug extension: Eventbug
  • 17. Tooling for Design Patterns Eventbug with plain JS Eventbug with a toolkit
  • 18. Tooling for Design Patterns Example: Inspecting Events Firebug extension: Eventbug Fails to give useful information, everything links to the same toolkit bind handler
  • 19. Tooling for Design Patterns Design Patterns Singletons, Sets, Composites, etc. Model – View – Controller Model Associations – HasMany / BelongsTo
  • 20. Debugging Patterns Inspecting Log Streaming Divide & Conquer / Binary Search Test and Assertion Driven
  • 25. Log Streaming Watch what is happening flow by Great for training your intuition Great for spotting odd behavior Great for spotting errors that otherwise get caught and hidden from the user
  • 26. Log Streaming console.log/info/warn/error/exception/assert Use the different levels and filter in the console console.group/groupCollapsed/groupEnd Bundle many together console.dir/dirxml/table Formatted data to the console console.count() and “Log calls to …” Following the flow console.profile/profileEnd & time/timeEnd http://getfirebug.com/wiki/index.php/Console_API
  • 27. Divide & Conquer Tracking the bugger down Break half way though and check Interate But first…
  • 28. Basics Review Don’t try and use minified version of JS libs. Use “debug” versions. (YUI/ExtJS4) Try {} catch(e){} pros and cons JSLint/ IDE checking Trailing comma in IE return{ some: “thing” } Yes, speling and caSe matter
  • 29. JavaScript Comparisons No block level scope Function scopes and hoisting Careful with with{} Closures
  • 30. JavaScript Comparisons Rarely a need for explicit true (!!) === does not perform type conversion null/undefined are different
  • 31. JavaScript No block level scope Function scopes and hoisting Careful with with(){}
  • 32. JavaScript Closures Variable binding Variable shadowing Closure scopes
  • 33. JavaScript var links = document.getElementByTagName(‘a’), len = links.length; for (var i = 0; i < len; i++) { links[i].onclick= function() { alert(i+1); }; }
  • 34. JavaScript var links = document.getElementsByTagName('a'), len = links.length; for (var i = 0; i < len; i++) { links[i].onclick = (function(i) { return function() { alert(i); return false; }; })(i); }
  • 35. JavaScript varLIB = { helper:function(){console.log("helped");} }; function test() { with (LIB){ var helper = 5; console.log(helper); } } test(); // 5 console.log(LIB.helper); // also 5, not a function
  • 36. JavaScript varLIB = { helper:function(){console.log("helped");} }; function test() { with (LIB){ function helper(){console.log("good");}; // ... helper(); } } test(); // helped LIB.helper(); // helped, not good…
  • 37. Breakpoints debugger; // unique comment to search for After an error, from the console With conditions Disabled breakpoints Break-On-Next (BON) Error XHR Cookie DOM Mutation
  • 38. OK, I’m Broken, Now What? Watches Scope and closure chains are viewable Call Stacks Function chains are viewable Command line in the current scope
  • 39. Illuminations I'm really excited to see tools which try to help the developer piece together a coherent story about the state of their web apps, instead of just burying them in a pile of data. I think the natural next step is to follow this evolution from straight data to... interpreted data, data that anticipates the questions web developers are trying to answer. I think Illuminations really nails that when it comes to web toolkit users. —Johnathan Nightingale, Director of Firefox Development, Mozilla
  • 40. Illuminations Object NamingIt presents the framework objects in a smarter way. It recognizes what they are and shows the whole name, like "Ext.DataView" instead of "Object" in the console and the other Firebug panels. And instead of random properties being appended, it looks for a ID-ish and a Value-ish property to show. This gives you an idea what you are looking at when you are debugging. See the example without and with Illumination:
  • 41. Illuminations Element HighlightingNow, when you hover the mouse over a Widget like Ext.DataView, it will highlight the component on the page. In the case where the coder didn't give a descriptive itemId or name, hovering over it does the trick -- it shows you exactly what that object is all about. This Components and Elements, and it even works for Ext Composites -- it highlights all of its nodes on the page!
  • 42. Illuminations Contextual MenuWhen you right click on an element in Firefox, Firebug adds an "Inspect Element" menu item to open Firebug and bring you to that element in the HTML panel. Illumination does the same sort of thing, but tries to find the best match: ideally some sort of UI widget, otherwise an Ext Element.
  • 43. Illuminations Illumination PanelThere is a new panel added to Firebug called Illuminations, and when you use the contextual menu above, this is where it brings you. The Illumination panel is the place to inspect ExtJS objects: Widgets (or Views), Data (stores, records, fields), and Elements (Ext.Element and its brethren). These views show the hierarchical structure of your code:
  • 45. Debugging Performance Inside your code console.time() console.profile() Web Inspector Audits Tools for Production YSlow (Firefox / Firebug) PageSpeed (Firefox / Firebug) SpeedTracer (Chrome) DynaTrace (IE)
  • 46. You Are the Best Debugger Stop, and read the code Maybe even read the manual Walk away (or sleep on it) Get a second pair of eyes Some of the simplest things can be spotted by someone that has not been staring at for the last eight hours. Create a test case of a problem and explanation – many times this process is enough
  • 47. Exploring Further http://getfirebug.com/ http://code.google.com/chrome/devtools/ http://getfirebug.com/downloads#extensions http://code.google.com/webtoolkit/speedtracer/ http://www.illuminations-for-developers.com/
  • 48. Question & Answer http://www.sroussey.com/ http://www.twitter.com/sroussey http://www.illuminations-for-developers.com/