www.pudn.com > addons.rar > joeyOverlay.js


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Joey Mozilla Project.
 *
 * The Initial Developer of the Original Code is
 * Doug Turner .
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Marcio Galli 
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

var g_joey_data;
var g_joey_content_type;
var g_joey_title;
var g_joey_url;
var g_joey_isfile;
var g_joey_media_url  = null;
var g_joey_media_type = null;
var g_joey_areaWindow = null;

var g_joey_gBrowser = null;                // presents the main browser, used by the joey_feed code.
var g_joey_browserStatusHandler = null;    // to track onloction changes in the above browser ( tab browser ) element.
var g_joey_statusUpdateObject = null;      // the proxy object to deal with UI 

var g_joey_bundleElement = null;

var g_joey_mediaContentTypes = ['flv','mov','wmv','avi','mpeg','mp3','wav']; 

function joeyString(ref) {

     return g_joey_bundleElement.getString(ref);

}

/* 
 * Event listeners associated to the joeyOverlay app 
 */

window.addEventListener("load", joeyStartup, false);

var gImageSource;

function joey_listener() {}

joey_listener.prototype =
{
    onProgressChange: function (current, total)
    {
        g_joey_statusUpdateObject.tellStatus("upload", current, total);
    },

    onStatusChange: function (action, status)
    {
        if (action == "login")
        {
            if (status == 0)
            {
                g_joey_statusUpdateObject.loginStatus("login","completed");
            }
            else if (status == -1 )
            {
                g_joey_statusUpdateObject.loginStatus("login","failed");


                var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                        .getService(Components.interfaces.nsIPromptService);

                var result = prompts.confirm(null, joeyString("loginFailedShort"), joeyString("loginFailedQuestion"));
                if (result == true)
                {
                    // Clear the username and password and try again.
                    clearLoginData();
                    setTimeout(uploadDataFromGlobals, 500); // give enough time for us to leave the busy check
                }
            }

            return;
        }

        if (action == "upload")
        {
            if (status == 1) {
                g_joey_statusUpdateObject.tellStatus("upload",null,null,"completed");
            } 
            else {            
                g_joey_statusUpdateObject.tellStatus("upload",null,null,"failed"); 

                var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                        .getService(Components.interfaces.nsIPromptService);
                prompts.alert(null, joeyString("uploadFailed"), joeyString("uploadFailed"));
            }
            return;
        }
    },

    QueryInterface: function (iid)
    {
        if (iid.equals(Components.interfaces.mocoJoeyListener) ||
            iid.equals(Components.interfaces.nsISupports))
            return this;
        
        Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
        return null;
    },
};

function uploadDataFromGlobals()
{

    // marcio

    try {
                     
        var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
                                .getService(Components.interfaces.nsIPromptService);
        
        var titleObject = {value: g_joey_title}; // default the username to user


        var psvc = Components.classes["@mozilla.org/preferences-service;1"]
                         .getService(Components.interfaces.nsIPrefBranch);

        var askState = psvc.getBoolPref("joey.askForTitle");
     
        if(askState == true ) {
        
            var check = {value: askState};  // default the checkbox to true
            
            var result = prompts.prompt(null, joeyString("promptTitle.windowTitle"), 
                                                           joeyString("promptTitle.label"),
                                                           titleObject,
                                                           joeyString("promptTitle.prefQuestion"), 
                                                           check);
            
            if(result) {
                g_joey_title = titleObject.value;
            }
            
            
            psvc.setBoolPref("joey.askForTitle",check.value);       
            
            
        } 
                              
    } catch (i) { joeyDumpToConsole(i) }


    var joey = Components.classes["@mozilla.com/joey;1"]
                         .createInstance(Components.interfaces.mocoJoey);
    
    joey.setListener(new joey_listener());
    
	if (g_joey_isfile)
	{
		joey.uploadFile(g_joey_title,
                        g_joey_url,
                        g_joey_file,
                        g_joey_content_type);
	}
	else
	{
	    joey.uploadData(g_joey_title,
                        g_joey_url,
                        g_joey_data,
                        g_joey_content_type);
	}
}


function joeyOnMouseDown(e)
{
	if (e.which == 3) 
	{

		var target = e.target;
		var classname = e.target.toString();

		if (classname.match(/ImageElement/))
        {
			// Simpler, but probably less efficient syntax: target.src;
			var hie = target.QueryInterface(Components.interfaces.nsIDOMHTMLImageElement);
			if (hie != null)
				// show menu item:
				setImageSource(hie);
			else
				setImageSource(null);
		} 
        else
        {
            setImageSource(null);
        }
        
        var selectedRange=g_joey_gBrowser.selectedBrowser.contentDocument.getSelection();
        if( selectedRange && selectedRange.toString() ) {
           document.getElementById("joey_selectedText").hidden=false;
        } else {
          document.getElementById("joey_selectedText").hidden=true;
        }

    }
}

function setImageSource(imageElement)
{
	if (imageElement != null)
		gImageSource = imageElement.src;
	else
		gImageSource = null;
    
    try 
    {    
    	var menuItem = document.getElementById('joey_selectedImage');
	    menuItem.setAttribute("hidden", gImageSource == null ? "true" : "false");
    } 
    catch (e) {}
}

function replaceAll( str, from, to )
{
    // regular expression faster?
    
    var idx = str.indexOf( from );
    
    while ( idx > -1 ) {
        str = str.replace( from, to );
        idx = str.indexOf( from );
    }
    
    return str;
}

function joey_launchCloudSite() 
{
	g_joey_gBrowser.loadURI(getJoeyServerURL()+"/uploads");
}

function joey_selectedText() 
{
    var focusedWindow = document.commandDispatcher.focusedWindow;
    var selection = focusedWindow.getSelection().toString();
    
    selection = replaceAll(selection, "\t", "\r\n");
    
    var file = Components.classes["@mozilla.org/file/directory_service;1"]
                         .getService(Components.interfaces.nsIProperties)
                         .get("TmpD", Components.interfaces.nsIFile);
    file.append("joey-selected-text.tmp");
    file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);

    // file is nsIFile, data is a string
    var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                             .createInstance(Components.interfaces.nsIFileOutputStream);

    // use 0x02 | 0x10 to open file for appending.
    foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
    foStream.write(selection, selection.length);
    foStream.close();

    g_joey_file = file;
    g_joey_isfile = true;
    g_joey_content_type = "text/plain";
    g_joey_title = focusedWindow.document.title;
    g_joey_url  = focusedWindow.location.href;

    uploadDataFromGlobals(false);
}


function joey_selected()
{
	if (gImageSource)
		return joey_selectedImage();
    
    var focusedWindow = document.commandDispatcher.focusedWindow;
    var selection = focusedWindow.getSelection().toString();
    
	if (selection != null || selection != "")
		return joey_selectedText();
    
	joey_selectedArea();
}

function joey_feed()
{
    /* We can detect which type of feed and send additional information 
     * as well, such as the title. On the other hand, the feed title 
     * may change, so that could be something chechec ( and refreshed ) 
     * on the server 
     */
    
    var feedLocation = g_joey_gBrowser.mCurrentBrowser.feeds[0].href;
    
    g_joey_data = feedLocation;
    g_joey_isfile = false;
    g_joey_content_type = "rss-source/text";
    g_joey_title = "Feed / We can put a title in it with one more client call. ";
    g_joey_url  = feedLocation;
    uploadDataFromGlobals(false);
}

// Check XUL statusbar item
function joey_launchPopup() 
{

  joeySetCurrentFeed();
  document.getElementById('joeyStatusPopup').showPopup(document.getElementById('joeyStatusButton'),-1,-1,'popup','topright', 'bottomright')



}

/* FIXME to be as an instance */

function getMediaCallback(content_type, file)
{
	if (length>0)
    { 
        joeyDumpToConsole("Download successful... (" + content_type + ")");
        
        g_joey_statusUpdateObject.tellStatus("download",null,null,"completed");
        
        g_joey_content_type = content_type;
        g_joey_file = file;
        uploadDataFromGlobals(false);
        return;
	}
    else {
    
        /* This should become failed? */
        
        g_joey_statusUpdateObject.tellStatus("download",null,null,"failed");
        joeyDumpToConsole("Problem downloading media to joey!\n");
    }
}


function JoeyStatusUpdateClass() {

  /* We have now the XUL stack with elements in it. 
   * A background Layer and the top layer for 
   * the label. 
   */
 
  this.progressElement   = document.getElementById("joeyProgressLayer");
  this.progressBoxObject = document.getBoxObjectFor(document.getElementById("joeyStatusTeller"));  

}

/* 
 * UI Wrapper / Deals with the UI 
 * -------
 * TODO: Need to work with multiple instances
 */
JoeyStatusUpdateClass.prototype = 
{
	/* 
 	 * We have to separate the login information from the 
     * loading status processes 
     */
	
    loginStatus: function (aMode,aAdVerb)
    {
	},

    tellStatus:function(verb,from,to,adverb) 
    {
        var value; 
        var percentage = parseInt((from/to)*parseInt(this.progressBoxObject.width));

        // account for roundoff error that we have been seeing.
        if (percentage > 100)
            percentage = 100;
        
        if (verb == "upload") 
        {
            // value = "Uploading... ("+from+"/"+to+")";
            value = joeyString("uploading") + "(" + percentage + "%)";
        }
        else
        {
            if (from==to)
            {
                // this might not be entirely true... basically, at ths point we are waiting to upload...
                value = joeyString("loggingin"); 
            }
            else 
            {
                // value = "Downloading... ("+from+"/"+to+")";
                value = joeyString("downloading") + "("+percentage+"%)";  
            }
        }   

        if(verb =="upload") {
              this.progressElement.width=percentage;
        } else {
              if(verb =="download") {
                percentage = this.progressBoxObject.width - percentage;
                this.progressElement.width=percentage;
              } 
        } 
        document.getElementById("joeyStatusTeller").value=value;
        
        /* adverb */
       
        if(adverb=="completed") {
            this.progressElement.width=0;
            if(verb=="download") {
                 document.getElementById("joeyStatusTeller").value=joeyString("downloadCompleted");
            } 
            if(verb=="upload") {
                 document.getElementById("joeyStatusTeller").value=joeyString("uploadCompleted");
            } 
            /* Dougt timer status cleanup */
            setTimeout("document.getElementById('joeyStatusTeller').value=''", 600);
        }

        if(adverb == "failed") {
            if(verb=="download") {
                 document.getElementById("joeyStatusTeller").value=joeyString("downloadFailed");
            } 
            if(verb=="upload") {
                 document.getElementById("joeyStatusTeller").value=joeyString("uploadFailed");
            } 
            /* Dougt timer status cleanup */
            setTimeout("document.getElementById('joeyStatusTeller').value=''", 600);
        }
                  
    }
    
}

/* 
 * This is nice for the Download + Progress functional
 */
 
function JoeyMediaFetcherStreamListener(aCallbackFunc)
{
  this.mCallbackFunc = aCallbackFunc;
}

JoeyMediaFetcherStreamListener.prototype = 
{
  mStream: null,
  mContentType : null,
  
  // nsIStreamListener
  onStartRequest: function (aRequest, aContext) 
  {
      this.file = Components.classes["@mozilla.org/file/directory_service;1"]
                            .getService(Components.interfaces.nsIProperties)
                            .get("TmpD", Components.interfaces.nsIFile);

      this.file.append("joeymedia.tmp");

      this.file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);

      var fos = Components.classes["@mozilla.org/network/file-output-stream;1"]
                          .createInstance(Components.interfaces.nsIFileOutputStream);

      fos.init(this.file, 0x02 | 0x08 | 0x20, 0644, 0);
      
      this.mstream = Components.classes["@mozilla.org/binaryoutputstream;1"].createInstance(Components.interfaces.nsIBinaryOutputStream);
      this.mstream.setOutputStream(fos);

      try
      {
          var http = aRequest.QueryInterface(Components.interfaces.nsIHttpChannel);
          this.mContentType = http.contentType;
      } 
      catch (ex) { joeyDumpToConsole(ex); }	
  },

  onDataAvailable: function (aRequest, aContext, aStream, aSourceOffset, aLength)
  {
      var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
                          .createInstance(Components.interfaces.nsIBinaryInputStream);
      bis.setInputStream(aStream);
      
      var n=0;
      while(n=0;i--){revstr+=str.charAt(i)}
   return revstr;
 }


var httpscanner = {
  observe: function(subject,topic,data){
        var response=subject.QueryInterface(Components.interfaces.nsIHttpChannel);
	    var contentType=response.getResponseHeader('Content-Type');         

        function testContentType(types){
                var isMediaFile = false;
                for(var i=types.length;i>=0;i--){
					if(contentType.indexOf(types[i])>-1 || mediaLocation.indexOf('.'+types[i])>-1) isMediaFile = true;
                }
                return isMediaFile;
        }

        if(contentType.indexOf('video')>-1 || contentType.indexOf('audio')>-1 || contentType.indexOf('octet')>-1){
                var mediaLocation = subject.QueryInterface(Components.interfaces.nsIChannel).URI;
                        mediaLocation=mediaLocation.prePath+mediaLocation.path;

                if(testContentType(g_joey_mediaContentTypes)){
                        joeyDumpToConsole("media content found: "+ mediaLocation);
                        document.getElementById("joeyMediaMenuItem").setAttribute("hidden","false");
                        g_joey_media_type = contentType;
                        g_joey_media_url = mediaLocation;
                }
        }
	}
}
var observerService = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
	observerService.addObserver(httpscanner,"http-on-examine-response",false);

function joey_uploadFoundMedia() // refactor with joey_selectedImage
{
    var focusedWindow = document.commandDispatcher.focusedWindow;
    
    g_joey_title = focusedWindow.document.title;
    g_joey_url = focusedWindow.location.href;
    g_joey_isfile = true;
    g_joey_content_type = g_joey_media_type;

    // g_joey_data, g_joey_content_type
    // will be filled in when we have the media data.
    
    // the IO service
	var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                              .getService(Components.interfaces.nsIIOService);

    // create an nsIURI
    var uri = ioService.newURI(g_joey_media_url, null, null);
	
	// get an listener
	var listener = new JoeyMediaFetcherStreamListener(getMediaCallback);
    
    // get a channel for that nsIURI
    var channel = ioService.newChannelFromURI(uri);
    channel.notificationCallbacks = listener;
	channel.asyncOpen(listener, null);
}

/* 
 * This refreshes feed information in the menu. 
 * The currentBrowser.feeds array is populated from the 
 * joeyLinkAddedHandler. 
 */

function joeySetCurrentFeed()
{
	try
    {
		var currentFeedURI = g_joey_gBrowser.mCurrentBrowser.feeds;
        
		if(currentFeedURI ||currentFeedURI.length>0) 
        {
			document.getElementById("joey_activeRSSLink").setAttribute("hidden","false");
		} 
	} 
    catch(i)
    {
		document.getElementById("joey_activeRSSLink").setAttribute("hidden","true");
	} 
}

function joeyStartup()
{
    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                      .getService(Components.interfaces.nsIWindowMediator);
    
    gWin = wm.getMostRecentWindow("navigator:browser");
    
    g_joey_gBrowser = gWin.gBrowser;
    
    g_joey_browserStatusHandler = new joeyBrowserStatusHandler();

    g_joey_gBrowser.addProgressListener( g_joey_browserStatusHandler , Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);


    var psvc = Components.classes["@mozilla.org/preferences-service;1"]
                         .getService(Components.interfaces.nsIPrefBranch);

    g_joey_statusUpdateObject = new JoeyStatusUpdateClass();

    /* 
     * Joey Event Listeners 
     */

    window.addEventListener("mousedown",joeyOnMouseDown,false); 

    /* 
     * First Run function..
     */ 
    try {
       if(psvc.getBoolPref("joey.firstRun")) {
            psvc.setBoolPref("joey.firstRun",false);     
            joeyLaunchPreferences();        
       }
    } catch(i) { joeyDumpToConsole(i) } 

    /* 
     *  Bundle object for local string access
     */
    
    g_joey_bundleElement = document.getElementById("joey_properties");
    // We can use now joeyString to get strings...     

}


function joeyDumpToConsole(aMessage) {
    try {   
        var psvc = Components.classes["@mozilla.org/preferences-service;1"]
                             .getService(Components.interfaces.nsIPrefBranch);

        if(psvc.getBoolPref("joey.enable_logging")) {

            var cs = Components.classes["@mozilla.org/consoleservice;1"]
                               .getService(Components.interfaces.nsIConsoleService);

            cs.logStringMessage("joey: " + aMessage);
        }
    } 
    catch (i) {}
}

/* 
 * Prefs launcher 
 */
function joeyLaunchPreferences() {
        window.open("chrome://joey/content/joeyOptions.xul",
                    "welcome", 
                    "chrome,resizable=yes");
}

function joeyBrowserStatusHandler() {}

joeyBrowserStatusHandler.prototype = 
{

    QueryInterface : function(aIID)
    {
        if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
            aIID.equals(Components.interfaces.nsIXULBrowserWindow) ||
            aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
            aIID.equals(Components.interfaces.nsISupports))
        {
            return this;
        }
        throw Components.results.NS_NOINTERFACE;
    },
    
    init : function()
    {
    },
    
    destroy : function()
    {
    },
    
    onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
    {
         /* via state changes we notify our local startDocumentLoad and endDocumentLoad
         * Joey uses endDocumentLoad to do a little sniffing in the doc, check feeds, 
         * check media items, and possibly more..
         */
         
        const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
        const nsIChannel = Components.interfaces.nsIChannel;
        if (aStateFlags & nsIWebProgressListener.STATE_START) {
            if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
                aRequest && aWebProgress.DOMWindow == content)
              this.startDocumentLoad(aRequest);
              
        }
        else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
          if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
            if (aWebProgress.DOMWindow == content) {
              if (aRequest)
                this.endDocumentLoad(aRequest, aStatus);
            }
          }
        }
    },

    onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress)
    {
    },

    onLocationChange : function(aWebProgress, aRequest, aLocation)
    {

    },
    
    onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage)
    {
    },
    
    startDocumentLoad : function(aRequest)
    {
    },
    
    endDocumentLoad : function(aRequest, aStatus)
    {
        setTimeout(g_joeySelectorService.disable, 0);
        setTimeout(g_joeyFeedwatcher, 0);
    },

    onSecurityChange : function(aWebProgress, aRequest, aState)
    {
    },

    setJSStatus : function(status)
    {
    },
    
    setJSDefaultStatus : function(status)
    {
    },

    setDefaultStatus : function(status)
    {
    },

    setOverLink : function(link, b)
    {
    }
}


function g_joeyFeedwatcher()
{
    // we need to tie into a way to purge via clear private data!

    var feedWatchLimit = 10;
    try {
        feedWatchLimit = psvc.getIntPref("joey.rss-watch-limit");
    } catch(a) {}


    try {
        
        // this will change with Places.

        var feedLocation = g_joey_gBrowser.mCurrentBrowser.feeds[0].href;
        
        // make into a pref name.  (slashes have to be removed)
        feedLocation = feedLocation.replace(/\//g, "s");
            
        var psvc = Components.classes["@mozilla.org/preferences-service;1"]
                             .getService(Components.interfaces.nsIPrefBranch);

        var seen = 0;
        try {
            seen = psvc.getIntPref("joey.rss." + feedLocation + ".seen");
        } catch(a) {}

        seen++;
        
        if (seen > feedWatchLimit)
        {
            joey_feed();
            seen = 0;
        }

        psvc.setIntPref("joey.rss." + feedLocation + ".seen", seen);

    }
    catch(ignore) {}
}


/* 
 * Joey Element Selection Service Singleton
 * ----------------------------------------
 * This implementation is to be moved outside
 * this JS file. This uses the /contrib/uSummaryGenerator.js
 * This is the UI contextual function that allows and end-user 
 * to pick an area and then the uSummaryGenerator service is 
 * called so that we can wrap the generator XML info and  
 * upload it to Joey!. 
 */

var sigmaCall = null;
var omegaCall = null;
var deltaCall = null;

var g_joeySelectorService = {

    currentEvent         :null,
    previousTargetElement:null,
    associatedDocument   :null,
    enabled              :false,
    currentElementLeft   :null,
    currentElementTop    :null,
    currentElementRight  :null,
    currentElementBottom :null,
    timer                :null,
    
    enable: function () {

        joeyDumpToConsole("g_joeySelectorService enable");

        if (this.enabled == true)
            this.disable();

        this.enabled = true;

        this.associatedDocument = g_joey_gBrowser.selectedBrowser.contentDocument;

        this.createBox();
        
        var thisInstance = this;
        
        sigmaCall = function(e) { thisInstance.mouseMoveListener(e) };
        
    	g_joey_gBrowser.selectedBrowser
    	               .contentDocument
    	               .addEventListener("mousemove"
    	                                 ,sigmaCall
    	                                 ,false);

        omegaCall = function(e) { thisInstance.mouseClickListener(e) };
        
    	g_joey_gBrowser.selectedBrowser
    	               .contentDocument
    	               .addEventListener("mousedown"
    	                                 ,omegaCall 
    	                                 ,false);
    

        deltaCall = function(e) { thisInstance.keyDownListener(e) };
        
    	g_joey_gBrowser.selectedBrowser
    	               .contentDocument
    	               .addEventListener("keydown"
    	                                 ,deltaCall 
    	                                 ,false);

	    this.runtimer();    // timer-based refresh function..

	    
    },
    
    disable: function () {

        if (this.enabled == false)
            return;

        joeyDumpToConsole("g_joeySelectorService disable");
    
        clearTimeout(this.timer);
        this.timer = null;

        if (sigmaCall !=null)
            g_joey_gBrowser.selectedBrowser
         	               .contentDocument
    	                   .removeEventListener("mousemove"
                                                ,sigmaCall
                                                ,false);        
        if (omegaCall != null)
            g_joey_gBrowser.selectedBrowser
         	               .contentDocument
    	                   .removeEventListener("mousedown"
                                                ,omegaCall 
                                                ,false);

        if (deltaCall != null)
            g_joey_gBrowser.selectedBrowser
        	               .contentDocument
        	               .removeEventListener("keydown"
                                                ,deltaCall 
                                                ,false);

        this.removeBox();
        this.associatedDocument    = null;   
        this.currentEvent          = null;
        this.previousTargetElement = null;
           
        this.enabled = false;

    },

    mouseMoveListener: function (e) {
		if (this.previousTargetElement != e.target) {
			this.currentEvent = e;
			this.previousTargetElement = e.target;
		}                                 
    },

    mouseClickListener: function (e) {
    
    try {

        if(e.button == 0) {
            /* 
             * We may revisit this to elect target elements 
             * if they make sense. For example I assume we dont want to elect 
             * the hole page. .. or not :) 
             */
             
	        joey_selectedTarget(this.currentEvent.target);
            e.preventDefault(); // eat the event
        }            

	} catch (e) {}

	    this.disable();
    },

    keyDownListener: function (e) {
        this.disable();
        e.preventDefault();
    },

    createBox: function () {

                var newDiv= this.associatedDocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
                newDiv.style.position="absolute";
                newDiv.style.zIndex="1000";
                newDiv.style.background="url(chrome://joey/skin/selector-tile.png)";
                newDiv.style.border="0px";
                newDiv.style.height="4px";

                this.currentElementTop=newDiv;
                                   	
                var newDiv= this.associatedDocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
                newDiv.style.position="absolute";
                newDiv.style.zIndex="1000";
                newDiv.style.background="url(chrome://joey/skin/selector-tile.png)";
                newDiv.style.left="0px";          	
                newDiv.style.border="0px";          	
                newDiv.style.height="4px";

                this.currentElementBottom=newDiv;
                this.currentElementTop.appendChild(this.currentElementBottom);
    	
                var newDiv= this.associatedDocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
                newDiv.style.position="absolute";
                newDiv.style.zIndex="1000";
                newDiv.style.top="0px";
                newDiv.style.left="0px";
                newDiv.style.width="4px";
                newDiv.style.background="url(chrome://joey/skin/selector-tile.png)";
                newDiv.style.border="0px";
                
                this.currentElementLeft=newDiv;
                this.currentElementTop.appendChild(this.currentElementLeft);
    	
                var newDiv = this.associatedDocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
                newDiv.style.position="absolute";
                newDiv.style.zIndex="1000";
                newDiv.style.top="0px";
                newDiv.style.width="4px";
                newDiv.style.background="url(chrome://joey/skin/selector-tile.png)";
                newDiv.style.border="0px";
                
                this.currentElementRight=newDiv;
                this.currentElementTop.appendChild(this.currentElementRight);
                
                try {
                    this.associatedDocument.body.appendChild(this.currentElementTop);
                } catch (ignore) {
                    joeyDumpToConsole(ignore);
                }
                
            
    }, 

    removeBox: function () {
    
        try {
        
            if(this.currentElementTop.parentNode) {
                this.currentElementTop.parentNode.removeChild(this.currentElementTop);
        	}        	 
        	
        } catch (i) { joeyDumpToConsole(i) } 
         	        	
    },

    runtimer: function() {

        try {

            /* 
             * We want UI to be smooth so we keep this at 150 miliseconds. 
             * Otherwise the Contextual Box moves too much in the screen for every little DOM node.
             *
             */ 
            if (this.currentEvent && this.associatedDocument) {
                
                var currentDocument = this.associatedDocument;
                
                var boxObject = currentDocument.getBoxObjectFor(this.currentEvent.target);
                
                const borderSize=4;
                
                var boxObjectX = boxObject.x - borderSize;
                var boxObjectY = boxObject.y - borderSize;
                var rawWidth = boxObject.width;
                var rawHeight = boxObject.height;
                
                var restWidth = rawWidth % 4;
                var restHeight = rawHeight % 4;
                
                var boxCounterWidth = (rawWidth - restWidth)/4 + 1; 
                var boxCounterHeight = (rawHeight- restHeight)/4 + 1; 
                
                var boxObjectWidth  = ( rawWidth - restWidth )  + ( borderSize * 2 );
                var boxObjectHeight = ( rawHeight - restHeight )  + ( borderSize * 2 ) ;
                
                var modOddWidth = boxCounterWidth % 2;
                var modOddHeight = boxCounterHeight % 2;            
            
                if( parseInt(modOddWidth) == 0) {
                    boxObjectWidth+=4;
                    boxObjectX-=4;
                }
            
                if( parseInt(modOddHeight) == 0) {
                    boxObjectHeight+=4;
                    boxObjectY-=4;
                }
                
                this.currentElementTop.style.top=boxObjectY+"px";
                this.currentElementTop.style.left=boxObjectX+"px";
                this.currentElementTop.style.width=boxObjectWidth+"px";
                this.currentElementBottom.style.top=boxObjectHeight+"px";
                this.currentElementBottom.style.width=boxObjectWidth+4+"px";
                this.currentElementLeft.style.height=boxObjectHeight+"px";
                this.currentElementRight.style.left=boxObjectWidth+"px";
                this.currentElementRight.style.height=boxObjectHeight+4+"px";
                
            } // end of current event...
            
            if (this.associatedDocument) {
                this.timer = setTimeout("g_joeySelectorService.runtimer()",122);
            }	
        }
        catch(e)
        {
            // if any of this fails, just kill it.
            setTimeout(g_joeySelectorService.disable, 0);
        }
    } // end of runtimer  
    
}


/**
 * Determine whether a node's text content is entirely whitespace.
 *
 * @param nod  A node implementing the |CharacterData| interface (i.e.,
 *             a |Text|, |Comment|, or |CDATASection| node
 * @return     True if all of the text content of |nod| is whitespace,
 *             otherwise false.
 */
function is_all_ws( nod )
{
  // Use ECMA-262 Edition 3 String and RegExp features
  return !(/[^\t\n\r ]/.test(nod.data));
}


/**
 * Determine if a node should be ignored by the iterator functions.
 *
 * @param nod  An object implementing the DOM1 |Node| interface.
 * @return     true if the node is:
 *                1) A |Text| node that is all whitespace
 *                2) A |Comment| node
 *             and otherwise false.
 */

function is_ignorable( nod )
{
  return ( nod.nodeType == 8) || // A comment node
         ( (nod.nodeType == 3) && is_all_ws(nod) ); // a text node, all ws
}

/**
 * Version of |previousSibling| that skips nodes that are entirely
 * whitespace or comments.  (Normally |previousSibling| is a property
 * of all DOM nodes that gives the sibling node, the node that is
 * a child of the same parent, that occurs immediately before the
 * reference node.)
 *
 * @param sib  The reference node.
 * @return     Either:
 *               1) The closest previous sibling to |sib| that is not
 *                  ignorable according to |is_ignorable|, or
 *               2) null if no such node exists.
 */
function node_before( sib )
{
  while ((sib = sib.previousSibling)) {
    if (!is_ignorable(sib)) return sib;
  }
  return null;
}

function joey_buildXPath(targetElement)
{
    if (targetElement == null)
        return null;

    var buffer = "";
    var cur = targetElement;

    do {

        var name = "";
        var sep = "/";
        var occur = 0;
        var ignore = false;
        var type = targetElement.nodeType; 

        //        alert(buffer);

        if (type == Node.DOCUMENT_NODE)
        {
            buffer = "/" + buffer;
            break;
        }
        else if (type == Node.ATTRIBUTE_NODE)
        {
            sep = "@";
            name = cur.nodeName;
            next = cur.parentNode;
        }
        else
        {
            if (type == Node.ELEMENT_NODE) {
                if (cur.nodeName.toLowerCase() == "a"      || cur.nodeName.toLowerCase() == "img" ||
                    cur.nodeName.toLowerCase() == "ul"     || cur.nodeName.toLowerCase() == "document" ||
                    cur.nodeName.toLowerCase() == "document" ||
                    cur.nodeName.toLowerCase() == "font"   || cur.nodeName.toLowerCase() == "#document" )
                    ignore = true;

                var id = null;

                try {// why would this throw?
                    id = cur.getAttribute('id');
                } catch (e) {}

                if (id != null) {
                    
                    if (buffer == "")
                    {
                        buffer = "id('"+id+"')";
                        return buffer;
                    }

                    buffer = "id('" + id + "')" + buffer;
                    return buffer;
                }
            }

            name = cur.nodeName.toLowerCase();
            next = cur.parentNode;

            // now figure out the index
            var tmp = node_before(cur);
            while (tmp != null) {
                if (name == tmp.nodeName.toLowerCase()) {
                    occur++;
                }
                tmp = node_before(tmp);
            }
            occur++;
            
            if (type != Node.ELEMENT_NODE) {

                // fix the names for those nodes where xpath query and dom node name don't match
                if (type == Node.COMMENT_NODE) {
                    ignore = true;
                    name = 'comment()';
                }
                else if (type == Node.PI_NODE) {
                    ignore = true;
                    name = 'processing-instruction()';
                }
                else if (type == Node.TEXT_NODE) {
                    ignore = true;
                    name = 'text()';
                }
                // anything left here has not been coded yet (cdata is broken)
                else {
                    name = '';
                    sep = '';
                    occur = 0;
                }
            }
        }

        if (cur.nodeName.toLowerCase() == "html" ||
            cur.nodeName.toLowerCase() == "body" )
            occur = 0;

        if (ignore == true) {
        }
        else if (occur == 0) {
            buffer = sep + name + buffer;
        }
        else {
            buffer = sep + name + '[' + occur + ']' + buffer;
        }
        ignore = false;
        
        cur = next;
        
    } while (cur != null);

    return buffer;
}

function toXMLString(str) {
    return str.replace(/&/g, "&").replace(/>/g, ">").replace(/\n'
		+ ' \n'
		+ '   ' 
		+ toXMLString(focusedWindow.location.href)
		+ '\n';

    var hint = toXMLString(targetElement.innerHTML);
    hint ="";

    str += ' \n'
		+ ' \n'
        + '' + hint + ''
		+ '\n';

    //    alert(str);

    g_joey_data = str;

    uploadDataFromGlobals(false);
}

/* 
 * 
 */
function joey_enableSelection() {

    g_joeySelectorService.enable();

}