home *** CD-ROM | disk | FTP | other *** search
/ Revista do CD-ROM 97 / CD-ROM 97 / CD-ROM 97.iso / internet / ghostzilla / ghsetup.exe / chrome / comm.jar / content / communicator / nsContextMenu.js < prev    next >
Encoding:
JavaScript  |  2002-06-13  |  35.4 KB  |  829 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public
  4.  * License Version 1.1 (the "License"); you may not use this file
  5.  * except in compliance with the License. You may obtain a copy of
  6.  * the License at http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS
  9.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10.  * implied. See the License for the specific language governing
  11.  * rights and limitations under the License.
  12.  *
  13.  * The Original Code is Mozilla Communicator client code,
  14.  * released March 31, 1998.
  15.  *
  16.  * The Initial Developer of the Original Code is Netscape Communications
  17.  * Corporation.  Portions created by Netscape are
  18.  * Copyright (C) 1998 Netscape Communications Corporation. All
  19.  * Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *     William A. ("PowerGUI") Law <law@netscape.com>
  23.  *     Blake Ross <blakeross@telocity.com>
  24.  *     Gervase Markham <gerv@gerv.net>
  25.  */
  26.  
  27. /*------------------------------ nsContextMenu ---------------------------------
  28. |   This JavaScript "class" is used to implement the browser's content-area    |
  29. |   context menu.                                                              |
  30. |                                                                              |
  31. |   For usage, see references to this class in navigator.xul.                  |
  32. |                                                                              |
  33. |   Currently, this code is relatively useless for any other purpose.  In the  |
  34. |   longer term, this code will be restructured to make it more reusable.      |
  35. ------------------------------------------------------------------------------*/
  36. function nsContextMenu( xulMenu ) {
  37.     this.target         = null;
  38.     this.menu           = null;
  39.     this.onTextInput    = false;
  40.     this.onImage        = false;
  41.     this.onLink         = false;
  42.     this.onMailtoLink   = false;
  43.     this.onSaveableLink = false;
  44.     this.link           = false;
  45.     this.inFrame        = false;
  46.     this.hasBGImage     = false;
  47.     this.isTextSelected = false;
  48.     this.inDirList      = false;
  49.     this.shouldDisplay  = true;
  50.  
  51.     // Initialize new menu.
  52.     this.initMenu( xulMenu );
  53. }
  54.  
  55. // Prototype for nsContextMenu "class."
  56. nsContextMenu.prototype = {
  57.     // onDestroy is a no-op at this point.
  58.     onDestroy : function () {
  59.     },
  60.     // Initialize context menu.
  61.     initMenu : function ( popup ) {
  62.         // Save menu.
  63.         this.menu = popup;
  64.  
  65.         // Get contextual info.
  66.         this.setTarget( document.popupNode );
  67.  
  68.         this.isTextSelected = this.isTextSelection();
  69.  
  70.         // Initialize (disable/remove) menu items.
  71.         this.initItems();
  72.     },
  73.     initItems : function () {
  74.         this.initOpenItems();
  75.         this.initNavigationItems();
  76.         this.initViewItems();
  77.         this.initMiscItems();
  78.         this.initSaveItems();
  79.         this.initClipboardItems();
  80.         this.initMetadataItems();
  81.     },
  82.     initOpenItems : function () {
  83.         this.showItem( "context-openlink", this.onSaveableLink || ( this.inDirList && this.onLink ) );
  84.         this.showItem( "context-openlinkintab", this.onSaveableLink || ( this.inDirList && this.onLink ) );
  85.  
  86.         this.showItem( "context-sep-open", this.onSaveableLink || ( this.inDirList && this.onLink ) );
  87.     },
  88.     initNavigationItems : function () {
  89.         // Back determined by canGoBack broadcaster.
  90.         this.setItemAttrFromNode( "context-back", "disabled", "canGoBack" );
  91.  
  92.         // Forward determined by canGoForward broadcaster.
  93.         this.setItemAttrFromNode( "context-forward", "disabled", "canGoForward" );
  94.  
  95.         this.showItem( "context-back", !( this.isTextSelected || this.onLink || this.onImage || this.onTextInput ) );
  96.         this.showItem( "context-forward", !( this.isTextSelected || this.onLink || this.onImage || this.onTextInput ) );
  97.  
  98.         this.showItem( "context-reload", !( this.isTextSelected || this.onLink || this.onImage || this.onTextInput ) );
  99.  
  100.         this.showItem( "context-stop", !( this.isTextSelected || this.onLink || this.onImage || this.onTextInput ) );
  101.         this.showItem( "context-sep-stop", !( this.isTextSelected || this.onLink || this.onImage || this.onTextInput ) );
  102.  
  103.         // XXX: Stop is determined in navigator.js; the canStop broadcaster is broken
  104.         //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
  105.     },
  106.     initSaveItems : function () {
  107.         this.showItem( "context-savepage", !( this.inDirList || this.isTextSelected || this.onTextInput ) && !( this.onLink && this.onImage ) );
  108.  
  109.         // Save link depends on whether we're in a link.
  110.         this.showItem( "context-savelink", this.onSaveableLink );
  111.  
  112.         // Save image depends on whether there is one.
  113.         this.showItem( "context-saveimage", this.onImage );
  114.  
  115.         this.showItem( "context-sendimage", this.onImage );
  116.     },
  117.     initViewItems : function () {
  118.         // View source is always OK, unless in directory listing.
  119.         this.showItem( "context-viewsource", !( this.inDirList || this.onImage || this.isTextSelected || this.onLink || this.onTextInput ) );
  120.         this.showItem( "context-viewinfo", !( this.inDirList || this.onImage || this.isTextSelected || this.onLink || this.onTextInput ) );
  121.  
  122.         this.showItem( "context-sep-properties", !( this.inDirList || this.isTextSelected || this.onTextInput ) );
  123.         // Set As Wallpaper depends on whether an image was clicked on, and only works on Windows.
  124.         this.showItem( "context-setWallpaper", this.onImage && navigator.appVersion.indexOf("Windows") != -1);
  125.  
  126.         this.showItem( "context-sep-setWallpaper", this.onImage && navigator.appVersion.indexOf("Windows") != -1);
  127.  
  128.         // View Image depends on whether an image was clicked on.
  129.         this.showItem( "context-viewimage", this.onImage );
  130.  
  131.         // View background image depends on whether there is one.
  132.         this.showItem( "context-viewbgimage", !( this.inDirList || this.onImage || this.isTextSelected || this.onLink || this.onTextInput ) );
  133.         this.showItem( "context-sep-viewbgimage", !( this.inDirList || this.onImage || this.isTextSelected || this.onLink || this.onTextInput ) );
  134.         var menuitem = document.getElementById("context-viewbgimage");
  135.  
  136.         if (this.hasBGImage)
  137.           menuitem.removeAttribute("disabled");
  138.         else
  139.           menuitem.setAttribute("disabled", "true");
  140.     },
  141.     initMiscItems : function () {
  142.         // Use "Bookmark This Link" if on a link.
  143.         this.showItem( "context-bookmarkpage", !( this.isTextSelected || this.onTextInput ) );
  144.         this.showItem( "context-bookmarklink", this.onLink && !this.onMailtoLink );
  145.         this.showItem( "context-searchselect", this.isTextSelected );
  146.         this.showItem( "frame", this.inFrame );
  147.         this.showItem( "frame-sep", this.inFrame );
  148.     },
  149.     initClipboardItems : function () {
  150.  
  151.         // Copy depends on whether there is selected text.
  152.         // Enabling this context menu item is now done through the global
  153.         // command updating system
  154.         // this.setItemAttr( "context-copy", "disabled", !this.isTextSelected() );
  155.  
  156.         goUpdateGlobalEditMenuItems();
  157.  
  158.         this.showItem( "context-undo", this.isTextSelected || this.onTextInput );
  159.         this.showItem( "context-sep-undo", this.isTextSelected || this.onTextInput );
  160.         this.showItem( "context-cut", this.isTextSelected || this.onTextInput );
  161.         this.showItem( "context-copy", this.isTextSelected || this.onTextInput );
  162.         this.showItem( "context-paste", this.isTextSelected || this.onTextInput );
  163.         this.showItem( "context-delete", this.isTextSelected || this.onTextInput );
  164.         this.showItem( "context-sep-paste", this.isTextSelected || this.onTextInput );
  165.         this.showItem( "context-selectall", this.isTextSelected || this.onTextInput );
  166.         this.showItem( "context-sep-selectall", this.isTextSelected );
  167.  
  168.         // XXX dr
  169.         // ------
  170.         // nsDocumentViewer.cpp has code to determine whether we're
  171.         // on a link or an image. we really ought to be using that...
  172.  
  173.         // Copy email link depends on whether we're on an email link.
  174.         this.showItem( "context-copyemail", this.onMailtoLink );
  175.  
  176.         // Copy link location depends on whether we're on a link.
  177.         this.showItem( "context-copylink", this.onLink );
  178.         this.showItem( "context-sep-copylink", this.onLink );
  179.  
  180.         // Copy image location depends on whether we're on an image.
  181.         this.showItem( "context-copyimage", this.onImage );
  182.         this.showItem( "context-sep-copyimage", this.onImage );
  183.     },
  184.     initMetadataItems : function () {
  185.         // Show if user clicked on something which has metadata.
  186.         this.showItem( "context-metadata", this.onMetaDataItem );
  187.     },
  188.     // Set various context menu attributes based on the state of the world.
  189.     setTarget : function ( node ) {
  190.         const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  191.         if ( node.namespaceURI == xulNS ) {
  192.           this.shouldDisplay = false;
  193.           return;
  194.         }
  195.         // Initialize contextual info.
  196.         this.onImage    = false;
  197.         this.onMetaDataItem = false;
  198.         this.onTextInput = false;
  199.         this.imageURL   = "";
  200.         this.onLink     = false;
  201.         this.inFrame    = false;
  202.         this.hasBGImage = false;
  203.         this.bgImageURL = "";
  204.  
  205.         // Remember the node that was clicked.
  206.         this.target = node;
  207.         // See if the user clicked on an image.
  208.         if ( this.target.nodeType == Node.ELEMENT_NODE ) {
  209.              if ( this.target.localName.toUpperCase() == "IMG" ) {
  210.                 this.onImage = true;
  211.                 this.imageURL = this.target.src;
  212.                 // Look for image map.
  213.                 var mapName = this.target.getAttribute( "usemap" );
  214.                 if ( mapName ) {
  215.                     // Find map.
  216.                     var map = this.target.ownerDocument.getElementById( mapName.substr(1) );
  217.                     if ( map ) {
  218.                         // Search child <area>s for a match.
  219.                         var areas = map.childNodes;
  220.                         //XXX Client side image maps are too hard for now!
  221.                         areas.length = 0;
  222.                         for ( var i = 0; i < areas.length && !this.onLink; i++ ) {
  223.                             var area = areas[i];
  224.                             if ( area.nodeType == Node.ELEMENT_NODE
  225.                                  &&
  226.                                  area.localName.toUpperCase() == "AREA" ) {
  227.                                 // Get type (rect/circle/polygon/default).
  228.                                 var type = area.getAttribute( "type" );
  229.                                 var coords = this.parseCoords( area );
  230.                                 switch ( type.toUpperCase() ) {
  231.                                     case "RECT":
  232.                                     case "RECTANGLE":
  233.                                         break;
  234.                                     case "CIRC":
  235.                                     case "CIRCLE":
  236.                                         break;
  237.                                     case "POLY":
  238.                                     case "POLYGON":
  239.                                         break;
  240.                                     case "DEFAULT":
  241.                                         // Default matches entire image.
  242.                                         this.onLink = true;
  243.                                         this.link = area;
  244.                                         this.onSaveableLink = this.isLinkSaveable( this.link );
  245.                                         break;
  246.                                 }
  247.                             }
  248.                         }
  249.                     }
  250.                 }
  251.              } else if ( this.target.localName.toUpperCase() == "OBJECT"
  252.                          &&
  253.                          // See if object tag is for an image.
  254.                          this.objectIsImage( this.target ) ) {
  255.                 // This is an image.
  256.                 this.onImage = true;
  257.                 // URL must be constructed.
  258.                 this.imageURL = this.objectImageURL( this.target );
  259.              } else if ( this.target.localName.toUpperCase() == "INPUT") {
  260.                type = this.target.getAttribute("type");
  261.                if(type && type.toUpperCase() == "IMAGE") {
  262.                  this.onImage = true;
  263.                  // Convert src attribute to absolute URL.
  264.                  this.imageURL = this.makeURLAbsolute( this.target.baseURI,
  265.                                                        this.target.src );
  266.                } else /* if (this.target.getAttribute( "type" ).toUpperCase() == "TEXT") */ {
  267.                  this.onTextInput = this.isTargetATextBox(this.target);
  268.                }
  269.             } else if ( this.target.localName.toUpperCase() == "TEXTAREA" ) {
  270.                  this.onTextInput = true;
  271.             } else if ( this.target.localName.toUpperCase() == "HTML" ) {
  272.                // pages with multiple <body>s are lame. we'll teach them a lesson.
  273.                var bodyElt = this.target.ownerDocument.getElementsByTagName("body")[0];
  274.                if ( bodyElt ) {
  275.                  var attr = bodyElt.getAttribute( "background" );
  276.                  if ( attr ||
  277.                       ( attr = this.getComputedURL( bodyElt, "background-image" ) ) ) {
  278.                    this.hasBGImage = true;
  279.                    this.bgImageURL = this.makeURLAbsolute( bodyElt.baseURI,
  280.                                                            attr );
  281.                  }
  282.                }
  283.             } else if ( "HTTPIndex" in _content &&
  284.                         _content.HTTPIndex instanceof Components.interfaces.nsIHTTPIndex ) {
  285.                 this.inDirList = true;
  286.                 // Bubble outward till we get to an element with URL attribute
  287.                 // (which should be the href).
  288.                 var root = this.target;
  289.                 while ( root && !this.link ) {
  290.                     if ( root.tagName == "tree" ) {
  291.                         // Hit root of tree; must have clicked in empty space;
  292.                         // thus, no link.
  293.                         break;
  294.                     }
  295.                     if ( root.getAttribute( "URL" ) ) {
  296.                         // Build pseudo link object so link-related functions work.
  297.                         this.onLink = true;
  298.                         this.link = { href : root.getAttribute("URL"),
  299.                                       getAttribute: function (attr) {
  300.                                           if (attr == "title") {
  301.                                               return root.firstChild.firstChild.getAttribute("label");
  302.                                           } else {
  303.                                               return "";
  304.                                           }
  305.                                       }
  306.                                     };
  307.                         // If element is a directory, then you can't save it.
  308.                         if ( root.getAttribute( "container" ) == "true" ) {
  309.                             this.onSaveableLink = false;
  310.                         } else {
  311.                             this.onSaveableLink = true;
  312.                         }
  313.                     } else {
  314.                         root = root.parentNode;
  315.                     }
  316.                 }
  317.             }
  318.         }
  319.  
  320.         // We have meta data on images.
  321.         this.onMetaDataItem = this.onImage;
  322.  
  323.         // See if the user clicked in a frame.
  324.         if ( this.target.ownerDocument != window._content.document ) {
  325.             this.inFrame = true;
  326.         }
  327.  
  328.         // Bubble out, looking for items of interest
  329.         var elem = this.target;
  330.         while ( elem ) {
  331.             if ( elem.nodeType == Node.ELEMENT_NODE ) {
  332.                 var localname = elem.localName.toUpperCase();
  333.  
  334.                 // Link?
  335.                 if ( !this.onLink &&
  336.                     ( (localname === "A" && elem.href) ||
  337.                       localname === "AREA" ||
  338.                       localname === "LINK" ||
  339.                       elem.getAttributeNS( "http://www.w3.org/1999/xlink", "type") == "simple" ) ) {
  340.                     // Clicked on a link.
  341.                     this.onLink = true;
  342.                     this.onMetaDataItem = true;
  343.                     // Remember corresponding element.
  344.                     this.link = elem;
  345.                     this.onMailtoLink = this.isLinkType( "mailto:", this.link );
  346.                     // Remember if it is saveable.
  347.                     this.onSaveableLink = this.isLinkSaveable( this.link );
  348.                 }
  349.  
  350.                 // Text input?
  351.                 if ( !this.onTextInput ) {
  352.                     // Clicked on a link.
  353.                     this.onTextInput = this.isTargetATextBox(elem);
  354.                 }
  355.  
  356.                 // Metadata item?
  357.                 if ( !this.onMetaDataItem ) {
  358.                     // We currently display metadata on anything which fits
  359.                     // the below test.
  360.                     if ( ( localname === "BLOCKQUOTE" && 'cite' in elem && elem.cite)  ||
  361.                          ( localname === "Q" && 'cite' in elem && elem.cite)           ||
  362.                          ( localname === "TABLE" && 'summary' in elem && elem.summary) ||
  363.                          ( ( localname === "INS" || localname === "DEL" ) &&
  364.                            ( ( 'cite' in elem && elem.cite ) ||
  365.                              ( 'dateTime' in elem && elem.dateTime ) ) )               ||
  366.                          ( 'title' in elem && elem.title )                             ||
  367.                          ( 'lang' in elem && elem.lang ) ) {
  368.                         dump("On metadata item.\n");
  369.                         this.onMetaDataItem = true;
  370.                     }
  371.                 }
  372.  
  373.                 // Background image?  We don't bother if we've already found a background
  374.                 // image further down the hierarchy.  Otherwise, we look for background=
  375.                 // attribute on html elements that support that, or, background-image style.
  376.                 var bgImgUrl = null;
  377.                 if ( !this.hasBGImage &&
  378.                      ( ( localname.search( /^(?:TD|TH|TABLE|BODY)$/ ) != -1 &&
  379.                          ( bgImgUrl = elem.getAttribute( "background" ) ) ) ||
  380.                        ( bgImgUrl = this.getComputedURL( elem, "background-image" ) ) ) ) {
  381.                     this.hasBGImage = true;
  382.                     this.bgImageURL = this.makeURLAbsolute( elem.baseURI,
  383.                                                             bgImgUrl );
  384.                 }
  385.             }
  386.             elem = elem.parentNode;
  387.         }
  388.     },
  389.     // Returns the computed style attribute for the given element.
  390.     getComputedStyle: function( elem, prop ) {
  391.          return elem.ownerDocument.defaultView.getComputedStyle( elem, '' ).getPropertyValue( prop );
  392.     },
  393.     // Returns a "url"-type computed style attribute value, with the url() stripped.
  394.     getComputedURL: function( elem, prop ) {
  395.          var url = elem.ownerDocument.defaultView.getComputedStyle( elem, '' ).getPropertyCSSValue( prop );
  396.          return ( url.primitiveType == CSSPrimitiveValue.CSS_URI ) ? url.getStringValue() : null;
  397.     },
  398.     // Returns true iff clicked on link is saveable.
  399.     isLinkSaveable : function ( link ) {
  400.         // We don't do the Right Thing for news/snews yet, so turn them off
  401.         // until we do.
  402.         return !(this.isLinkType( "mailto:" , link )     ||
  403.                  this.isLinkType( "javascript:" , link ) ||
  404.                  this.isLinkType( "news:", link )        ||
  405.                  this.isLinkType( "snews:", link ) );
  406.     },
  407.     // Returns true iff clicked on link is of type given.
  408.     isLinkType : function ( linktype, link ) {
  409.         try {
  410.             // Test for missing protocol property.
  411.             if ( !link.protocol ) {
  412.                 // We must resort to testing the URL string :-(.
  413.                 var protocol;
  414.                 if ( link.href ) {
  415.                     protocol = link.href.substr( 0, linktype.length );
  416.                 } else {
  417.                     protocol = link.getAttributeNS("http://www.w3.org/1999/xlink","href");
  418.                     if ( protocol ) {
  419.                         protocol = protocol.substr( 0, linktype.length );
  420.                     }
  421.                 }
  422.                 return protocol.toLowerCase() === linktype;
  423.             } else {
  424.                 // Presume all but javascript: urls are saveable.
  425.                 return link.protocol.toLowerCase() === linktype;
  426.             }
  427.         } catch (e) {
  428.             // something was wrong with the link,
  429.             // so we won't be able to save it anyway
  430.             return false;
  431.         }
  432.     },
  433.     // Open linked-to URL in a new window.
  434.     openLink : function () {
  435.         // Determine linked-to URL.
  436.         openNewWindowWith( this.linkURL() );
  437.     },
  438.     // Open linked-to URL in a new tab.
  439.     openLinkInTab : function () {
  440.         // Determine linked-to URL.
  441.         openNewTabWith( this.linkURL() );
  442.     },
  443.     // [ghzil]: added this function {
  444.     // Open linked-to URL in a new tab in background
  445.     openLinkInTabBackground : function () {
  446.         // Determine linked-to URL.
  447.         openNewTabWithBackground( this.linkURL() );
  448.     },
  449.     // } [ghzil]
  450.     // Open frame in a new tab.
  451.     openFrameInTab : function () {
  452.         // Determine linked-to URL.
  453.         openNewTabWith( this.target.ownerDocument.location.href );
  454.     },
  455.     // Reload clicked-in frame.
  456.     reloadFrame : function () {
  457.         this.target.ownerDocument.location.reload();
  458.     },
  459.     // Open clicked-in frame in its own window.
  460.     openFrame : function () {
  461.         openNewWindowWith( this.target.ownerDocument.location.href );
  462.     },
  463.     // Open clicked-in frame in the same window
  464.     showOnlyThisFrame : function () {
  465.         window.loadURI(this.target.ownerDocument.location.href);
  466.     },
  467.     // Open new "view source" window with the frame's URL.
  468.     viewFrameSource : function () {
  469.         BrowserViewSourceOfDocument(this.target.ownerDocument);
  470.     },
  471.     viewInfo : function () {
  472.       BrowserPageInfo();
  473.     },
  474.     viewFrameInfo : function () {
  475.       BrowserPageInfo(this.target.ownerDocument);
  476.     },
  477.     // Change current window to the URL of the image.
  478.     viewImage : function () {
  479.         openTopWin( this.imageURL );
  480.     },
  481.     // Change current window to the URL of the background image.
  482.     viewBGImage : function () {
  483.         openTopWin( this.bgImageURL );
  484.     },
  485.     setWallpaper: function() {
  486.       var winhooks = Components.classes[ "@mozilla.org/winhooks;1" ].
  487.                        getService(Components.interfaces.nsIWindowsHooks);
  488.  
  489.       winhooks.setImageAsWallpaper(this.target, false);
  490.     },
  491.     // Save URL of clicked-on frame.
  492.     saveFrame : function () {
  493.         saveDocument( this.target.ownerDocument );
  494.     },
  495.     // Save URL of clicked-on link.
  496.     saveLink : function () {
  497.         saveURL( this.linkURL(), this.linkText(), null, true );
  498.     },
  499.     // Save URL of clicked-on image.
  500.     saveImage : function () {
  501.         saveURL( this.imageURL, null, "SaveImageTitle", false );
  502.     },
  503.     // Generate email address and put it on clipboard.
  504.     copyEmail : function () {
  505.         // Copy the comma-separated list of email addresses only.
  506.         // There are other ways of embedding email addresses in a mailto:
  507.         // link, but such complex parsing is beyond us.
  508.         var url = this.linkURL();
  509.         var qmark = url.indexOf( "?" );
  510.         var addresses;
  511.  
  512.         if ( qmark > 7 ) {                   // 7 == length of "mailto:"
  513.             addresses = url.substring( 7, qmark );
  514.         } else {
  515.             addresses = url.substr( 7 );
  516.         }
  517.  
  518.         var clipboard = this.getService( "@mozilla.org/widget/clipboardhelper;1",
  519.                                          Components.interfaces.nsIClipboardHelper );
  520.         clipboard.copyString(addresses);
  521.     },
  522.     addBookmark : function() {
  523.       var docshell = document.getElementById( "content" ).webNavigation;
  524.       BookmarksUtils.addBookmark( docshell.currentURI.spec,
  525.                                   docshell.document.title,
  526.                                   docshell.document.charset,
  527.                                   false );
  528.     },
  529.     addBookmarkForFrame : function() {
  530.       var doc = this.target.ownerDocument;
  531.       var uri = doc.location.href;
  532.       var title = doc.title;
  533.       if ( !title )
  534.         title = uri;
  535.       BookmarksUtils.addBookmark( uri,
  536.                                   title,
  537.                                   doc.charset,
  538.                                   false );
  539.     },
  540.     // Open Metadata window for node
  541.     showMetadata : function () {
  542.         window.openDialog(  "chrome://navigator/content/metadata.xul",
  543.                             "_blank",
  544.                             "scrollbars,resizable,chrome,dialog=no",
  545.                             this.target);
  546.     },
  547.  
  548.     ///////////////
  549.     // Utilities //
  550.     ///////////////
  551.  
  552.     // Create instance of component given contractId and iid (as string).
  553.     createInstance : function ( contractId, iidName ) {
  554.         var iid = Components.interfaces[ iidName ];
  555.         return Components.classes[ contractId ].createInstance( iid );
  556.     },
  557.     // Get service given contractId and iid (as string).
  558.     getService : function ( contractId, iidName ) {
  559.         var iid = Components.interfaces[ iidName ];
  560.         return Components.classes[ contractId ].getService( iid );
  561.     },
  562.     // Show/hide one item (specified via name or the item element itself).
  563.     showItem : function ( itemOrId, show ) {
  564.         var item = itemOrId.constructor == String ? document.getElementById(itemOrId) : itemOrId;
  565.         if (item)
  566.           item.hidden = !show;
  567.     },
  568.     // Set given attribute of specified context-menu item.  If the
  569.     // value is null, then it removes the attribute (which works
  570.     // nicely for the disabled attribute).
  571.     setItemAttr : function ( id, attr, val ) {
  572.         var elem = document.getElementById( id );
  573.         if ( elem ) {
  574.             if ( val == null ) {
  575.                 // null indicates attr should be removed.
  576.                 elem.removeAttribute( attr );
  577.             } else {
  578.                 // Set attr=val.
  579.                 elem.setAttribute( attr, val );
  580.             }
  581.         }
  582.     },
  583.     // Set context menu attribute according to like attribute of another node
  584.     // (such as a broadcaster).
  585.     setItemAttrFromNode : function ( item_id, attr, other_id ) {
  586.         var elem = document.getElementById( other_id );
  587.         if ( elem && elem.getAttribute( attr ) == "true" ) {
  588.             this.setItemAttr( item_id, attr, "true" );
  589.         } else {
  590.             this.setItemAttr( item_id, attr, null );
  591.         }
  592.     },
  593.     // Temporary workaround for DOM api not yet implemented by XUL nodes.
  594.     cloneNode : function ( item ) {
  595.         // Create another element like the one we're cloning.
  596.         var node = document.createElement( item.tagName );
  597.  
  598.         // Copy attributes from argument item to the new one.
  599.         var attrs = item.attributes;
  600.         for ( var i = 0; i < attrs.length; i++ ) {
  601.             var attr = attrs.item( i );
  602.             node.setAttribute( attr.nodeName, attr.nodeValue );
  603.         }
  604.  
  605.         // Voila!
  606.         return node;
  607.     },
  608.     // Generate fully-qualified URL for clicked-on link.
  609.     linkURL : function () {
  610.         if (this.link.href) {
  611.           return this.link.href;
  612.         }
  613.         var href = this.link.getAttributeNS("http://www.w3.org/1999/xlink","href");
  614.         if (!href || !href.match(/\S/)) {
  615.           throw "Empty href"; // Without this we try to save as the current doc, for example, HTML case also throws if empty
  616.         }
  617.         href = this.makeURLAbsolute(this.link.baseURI,href);
  618.         return href;
  619.     },
  620.     // Get text of link.
  621.     linkText : function () {
  622.         var text = gatherTextUnder( this.link );
  623.         if (!text || !text.match(/\S/)) {
  624.           text = this.link.getAttribute("title");
  625.           if (!text || !text.match(/\S/)) {
  626.             text = this.link.getAttribute("alt");
  627.             if (!text || !text.match(/\S/)) {
  628.               if (this.link.href) {
  629.                 text = this.link.href;
  630.               } else {
  631.                 text = getAttributeNS("http://www.w3.org/1999/xlink", "href");
  632.                 if (text && text.match(/\S/)) {
  633.                   text = this.makeURLAbsolute(this.link.baseURI, text);
  634.                 }
  635.               }
  636.             }
  637.           }
  638.         }
  639.  
  640.         return text;
  641.     },
  642.  
  643.     //Get selected object and convert it to a string to get
  644.     //selected text.   Only use the first 15 chars.
  645.     isTextSelection : function() {
  646.         var result = false;
  647.         var selection = this.searchSelected();
  648.         var searchSelect = document.getElementById('context-searchselect');
  649.  
  650.         var bundle = srGetStrBundle("chrome://communicator/locale/contentAreaCommands.properties");
  651.  
  652.         var searchSelectText;
  653.         if (selection != "") {
  654.             searchSelectText = selection.toString();
  655.             if (searchSelectText.length > 15)
  656.                 searchSelectText = searchSelectText.substr(0,15) + "...";
  657.             result = true;
  658.  
  659.           // format "Search for <selection>" string to show in menu
  660.           searchSelectText = bundle.formatStringFromName("searchText",
  661.                                                          [searchSelectText], 1);
  662.           searchSelect.setAttribute("label", searchSelectText);
  663.         }
  664.         return result;
  665.     },
  666.  
  667.     searchSelected : function() {
  668.         var focusedWindow = document.commandDispatcher.focusedWindow;
  669.         var searchStr = focusedWindow.getSelection();
  670.         searchStr = searchStr.toString();
  671.         searchStr = searchStr.replace( /^\s+/, "" );
  672.         searchStr = searchStr.replace(/(\n|\r|\t)+/g, " ");
  673.         searchStr = searchStr.replace(/\s+$/,"");
  674.         return searchStr;
  675.     },
  676.  
  677.     // Determine if target <object> is an image.
  678.     objectIsImage : function ( objElem ) {
  679.         var result = false;
  680.         // Get type and data attributes.
  681.         var type = objElem.getAttribute( "type" );
  682.         var data = objElem.getAttribute( "data" );
  683.         // Presume any mime type of the form "image/..." is an image.
  684.         // There must be a data= attribute with an URL, also.
  685.         if ( type.substring( 0, 6 ) == "image/" && data && data != "" ) {
  686.             result = true;
  687.         }
  688.         return result;
  689.     },
  690.     // Extract image URL from <object> tag.
  691.     objectImageURL : function ( objElem ) {
  692.         // Extract url from data= attribute.
  693.         var data = objElem.getAttribute( "data" );
  694.         // Make it absolute.
  695.         return this.makeURLAbsolute( objElem.baseURI, data );
  696.     },
  697.     // Convert relative URL to absolute, using document's <base>.
  698.     makeURLAbsolute : function ( base, url ) {
  699.         // Construct nsIURL.
  700.         var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  701.                       .getService(Components.interfaces.nsIIOService);
  702.         var baseURI  = ioService.newURI(base, null, null);
  703.  
  704.         return ioService.newURI(baseURI.resolve(url), null, null).spec;
  705.     },
  706.     // Parse coords= attribute and return array.
  707.     parseCoords : function ( area ) {
  708.         return [];
  709.     },
  710.     toString : function () {
  711.         return "contextMenu.target     = " + this.target + "\n" +
  712.                "contextMenu.onImage    = " + this.onImage + "\n" +
  713.                "contextMenu.onLink     = " + this.onLink + "\n" +
  714.                "contextMenu.link       = " + this.link + "\n" +
  715.                "contextMenu.inFrame    = " + this.inFrame + "\n" +
  716.                "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
  717.     },
  718.     isTargetATextBox : function ( node )
  719.     {
  720.       if (node.nodeType != Node.ELEMENT_NODE)
  721.         return false;
  722.  
  723.       if (node.localName.toUpperCase() == "INPUT") {
  724.         var attrib = "";
  725.         var type = node.getAttribute("type");
  726.  
  727.         if (type)
  728.           attrib = type.toUpperCase();
  729.  
  730.         return( (attrib != "IMAGE") &&
  731.                 (attrib != "CHECKBOX") &&
  732.                 (attrib != "RADIO") &&
  733.                 (attrib != "SUBMIT") &&
  734.                 (attrib != "RESET") &&
  735.                 (attrib != "FILE") &&
  736.                 (attrib != "HIDDEN") &&
  737.                 (attrib != "RESET") &&
  738.                 (attrib != "BUTTON") );
  739.       } else  {
  740.         return(node.localName.toUpperCase() == "TEXTAREA");
  741.       }
  742.     },
  743.  
  744.     // Determines whether or not the separator with the specified ID should be
  745.     // shown or not by determining if there are any non-hidden items between it
  746.     // and the previous separator.
  747.     shouldShowSeparator : function ( aSeparatorID )
  748.     {
  749.       var separator = document.getElementById(aSeparatorID);
  750.       if (separator) {
  751.         var sibling = separator.previousSibling;
  752.         while (sibling && sibling.localName != "menuseparator") {
  753.           if (sibling.getAttribute("hidden") != "true")
  754.             return true;
  755.           sibling = sibling.previousSibling;
  756.         }
  757.       }
  758.       return false;
  759.     }
  760. };
  761.  
  762. /*************************************************************************
  763.  *
  764.  *   nsDefaultEngine : nsIObserver
  765.  *
  766.  *************************************************************************/
  767. function nsDefaultEngine()
  768. {
  769.     try
  770.     {
  771.         var pb = Components.classes["@mozilla.org/preferences-service;1"].
  772.                    getService(Components.interfaces.nsIPrefBranch);
  773.         var pbi = pb.QueryInterface(
  774.                     Components.interfaces.nsIPrefBranchInternal);
  775.         pbi.addObserver(this.domain, this, false);
  776.  
  777.         // reuse code by explicitly invoking initial |observe| call
  778.         // to initialize the |icon| and |name| member variables
  779.         this.observe(pb, "", this.domain);
  780.     }
  781.     catch (ex)
  782.     {
  783.     }
  784. }
  785.  
  786. nsDefaultEngine.prototype =
  787. {
  788.     name: "",
  789.     icon: "",
  790.     domain: "browser.search.defaultengine",
  791.  
  792.     // nsIObserver implementation
  793.     observe: function(aPrefBranch, aTopic, aPrefName)
  794.     {
  795.         try
  796.         {
  797.             var rdf = Components.
  798.                         classes["@mozilla.org/rdf/rdf-service;1"].
  799.                         getService(Components.interfaces.nsIRDFService);
  800.             var ds = rdf.GetDataSource("rdf:internetsearch");
  801.             var defaultEngine = aPrefBranch.getCharPref(aPrefName);
  802.             var res = rdf.GetResource(defaultEngine);
  803.  
  804.             // get engine ``pretty'' name
  805.             const kNC_Name = rdf.GetResource(
  806.                                "http://home.netscape.com/NC-rdf#Name");
  807.             var engineName = ds.GetTarget(res, kNC_Name, true);
  808.             if (engineName)
  809.             {
  810.                 this.name = engineName.QueryInterface(
  811.                               Components.interfaces.nsIRDFLiteral).Value;
  812.             }
  813.  
  814.             // get URL to engine vendor icon
  815.             const kNC_Icon = rdf.GetResource(
  816.                                "http://home.netscape.com/NC-rdf#Icon");
  817.             var iconURL = ds.GetTarget(res, kNC_Icon, true);
  818.             if (iconURL)
  819.             {
  820.                 this.icon = iconURL.QueryInterface(
  821.                   Components.interfaces.nsIRDFLiteral).Value;
  822.             }
  823.         }
  824.         catch (ex)
  825.         {
  826.         }
  827.     }
  828. }
  829.