home *** CD-ROM | disk | FTP | other *** search
/ PC World 2006 December / PCWorld_2006-12_cd.bin / komunikace / netscape / nsb-install-8-1-2.exe / chrome / browser.jar / content / browser / pageInfo.js < prev    next >
Text File  |  2006-01-06  |  35KB  |  1,050 lines

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