home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 September / Chip_2001-09_cd1.bin / zkuste / downman / ReGet / regetjr.exe / RCDATA / CABINET / nsContextMenu.js < prev    next >
Text File  |  2001-03-05  |  35KB  |  847 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  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 <BlakeR1234@aol.com>
  24.  */
  25.  
  26. /*------------------------------ nsContextMenu ---------------------------------
  27. |   This JavaScript "class" is used to implement the browser's content-area    |
  28. |   context menu.                                                              |
  29. |                                                                              |
  30. |   For usage, see references to this class in navigator.xul.                  |
  31. |                                                                              |
  32. |   Currently, this code is relatively useless for any other purpose.  In the  |
  33. |   longer term, this code will be restructured to make it more reusable.      |
  34. ------------------------------------------------------------------------------*/
  35. function nsContextMenu( xulMenu ) {
  36.     this.target     = null;
  37.     this.menu       = null;
  38.     this.onTextInput = false;
  39.     this.onImage    = false;
  40.     this.onLink     = false;
  41.     this.onSaveableLink = false;
  42.     this.link       = false;
  43.     this.inFrame    = false;
  44.     this.hasBGImage = false;
  45.     this.inDirList  = false;
  46.     this.shouldDisplay = true;
  47.     this.disableCapture = false;
  48.  
  49.     // Initialize new menu.
  50.     this.initMenu( xulMenu );
  51. }
  52.  
  53. function initCatcherMenu( menu ) {
  54.     var olditem=document.getElementById( "download-by-catcher" ); 
  55.     var common = Components.classes["RgCommon"].createInstance();
  56.     common = common.QueryInterface(Components.interfaces.nsIRgCommon);
  57.     if (!olditem)
  58.     {
  59.         var newitem = document.createElement( "menuitem" );
  60.         newitem.setAttribute( "id", "download-by-catcher" );
  61.         newitem.setAttribute( "value",  "Download by" );
  62.         newitem.setAttribute( "name", "download_by_catcher" );
  63.         newitem.setAttribute( "oncommand", "DownloadBy(contextMenu);" );
  64.         var saveLink = document.getElementById( "context-savepage" );
  65.         menu.menu.insertBefore(newitem, saveLink);
  66.         var newitem2 = document.createElement( "menuitem" );
  67.         newitem2.setAttribute( "id", "download-all-by-catcher" );
  68.         newitem2.setAttribute( "value", "Download All by");
  69.         newitem2.setAttribute( "name", "download_all_by_catcher" );
  70.         newitem2.setAttribute( "oncommand", "DownloadAll(contextMenu);" );
  71.         menu.menu.insertBefore(newitem2, saveLink);
  72.      }   
  73.      var old1=document.getElementById( "download-by-catcher" ); 
  74.      var old2=document.getElementById( "download-all-by-catcher" ); 
  75.     if (common.menu1 != null) 
  76.     {
  77.          old1.setAttribute( "value", common.menu1);
  78.          menu.showItem("download-by-catcher",menu.onLink);
  79.     }
  80.     else
  81.         menu.showItem("download-by-catcher",false);
  82.     if (common.menu2 != null) 
  83.     {
  84.          old2.setAttribute( "value", common.menu2);
  85.          menu.showItem("download-all-by-catcher",true);
  86.     }
  87.     else
  88.         menu.showItem("download-all-by-catcher",false);
  89.  
  90. }
  91.  
  92. function DownloadBy( menu ) {
  93.     var url = Components.classes["RgUrl"].createInstance();
  94.     url = url.QueryInterface(Components.interfaces.nsIRgUrl);
  95.     url.url = menu.linkURL();
  96.     url.info = menu.linkText();
  97.     url.referer = window._content.location.href;
  98.     url.add();
  99. }
  100.  
  101. function DownloadAll( menu ) {
  102.     var list = Components.classes["RgUrlList"].createInstance();
  103.     list = list.QueryInterface(Components.interfaces.nsIRgUrlList);
  104.     var doc = menu.target.ownerDocument;
  105.     var links = doc.links
  106.     for (i = 0;i<links.length;i++)
  107.     {
  108.         var url = Components.classes["RgUrl"].createInstance();
  109.         url = url.QueryInterface(Components.interfaces.nsIRgUrl);
  110.         url.url = links[i].href;
  111.         url.info = links[i].text; 
  112.         url.referer = window._content.location.href;
  113.         list.add(url);
  114.     }
  115.     list.list();
  116.  
  117. }
  118. // Prototype for nsContextMenu "class."
  119. nsContextMenu.prototype = {
  120.     // onDestroy is a no-op at this point.
  121.     onDestroy : function () {
  122.     },
  123.     // Initialize context menu.
  124.     initMenu : function ( popup, event ) {
  125.         // Save menu.
  126.         this.menu = popup;
  127.  
  128.         // Get contextual info.
  129.         this.setTarget( document.popupNode );
  130.     
  131.         // Initialize (disable/remove) menu items.
  132.         this.initItems();
  133.     },
  134.     initItems : function () {
  135.     initCatcherMenu(this);
  136.         this.initOpenItems();
  137.         this.initNavigationItems();
  138.         this.initViewItems();
  139.         this.initMiscItems();
  140.         this.initSaveItems();
  141.         this.initClipboardItems();
  142.     },
  143.     initOpenItems : function () {
  144.         // Remove open/edit link if not applicable.
  145.         this.showItem( "context-openlink", this.onSaveableLink || ( this.inDirList && this.onLink ) );
  146.         this.showItem( "context-editlink", this.onSaveableLink && !this.inDirList );
  147.     
  148.         // Remove open frame if not applicable.
  149.         this.showItem( "context-openframe", this.inFrame );
  150.     
  151.         // Remove separator after open items if neither link nor frame.
  152.         this.showItem( "context-sep-open", this.onSaveableLink || ( this.inDirList && this.onLink ) || this.inFrame );
  153.     },
  154.     initNavigationItems : function () {
  155.         // Back determined by canGoBack broadcaster.
  156.         this.setItemAttrFromNode( "context-back", "disabled", "canGoBack" );
  157.     
  158.         // Forward determined by canGoForward broadcaster.
  159.         this.setItemAttrFromNode( "context-forward", "disabled", "canGoForward" );
  160.     
  161.         // Reload is OK if not on a frame; vice-versa for reload-frame.
  162.         this.showItem( "context-reload", !this.inFrame );
  163.         this.showItem( "context-reload-frame", this.inFrame );
  164.     
  165.         // XXX: Stop is determined in navigator.js; the canStop broadcaster is broken
  166.         //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
  167.     },
  168.     initSaveItems : function () {
  169.         // Save page is always OK, unless in directory listing.
  170.         this.showItem( "context-savepage", !this.inDirList );
  171.     
  172.         // Save frame as depends on whether we're in a frame.
  173.         this.showItem( "context-saveframe", this.inFrame );
  174.     
  175.         // Save link depends on whether we're in a link.
  176.         this.showItem( "context-savelink", this.onSaveableLink );
  177.     
  178.         // Save background image depends on whether there is one.
  179.         this.showItem( "context-savebgimage", this.hasBGImage );
  180.     
  181.         // Save image depends on whether there is one.
  182.         this.showItem( "context-saveimage", this.onImage );
  183.  
  184.         // Remove separator if none of these were shown.
  185.         var showSep = !this.inDirList || this.inFrame || this.onSaveableLink || this.hasBGImage || this.onImage;
  186.         this.showItem( "context-sep-save", showSep );
  187.     },
  188.     initViewItems : function () {
  189.         // View source is always OK, unless in directory listing.
  190.         this.showItem( "context-viewsource", !this.inDirList );
  191.     
  192.         // View frame source depends on whether we're in a frame.
  193.         this.showItem( "context-viewframesource", this.inFrame );
  194.     
  195.         // View Info don't work no way no how.
  196.         this.showItem( "context-viewinfo", false );
  197.     
  198.         // View Frame Info isn't working, either.
  199.         this.showItem( "context-viewframeinfo", false );
  200.     
  201.         // View Image depends on whether an image was clicked on.
  202.         this.showItem( "context-viewimage", this.onImage );
  203.  
  204.         // Block image depends on whether an image was clicked on, and,
  205.         // whether the user pref is enabled.
  206.         this.showItem( "context-blockimage", this.onImage && this.isBlockingImages() );
  207.  
  208.         // Capture depends on whether a form is being displayed.
  209.         this.showItem( "context-capture", this.okToCapture() );
  210.         this.setItemAttr( "context-capture", "disabled", this.disableCapture);
  211.  
  212.         // Prefill depends on whether a form is being displayed.
  213.         this.showItem( "context-prefill", this.okToPrefill() );
  214.  
  215.         // Remove separator if all items are removed.
  216.         this.showItem( "context-sep-view", !this.inDirList || this.inFrame || this.onImage );
  217.     },
  218.     initMiscItems : function () {
  219.         // Use "Bookmark This Link" if on a link.
  220.         this.showItem( "context-bookmarkpage", !this.onLink );
  221.         this.showItem( "context-bookmarklink", this.onLink );
  222.     
  223.         // Send Page not working yet.
  224.         this.showItem( "context-sendpage", false );
  225.     },
  226.     initClipboardItems : function () {
  227.         // Select All is always OK, unless in directory listing.
  228.         this.showItem( "context-selectall", !this.inDirList );
  229.     
  230.         // Copy depends on whether there is selected text.
  231.         // Enabling this context menu item is now done through the global
  232.         // command updating system
  233.         // this.setItemAttr( "context-copy", "disabled", this.isNoTextSelected() );
  234.  
  235.         goUpdateGlobalEditMenuItems();
  236.  
  237.         // Items for text areas
  238.         this.showItem( "context-cut", this.onTextInput );
  239.         this.showItem( "context-paste", this.onTextInput );
  240.         
  241.         // Copy link location depends on whether we're on a link.
  242.         this.showItem( "context-copylink", this.onLink );
  243.     
  244.         // Copy image location depends on whether we're on an image.
  245.         this.showItem( "context-copyimage", this.onImage );
  246.     },
  247.     // Set various context menu attributes based on the state of the world.
  248.     setTarget : function ( node ) {
  249.         // Initialize contextual info.
  250.         this.onImage    = false;
  251.         this.onTextInput = false;
  252.         this.imageURL   = "";
  253.         this.onLink     = false;
  254.         this.inFrame    = false;
  255.         this.hasBGImage = false;
  256.     
  257.         // Remember the node that was clicked.
  258.         this.target = node;
  259.     
  260.         // See if the user clicked on an image.
  261.         if ( this.target.nodeType == 1 ) {
  262.              if ( this.target.tagName.toUpperCase() == "IMG" ) {
  263.                 this.onImage = true;
  264.                 this.imageURL = this.target.src;
  265.                 // Look for image map.
  266.                 var mapName = this.target.getAttribute( "usemap" );
  267.                 if ( mapName ) {
  268.                     // Find map.
  269.                     var map = this.target.ownerDocument.getElementById( mapName.substr(1) );
  270.                     if ( map ) {
  271.                         // Search child <area>s for a match.
  272.                         var areas = map.childNodes;
  273.                         //XXX Client side image maps are too hard for now!
  274.                         dump( "Client side image maps not supported yet, sorry!\n" );
  275.                         areas.length = 0;
  276.                         for ( var i = 0; i < areas.length && !this.onLink; i++ ) {
  277.                             var area = areas[i];
  278.                             if ( area.nodeType == 1
  279.                                  &&
  280.                                  area.tagName.toUpperCase() == "AREA" ) {
  281.                                 // Get type (rect/circle/polygon/default).
  282.                                 var type = area.getAttribute( "type" );
  283.                                 var coords = this.parseCoords( area );
  284.                                 switch ( type.toUpperCase() ) {
  285.                                     case "RECT":
  286.                                     case "RECTANGLE":
  287.                                         break;
  288.                                     case "CIRC":
  289.                                     case "CIRCLE":
  290.                                         break;
  291.                                     case "POLY":
  292.                                     case "POLYGON":
  293.                                         break;
  294.                                     case "DEFAULT":
  295.                                         // Default matches entire image.
  296.                                         this.onLink = true;
  297.                                         this.link = area;
  298.                                         this.onSaveableLink = this.isLinkSaveable( this.link );
  299.                                         break;
  300.                                 }
  301.                             }
  302.                         }
  303.                     }
  304.                 }   
  305.              } else if (this.target.tagName.toUpperCase() == "INPUT") {
  306.                if(this.target.getAttribute( "type" ).toUpperCase() == "IMAGE") {
  307.                  this.onImage = true;
  308.                  this.imageURL = this.target.src;
  309.                } else /* if (this.target.getAttribute( "type" ).toUpperCase() == "TEXT") */ {
  310.                  this.onTextInput = this.isTargetATextField(this.target);
  311.                }
  312.             } else if (this.target.tagName.toUpperCase() == "TEXTAREA") {
  313.                  this.onTextInput = true;
  314.             } else if (this.target.getAttribute( "background" )) {
  315.                this.onImage = true;
  316.                this.imageURL = this.target.getAttribute( "background" );
  317.             } else if ( window._content.HTTPIndex == "[xpconnect wrapped nsIHTTPIndex]"
  318.                         &&
  319.                         typeof window._content.HTTPIndex == "object"
  320.                         &&
  321.                         !window._content.HTTPIndex.constructor ) {
  322.                 // The above test is a roundabout way of determining whether
  323.                 // the content area contains chrome://global/content/directory/directory.xul.
  324.                 this.inDirList = true;
  325.                 // Bubble outward till we get to an element with id= attribute
  326.                 // (which should be the href).
  327.                 var root = this.target;
  328.                 while ( root && !this.link ) {
  329.                     if ( root.getAttribute( "id" ) ) {
  330.                         if ( root.tagName == "tree" ) {
  331.                             // Hit root of tree; must have clicked in empty space;
  332.                             // thus, no link.
  333.                             break;
  334.                         }
  335.                         // Build pseudo link object so link-related functions work.
  336.                         this.onLink = true;
  337.                         this.link = { href : root.getAttribute( "id" ) };
  338.                         // If element is a directory, then you can't save it.
  339.                         if ( root.getAttribute( "container" ) == "true" ) {
  340.                             this.onSaveableLink = false;
  341.                         } else {
  342.                             this.onSaveableLink = true;                        
  343.                         }
  344.                     } else {
  345.                         root = root.parentNode;
  346.                     }
  347.                 }
  348.             } else if ( this.target.parentNode.tagName == "scrollbar" 
  349.                         ||
  350.                         this.target.parentNode.tagName == "thumb" 
  351.                         || 
  352.                         this.target.parentNode.tagName == "xul:slider") {
  353.                 this.shouldDisplay = false;
  354.             } else {
  355.                 try {
  356.                     var cssAttr = this.target.style.getPropertyValue( "list-style-image" ) ||
  357.                                   this.target.style.getPropertyValue( "list-style" ) || 
  358.                                   this.target.style.getPropertyValue( "background-image" ) || 
  359.                                   this.target.style.getPropertyValue( "background" );
  360.                     if ( cssAttr ) {
  361.                         this.onImage = true;
  362.                         this.imageURL = cssAttr.toLowerCase().replace(/url\("*(.+)"*\)/, "$1");
  363.                     }
  364.                 } catch ( exception ) {
  365.                 }
  366.             }
  367.         }
  368.     
  369.         // See if the user clicked in a frame.
  370.         if ( this.target.ownerDocument != window._content.document ) {
  371.             this.inFrame = true;
  372.         }
  373.  
  374.         // Bubble up looking for an input or textarea
  375.         var elem = this.target;
  376.         while ( elem && !this.onTextInput ) {
  377.             // Test for element types of interest.
  378.             if ( elem.nodeType == 1 ) {
  379.                 // Clicked on a link.
  380.                 this.onTextInput = this.isTargetATextField(elem);
  381.             }
  382.             elem = elem.parentNode;
  383.         }
  384.     
  385.         // Bubble out, looking for link.
  386.         elem = this.target;
  387.         while ( elem && !this.onLink ) {
  388.             // Test for element types of interest.
  389.             if ( elem.nodeType == 1 && 
  390.                  ( elem.tagName.toUpperCase() == "A"
  391.                    ||
  392.                    elem.tagName.toUpperCase() == "AREA"
  393.                    ||
  394.                    elem.getAttributeNS("http://www.w3.org/1999/xlink","type") == "simple")) {
  395.                 // Clicked on a link.
  396.                 this.onLink = true;
  397.                 // Remember corresponding element.
  398.                 this.link = elem;
  399.                 // Remember if it is saveable.
  400.                 this.onSaveableLink = this.isLinkSaveable( this.link );
  401.             }
  402.             elem = elem.parentNode;
  403.         }
  404.     },
  405.     // Returns true iff clicked on link is saveable.
  406.     isLinkSaveable : function ( link ) {
  407.         // Test for missing protocol property.
  408.         if ( !link.protocol ) {
  409.            // We must resort to testing the URL string :-(.
  410.            var protocol;
  411.            if (link.href) {
  412.              protocol = link.href.substr( 0, 11 );
  413.            } else {
  414.              protocol = link.getAttributeNS("http://www.w3.org/1999/xlink","href");
  415.              if (protocol) {
  416.                protocol = protocol.substr( 0, 11 );
  417.              }
  418.            }           
  419.            return protocol.toLowerCase() != "javascript:";
  420.         } else {
  421.            // Presume all but javascript: urls are saveable.
  422.            return link.protocol.toLowerCase() != "javascript:";
  423.         }
  424.     },
  425.     // Open linked-to URL in a new window.
  426.     openLink : function () {
  427.         // Determine linked-to URL.
  428.         openNewWindowWith( this.linkURL() );
  429.     },
  430.     // Edit linked-to URL in a new window.
  431.     editLink : function () {
  432.         BrowserEditPage( this.linkURL() );
  433.     },
  434.     // Reload clicked-in frame.
  435.     reloadFrame : function () {
  436.         this.target.ownerDocument.location.reload();
  437.     },
  438.     // Open clicked-in frame in its own window.
  439.     openFrame : function () {
  440.         openNewWindowWith( this.target.ownerDocument.location.href );
  441.     },
  442.     // Open new "view source" window with the frame's URL.
  443.     viewFrameSource : function () {
  444.     window.openDialog(  "chrome://navigator/content/viewSource.xul",
  445.                         "_blank",
  446.                         "scrollbars,resizable,chrome,dialog=no",
  447.                         this.target.ownerDocument.location.href);
  448.     },
  449.     viewInfo : function () {
  450.         dump( "nsContextMenu.viewInfo not implemented yet\n" );
  451.     },
  452.     viewFrameInfo : function () {
  453.         dump( "nsContextMenu.viewFrameInfo not implemented yet\n" );
  454.     },
  455.     // Open new window with the URL of the image.
  456.     viewImage : function () {
  457.         openNewWindowWith( this.imageURL );
  458.     },
  459.     // Save URL of clicked-on frame.
  460.     saveFrame : function () {
  461.         this.savePage( this.target.ownerDocument.location.href, true );
  462.     },
  463.     // Save URL of clicked-on link.
  464.     saveLink : function () {
  465.         this.savePage( this.linkURL(), false );
  466.     },
  467.     // Save URL of clicked-on image.
  468.     saveImage : function () {
  469.         this.savePage( this.imageURL, true );
  470.     },
  471.     // Save URL of background image.
  472.     saveBGImage : function () {
  473.         this.savePage( this.bgImageURL(), true );
  474.     },
  475.     // Generate link URL and put it on clibboard.
  476.     copyLink : function () {
  477.         this.copyToClipboard( this.linkURL() );
  478.     },
  479.     // Generate image URL and put it on the clipboard.
  480.     copyImage : function () {
  481.         this.copyToClipboard( this.imageURL );
  482.     },
  483.  
  484.     // Capture the values that are filled in on the form being displayed.
  485.     capture : function () {
  486.       if( appCore ) {
  487.         status = appCore.walletRequestToCapture(window._content);
  488.       }
  489.     },
  490.     // Prefill the form being displayed.
  491.     prefill : function () {
  492.       if( appCore ) {
  493.         appCore.walletPreview(window, window._content);
  494.       }
  495.     },
  496.  
  497.     // Determine if "Save Form Data" is to appear in the menu.
  498.     okToCapture: function () {
  499.       this.disableCapture = false;
  500.       var rv = false;
  501.       if (!window._content.document) {
  502.         return false;
  503.       }
  504.       // test for a form with at least one text element that has a value
  505.       var formsArray = this.target.ownerDocument.forms;
  506.       if (!formsArray) {
  507.         return false;
  508.       }
  509.       var form;
  510.       for (form=0; form<formsArray.length; form++) {
  511.         var elementsArray = formsArray[form].elements;
  512.         var element;
  513.         for (element=0; element<elementsArray.length; element++) {
  514.           var type = elementsArray[element].type;
  515.           if (type=="" || type=="text") {
  516.             // we have a form with at least one text element
  517.             var value = elementsArray[element].value;
  518.             rv = true;
  519.             if (value != "") {
  520.               // at least one text element has a value, thus capture is to appear in menu
  521.               return rv;
  522.             }
  523.           } 
  524.         }
  525.       }
  526.       // if we got here, then there was no text element with a value
  527.       if (rv) {
  528.         // if we got here, then we had a form with at least one text element
  529.         // in that case capture is to appear in menu but will be disabled
  530.         this.disableCapture = true;
  531.       }
  532.       return rv;
  533.     },
  534.  
  535.     // Determine if "Prefill Form" is to appear in the menu.
  536.     okToPrefill: function () {
  537.       if (!window._content.document) {
  538.         return false;
  539.       }
  540.       var formsArray = this.target.ownerDocument.forms;
  541.       if (!formsArray) {
  542.         return false;
  543.       }
  544.       var form;
  545.       for (form=0; form<formsArray.length; form++) {
  546.         var elementsArray = formsArray[form].elements;
  547.         var element;
  548.         for (element=0; element<elementsArray.length; element++) {
  549.           var type = elementsArray[element].type;
  550.           var value = elementsArray[element].value;
  551.           if (type=="" || type=="text" || type=="select-one") {
  552.             return true;
  553.           }
  554.         }
  555.       }
  556.       return false;
  557.     },
  558.  
  559.     // Determine if "Block Image" is to appear in the menu.
  560.     // Return true if "imageBlocker.enabled" pref is set and image is not already blocked.
  561.     isBlockingImages: function () {
  562.         /* determine if "imageBlocker.enabled" pref is set */
  563.         var pref = this.getService( '@mozilla.org/preferences;1', 'nsIPref' );
  564.         var result = false;
  565.         try {
  566.            result = pref.GetBoolPref( "imageblocker.enabled" );
  567.         } catch(e) {
  568.         }
  569.         if (!result) {
  570.           /* pref is not set so return false */
  571.           return false;
  572.         }
  573.  
  574.         /* determine if image is already being blocked */
  575.         var cookieViewer = this.createInstance
  576.           ("@mozilla.org/cookieviewer/cookieviewer-world;1", "nsICookieViewer");
  577.         var list = cookieViewer.GetPermissionValue(1);
  578.         var permissionList  = list.split(list[0]);
  579.         for(var i = 1; i < permissionList.length; i+=2) {
  580.           permStr = permissionList[i+1];
  581.           var type = permStr.substring(0,1);
  582.           if (type == "-") {
  583.             /* some host is being blocked, need to find out if it's our image's host */
  584.             var host = permStr.substring(1,permStr.length);
  585.             if (this.imageURL.search(host) != -1) {
  586.               /* it's our image's host that's being blocked */
  587.               return false;
  588.             }
  589.           }
  590.         }  
  591.         /* image is not already being blocked, so "Block Image" can appear on the menu */
  592.         return true;
  593.     },
  594.     // Block image from loading in the future.
  595.     blockImage : function () {
  596.         var cookieViewer = this.createInstance( "@mozilla.org/cookieviewer/cookieviewer-world;1",
  597.                                                 "nsICookieViewer" );
  598.         cookieViewer.BlockImage(this.imageURL);
  599.     },
  600.  
  601.     ///////////////
  602.     // Utilities //
  603.     ///////////////
  604.  
  605.     // Create instance of component given contractId and iid (as string).
  606.     createInstance : function ( contractId, iidName ) {
  607.         var iid = Components.interfaces[ iidName ];
  608.         return Components.classes[ contractId ].createInstance( iid );
  609.     },
  610.     // Get service given contractId and iid (as string).
  611.     getService : function ( contractId, iidName ) {
  612.         var iid = Components.interfaces[ iidName ];
  613.         return Components.classes[ contractId ].getService( iid );
  614.     },
  615.     // Show/hide one item (specified via name or the item element itself).
  616.     showItem : function ( itemOrId, show ) {
  617.         var item = null;
  618.         if ( itemOrId.constructor == String ) {
  619.             // Argument specifies item id.
  620.             item = document.getElementById( itemOrId );
  621.         } else {
  622.             // Argument is the item itself.
  623.             item = itemOrId;
  624.         }
  625.         if ( item ) {
  626.             var styleIn = item.getAttribute( "style" );
  627.             var styleOut = styleIn;
  628.             if ( show ) {
  629.                 // Remove style="display:none;".
  630.                 styleOut = styleOut.replace( "display:none;", "" );
  631.  
  632.             } else {
  633.                 // Set style="display:none;".
  634.                 if ( styleOut.indexOf( "display:none;" ) == -1 ) {
  635.                     // Add style the first time we need to.
  636.                     styleOut += "display:none;";
  637.                 }
  638.             }
  639.             // Only set style if it's different.
  640.             if ( styleIn != styleOut ) {
  641.                 item.setAttribute( "style", styleOut );
  642.             }
  643.         }
  644.     },
  645.     // Set given attribute of specified context-menu item.  If the
  646.     // value is null, then it removes the attribute (which works
  647.     // nicely for the disabled attribute).
  648.     setItemAttr : function ( id, attr, val ) {
  649.         var elem = document.getElementById( id );
  650.         if ( elem ) {
  651.             if ( val == null ) {
  652.                 // null indicates attr should be removed.
  653.                 elem.removeAttribute( attr );
  654.             } else {
  655.                 // Set attr=val.
  656.                 elem.setAttribute( attr, val );
  657.             }
  658.         }
  659.     },
  660.     // Set context menu attribute according to like attribute of another node
  661.     // (such as a broadcaster).
  662.     setItemAttrFromNode : function ( item_id, attr, other_id ) {
  663.         var elem = document.getElementById( other_id );
  664.         if ( elem && elem.getAttribute( attr ) == "true" ) {
  665.             this.setItemAttr( item_id, attr, "true" );
  666.         } else {
  667.             this.setItemAttr( item_id, attr, null );
  668.         }
  669.     },
  670.     // Temporary workaround for DOM api not yet implemented by XUL nodes.
  671.     cloneNode : function ( item ) {
  672.         // Create another element like the one we're cloning.
  673.         var node = document.createElement( item.tagName );
  674.     
  675.         // Copy attributes from argument item to the new one.
  676.         var attrs = item.attributes;
  677.         for ( var i = 0; i < attrs.length; i++ ) {
  678.             var attr = attrs.item( i );
  679.             node.setAttribute( attr.nodeName, attr.nodeValue );
  680.         }
  681.     
  682.         // Voila!
  683.         return node;
  684.     },
  685.     // Generate fully-qualified URL for clicked-on link.
  686.     linkURL : function () {
  687.         if (this.link.href) {
  688.           return this.link.href;
  689.         }
  690.         // XXX TODO Relative URLs, XML Base
  691.         var href = this.link.getAttributeNS("http://www.w3.org/1999/xlink","href");
  692.         if (href == "") {
  693.           throw "Empty href"; // Without this we try to save as the current doc, for example, HTML case also throws if empty
  694.         }
  695.         return href;
  696.     },
  697.     // Get text of link (if possible).
  698.     linkText : function () {
  699.         var text = this.gatherTextUnder( this.link );
  700.         return text;
  701.     },
  702.     // Gather all descendent text under given document node.
  703.     gatherTextUnder : function ( root ) {
  704.          var text = "";
  705.          var node = root.firstChild;
  706.          var depth = 1;
  707.          while ( node && depth > 0 ) {
  708.              // See if this node is text.
  709.              if ( node.nodeName == "#text" ) {
  710.                  // Add this text to our collection.
  711.                  text += " " + node.data;
  712.              } else if ( node.tagName == "IMG" ) {
  713.                  // If it has an alt= attribute, use that.
  714.                  altText = node.getAttribute( "alt" );
  715.                  if ( altText && altText != "" ) {
  716.                      text = altText;
  717.                      break;
  718.                  }
  719.              }
  720.              // Find next node to test.
  721.              // First, see if this node has children.
  722.              if ( node.hasChildNodes() ) {
  723.                  // Go to first child.
  724.                  node = node.firstChild;
  725.                  depth++;
  726.              } else {
  727.                  // No children, try next sibling.
  728.                  if ( node.nextSibling ) {
  729.                      node = node.nextSibling;
  730.                  } else {
  731.                      // Last resort is our next oldest uncle/aunt.
  732.                      node = node.parentNode.nextSibling;
  733.                      depth--;
  734.                  }
  735.              }
  736.          }
  737.          // Strip leading whitespace.
  738.          text = text.replace( /^\s+/, "" );
  739.          // Strip trailing whitespace.
  740.          text = text.replace( /\s+$/, "" );
  741.          // Compress remaining whitespace.
  742.          text = text.replace( /\s+/g, " " );
  743.          return text;
  744.     },
  745.     // Returns "true" if there's no text selected, null otherwise.
  746.     isNoTextSelected : function ( event ) {
  747.         // Not implemented so all text-selected-based options are disabled.
  748.         return "true";
  749.     },
  750.     // Copy link/image url to clipboard.
  751.     copyToClipboard : function ( text ) {
  752.         // Get clipboard.
  753.         var clipboard = this.getService( "@mozilla.org/widget/clipboard;1",
  754.                                          "nsIClipboard" );
  755.  
  756.         // Create tranferable that will transfer the text.
  757.         var transferable = this.createInstance( "@mozilla.org/widget/transferable;1",
  758.                                                 "nsITransferable" );
  759.  
  760.         if ( clipboard && transferable ) {
  761.           transferable.addDataFlavor( "text/unicode" );
  762.           // Create wrapper for text.
  763.           var data = this.createInstance( "@mozilla.org/supports-wstring;1",
  764.                                           "nsISupportsWString" );
  765.           if ( data ) {
  766.             data.data = text;
  767.             transferable.setTransferData( "text/unicode", data, text.length * 2 );
  768.             // Put on clipboard.
  769.             clipboard.setData( transferable, null, Components.interfaces.nsIClipboard.kGlobalClipboard );
  770.           }
  771.         }
  772.  
  773.         // Create a second transferable to copy selection.  Unix needs this,
  774.         // other OS's will probably map to a no-op.
  775.         var transferableForSelection = this.createInstance( "@mozilla.org/widget/transferable;1",
  776.                                                          "nsITransferable" );
  777.         
  778.         if ( clipboard && transferableForSelection ) {
  779.           transferableForSelection.addDataFlavor( "text/unicode" );
  780.           // Create wrapper for text.
  781.           var selectionData = this.createInstance( "@mozilla.org/supports-wstring;1",
  782.                                           "nsISupportsWString" );
  783.           if ( selectionData ) {
  784.             selectionData.data = text;
  785.             transferableForSelection.setTransferData( "text/unicode", selectionData, text.length * 2 );
  786.             // Put on clipboard.
  787.             clipboard.setData( transferableForSelection, null, 
  788.                                Components.interfaces.nsIClipboard.kSelectionClipboard );
  789.           }
  790.         }
  791.  
  792.  
  793.     },
  794.     // Save specified URL in user-selected file.
  795.     savePage : function ( url, doNotValidate ) {
  796.         var postData = null; // No post data, usually.
  797.         // Default is to save current page.
  798.         if ( !url ) {
  799.             url = window._content.location.href;
  800.             // Post data comes from appcore.
  801.             if ( window.appCore ) {
  802.                 postData = window.appCore.postData;
  803.             }
  804.         }
  805.         // Use stream xfer component to prompt for destination and save.
  806.         var xfer = this.getService( "@mozilla.org/appshell/component/xfer;1",
  807.                                     "nsIStreamTransfer" );
  808.         try {
  809.             xfer.SelectFileAndTransferLocationSpec( url, window, "", "", doNotValidate, postData );
  810.         } catch( exception ) {
  811.             // Failed (or cancelled), give them another chance.
  812.             dump( "SelectFileAndTransferLocationSpec failed, rv=" + exception + "\n" );
  813.         }
  814.         return;
  815.     },
  816.     // Parse coords= attribute and return array.
  817.     parseCoords : function ( area ) {
  818.         return [];
  819.     },
  820.     toString : function () {
  821.         return "contextMenu.target     = " + this.target + "\n" +
  822.                "contextMenu.onImage    = " + this.onImage + "\n" +
  823.                "contextMenu.onLink     = " + this.onLink + "\n" +
  824.                "contextMenu.link       = " + this.link + "\n" +
  825.                "contextMenu.inFrame    = " + this.inFrame + "\n" +
  826.                "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
  827.     },
  828.     isTargetATextField : function ( node )
  829.     {
  830.       if (node.tagName.toUpperCase() == "INPUT") {
  831.         var attrib = node.getAttribute("type").toUpperCase();
  832.         return( (attrib != "IMAGE") &&
  833.                 (attrib != "PASSWORD") &&
  834.                 (attrib != "CHECKBOX") &&
  835.                 (attrib != "RADIO") &&
  836.                 (attrib != "SUBMIT") &&
  837.                 (attrib != "RESET") &&
  838.                 (attrib != "FILE") &&
  839.                 (attrib != "HIDDEN") &&
  840.                 (attrib != "RESET") &&
  841.                 (attrib != "BUTTON") );
  842.       } else  {
  843.         return(node.tagName.toUpperCase() == "TEXTAREA");
  844.       }
  845.     }
  846. };
  847.