Friday, September 3, 2010

Updated APEX 4.0 Builder Hotkeys Userscript

I have updated my user script to include shortcut keys for navigating to Shared Components, Edit Page and Cancel.

Current shortcut keys are:
  • F8: Run Page
  • F9: Apply Changes
  • Alt + F9: Clicks the highlighted button
  • Shift + F9: Cancel
  • Alt + PageDown: Next
  • Alt + PageUp: Previous
  • Ctrl + Shift + S: Shared Components
  • Ctrl + Shift + E: Edit Page

It is tested with Google Chrome and Firefox (Greasemonkey must be installed).

You can download the script here: http://userscripts.org/scripts/show/81058

Press the big green Install-button to install the script in your browser :-)

Developing APEX Applications in Google Chrome

Earlier this year I read Scott Wesleys post called Why I use Google Chrome for Oracle Apex Development, but it did not quite register then. Recently I started using Google Chrome as my main APEX development browser.

The sole reason for starting development in Chrome was the speed, but over time I have become more familiar with it's web developer environment. Here I will sum up some of the features I use, and my experiences so far.

Invoking Chrome Developer Tools
Besides the blistering speed of the browser, the native developer tools in Chrome is a great feature.

There are a number of ways to start the developer toolbar
  • You can click the icon in the upper right corner (tedious)
  • You can right click anywhere on the page (handy)
  • You can press <ctrl> + <shift> + I (handy indeed).
You can also press <ctrl> + <shift> + J to go directly to the javascript console.

The developer tools are feature rich, so I will only highlight some of the features.

Elements
This is the default screen when starting developer tools. You get what you expect from an element inspector (like Firebug):

  • Formatted HTML source code and instant view of element styles (also computed overview of all element style attributes in play)
  • Navigate through source code with keys or mouse to highlight page elements
  • Click the magnifying glass at the bototm and click page elements to inspect single elements
  • Double click element to change it (both in HTML document and element style attributes)

Resources
Resources gives you an overview of the complete page including external files. It sports an easy graphical view over how much time each part takes (like the Net tab of Firebug). Click any resource to view content and headers. It also gives you a nasty red dot with (preferrably a low) number in it, if the browser encounters any structural error in the documents it has to rewrite. If you click the document, the rewrites are highlighted so you can correct the issues.



You can filter the resources by clicking the buttons (like Stylesheets, Images, Scripts, etc.).

One of the features I liked most about Firebug, was the ability to easely track AJAX calls (from the console or Net). Resources tab in Crome has the same ability, just click XHR-button (XHR = XML HTTP Request) to view AJAX type requests and responses.



Console and Scripts
The console has it's own shortcut keys, <ctrl> + <shift> + J gets you directly there. The console will display all errors encountered by the browser to start with, and let's you write javascript to interact with the page.


The Scripts tab let's you view, alter and debug javascripts


  • Set/enable/disable breakpoints
  • Play/pause, step in/out/over, etc.
  • View and change variable values
Other Features
  • Timeline - Nifty feature to watch real-time javascript events as you interact with the page. Filtering options are available (Loading, Rendering, Scripting).
  • Profiles - Profile your pages (or actions on page) in view of CPU intensity or memory usage
  • Storage - The only real use I have for this is the Cookies overview.
  • Audits - This is like YSlow for Firefox, giving you advice on how to speed up your pages

Extensions and Userscripts
All the features mentioned above is integrated in the browser, in addition, there are a number of extensions you can download. I just have Web Developer installed, but you can browse Google Chrome Extensions for more. You can also see Creating a Web Development Environment using Google Chrome Extensions from Speckboy Design Magazine for more on related extensions.

Sadly, the APEX Builder Plugin does not work for Chrome. I use my own userscript to get shortcut keys working in the APEX builder. Many of the Greasemonkey userscripts also works in Chrome, see userscripts.org for a large amount of those.

Experiences so far
I now go days without resorting to Firefox and Firebug. (In truth, that is not just Chrome's achievement. Dynamic Actions in APEX 4.0 makes rich interfaces much, much easier than it was.) I find that most of what I need, most of the time is in place within the developer toolbar of Chrome. I can also work full working days without having to restart the browser because of memory leaks, etc (browser getting sluggish, you know what I am talking about...).

Oh, did I mention it is fast?

For future reference: This was written based on Google Chrome version 6.0.472.53.

Thursday, August 26, 2010

Formatted Text with Dynamic Actions

This is a quick note on how to use Dynamic Actions in Oracle APEX 4.0 to display unstructured and formatted text from the database based on user selection. This was a bit more cumbersome in the previous versions of APEX.
Image by Jesper Rønn-Jensen under Creative Commons License.

If you have ever let the users get at the Rich Text Editor items (HTML Editor in the previous versions), then you probably have som text with HTML-markup stored in the database. Below is a description on how to get and display the text based on browser events. The example below will display a description of a department when the user clicks department name in a SQL Report (quite stupid really, but serves the purpose).

Demo Application
I have updated my demo application with the example, see it in action here:http://apex.oracle.com/pls/apex/f?p=45420:5. Download the application here: http://apex.oracle.com/pls/apex/f?p=45410:DOWNLOAD.

Prepare the Report
Create a region of type SQL Report based on table DEPT:
select deptno, dname from dept
Modify the DNAME-column in the report:
  • Set Link Text to #DNAME#
  • Set Link Attributes to id="departmentName#DEPTNO#" (this will uniquely identify each anchor element, and give the ability of extracting DEPTNO from the department name)
  • Set Target to URL
  • Set URL to # (this really does nothing)

Add an item to the report region, this will eventually hold the DEPTNO of the chosen department from the report.
  • Right click the department report region and choose Create Page Item
  • Select Hidden
  • Give the item a name (P5_DEPTNO)
  • Leave the rest default (you might want to reconsider Source Used, if you set it to "Always, replacing any existing value in session state", and leaves Source Value blank, you ensure the item starts with no value when the page is displayed).

Create Region to Display Text
For the sake of simplicity, the text will be displayed in it's own HTML Region

Right click Regions and choose Create
  • Choose HTML
  • Choose HTML again
  • Give it a descriptive title: Department Description
  • Create a div to display the text, set Region Source to:
  • <div id="departmentDescription"></div>


Create the Dynamic Action
The first we need to do, is to create a Dynamic Action to get the DEPTNO from the chosen department name, and stuff it into P5_DEPTNO.

Right click Dynamic Actions and choose Create
Choose Advanced
Give it a name (onclick - Department Name)
Set the values as seen below, this means the Dynamic Action will fire when anchors with id's starting with departmentName is clicked

Set Action to Set Value, uncheck Fire on Page Load (we only want it to fire when clicked), set the Set Type to Javascript, and Javascript Expression to
this.triggeringElement.id.replace('departmentName','');

This will extract the DEPTNO from the id of the element clicked (by replacing departmentName with nothing).

Set Selection Type to Item(s), and transfer P5_DEPTNO to the right hand side

The final result should look something like this:



The next step is to get and display the desired text from the database. In order to do this we must create a new True Action to the same Dynamic Action.
  • Right click the Dynamic Action and choose Edit
  • Click Add True Action
  • Set Action: SetValue
  • Uncheck Fire on Page Load
  • Set Set Type: PL/SQL Function Body
  • Set PL/SQL Function Body to code below:
declare
   l_ret varchar2(32000);
begin
   for r in (select dep.*
               from dept dep
              where dep.deptno = :p5_deptno)
   loop
      l_ret := 'HTML formatted return value for <b>'|| r.dname ||'</b> located in <i>'|| upper(r.loc) ||'</i>.';
   end loop;
   return l_ret;
end;
  • Set Page Items to Submit: P5_DEPTNO
  • Set Escape Special Characters: No
  • Set Selection Type: jQuery Selector
  • Set jQuery Selector: div[id=departmentDescription]


Finally edit the Dynamic Action, and set Event Scope: live. This is to ensure the onclick event is attached to Department Name even after PPR refresh of the report.


A Word of Caution
Even though APEX takes care of escaping/encoding your HTML according to JSON specification, this will bloat your return message. There seems to be the standard PL/SQL 32k limit to the JSON-message (Large texts resulting in ORA-06502).

Take care what you return in your message, switching off escape special characters leaves it pretty much wide open to anything. HTML in JSON is a debated issue.

In short: Keep it small and neat :-)

Monday, August 2, 2010

More on Modal Pop Ups

I previously wrote a post on how to use the new APEX 4.0 native features to conjure modal inline dialogs. It was followed by a brief (very brief on my part, to say the least) discussion on how to achieve the same functionality for a create button. In this post I will elaborate a bit on how to do exactly that.
The solution sketched out below definitely has potential for improvement, but can serve as a diving board for the interested.

Demo Application
If you are curious, or just down right bored with long posts, then I have a running copy of the demo application here: http://apex.oracle.com/pls/apex/f?p=45420:3. The demo application can be downloaded here: http://apex.oracle.com/pls/apex/f?p=45410:DOWNLOAD. To keep the examples as clean as possible, I have created a new page with the Create-button, so you have both alternatives available.

The Quick Solution
And what is wrong with a quick solution? Personally I feel the we programmers (me definitely included) are very good at complicating things. The rest of this post is dedicated to a more generic approach, but in my original comment I suggested this:
  • Create an anchor-tag (link) anywhere on the page
  • Set href attribute to the URL of your edit form page, let primary key item values remain blank (but be sure to pass them)
  • Set the id attribute to callModalDialog
In my sample application that would be: <a id="callModalDialog" href="f?p=&APP_ID.:4:&SESSION.::NO::P4_EMPNO:">Create Employee</a>.

This would work just like the edit links in my original example, but ready to insert a new employee. Like magic :-)

The Elaborate Solution
The elaborate solution uses more Dynamic Actions, a bit more javascripting and a couple of dirty tricks to accomplish the same as the quick solution. The goal is to make a normal create button to have the same behavior with inline modal dialog, as the edit links does.

The explanation below is based on the original modal dialog page in my sample application. To follow the example and repeat the steps, you need to start off with a copy of page 2 in my sample application.

To follow the example, start by creating a region button with the following values (mostly just accept defaults):
Button name: CREATE

The button should take you to the create/edit page, but without the fancy modal dialog.

A Note for Later
If you name your button to something else than Create (or Text Label/ALT-property for the page button in APEX), you must adjust the following JQuery selectors accordingly for it to work. You may also expect some issues when using an image button (the javascript onclick function extraction may fail).

Removing the Original onclick-Event
APEX uses a javascript function to redirect the browser (also when using anchor button template). There are two things that needs to be done when the page loads; store the original create link, and remove the original button onclick event. I use JQuery and javascript native regexp capabilities to achieve this.

To create a Dynamic Action which fires when the page has finished loading, do the following:
Create a new Dynamic Action at the page level

Choose Advanced

Give it a sensible name ("On Page Load")

Choose Event Page Load

Choose Action Execute Javascript Code, and paste in the javascript code below:

/* get original onclick event */
var origAction = $('button[value=Create]').attr('onclick').toString();
/* get link from original onclick event using regular expression */
var link = origAction.match(/(redirect\((\'|\"))([^\'\)|\"\)]*)/)[3];
/* Remove original onclick event */
$('button[value=Create]').removeAttr('onclick');
/* store link as title attribute of button */
$('button[value=Create]').attr('title', link);
Looks a bit Greek? Even if I have actually included comments? If you are not familiar with JQuery, it definitely will. If you are not familiar with regular expressions, even more so. If you are not familiar with javascript at all, you are allowed to test the code, but not use it in production unless you have truly understood what it means :-) There are very good sources on the web for all the knowledge required.

And why, oh, why store the link value in an element attribute definitely not meant to hold a link value!?! It will overwrite any existing title-values, and makes both setting and retrieving code hard to read. On the other hand, it will keep the value with it's element, and support more than one create button on any given page. There are more than one alternate way of doing this, but hey, feel free to bring suggestions :-)

Anyway, click Create and you are done.

If you run your page now, clicking the Create button will not do anything (the onclick event was removed, but not replaced).

That was the hard part!

Calling the Dialog
The edit dialog Dynamic Action is already in place, all that has to be done is to adapt the existing Dynamic Action to include the new create button.
Edit the Modal Dialog Dynamic Action
Include: button[value=Create] as the JQuery Selector expression (total expression will now be: a[id^=callModalDialog],button[value=Create])

Edit the True Action Javascript Expression to be:
/* prevent default behaviour on click */
var e = this.browserEvent;
e.preventDefault();
/* Find page link */
var link;
if (this.triggeringElement.tagName=='A') {
   link = this.triggeringElement.href;
} else if (this.triggeringElement.tagName=='BUTTON') {
   link = this.triggeringElement.title;
}
/* Trigger JQuery UI dialog */
var horizontalPadding = 30;
var verticalPadding = 30;
$('<iframe id="modalDialog" src="' + link + '" />').dialog({
   title: "Edit Employee",
   autoOpen: true,
   width: 700,
   height: 300,
   modal: true,
   close: function(event, ui) {apex.event.trigger('#P3_AFTER_MODAL','select',''); $(this).remove();},
   overlay: {
       opacity: 0.5,
       background: "black"}
}).width(700 - horizontalPadding).height(300 - verticalPadding);
return false;            
The difference from the original javascript code is the extra bit extracting the link to use, which differs from a normal anchor element, and our button with the special id attribute.
Click Apply and you are done.

The Rest?
Is the same as detailed in my last post. I have updated the dialog page in the sample application to include Create and Delete buttons, but that is it.

Quick or Slow?
Quick sound promising! Simplicity rules! There are some draw-backs, of course. Like where to put the link to find it later (a Display Only item properly named perhaps?)? Like the need to style a link as a button? Like the need to place the link in a region template position? It all adds up.

The generic (more elaborate) approach will work with any create button (well, not with the anchor template). Drop the Dynamic Actions onto the page, and it will work. On the other hand, the generic approach involves more code, more code is harder to maintain and more prone to breaking. Generic code requires a delicate hand (ie takes time), and is generally harder to read than code created for a specific task. This is all in the eye of the beholder, but on more than one occasion I have had the pleasure of revisiting my own old code and though: I didn't need to do that... I digress, I know. Besides, that is too great a topic to just be delegated to a digression :-)

So, should you use the quick link (pun intended :-))? My answer (being a consultant) is a definitive: It depends!

Enjoy :-)

Friday, July 16, 2010

Quick Notes

Just a bit of housekeeping.

APEX 4.0 Builder Hotkeys
This is just a quick note on the APEX 4.0 Builder Hotkeys. I have updated the script because not all previous/next buttons were included in the last version. Alt + PageUp/PageDown should now give the right response.

You can download and install the script here: http://userscripts.org/scripts/show/81058

Installation instructions can be found here: http://monkeyonoracle.blogspot.com/2010/07/apex-40-builder-hotkeys.html

Ye Olde Sample App
...is still working! I just went through the Application Upgrade and changed the theme while at it. I even updated the Google Maps example. ("Updated" sound so much better than "fixed a bug". Quick solutions will make you do that...) This application will probably not be updated anytime soon. There are a few more elaborate examples I had in mind, but then there is this "time"-thing all over again. Exploring APEX 4.0 is at the top of my spare time roster for now.

The application will be left alone for the time being, and can be found here: http://apex.oracle.com/pls/apex/f?p=28990:1

Summer Vacation
Finally, my last day of work before I start my summer vacation. One of the nicer benefits as a worker in Norway, is the length of paid vacation established by national law. I guess this is not so normal for Americans.

True story from the ODTUG Kaleidoscope this year. An American guy comes up to me with his friend, and after introductions the following conversation takes place:
Him: So, you're from Norway?
Me: Yep!
Him: Could you tell my colleague here how much paid vacation you guys have?
Me: Five weeks!
Friend: ... (going through the whole stunned jaw dropping improvised look)

Cheers :-)

Thursday, July 15, 2010

Modal Pop Up with Dynamic Actions

You have all seen it in the more "modern" web applications, pop-up windows displayed inline, and not in a separate window. With JQuery embedded into Oracle APEX 4.0, and JQuery UI equally at your disposal (at least in the new themes I have tested), creating this type of dialogs is well withing reach. The JQuery UI dialog is already in use in the application templates (the tool tip dialogs).

I wanted to use Dynamic Actions to create a modal pop-up dialog of APEX-pages, and this is what I came up with. It is not as streamlined as I would have liked it to be, but a big leap in the right direction nonetheless.

Demo Application
I have created a demo application which you can test here: http://apex.oracle.com/pls/apex/f?p=45420:2. You can also download the application here: http://apex.oracle.com/pls/apex/f?p=45410:DOWNLOAD.

Identifying Triggering Elements
The trick with the Dynamic Actions is again to identify which elements you would like to use to trigger the dialog. In my example I have a plain SQL-report, but I have edited the primary key column to display an edit icon, and under the Column Link properties, I have set Link Attributes to include id="callModalDialog#EMPNO#". This gives me the possibility to identify the links using a JQuery selector expression.

I created a Dynamic Action on the click event, with Selection Type JQuery Selector with value a[id^=callModalDialog]. This will identfy all items with attribute id starting with callModalDialog, which, incidentally, is the same id I used on my edit links in the report.

To ensure that the event triggers after refresh of the report, I set the Event Scope to live under Advanced (as opposed to bind, which is normal). This saves me the effort of re-binding the click-events when the report is refreshed.


Calling the Dialog
When the user clicks the edit links, I want to do two things: suppress the default browser behavior, and call the modal dialog. I achieve this by adding a true Action of type Execute Javascript Code.

This contains the javascript code to do both the things I want to do:
/* prevent default behavior on click */
var e = this.browserEvent;
e.preventDefault();
/* Trigger JQuery UI dialog */
var horizontalPadding = 30;
var verticalPadding = 30;
$('<iframe id="modalDialog" src="' + this.triggeringElement.href + '" />').dialog({
   title: "Edit Employee",
   autoOpen: true,
   width: 700,
   height: 300,
   modal: true,
   close: function(event, ui) {apex.event.trigger('#P2_AFTER_MODAL','select',''); $(this).remove();},
   overlay: {
       opacity: 0.5,
       background: "black"}
}).width(700 - horizontalPadding).height(300 - verticalPadding);
return false;            
Some explanation for this is in order. The first part is just to prevent the default browser behavior (user click a ink, and the browser naturally wants to open that page). The second part is by far more complex, and contains all the code needed to start the JQuery UI dialog.

The edit link points to an APEX application page, and the code above invokes the page in an IFRAME, and uses the JQuery UI dialog to show the IFRAME inline in a modal dialog. I extract the link from the triggeringElement, set the title (this could also have been extracted from the triggeringElement), set various attributes for the dialog and IFRAME. In short the code above says open the HREF of the triggeringElement in an IFRAME, show the IFRAME in a modal dialog and open the dialog immediately.

Returning from the Dialog
If you notice the code above, the close configuration for the dialog looks quite complex. The reason for this is that I want to execute a Dynamic Action when the dialog is closed. There are two problems with this; (as far as I know) I cannot execute a Dynamic Action directly from Javascript, and I have nowhere to attach the Dynamic Event anyways. Write som javascript function you say? Yes I could do that, but the Dynamic Action is too good to pass up. Let us say that I want to refresh a report, set some item values and execute some PL/SQL when returning, with Dynamic Actions that is a walk in the park.

This is where I cheat :-)

The dialog does not exist when the page renders, so I have nowhere to attach my Dynamic Action to execute when returning from the dialog. My (kludgy) solution to this was to create a hidden item in the report region, and attach the Dynamic Action to the item. This way I can see all that happens in the Builder (easier to maintain), and if I give it sensible names, I can even discern what it does. In my case I created an item called P2_AFTER_MODAL, and attached a Dynamic Action called Refresh Report.

So, now I have defined what to do after returning, but how to trigger the event? And I cheat again... I attach the Dynamic Event to P2_AFTER_MODAL with the select event. The select event on a hidden item does not get triggered very easily. In effect this gives me a way to control that the Dynamic Action only executes when I programmatically tells it to. That is exactly what I tell it to when closing the dialog, using the APEX javascript API apex.event.trigger.

I also destroy the dialog after use, so I can create a new on the fly when the user wants to edit another employee, $(this).remove(); takes care of that (removing the IFRAME in effect, I could not get the dialog.destroy-method to work properly).

Closing the Dialog
When creating an APEX page that is to be a pop-up window, you need to take special care on the branching. In this case, you want to take care to close the dialog, both on cancel and after processing.

To handle cancel, let the cancel button redirect to URL, and set the URL target to: javascript:parent.$('#modalDialog').dialog('close');.

Notice the use of parent, the dialog is spawned by the original web page, and the dialog and its method belongs to the parent.

When branching after processing you have to be a bit more creative, create an unconditional branch to PL/SQL Procedure that runs the following code:
BEGIN
--closes this popup window
htp.p('<body>');
htp.p('<script type="text/javascript">');
htp.p('parent.$(''#modalDialog'').dialog(''close'');');
htp.p('</script>');
htp.p('</body>');
END;
This is in effect the same as the URL target behind the cancel button, but rendered by htp.p in PL/SQL.


Layout of the Dialog Page
In the sample application have have copied the Printer Friendly template, called it Modal Dialog, and used that as a page template for the dialog page. The modifications is basically just removing the navbar, and overriding the default "min-height" and "min-width" css attributes by explicitly setting style-attributes for the body-tag and div with id="body". This will vary, depending on the theme you are using. The goal is to strip the dialog of all unnecessary layout clutter.

Some Notes
I have created a few modal dialogs in the previous versions of APEX, and the javascripting involved is a pain. Maintaining the code even more painful. Even with the simple trick (or cheat, if you will) of attaching the Dynamic Action to an item in a declarative way, helps me getting control over the classic "what happens where, when and why".

Even though the basics is demonstrated in the sample application, there is a bit of work left. When you return from the dialog and refresh the report, you might want to take care to maintain the pagination, and if the user closes the dialog with cancel or window close, you might not want to refresh the report, etc., etc.

Enjoy :-)

Friday, July 9, 2010

APEX 4.0 Builder Hotkeys

Until recently I only developed in Oracle APEX using Mozilla Firefox. Partly because of the add-ons (like Firebug), but most of all because of Patrick Wolf's Oracle APEX Builder Plugin. It can be really annoying at times to scroll and click to apply changes.

After upgrading to APEX 4.0, the APEX Builder Plugin stopped working. I tried a quick and dirty approach, and updated the config files to look for button-elements instead of input-elements, but that did not work.

Enter Google Chrome
I've been using this browser on and off for some time, and it is incredibly fast compared to Firefox (even without add-ons, I checked). I did a bit of research (sounds better than a quick google search :-)), and found that Chrome supports Greasemonkey scripts natively. How nice! If only I had a Greasemonkey script that worked, that is...

So, I had to make my own. It's an absolute first try, so don't hold your breath. I have probably made any number of rookie mistakes, feel free to correct me :-)

Greasemonkey?
Greasemonkey is a Mozilla Firefox add-on which can run scripts after page load, and do pretty much everything with it. Greasemonkey scripts are nothing but javascript with some "special" declarations at the beginning of the file, but runs in it's own sandbox.

Google Chrome understands and runs Greasemonkey scripts as add-ons (no install of Greasemonkey necessary). Chrome was my target after all.

APEX 4.0 Builder Hotkeys
This is nothing like the more sophisticated Builder Plugin from Patrick Wolf, I have only aimed at the dearly missed hotkeys (or shortcut keys if you will).

Hotkeys implemented so far:
  • F8 - Run page
  • F9 - Apply Changes
  • Alt + F9 - Click the orange button
  • Alt + PageUp - Click button named "<" or "< Previous"
  • Alt + PageDown - Click button named ">" or "Next >"

Install
Installation instructions for both Chrome and Firefox:
  • Point browser to http://userscripts.org/scripts/show/81058
  • Click green Install-button to the right
  • Click Install in the dialog (In Chrome: Click Continue at the bottom of the page, and then Install in the dialog)

Firefox users have to install Greasemonkey before installing the script above. I have hosted the script at userscripts.org. Have a look around while you are there, the number of scripts (and the crazy things people do to web pages) is just staggering.

Uninstall
Chrome:
  • Point the browser to chrome://extensions/
  • Click Uninstall under the "Oracle APEX 4.0 Builder Hotkeys" extension.

Firefox:
  • Right click the little monkey in the lower right corner of the window
  • Choose Manage User Scripts
  • Click "Oracle APEX 4.0 Builder Hotkeys" on the left
  • Check "Also uninstall associated preferences"
  • Click Uninstall

Some Notes
I had to revert to JQuery 1.3.2 to make it play nice with Greasemonkey. I know there are ways make it work for 1.4.x, but I am not using JQuery to it's full potential (only simple selectors and click-events). I didn't even bother trying. I also had to include the whole script to make it work with Chrome (no external references).

There might be some problems with namespacing and JQuery conflicts, but I have encountered none so far.

Feel free to modify the scripts, or to make enhancement requests (hey, I might even do something about them. It could happen! Really!)

So, will I retire Firefox now? Nope, Firebug, YSlow, WebDeveloper, etc. are all good reasons not to. I am not that comfortable with the Chrome developer tool.