Thursday, July 8, 2010

View Tab : The Implementation - Part 3

So far, I've discussed how the page to be displayed in the preview thumbnail (PT) is scaled down, snipped and drawn on a canvas, which, indeed, was the trickiest part of the implementation. Well, it seems straightforward once you know what is to be done, but less than six weeks ago, we hardly knew the A,B,C of Javascript, and this seemed quite an impossible task. We learnt about the canvas element almost by chance. I'd noticed, during my initial, failed attempts, that attempting to drag a tab in Firefox creates a tiny thumbnail showing the contents of that tab (you can try that right now, if you've never noticed it). So, I decided to look into the browser's main javascript file : chrome://browser/content/browser.js, which is where I came across the canvas element for the first time. It was only after that, that we could actually start working on View Tab.

As I said at the end of the last post, we are done with creating a preview canvas, and all that's left to do is make sure it shows up at the right place. For this, first we create a popup element.

//Create a Tooltip to show the canvas
    prevTooltip = document.createElement("popup");
    prevTooltip.id="viewTabTooltip";
    prevTooltip.position="after_pointer";
    prevTooltip.appendChild(prevCanvas);
    prevTooltip.setAttribute("style","margin:21px 0px;
        -moz-box-shadow:10px 10px 2px black;
        text-rendering:geometricPrecision !important;");

The popup element is, as the name suggests, a popup, which is not shown in the document, and has to be attached to another element in one of three ways, to cause it to show on clicking, right-clicking and hovering the mouse over the element. More on popups here.

The popup, called prevTooltip, is given the id "viewTabTooltip" and its position is set to "after_pointer", which ensures it shows up below the mouse pointer, when it is invoked. Then, the canvas, containing the preview is appended to prevTooltip, and the popup is styled.

Finally, the popup is appended to the browser's mainPopupSet, and attached to aTab using it's tooltip attribute.

//Attach the tooltip to aTab
    document.getElementById("mainPopupSet")
            .appendChild(prevTooltip);
    aTab.setAttribute("tooltip","viewTabTooltip");
    }
}

With this the function viewTab.createPreview() is complete, and will do the job of showing the PT if called when the mouse pointer is moved over a tab.

Next, the function to remove the popup is defined.

/* Function to End Preview */
viewTab.endPreview = function(aTab,firsttab){
    if(firsttab) aTab=gBrowser.tabContainer.
       getElementsByClassName("tabbrowser-tab")[0];

The first line of code in the function, again, as I mentioned in the first post, is included to avoid a specific error.

if(document.getElementById("viewTabTooltip")){
   document.getElementById("mainPopupSet").
     removeChild(document.getElementById
                          ("viewTabTooltip"));
   aTab.removeAttribute("tooltip");
}}

If the document contains an element with the id "viewTabTooltip", which is the popup that contains the preview canvas, then it is removed, and the tooltip attribute of aTab is also removed. This completes viewTab.endPreview().

Next, a function to set the onmouseover and onmouseout attributes of tabs, so that the functions defined earlier are invoked when they are needed, is defined.

/* Function to set the onmouseover property of the tabs*/
viewTab.init = function (){
  tabs = gBrowser.tabContainer.
getElementsByClassName("tabbrowser-tab");
for(var i=0;i
    if (i!=0){
    tabs[i].setAttribute("onmouseover",
        "viewTab.createPreview(tabs["+i+"]);");
    tabs[i].setAttribute("onmouseout",
        "viewTab.endPreview(tabs["+i+"]);");
    }
    else
    {
        tabs[0].setAttribute("onmouseover",
        "if(tabs[0] !=gBrowser.selectedTab) 
         viewTab.createPreview(tabs[0],true);");
    tabs[0].setAttribute("onmouseout",
        "viewTab.endPreview(tabs[0],true);");
    }
}
}
The function is pretty straightforward, an array of tabs is created and their respective onmouseover and onmouseout attributes are set to invoke viewTab.createPreview() and viewTab.endPreview(). The portion under else is required because, as mentioned earlier, the first tab causes some trouble, and needs to be dealt with separately.

Finally, event handlers are added to the gBrowser, the primary tabbrowser element, to invoke viewTab.init() whenever any change is made to the tabs.

//Add the event handlers to listen for new tabs
gBrowser.addEventListener
         ("load",viewTab.init,false);
gBrowser.addEventListener
         ("TabSelect",viewTab.init,false);
gBrowser.addEventListener
         ("TabClose",viewTab.init,false);
gBrowser.addEventListener
         ("TabMove",viewTab.init,false);

I'd initially assumed it would be enough to listen just for the "TabOpen" event (which I ultimately removed!), which is supposedly fired whenever a new tab is opened. But apparently it wasn't enough, so I tried listening for just the "load" event, (which is fired in more places than "TabOpen"), but it wasn't enough either,and so, after a experimenting for a while with possible ways of manipulating tabs, I was finally able to come up with this almost exhaustive list of events to listen for, so that the PT's always show up. Including "TabSelect" has an added advantage: even if something's gone wrong and PT's aren't showing up, selecting a different tab invokes viewTab.init and that puts things back in place.

With that, the implementation of View Tab is complete! Hope it was useful.

No comments:

Post a Comment