home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 March / PCWorld_2005-03_cd.bin / komunikace / kmeleon / kmeleon09.exe / comm.jar / content / navigator / pageInfo.js < prev    next >
Text File  |  2004-01-22  |  38KB  |  1,094 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public
  6.  * License Version 1.1 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * The Original Code is mozilla.org code.
  16.  *
  17.  * The Initial Developer of the Original Code is Netscape
  18.  * Communications Corporation.  Portions created by Netscape are
  19.  * Copyright (C) 1998 Netscape Communications Corporation. All
  20.  * Rights Reserved.
  21.  *
  22.  * Contributor(s): smorrison@gte.com
  23.  *   Terry Hayes <thayes@netscape.com>
  24.  *   Daniel Brooks <db48x@yahoo.com>
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or 
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the NPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the NPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK *****
  39. */
  40.  
  41. //******** define a js object to implement nsITreeView
  42. function pageInfoTreeView(columnids, copycol)
  43. {
  44.   // columnids is an array of strings indicating the names of the columns, in order
  45.   this.columnids = columnids;
  46.   this.colcount = columnids.length
  47.  
  48.   // copycol is the index number for the column that we want to add to 
  49.   // the copy-n-paste buffer when the user hits accel-c
  50.   this.copycol = copycol;
  51.   this.rows = 0;
  52.   this.tree = null;
  53.   this.data = new Array;
  54.   this.selection = null;
  55.   this.sortcol = null;
  56.   this.sortdir = 0;
  57. }
  58.  
  59. pageInfoTreeView.prototype = {
  60.   set rowCount(c) { throw "rowCount is a readonly property"; },
  61.   get rowCount() { return this.rows; },
  62.  
  63.   setTree: function(tree) 
  64.   {
  65.     this.tree = tree;
  66.   },
  67.  
  68.   getCellText: function(row, column)
  69.   {
  70.     var colidx = 0;
  71.     // loop through the list of column names to find the index to the column 
  72.     // we should be worrying about. very much a hack, but what can you do?
  73.     while(colidx < this.colcount && column != this.columnids[colidx])
  74.       colidx++;
  75.  
  76.     // row can be null, but js arrays are 0-indexed.
  77.     // colidx cannot be null, but can be larger than the number
  78.     // of columns in the array (when column is a string not in 
  79.     // this.columnids.) In this case it's the fault of
  80.     // whoever typoed while calling this function.
  81.     return this.data[row][colidx] || "";
  82.   },
  83.  
  84.   setCellText: function(row, column, value) 
  85.   {
  86.     var colidx = 0;
  87.     // loop through the list of column names to find the index 
  88.     // to the column the should be worrying about. very much a hack, but
  89.     // what can you do? 
  90.     // XXX: I think there's a better way to do this now...
  91.     while(colidx < this.colcount && column != this.columnids[colidx])
  92.       colidx++;
  93.     this.data[row][colidx] = value;
  94.   },
  95.  
  96.   addRow: function(row)
  97.   {
  98.     this.rows = this.data.push(row);
  99.     this.rowCountChanged(this.rows - 1, 1);
  100.   },
  101.  
  102.   addRows: function(rows)
  103.   {
  104.     var length = rows.length;
  105.     for(var i = 0; i < length; i++)
  106.       this.rows = this.data.push(rows[i]);
  107.     this.rowCountChanged(this.rows - length, length);
  108.   },
  109.  
  110.   rowCountChanged: function(index, count)
  111.   {
  112.     this.tree.rowCountChanged(index, count);
  113.   },
  114.  
  115.   invalidate: function()
  116.   {
  117.     this.tree.invalidate();
  118.   },
  119.  
  120.   clear: function()
  121.   {
  122.     this.data = new Array;
  123.     this.rows = 0;
  124.   },
  125.  
  126.   handleCopy: function(row)
  127.   {
  128.     return (row < 0 || this.copycol < 0) ? "" : (this.data[row][this.copycol] || "");
  129.   },
  130.  
  131.   performActionOnRow: function(action, row)
  132.   {
  133.     if (action == "copy")
  134.     {
  135.       var data = this.handleCopy(row)
  136.       this.tree.treeBody.parentNode.setAttribute("copybuffer", data);
  137.     }
  138.   },
  139.  
  140.   getRowProperties: function(row, column, prop) { },
  141.   getCellProperties: function(row, prop) { },
  142.   getColumnProperties: function(column, elem, prop) { },
  143.   isContainer: function(index) { return false; },
  144.   isContainerOpen: function(index) { return false; },
  145.   isSeparator: function(index) { return false; },
  146.   isSorted: function() { },
  147.   canDropOn: function(index) { return false; },
  148.   canDropBeforeAfter: function(index, before) { return false; },
  149.   drop: function(row, orientation) { return false; },
  150.   getParentIndex: function(index) { return 0; },
  151.   hasNextSibling: function(index, after) { return false; },
  152.   getLevel: function(index) { return 0; },
  153.   getImageSrc: function(row, column) { },
  154.   getProgressMode: function(row, column) { },
  155.   getCellValue: function(row, column) { },
  156.   toggleOpenState: function(index) { },
  157.   cycleHeader: function(col, elem) { },
  158.   selectionChanged: function() { },
  159.   cycleCell: function(row, column) { },
  160.   isEditable: function(row, column) { return false; },
  161.   performAction: function(action) { },
  162.   performActionOnCell: function(action, row, column) { }
  163. };
  164.  
  165. // mmm, yummy. global variables.
  166. var theWindow = null;
  167. var theDocument = null;
  168.  
  169. // column number to copy from, second argument to pageInfoTreeView's constructor
  170. const COPYCOL_NONE = -1;
  171. const COPYCOL_META_CONTENT = 1;
  172. const COPYCOL_FORM_ACTION = 2;
  173. const COPYCOL_LINK_ADDRESS = 1;
  174. const COPYCOL_IMAGE_ADDRESS = 0;
  175.  
  176. // one nsITreeView for each tree in the window
  177. var metaView = new pageInfoTreeView(["meta-name","meta-content"], COPYCOL_META_CONTENT);
  178. var formView = new pageInfoTreeView(["form-name","form-method","form-action","form-node"], COPYCOL_FORM_ACTION);
  179. var fieldView = new pageInfoTreeView(["field-label","field-field","field-type","field-value"], COPYCOL_NONE);
  180. var linkView = new pageInfoTreeView(["link-name","link-address","link-type","link-accesskey"], COPYCOL_LINK_ADDRESS);
  181. var imageView = new pageInfoTreeView(["image-address","image-type","image-alt","image-node", "image-bg"], COPYCOL_IMAGE_ADDRESS);
  182.  
  183. var intervalID = null;
  184.  
  185. // localized strings (will be filled in when the document is loaded)
  186. // this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
  187. var gStrings = {}
  188. var theBundle;
  189.  
  190. const DRAGSERVICE_CONTRACTID    = "@mozilla.org/widget/dragservice;1";
  191. const TRANSFERABLE_CONTRACTID   = "@mozilla.org/widget/transferable;1";
  192. const ARRAY_CONTRACTID          = "@mozilla.org/supports-array;1";
  193. const STRING_CONTRACTID         = "@mozilla.org/supports-string;1";
  194.  
  195. // a number of services I'll need later
  196. // the cache services
  197. const nsICacheService = Components.interfaces.nsICacheService;
  198. const cacheService = Components.classes["@mozilla.org/network/cache-service;1"].getService(nsICacheService);
  199. var httpCacheSession = cacheService.createSession("HTTP", 0, true);
  200. httpCacheSession.doomEntriesIfExpired = false;
  201. var ftpCacheSession = cacheService.createSession("FTP", 0, true);
  202. ftpCacheSession.doomEntriesIfExpired = false;
  203.  
  204. // scriptable date formater, for pretty printing dates
  205. const nsIScriptableDateFormat = Components.interfaces.nsIScriptableDateFormat;
  206. var dateService = Components.classes["@mozilla.org/intl/scriptabledateformat;1"].getService(nsIScriptableDateFormat);
  207.  
  208. // clipboard helper
  209. try
  210. {
  211.   const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
  212. }
  213. catch(e)
  214. {
  215.   // do nothing, later code will handle the error
  216. }
  217.  
  218. // interfaces for the different html elements
  219. const nsIAnchorElement   = Components.interfaces.nsIDOMHTMLAnchorElement
  220. const nsIImageElement    = Components.interfaces.nsIDOMHTMLImageElement
  221. const nsIAreaElement     = Components.interfaces.nsIDOMHTMLAreaElement
  222. const nsILinkElement     = Components.interfaces.nsIDOMHTMLLinkElement
  223. const nsIInputElement    = Components.interfaces.nsIDOMHTMLInputElement
  224. const nsIFormElement     = Components.interfaces.nsIDOMHTMLFormElement
  225. const nsIAppletElement   = Components.interfaces.nsIDOMHTMLAppletElement
  226. const nsIObjectElement   = Components.interfaces.nsIDOMHTMLObjectElement
  227. const nsIEmbedElement    = Components.interfaces.nsIDOMHTMLEmbedElement
  228. const nsIButtonElement   = Components.interfaces.nsIDOMHTMLButtonElement
  229. const nsISelectElement   = Components.interfaces.nsIDOMHTMLSelectElement
  230. const nsITextareaElement = Components.interfaces.nsIDOMHTMLTextAreaElement
  231.  
  232. // Interface for image loading content
  233. const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
  234.  
  235. // namespaces, don't need all of these yet...
  236. const XLinkNS  = "http://www.w3.org/1999/xlink";
  237. const XULNS    = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  238. const XMLNS    = "http://www.w3.org/XML/1998/namespace";
  239. const XHTMLNS  = "http://www.w3.org/1999/xhtml";
  240. const XHTML2NS = "http://www.w3.org/2002/06/xhtml2"
  241.  
  242. const XHTMLNSre  = "^http\:\/\/www\.w3\.org\/1999\/xhtml$";
  243. const XHTML2NSre = "^http\:\/\/www\.w3\.org\/2002\/06\/xhtml2$";
  244. const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");
  245.  
  246. /* Overlays register init functions here.
  247.  *   Add functions to call by invoking "onLoadRegistry.append(XXXLoadFunc);"
  248.  *   The XXXLoadFunc should be unique to the overlay module, and will be
  249.  *   invoked as "XXXLoadFunc();"
  250.  */
  251. var onLoadRegistry = [ ];
  252.  
  253. /* Called when PageInfo window is loaded.  Arguments are:
  254.  *  window.arguments[0] - document to use for source (null=Page Info, otherwise Frame Info)
  255.  *  window.arguments[1] - tab name to display first (may be null)
  256. */
  257. function onLoadPageInfo()
  258. {
  259.   //dump("===============================================================================\n");
  260.  
  261.   theBundle = document.getElementById("pageinfobundle");
  262.   gStrings.unknown = theBundle.getString("unknown");
  263.   gStrings.notSet = theBundle.getString("notset");
  264.   gStrings.emptyString = theBundle.getString("emptystring");
  265.   gStrings.linkAnchor = theBundle.getString("linkAnchor");
  266.   gStrings.linkArea = theBundle.getString("linkArea");
  267.   gStrings.linkSubmit = theBundle.getString("linkSubmit");
  268.   gStrings.linkSubmission = theBundle.getString("linkSubmission");
  269.   gStrings.linkRel = theBundle.getString("linkRel");
  270.   gStrings.linkStylesheet = theBundle.getString("linkStylesheet");
  271.   gStrings.linkRev = theBundle.getString("linkRev");
  272.   gStrings.linkX = theBundle.getString("linkX");
  273.   gStrings.mediaImg = theBundle.getString("mediaImg");
  274.   gStrings.mediaBGImg = theBundle.getString("mediaBGImg");
  275.   gStrings.mediaApplet = theBundle.getString("mediaApplet");
  276.   gStrings.mediaObject = theBundle.getString("mediaObject");
  277.   gStrings.mediaEmbed = theBundle.getString("mediaEmbed");
  278.   gStrings.mediaLink = theBundle.getString("mediaLink");
  279.   gStrings.mediaInput = theBundle.getString("mediaInput");
  280.  
  281.   var docTitle = "";
  282.   if ("arguments" in window && window.arguments.length >= 1 && window.arguments[0])
  283.   {
  284.     theWindow = null;
  285.     theDocument = window.arguments[0];
  286.     docTitle = theBundle.getString("frameInfo.title");
  287.   } 
  288.   else 
  289.   {
  290.     if ("gBrowser" in window.opener)
  291.       theWindow = window.opener.gBrowser.contentWindow;
  292.     else
  293.       theWindow = window.opener.frames[0];
  294.     theDocument = theWindow.document;
  295.  
  296.     docTitle = theBundle.getString("pageInfo.title");
  297.   }
  298.  
  299.   document.title = docTitle;
  300.  
  301.   // do the easy stuff first
  302.   makeGeneralTab();
  303.  
  304.   // and then the hard stuff
  305.   makeTabs(theDocument, theWindow);
  306.  
  307.   /* Call registered overlay init functions */
  308.   for (x in onLoadRegistry)
  309.   {
  310.     onLoadRegistry[x]();
  311.   }
  312.  
  313.   /* Select the requested tab, if the name is specified */
  314.   if ("arguments" in window && window.arguments.length > 1)
  315.   {
  316.     var tabName = window.arguments[1];
  317.  
  318.     if (tabName)
  319.     {
  320.       var tabControl = document.getElementById("tabbox");
  321.       var tab = document.getElementById(tabName);
  322.  
  323.       if (tabControl && tab)
  324.       {
  325.         tabControl.selectedTab = tab;
  326.       }
  327.     }
  328.   }
  329. }
  330.  
  331. function doHelpButton() {
  332.   var helpdoc;
  333.   var tabControl = document.getElementById("tabbox");
  334.   switch (tabControl.selectedTab.id) {
  335.     case "generalTab":
  336.       helpdoc = "pageinfo_general";
  337.       break;
  338.     case "formsTab":
  339.       helpdoc = "pageinfo_forms";
  340.       break;
  341.     case "linksTab":
  342.       helpdoc = "pageinfo_links";
  343.       break;
  344.     case "mediaTab":
  345.       helpdoc = "pageinfo_media";
  346.       break;
  347.     case "securityTab":
  348.       helpdoc = "pageinfo_security";
  349.       break;
  350.     case "p3pTab":
  351.       helpdoc = "pageinfo_privacy";
  352.       break;
  353.     default:
  354.       helpdoc = "pageinfo_general";
  355.       break;
  356.   }
  357.   openHelp(helpdoc);  
  358. }
  359.  
  360. function makeGeneralTab()
  361. {
  362.   var title = (theDocument.title) ? theBundle.getFormattedString("pageTitle", [theDocument.title]) : theBundle.getString("noPageTitle");
  363.   document.getElementById("titletext").value = title;
  364.  
  365.   var url = theDocument.location;
  366.   document.getElementById("urltext").value = url;
  367.  
  368.   var mode = ("compatMode" in theDocument && theDocument.compatMode == "BackCompat") ? theBundle.getString("generalQuirksMode") : theBundle.getString("generalStrictMode");
  369.   document.getElementById("modetext").value = mode;
  370.  
  371.   var referrer = ("referrer" in theDocument && theDocument.referrer) || theBundle.getString("generalNoReferrer");
  372.   document.getElementById('refertext').value = referrer;
  373.  
  374.   // find out the mime type
  375.   var mimeType = theDocument.contentType || gStrings.unknown;
  376.   document.getElementById("typetext").value = mimeType;
  377.   
  378.   // get the meta tags
  379.   var metaNodes = theDocument.getElementsByTagName("meta");
  380.   var metaTree = document.getElementById("metatree");
  381.  
  382.   metaTree.treeBoxObject.view = metaView;
  383.  
  384.   var length = metaNodes.length;
  385.   for (var i = 0; i < length; i++)
  386.     metaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv, metaNodes[i].content]);
  387.  
  388.   // get the document characterset
  389.   var encoding = theDocument.characterSet;
  390.   document.getElementById("encodingtext").value = encoding;
  391.  
  392.   // get the date of last modification
  393.   var modifiedText = formatDate(theDocument.lastModified, gStrings.notSet);
  394.   document.getElementById("modifiedtext").value = modifiedText;
  395.   
  396.   // get cache info
  397.   var sourceText = theBundle.getString("generalNotCached");
  398.   var expirationText = theBundle.getString("generalNoExpiration");
  399.   var sizeText = gStrings.unknown;
  400.  
  401.   var pageSize = 0; 
  402.   var kbSize = 0;
  403.   var expirationTime = 0;
  404.  
  405.   try
  406.   {
  407.     var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  408.     if (cacheEntryDescriptor)
  409.     { 
  410.       switch(cacheEntryDescriptor.deviceID)
  411.       {
  412.         case "disk":
  413.           sourceText = theBundle.getString("generalDiskCache");
  414.           break;
  415.         case "memory":
  416.           sourceText = theBundle.getString("generalMemoryCache");
  417.           break;
  418.         default:
  419.           sourceText = cacheEntryDescriptor.deviceID;
  420.           break;
  421.       }
  422.  
  423.       pageSize = cacheEntryDescriptor.dataSize;
  424.       kbSize = pageSize / 1024;
  425.       sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  426.  
  427.       expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, gStrings.notSet);
  428.     }
  429.   }
  430.   catch(ex)
  431.   {
  432.     try
  433.     {
  434.       cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  435.       if (cacheEntryDescriptor)
  436.       {
  437.         switch(cacheEntryDescriptor.deviceID)
  438.         {
  439.           case "disk":
  440.             sourceText = theBundle.getString("generalDiskCache");
  441.             break;
  442.           case "memory":
  443.             sourceText = theBundle.getString("generalMemoryCache");
  444.             break;
  445.           default:
  446.             sourceText = cacheEntryDescriptor.deviceID;
  447.             break;
  448.         }
  449.  
  450.         pageSize = cacheEntryDescriptor.dataSize;
  451.         kbSize = pageSize / 1024;
  452.         sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  453.  
  454.         expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, gStrings.notSet);
  455.       }
  456.     }
  457.     catch(ex2)
  458.     {
  459.       sourceText = theBundle.getString("generalNotCached");
  460.     }
  461.   }
  462.   document.getElementById("sourcetext").value = sourceText;
  463.   document.getElementById("expirestext").value = expirationText;
  464.   document.getElementById("sizetext").value = sizeText;
  465. }
  466.  
  467. //******** Generic Build-a-tab
  468. // Assumes the views are empty. Only called once to build the tabs, and
  469. // does so by farming the task off to another thread via setTimeout().
  470. // The actual work is done with a TreeWalker that calls doGrab() once for
  471. // each element node in the document.
  472.  
  473. function makeTabs(aDocument, aWindow)
  474. {
  475.   if (aWindow && aWindow.frames.length > 0)
  476.   {
  477.     var num = aWindow.frames.length;
  478.     for (var i = 0; i < num; i++)
  479.       makeTabs(aWindow.frames[i].document, aWindow.frames[i]);  // recurse through the frames
  480.   }
  481.  
  482.   var formTree = document.getElementById("formtree");
  483.   var linkTree = document.getElementById("linktree");
  484.   var imageTree = document.getElementById("imagetree");
  485.  
  486.   formTree.treeBoxObject.view = formView;
  487.   linkTree.treeBoxObject.view = linkView;
  488.   imageTree.treeBoxObject.view = imageView;
  489.   
  490.   var iterator = aDocument.createTreeWalker(aDocument, NodeFilter.SHOW_ELEMENT, grabAll, true);
  491.  
  492.   var meter = document.getElementById("piProgress");
  493.  
  494.   meter.setAttribute("value", 1);
  495.  
  496.   setTimeout(doGrab, 1, iterator, meter, 0);
  497. }
  498.  
  499. function doGrab(iterator, meter, i)
  500. {
  501.   if (iterator.nextNode())
  502.   {
  503.     setTimeout(doGrab, 1, iterator, meter, i);
  504.   }
  505.   else
  506.   {
  507.     meter.setAttribute("value", 0);
  508.     meter.setAttribute("mode", "determined");
  509.     meter.setAttribute("hidden", "true");
  510.     document.getElementById("piSpacer").setAttribute("flex", 1);
  511.   }
  512. }
  513.  
  514. function ensureSelection(view)
  515. {
  516.   // only select something if nothing is currently selected
  517.   // and if there's anything to select
  518.   if (view.selection.count == 0 && view.rowCount)
  519.     view.selection.select(0);
  520. }
  521.  
  522. function grabAll(elem)
  523. {
  524.   var linktext;
  525.  
  526.   // check for background images, any node may have one
  527.   var url = elem.ownerDocument.defaultView.getComputedStyle(elem, "").getPropertyCSSValue("background-image");
  528.   if (url && url.primitiveType == CSSPrimitiveValue.CSS_URI)
  529.     imageView.addRow([url.getStringValue(), gStrings.mediaBGImg, gStrings.notSet, elem, true]);
  530.  
  531.   // one swi^H^H^Hif-else to rule them all
  532.   if (elem instanceof nsIAnchorElement)
  533.   {
  534.     linktext = getValueText(elem);
  535.     linkView.addRow([linktext, getAbsoluteURL(elem.href, elem), gStrings.linkAnchor, elem.target, elem.accessKey]);
  536.   }
  537.   else if (elem instanceof nsIImageElement)
  538.   {
  539.     imageView.addRow([getAbsoluteURL(elem.src, elem), gStrings.mediaImg, (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false]);
  540.   }
  541.   else if (elem instanceof nsIAreaElement)
  542.   {
  543.     linkView.addRow([elem.alt, getAbsoluteURL(elem.href, elem), gStrings.linkArea, elem.target]);
  544.   }
  545.   else if (elem instanceof nsILinkElement)
  546.   {
  547.     if (elem.rel)
  548.     {
  549.       var rel = elem.rel;
  550.       if (/\bicon\b/i.test(rel))
  551.         imageView.addRow([getAbsoluteURL(elem.href, elem), gStrings.mediaLink, "", elem, false]);
  552.       else if (/\bstylesheet\b/i.test(rel))
  553.         linkView.addRow([elem.rel, getAbsoluteURL(elem.href, elem), gStrings.linkStylesheet, elem.target]);
  554.       else
  555.         linkView.addRow([elem.rel, getAbsoluteURL(elem.href, elem), gStrings.linkRel, elem.target]);
  556.     }
  557.     else
  558.       linkView.addRow([elem.rev, getAbsoluteURL(elem.href, elem), gStrings.linkRev, elem.target]);
  559.  
  560.   }
  561.   else if (elem instanceof nsIInputElement)
  562.   {
  563.     if (/^image$/i.test(elem.type))
  564.       imageView.addRow([getAbsoluteURL(elem.src, elem), gStrings.mediaInput, (elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false]);
  565.     else if (/^submit$/i.test(elem.type))
  566.       linkView.addRow([elem.value || gStrings.linkSubmit, getAbsoluteURL(elem.form.getAttribute("action"), elem), gStrings.linkSubmission, elem.form.getAttribute("target")]); // use getAttribute() due to bug 122128
  567.   }
  568.   else if (elem instanceof nsIFormElement)
  569.   {
  570.     formView.addRow([elem.name, elem.method, getAbsoluteURL(elem.getAttribute("action"), elem), elem]);  // use getAttribute() because of bug 122128
  571.   }
  572.   else if (elem instanceof nsIAppletElement)
  573.   {
  574.     //XXX When Java is enabled, the DOM model for <APPLET> is broken. Bug #59686.
  575.     // Also, some reports of a crash with Java in Media tab (bug 136535), and mixed
  576.     // content from two hosts (bug 136539) so just drop applets from Page Info when
  577.     // Java is on. For the 1.0.1 branch; get a real fix on the trunk.
  578.     if (!navigator.javaEnabled())
  579.       imageView.addRow([getAbsoluteURL(elem.code || elem.object, elem), gStrings.mediaApplet, "", elem, false]);
  580.   }
  581.   else if (elem instanceof nsIObjectElement)
  582.   {
  583.     imageView.addRow([getAbsoluteURL(elem.data, elem), gStrings.mediaObject, getValueText(elem), elem, false]);
  584.   }
  585.   else if (elem instanceof nsIEmbedElement)
  586.   {
  587.     imageView.addRow([getAbsoluteURL(elem.src, elem), gStrings.mediaEmbed, "", elem, false]);
  588.   }
  589.   else
  590.     if (elem.hasAttributeNS(XLinkNS, "href"))
  591.     {
  592.       linktext = getValueText(elem);
  593.       linkView.addRow([linktext, getAbsoluteURL(elem.href, elem), gStrings.linkX, ""]);
  594.     }
  595.  
  596.   return NodeFilter.FILTER_ACCEPT;
  597. }
  598.  
  599. //******** Form Stuff
  600. function onFormSelect()
  601. {
  602.   var formTree = document.getElementById("formtree");
  603.   if (!formView.rowCount) return;
  604.  
  605.   if (formView.selection.count == 1)
  606.   {
  607.     var formPreview = document.getElementById("formpreview");
  608.     fieldView.clear();
  609.     formPreview.treeBoxObject.view = fieldView;
  610.  
  611.     var clickedRow = formView.selection.currentIndex;
  612.     var form = formView.getCellText(clickedRow, "form-node");
  613.  
  614.     var ft = null;
  615.     if (form.name)
  616.       ft = theBundle.getFormattedString("formTitle", [form.name]);
  617.  
  618.     document.getElementById("formname").value = ft || theBundle.getString("formUntitled");
  619.     document.getElementById("formenctype").value = form.encoding || theBundle.getString("default");
  620.     document.getElementById("formtarget").value = form.target || theBundle.getString("formDefaultTarget");
  621.  
  622.     var formfields = form.elements;
  623.  
  624.     var length = formfields.length;
  625.     var i = 0;
  626.  
  627.     var checked = theBundle.getString("formChecked");
  628.     var unchecked = theBundle.getString("formUnchecked");    
  629.  
  630.     for (i = 0; i < length; i++)
  631.     {
  632.       var elem = formfields[i], val;
  633.  
  634.       if (elem instanceof nsIButtonElement)
  635.         val = getValueText(elem);
  636.       else
  637.         val = (/^password$/i.test(elem.type)) ? theBundle.getString("formPassword") : elem.value;
  638.  
  639.       fieldView.addRow(["", elem.name, elem.type, val]);
  640.     }
  641.  
  642.     var labels = form.getElementsByTagName("label");
  643.     var llength = labels.length;
  644.     var label;
  645.  
  646.     for (i = 0; i < llength; i++)
  647.     {
  648.       label = labels[i];
  649.       var whatfor = label.hasAttribute("for") ?
  650.         theDocument.getElementById(label.getAttribute("for")) :
  651.         findFirstControl(label);
  652.  
  653.       if (whatfor && (whatfor.form == form)) 
  654.       {
  655.         var labeltext = getValueText(label);
  656.         for (var j = 0; j < length; j++)
  657.           if (formfields[j] == whatfor)
  658.             fieldView.setCellText(j, "field-label", labeltext);
  659.       }
  660.     }
  661.   }
  662. }
  663.  
  664. function FormControlFilter(node) 
  665. {
  666.   if (node instanceof nsIInputElement || node instanceof nsISelectElement ||
  667.       node instanceof nsIButtonElement || node instanceof nsITextareaElement ||
  668.       node instanceof nsIObjectElement)
  669.       return NodeFilter.FILTER_ACCEPT;
  670.     return NodeFilter.FILTER_SKIP;
  671. }
  672.  
  673. function findFirstControl(node)
  674. {
  675.   var iterator = theDocument.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, FormControlFilter, true);
  676.  
  677.   return iterator.nextNode();
  678. }
  679.  
  680. //******** Link Stuff
  681. function openURL(target)
  682. {
  683.   var url = target.parentNode.childNodes[2].value;
  684.   window.open(url, "_blank", "chrome");
  685. }
  686.  
  687. function onBeginLinkDrag(event,urlField,descField)
  688. {
  689.   if (event.originalTarget.localName != "treechildren")
  690.     return;
  691.  
  692.   var tree = event.target;
  693.   if (!("treeBoxObject" in tree))
  694.     tree = tree.parentNode;
  695.  
  696.   var row = {};
  697.   var col = {};
  698.   var elt = {};
  699.   tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, elt);
  700.   if (row.value == -1)
  701.     return;
  702.  
  703.   // Getting drag-system needed services
  704.   var dragService = Components.classes[DRAGSERVICE_CONTRACTID].getService().QueryInterface(Components.interfaces.nsIDragService);
  705.   var transArray = Components.classes[ARRAY_CONTRACTID].createInstance(Components.interfaces.nsISupportsArray);
  706.   if (!transArray)
  707.     return;
  708.   var trans = Components.classes[TRANSFERABLE_CONTRACTID].createInstance(Components.interfaces.nsITransferable);
  709.   if (!trans)
  710.     return;
  711.   
  712.   // Adding URL flavor
  713.   trans.addDataFlavor("text/x-moz-url");
  714.   var url = tree.treeBoxObject.view.getCellText(row.value, urlField);
  715.   var desc = tree.treeBoxObject.view.getCellText(row.value, descField);
  716.   var stringURL = Components.classes[STRING_CONTRACTID].createInstance(Components.interfaces.nsISupportsString);
  717.   stringURL.data = url + "\n"+desc;
  718.   trans.setTransferData("text/x-moz-url", stringURL, stringURL.data.length * 2 );
  719.   transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
  720.  
  721.   dragService.invokeDragSession(event.target, transArray, null, dragService.DRAGDROP_ACTION_NONE);
  722. }
  723.  
  724. //******** Image Stuff
  725. function getSource(item)
  726. {
  727.   // Return the correct source without strict warnings
  728.   if ("href" in item && item.href)
  729.     return item.href;
  730.   if ("src" in item && item.src)
  731.     return item.src;
  732.   return null;
  733. }
  734.  
  735. function getSelectedImage(tree)
  736. {
  737.   if (!imageView.rowCount) return null;
  738.  
  739.   // Only works if only one item is selected
  740.   var clickedRow = tree.treeBoxObject.selection.currentIndex;
  741.   return imageView.getCellText(clickedRow, "image-node");
  742. }
  743.  
  744. function saveMedia()
  745. {
  746.   var tree = document.getElementById("imagetree");
  747.   var item = getSelectedImage(tree);
  748.   var url = getAbsoluteURL(getSource(item), item);
  749.  
  750.   if (url)
  751.     saveURL(url, null, 'SaveImageTitle', false );
  752. }
  753.  
  754. function onImageSelect()
  755. {
  756.   var tree = document.getElementById("imagetree");
  757.   var saveAsButton = document.getElementById("imagesaveasbutton");
  758.  
  759.   if (tree.treeBoxObject.selection.count == 1)
  760.   {
  761.     makePreview(tree.treeBoxObject.selection.currentIndex);
  762.     saveAsButton.setAttribute("disabled", "false");
  763.   }
  764.   else
  765.     saveAsButton.setAttribute("disabled", "true");
  766. }
  767.  
  768. function makePreview(row)
  769. {
  770.   var item = getSelectedImage(document.getElementById("imagetree"));
  771.   var url = imageView.getCellText(row, "image-address");
  772.   var isBG = imageView.getCellText(row, "image-bg");
  773.  
  774.   document.getElementById("imageurltext").value = url;
  775.   document.getElementById("imagetitletext").value = item.title || gStrings.notSet;
  776.  
  777.   var altText = null;
  778.   if (item.hasAttribute("alt") && ("alt" in item))
  779.     altText = item.alt;
  780.   else if (!isBG)
  781.     altText = getValueText(item);
  782.   if (altText == null)
  783.     altText = gStrings.notSet;
  784.   var textbox=document.getElementById("imagealttext");
  785.   
  786.   // IMO all text that is not really the value text should go in italics
  787.   // What if somebody has <img alt="Not specified">? =)
  788.   // We can't use textbox.style because of bug 7639
  789.   if (!altText) {
  790.       textbox.value = gStrings.emptyString;
  791.       textbox.setAttribute("style","font-style:italic");
  792.   } else {
  793.       textbox.value = altText;
  794.       textbox.setAttribute("style","font-style:inherit");
  795.   }
  796.   document.getElementById("imagelongdesctext").value = ("longDesc" in item && item.longDesc) || gStrings.notSet;
  797.  
  798.   // get cache info
  799.   var sourceText = theBundle.getString("generalNotCached");
  800.   var expirationText = gStrings.unknown;
  801.   var sizeText = gStrings.unknown;
  802.  
  803.   var pageSize = 0; 
  804.   var kbSize = 0;
  805.   var expirationTime = 0;
  806.   var expirationDate = null;
  807.  
  808.   try
  809.   {
  810.     var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);   // open for READ, in non-blocking mode
  811.     if (cacheEntryDescriptor)
  812.     {
  813.       switch(cacheEntryDescriptor.deviceID)
  814.       {
  815.         case "disk":
  816.           sourceText = theBundle.getString("generalDiskCache");
  817.           break;
  818.         case "memory":
  819.           sourceText = theBundle.getString("generalMemoryCache");
  820.           break;
  821.         default:
  822.           sourceText = cacheEntryDescriptor.deviceID;
  823.           break;
  824.       }
  825.     }
  826.   }
  827.   catch(ex)
  828.   {
  829.     try
  830.     {
  831.       cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);   // open for READ, in non-blocking mode
  832.       if (cacheEntryDescriptor)
  833.       {
  834.         switch(cacheEntryDescriptor.deviceID)
  835.         {
  836.           case "disk":
  837.             sourceText = theBundle.getString("generalDiskCache");
  838.             break;
  839.           case "memory":
  840.             sourceText = theBundle.getString("generalMemoryCache");
  841.             break;
  842.           default:
  843.             sourceText = cacheEntryDescriptor.deviceID;
  844.             break;
  845.         }
  846.       }
  847.     }
  848.     catch(ex2)
  849.     {
  850.       sourceText = theBundle.getString("generalNotCached");
  851.     }
  852.   }
  853.  
  854.   // find out the file size and expiration date
  855.   if (cacheEntryDescriptor)
  856.   {
  857.     pageSize = cacheEntryDescriptor.dataSize;
  858.     kbSize = pageSize / 1024;
  859.     sizeText = theBundle.getFormattedString("generalSize", [Math.round(kbSize*100)/100, pageSize]);
  860.  
  861.     expirationText = formatDate(cacheEntryDescriptor.expirationTime*1000, gStrings.notSet);
  862.   }
  863.  
  864.   var mimeType = ("type" in item && item.type) ||
  865.                  ("codeType" in item && item.codeType) ||
  866.                  ("contentType" in item && item.contentType) ||
  867.                  getContentTypeFromImgRequest(item) ||
  868.                  getContentTypeFromHeaders(cacheEntryDescriptor) ||
  869.                  gStrings.unknown;
  870.  
  871.   document.getElementById("imagetypetext").value = mimeType;
  872.   document.getElementById("imagesourcetext").value = sourceText;
  873.   document.getElementById("imageexpirestext").value = expirationText;
  874.   document.getElementById("imagesizetext").value = sizeText;
  875.  
  876.   var imageContainer = document.getElementById("theimagecontainer");
  877.   var oldImage = document.getElementById("thepreviewimage");
  878.  
  879.   var regex = new RegExp("^(https?|ftp|file|gopher)://");
  880.   var absoluteURL = getAbsoluteURL(url, item);
  881.   var isProtocolAllowed = regex.test(absoluteURL); 
  882.   var newImage = new Image();
  883.   newImage.setAttribute("id", "thepreviewimage");
  884.   var physWidth = 0, physHeight = 0;
  885.  
  886.   if ((item instanceof nsILinkElement || item instanceof nsIInputElement || 
  887.        item instanceof nsIImageElement || isBG) && isProtocolAllowed)  
  888.   {
  889.     newImage.src = absoluteURL;
  890.     physWidth = newImage.width;
  891.     physHeight = newImage.height;
  892.  
  893.     if ("width" in item && item.width)
  894.       newImage.width = item.width;
  895.     if ("height" in item && item.height)
  896.       newImage.height = item.height;
  897.   } 
  898.   else 
  899.   {
  900.     // fallback image for protocols not allowed (e.g., data: or javascript:) 
  901.     // or elements not [yet] handled (e.g., object, embed). XXX blank??
  902.     newImage.src = "resource://gre/re/loading-image.gif";
  903.     newImage.width = 40;
  904.     newImage.height = 40;
  905.   }
  906.  
  907.   var width = ("width" in item && item.width) || ("width" in newImage && newImage.width) || "0";
  908.   var height = ("height" in item && item.height) || ("height" in newImage && newImage.height) || "0";
  909.  
  910.   document.getElementById("imageSize").value = theBundle.getFormattedString("mediaSize", [width, height]);
  911.  
  912.   if (width != physWidth || height != physHeight)
  913.   {
  914.     document.getElementById("physSize").removeAttribute("hidden");
  915.     document.getElementById("physSize").value = theBundle.getFormattedString("mediaPhysSize", [physWidth, physHeight]);
  916.   }
  917.   else
  918.     document.getElementById("physSize").setAttribute("hidden", "true");
  919.  
  920.  
  921.   imageContainer.removeChild(oldImage);
  922.   imageContainer.appendChild(newImage);
  923. }
  924.  
  925. function getContentTypeFromHeaders(cacheEntryDescriptor)
  926. {
  927.   if (!cacheEntryDescriptor)
  928.     return null;
  929.  
  930.   return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi
  931.           .exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1];
  932. }
  933.  
  934. function getContentTypeFromImgRequest(item)
  935. {
  936.   var httpRequest;
  937.  
  938.   try
  939.   {
  940.     var imageItem = item.QueryInterface(nsIImageLoadingContent);
  941.     var imageRequest = imageItem.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
  942.     if (imageRequest) 
  943.       httpRequest = imageRequest.mimeType;
  944.   }
  945.   catch (ex)
  946.   {
  947.     // This never happened.  ;)
  948.   }
  949.  
  950.   return httpRequest;
  951. }
  952.  
  953. //******** Other Misc Stuff
  954. // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  955. // parse a node to extract the contents of the node
  956. function getValueText(node)
  957. {
  958.   var valueText = "";
  959.  
  960.   // form input elements don't generally contain information that is useful to our callers, so return nothing
  961.   if (node instanceof nsIInputElement || node instanceof nsISelectElement || node instanceof nsITextareaElement)
  962.     return valueText;
  963.  
  964.   // otherwise recurse for each child
  965.   var length = node.childNodes.length;
  966.   for (var i = 0; i < length; i++)
  967.   {
  968.     var childNode = node.childNodes[i];
  969.     var nodeType = childNode.nodeType;
  970.  
  971.     // text nodes are where the goods are
  972.     if (nodeType == Node.TEXT_NODE)
  973.       valueText += " " + childNode.nodeValue;
  974.     // and elements can have more text inside them
  975.     else if (nodeType == Node.ELEMENT_NODE)
  976.     {
  977.       // images are special, we want to capture the alt text as if the image weren't there
  978.       if (childNode instanceof nsIImageElement)
  979.         valueText += " " + getAltText(childNode);
  980.       else
  981.         valueText += " " + getValueText(childNode);
  982.     }
  983.   }
  984.  
  985.   return stripWS(valueText);
  986. }
  987.  
  988. // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  989. // traverse the tree in search of an img or area element and grab its alt tag
  990. function getAltText(node)
  991. {
  992.   var altText = "";
  993.   
  994.   if (node.alt)
  995.     return node.alt;
  996.   var length = node.childNodes.length;
  997.   for (var i = 0; i < length; i++)
  998.     if ((altText = getAltText(node.childNodes[i]) != undefined))  // stupid js warning...
  999.       return altText;
  1000.   return "";
  1001. }
  1002.  
  1003. // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
  1004. // strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
  1005. function stripWS(text)
  1006. {
  1007.   var middleRE = /\s+/g;
  1008.   var endRE = /(^\s+)|(\s+$)/g;
  1009.  
  1010.   text = text.replace(middleRE, " ");
  1011.   return text.replace(endRE, "");
  1012. }
  1013.  
  1014. function formatDate(datestr, unknown)
  1015. {
  1016.   var date = new Date(datestr);
  1017.   return (date.valueOf()) ? dateService.FormatDateTime("", dateService.dateFormatLong, dateService.timeFormatSeconds, date.getFullYear(), date.getMonth()+1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds()) : unknown;
  1018. }
  1019.  
  1020. /*
  1021.  * Takes care of XMLBase and <base>
  1022.  * url is the possibly relative url.
  1023.  * node is the node where the url was given (needed for XMLBase)
  1024.  *
  1025.  * This function is called in many places as a workaround for bug 72524
  1026.  * Once bug 72522 is fixed this code should use the Node.baseURI attribute
  1027.  *
  1028.  * for node==null or url=="", empty string is returned
  1029.  *
  1030.  * This is basically just copied from http://lxr.mozilla.org/seamonkey/source/xpfe/browser/resources/content/metadata.js,
  1031.  * though I've modified it so that it doesn't assign to .spec
  1032.  */
  1033.  
  1034. function getAbsoluteURL(url, node)
  1035. {
  1036.   if (!url || !node)
  1037.     return "";
  1038.   var urlArr = new Array(url);
  1039.  
  1040.   var doc = node.ownerDocument;
  1041.   if (node.nodeType == Node.ATTRIBUTE_NODE)
  1042.     node = node.ownerElement;
  1043.  
  1044.   while (node && node.nodeType == Node.ELEMENT_NODE) 
  1045.   {
  1046.     var att = node.getAttributeNS(XMLNS, "base");
  1047.     if (att != "")
  1048.       urlArr.unshift(att);
  1049.  
  1050.     node = node.parentNode;
  1051.   }
  1052.  
  1053.   // Look for a <base>.
  1054.   var baseTags = doc.getElementsByTagNameNS(XHTMLNS, "base");
  1055.  
  1056.   if (baseTags && baseTags.length) 
  1057.   {
  1058.     urlArr.unshift(baseTags[baseTags.length - 1].getAttribute("href"));
  1059.   }
  1060.  
  1061.   // resolve everything from bottom up, starting with document location
  1062.   var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  1063.   var URL = ioService.newURI(doc.location.href, null, null);
  1064.  
  1065.   for (var i=0; i<urlArr.length; i++) 
  1066.   {
  1067.     try
  1068.     {
  1069.       URL = ioService.newURI(urlArr[i], URL.originCharset, URL);
  1070.     }
  1071.     catch (ex)
  1072.     {
  1073.       ; // do nothing
  1074.     }
  1075.   }
  1076.  
  1077.   return URL.spec;
  1078. }
  1079.  
  1080. function doCopy(event)
  1081. {
  1082.   if (!gClipboardHelper) 
  1083.     return;
  1084.  
  1085.   var elem = event.originalTarget;
  1086.   if (elem && "treeBoxObject" in elem)
  1087.     elem.treeBoxObject.view.performActionOnRow("copy", elem.currentIndex);
  1088.  
  1089.   var text = elem.getAttribute("copybuffer");
  1090.   if (text) 
  1091.     gClipboardHelper.copyString(text);
  1092. }
  1093.  
  1094.