home *** CD-ROM | disk | FTP | other *** search
/ ftp.swcp.com / ftp.swcp.com.zip / ftp.swcp.com / mac / mozilla-macos9-1.3.1.sea.bin / Mozilla1.3.1 / Chrome / comm.jar / content / navigator / metadata.js < prev    next >
Text File  |  2003-06-08  |  19KB  |  609 lines

  1. /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Mozilla 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/MPL/
  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 this file as it was released on
  14.  * January 3, 2001.
  15.  *
  16.  * The Initial Developer of the Original Code is Jonas Sicking.
  17.  * Portions created by Jonas Sicking are Copyright (C) 2000
  18.  * Jonas Sicking.  All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Jonas Sicking <sicking@bigfoot.com> (Original Author)
  22.  *   Gervase Markham <gerv@gerv.net>
  23.  *   Heikki Toivonen <heikki@netscape.com>
  24.  *
  25.  * Alternatively, the contents of this file may be used under the
  26.  * terms of the GNU General Public License Version 2 or later (the
  27.  * "GPL"), in which case the provisions of the GPL are applicable
  28.  * instead of those above.  If you wish to allow use of your
  29.  * version of this file only under the terms of the GPL and not to
  30.  * allow others to use your version of this file under the MPL,
  31.  * indicate your decision by deleting the provisions above and
  32.  * replace them with the notice and other provisions required by
  33.  * the GPL.  If you do not delete the provisions above, a recipient
  34.  * may use your version of this file under either the MPL or the
  35.  * GPL.
  36.  *
  37.  */
  38.  
  39. const XLinkNS = "http://www.w3.org/1999/xlink";
  40. const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  41. const XMLNS = "http://www.w3.org/XML/1998/namespace";
  42. const XHTMLNS = "http://www.w3.org/1999/xhtml";
  43. var gMetadataBundle;
  44. var gLangBundle;
  45. var gRegionBundle;
  46. var nodeView;
  47. var htmlMode = false;
  48.  
  49. var onLink   = false;
  50. var onImage  = false;
  51. var onInsDel = false;
  52. var onQuote  = false;
  53. var onMisc   = false;
  54. var onTable  = false;
  55. var onTitle  = false;
  56. var onLang   = false;
  57.  
  58. const nsICacheService = Components.interfaces.nsICacheService;
  59. const cacheService = Components.classes["@mozilla.org/network/cache-service;1"]
  60.                      .getService(nsICacheService);
  61. var httpCacheSession = cacheService.createSession("HTTP", 0, true);
  62. httpCacheSession.doomEntriesIfExpired = false;
  63. var ftpCacheSession = cacheService.createSession("FTP", 0, true);
  64. ftpCacheSession.doomEntriesIfExpired = false;
  65.  
  66.  
  67. function onLoad()
  68. {
  69.     gMetadataBundle = document.getElementById("bundle_metadata");
  70.     gLangBundle = document.getElementById("bundle_languages");
  71.     gRegionBundle = document.getElementById("bundle_regions");
  72.     
  73.     showMetadataFor(window.arguments[0]);
  74.  
  75.     nodeView = window.arguments[0].ownerDocument.defaultView;
  76. }
  77.  
  78. function showMetadataFor(elem)
  79. {
  80.     // skip past non-element nodes
  81.     while (elem && elem.nodeType != Node.ELEMENT_NODE)
  82.         elem = elem.parentNode;
  83.  
  84.     if (!elem) {
  85.         alert(gMetadataBundle.getString("unableToShowProps"));
  86.         window.close();
  87.     }
  88.  
  89.     if (elem.ownerDocument.getElementsByName && !elem.ownerDocument.namespaceURI)
  90.         htmlMode = true;
  91.     
  92.     // htmllocalname is "" if it's not an html tag, or the name of the tag if it is.
  93.     var htmllocalname = "";
  94.     if (isHTMLElement(elem,"")) { 
  95.         htmllocalname = elem.localName.toLowerCase();
  96.     }
  97.     
  98.     // We only look for images once
  99.     checkForImage(elem, htmllocalname);
  100.     
  101.     // Walk up the tree, looking for elements of interest.
  102.     // Each of them could be at a different level in the tree, so they each
  103.     // need their own boolean to tell us to stop looking.
  104.     while (elem && elem.nodeType == Node.ELEMENT_NODE) {
  105.         if (!onLink)   checkForLink(elem, htmllocalname);
  106.         if (!onInsDel) checkForInsDel(elem, htmllocalname);
  107.         if (!onQuote)  checkForQuote(elem, htmllocalname);
  108.         if (!onTable)  checkForTable(elem, htmllocalname);
  109.         if (!onTitle)  checkForTitle(elem, htmllocalname);
  110.         if (!onLang)   checkForLang(elem, htmllocalname);
  111.           
  112.         elem = elem.parentNode;
  113.  
  114.         htmllocalname = "";
  115.         if (isHTMLElement(elem,"")) { 
  116.             htmllocalname = elem.localName.toLowerCase();
  117.         }
  118.     }
  119.     
  120.     // Decide which sections to show
  121.     var onMisc = onTable || onTitle || onLang;
  122.     if (!onMisc)   hideNode("misc-sec");
  123.     if (!onLink)   hideNode("link-sec");
  124.     if (!onImage)  hideNode("image-sec");
  125.     if (!onInsDel) hideNode("insdel-sec");
  126.     if (!onQuote)  hideNode("quote-sec");
  127.  
  128.     // Fix the Misc section visibilities
  129.     if (onMisc) {
  130.         if (!onTable) hideNode("misc-tblsummary");
  131.         if (!onLang)  hideNode("misc-lang");
  132.         if (!onTitle) hideNode("misc-title");
  133.     }
  134.  
  135.     // Get rid of the "No properties" message. This is a backstop -
  136.     // it should really never show, as long as nsContextMenu.js's
  137.     // checking doesn't get broken.
  138.     if (onLink || onImage || onInsDel || onQuote || onMisc)
  139.         hideNode("no-properties")
  140. }
  141.  
  142.  
  143. function checkForImage(elem, htmllocalname)
  144. {
  145.     var img;
  146.     var imgType;   // "img" = <img>
  147.                    // "object" = <object>
  148.                    // "input" = <input type=image>
  149.                    // "background" = css background (to be added later)
  150.     var ismap = false;
  151.  
  152.     if (htmllocalname === "img") {
  153.         img = elem;
  154.         imgType = "img";
  155.  
  156.     } else if (htmllocalname === "object" &&
  157.                elem.type.substring(0,6) == "image/" &&
  158.                elem.data) {
  159.         img = elem;
  160.         imgType = "object";
  161.  
  162.     } else if (htmllocalname === "input" &&
  163.                elem.type.toUpperCase() == "IMAGE") {
  164.         img = elem;
  165.         imgType = "input";
  166.  
  167.     } else if (htmllocalname === "area" || htmllocalname === "a") {
  168.  
  169.         // Clicked in image map?
  170.         var map = elem;
  171.         ismap = true;
  172.         setAlt(map);
  173.  
  174.         while (map && map.nodeType == Node.ELEMENT_NODE && !isHTMLElement(map,"map") )
  175.             map = map.parentNode;
  176.  
  177.         if (map && map.nodeType == Node.ELEMENT_NODE) {
  178.             img = getImageForMap(map);
  179.             var imgLocalName = img && img.localName.toLowerCase();
  180.             if (imgLocalName == "img" || imgLocalName == "object") {
  181.                 imgType = imgLocalName;
  182.             }
  183.         }
  184.  
  185.     }
  186.  
  187.     if (img) {
  188.  
  189.         var imgURL = imgType == "object" ? img.data : img.src;
  190.         setInfo("image-url", imgURL);
  191.         var size = getSize(imgURL);
  192.  
  193.         if (size != -1) {
  194.             var kbSize = size / 1024;
  195.             kbSize = Math.round(kbSize*100)/100;
  196.             setInfo("image-filesize", gMetadataBundle.getFormattedString("imageSize", [kbSize, size]));
  197.         } else {
  198.             setInfo("image-filesize", gMetadataBundle.getString("imageSizeUnknown"));
  199.         }
  200.         if ("width" in img && img.width != "") {
  201.             setInfo("image-width", gMetadataBundle.getFormattedString("imageWidth", [ img.width ]));
  202.             setInfo("image-height", gMetadataBundle.getFormattedString("imageHeight", [ img.height ]));
  203.         }
  204.         else {
  205.             setInfo("image-width", "");
  206.             setInfo("image-height", "");
  207.         }        
  208.          
  209.         if (imgType == "img") {
  210.             setInfo("image-desc", getAbsoluteURL(img.longDesc, img));
  211.         } else {
  212.             setInfo("image-desc", "");
  213.         }
  214.         
  215.         onImage = true;
  216.     }
  217.  
  218.     if (!ismap) {
  219.        if (imgType == "img" || imgType == "input") {
  220.            setAlt(img);
  221.        } else {
  222.            hideNode("image-alt");
  223.        }
  224.     }
  225. }
  226.  
  227. function checkForLink(elem, htmllocalname)
  228. {
  229.     if ((htmllocalname === "a" && elem.href != "") ||
  230.         htmllocalname === "area") {
  231.  
  232.         setInfo("link-lang", convertLanguageCode(elem.getAttribute("hreflang")));
  233.         setInfo("link-url",  elem.href);
  234.         setInfo("link-type", elem.getAttribute("type"));
  235.         setInfo("link-rel",  elem.getAttribute("rel"));
  236.         setInfo("link-rev",  elem.getAttribute("rev"));
  237.  
  238.         var target = elem.target;
  239.  
  240.         switch (target) {
  241.         case "_top":
  242.             setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
  243.             break;
  244.         case "_parent":
  245.             setInfo("link-target", gMetadataBundle.getString("parentFrameText"));
  246.             break;
  247.         case "_blank":
  248.             setInfo("link-target", gMetadataBundle.getString("newWindowText"));
  249.             break;
  250.         case "":
  251.         case "_self":
  252.             if (elem.ownerDocument != elem.ownerDocument.defaultView._content.document)
  253.                 setInfo("link-target", gMetadataBundle.getString("sameFrameText"));
  254.             else
  255.                 setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
  256.             break;
  257.         default:
  258.             setInfo("link-target", "\"" + target + "\"");
  259.         }
  260.         
  261.         onLink = true;
  262.     }
  263.  
  264.     else if (elem.getAttributeNS(XLinkNS,"href") != "") {
  265.         setInfo("link-url", getAbsoluteURL(elem.getAttributeNS(XLinkNS,"href"),elem));
  266.         setInfo("link-lang", "");
  267.         setInfo("link-type", "");
  268.         setInfo("link-rel", "");
  269.         setInfo("link-rev", "");
  270.  
  271.         switch (elem.getAttributeNS(XLinkNS,"show")) {
  272.         case "embed":
  273.             setInfo("link-target", gMetadataBundle.getString("embeddedText"));
  274.             break;
  275.         case "new":
  276.             setInfo("link-target", gMetadataBundle.getString("newWindowText"));
  277.             break;
  278.         case "":
  279.         case "replace":
  280.             if (elem.ownerDocument != elem.ownerDocument.defaultView._content.document)
  281.                 setInfo("link-target", gMetadataBundle.getString("sameFrameText"));
  282.             else
  283.                 setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
  284.             break;
  285.         default:
  286.             setInfo("link-target", "");
  287.             break;
  288.         }
  289.         
  290.         onLink = true;
  291.     }
  292. }
  293.  
  294. function checkForInsDel(elem, htmllocalname)
  295. {
  296.     if ((htmllocalname === "ins" || htmllocalname === "del") &&
  297.         (elem.cite || elem.dateTime)) {
  298.         setInfo("insdel-cite", getAbsoluteURL(elem.cite, elem));
  299.         setInfo("insdel-date", elem.dateTime);
  300.         onInsDel = true;
  301.     } 
  302. }
  303.  
  304.  
  305. function checkForQuote(elem, htmllocalname)
  306. {
  307.     if ((htmllocalname === "q" || htmllocalname === "blockquote") && elem.cite) {
  308.         setInfo("quote-cite", getAbsoluteURL(elem.cite, elem));
  309.         onQuote = true;
  310.     } 
  311. }
  312.  
  313. function checkForTable(elem, htmllocalname)
  314. {
  315.     if (htmllocalname === "table" && elem.summary) {
  316.         setInfo("misc-tblsummary", elem.summary);
  317.         onTable = true;
  318.     }
  319. }
  320.  
  321. function checkForLang(elem, htmllocalname)
  322. {
  323.     if ((htmllocalname && elem.lang) || elem.getAttributeNS(XMLNS, "lang")) {
  324.         var abbr;
  325.         if (htmllocalname && elem.lang)
  326.             abbr = elem.lang;
  327.         else
  328.             abbr = elem.getAttributeNS(XMLNS, "lang");
  329.             
  330.         setInfo("misc-lang", convertLanguageCode(abbr));
  331.         onLang = true;
  332.     }
  333. }
  334.     
  335. function checkForTitle(elem, htmllocalname)
  336. {
  337.     if (htmllocalname && elem.title) {
  338.         setInfo("misc-title", elem.title);
  339.         onTitle = true;
  340.     }    
  341. }
  342.  
  343. /*
  344.  * Set text of node id to value
  345.  * if value="" the node with specified id is hidden.
  346.  * Node should be have one of these forms
  347.  * <xul:label id="id-text" value=""/>
  348.  * <xul:description id="id-text"/>
  349.  */
  350. function setInfo(id, value)
  351. {
  352.     if (!value) {
  353.         hideNode(id);
  354.         return;
  355.     }
  356.  
  357.     var node = document.getElementById(id+"-text");
  358.  
  359.     if (node.namespaceURI == XULNS && node.localName == "label" ||
  360.        (node.namespaceURI == XULNS && node.localName == "textbox")) {
  361.         node.setAttribute("value",value);
  362.  
  363.     } else if (node.namespaceURI == XULNS && node.localName == "description") {
  364.         while (node.hasChildNodes())
  365.             node.removeChild(node.firstChild);
  366.         node.appendChild(node.ownerDocument.createTextNode(value));
  367.     }
  368. }
  369.  
  370. // Hide node with specified id
  371. function hideNode(id)
  372. {
  373.     var style = document.getElementById(id).getAttribute("style");
  374.     document.getElementById(id).setAttribute("style", "display:none;" + style);
  375. }
  376.  
  377. const nsIScriptSecurityManager = Components.interfaces.nsIScriptSecurityManager;
  378.  
  379. // opens the link contained in the node's "value" attribute.
  380. function openLink(node)
  381. {
  382.     var url = node.getAttribute("value");
  383.     // Security-Critical: Only links to 'safe' protocols should be functional.
  384.     // Specifically, javascript: and data: URLs must be made non-functional
  385.     // here, because they will run with full privilege.
  386.     var safeurls = /(^http(s)?:|^file:|^chrome:|^resource:|^mailbox:|^imap:|^news:|^about:|^mailto:|^ftp:|^gopher:)/i;
  387.     if (url.search(safeurls) == 0) {
  388.         var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService().
  389.                          QueryInterface(nsIScriptSecurityManager);
  390.         try {
  391.             secMan.checkLoadURIStr(nodeView._content.document.location,
  392.                                    url, nsIScriptSecurityManager.STANDARD);
  393.         } catch (e) {
  394.             return;
  395.         }
  396.         nodeView._content.document.location = url;
  397.         window.close();
  398.     }
  399. }
  400.  
  401. /*
  402.  * Find <img> or <object> which uses an imagemap.
  403.  * If more then one object is found we can't determine which one
  404.  * was clicked.
  405.  *
  406.  * This code has to be changed once bug 1882 is fixed.
  407.  * Once bug 72527 is fixed this code should use the .images collection.
  408.  */
  409. function getImageForMap(map)
  410. {
  411.     var mapuri = "#" + map.getAttribute("name");
  412.     var multipleFound = false;
  413.     var img;
  414.  
  415.     var list = getHTMLElements(map.ownerDocument, "img");
  416.     for (var i=0; i < list.length; i++) {
  417.         if (list.item(i).getAttribute("usemap") == mapuri) {
  418.             if (img) {
  419.                 multipleFound = true;
  420.                 break;
  421.             } else {
  422.                 img = list.item(i);
  423.                 imgType = "img";
  424.             }
  425.         }
  426.     }
  427.  
  428.     list = getHTMLElements(map.ownerDocument, "object");
  429.     for (i = 0; i < list.length; i++) {
  430.         if (list.item(i).getAttribute("usemap") == mapuri) {
  431.             if (img) {
  432.               multipleFound = true;
  433.               break;
  434.             } else {
  435.               img = list.item(i);
  436.               imgType = "object";
  437.             }
  438.         }
  439.     }
  440.  
  441.     if (multipleFound)
  442.         img = null;
  443.  
  444.     return img;
  445. }
  446.  
  447. /*
  448.  * Takes care of XMLBase and <base>
  449.  * url is the possibly relative url.
  450.  * node is the node where the url was given (needed for XMLBase)
  451.  *
  452.  * This function is called in many places as a workaround for bug 72524
  453.  * Once bug 72522 is fixed this code should use the Node.baseURI attribute
  454.  *
  455.  * for node==null or url=="", empty string is returned
  456.  */
  457. function getAbsoluteURL(url, node)
  458. {
  459.     if (!url || !node)
  460.         return "";
  461.  
  462.     var urlArr = new Array(url);
  463.     var doc = node.ownerDocument;
  464.  
  465.     if (node.nodeType == Node.ATTRIBUTE_NODE)
  466.         node = node.ownerElement;
  467.  
  468.     while (node && node.nodeType == Node.ELEMENT_NODE) {
  469.         if (node.getAttributeNS(XMLNS, "base") != "")
  470.             urlArr.unshift(node.getAttributeNS(XMLNS, "base"));
  471.  
  472.         node = node.parentNode;
  473.     }
  474.  
  475.     // Look for a <base>.
  476.     var baseTags = getHTMLElements(doc,"base");
  477.     if (baseTags && baseTags.length) {
  478.         urlArr.unshift(baseTags[baseTags.length - 1].getAttribute("href"));
  479.     }
  480.  
  481.     // resolve everything from bottom up, starting with document location
  482.     var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  483.                   .getService(Components.interfaces.nsIIOService);
  484.     var URL = ioService.newURI(doc.location.href, null, null);
  485.     for (var i=0; i<urlArr.length; i++) {
  486.         URL.spec = URL.resolve(urlArr[i]);
  487.     }
  488.  
  489.     return URL.spec;
  490. }
  491.  
  492. function getHTMLElements(node, name)
  493. {
  494.     if (htmlMode)
  495.         return node.getElementsByTagName(name);
  496.     return node.getElementsByTagNameNS(XHTMLNS, name);
  497. }
  498.  
  499. // name should be in lower case
  500. function isHTMLElement(node, name)
  501. {
  502.     if (node.nodeType != Node.ELEMENT_NODE)
  503.         return false;
  504.  
  505.     if (htmlMode)
  506.         return !name || node.localName.toLowerCase() == name;
  507.  
  508.     return (!name || node.localName == name) && node.namespaceURI == XHTMLNS;
  509. }
  510.  
  511. // This function coded according to the spec at:
  512. // http://www.bath.ac.uk/~py8ieh/internet/discussion/metadata.txt
  513. function convertLanguageCode(abbr)
  514. {
  515.     if (!abbr) return "";
  516.     var result;
  517.     var tokens = abbr.split("-");
  518.  
  519.     if (tokens[0] === "x" || tokens[0] === "i")
  520.     {
  521.         // x and i prefixes mean unofficial ones. So we upper-case the first
  522.         // word and leave the rest.
  523.         tokens.shift();
  524.  
  525.         if (tokens[0])
  526.         {
  527.             // Upper-case first letter
  528.             result = tokens[0].substr(0, 1).toUpperCase() + tokens[0].substr(1);
  529.             tokens.shift();
  530.  
  531.             if (tokens[0])
  532.             {
  533.                 // Add on the rest as space-separated strings inside the brackets
  534.                 result += " (" + tokens.join(" ") + ")";
  535.             }
  536.         }
  537.     }
  538.     else
  539.     {
  540.         // Otherwise we treat the first as a lang, the second as a region
  541.         // and the rest as strings.
  542.         try
  543.         {
  544.             result = gLangBundle.getString(tokens[0]);
  545.         }
  546.         catch (e) 
  547.         {
  548.             // Language not present in lang bundle
  549.             result = tokens[0]; 
  550.         }
  551.  
  552.         tokens.shift();
  553.  
  554.         if (tokens[0])
  555.         {
  556.             try
  557.             {
  558.                 // We don't add it on to the result immediately
  559.                 // because we want to get the spacing right.
  560.                 tokens[0] = gRegionBundle.getString(tokens[0].toLowerCase());
  561.             }
  562.             catch (e) 
  563.             {
  564.                 // Region not present in region bundle
  565.             }
  566.  
  567.             result += " (" + tokens.join(" ") + ")";
  568.         }
  569.     }
  570.  
  571.     return result;
  572. }
  573.  
  574. // Returns the size of the URL in bytes; must be cached and therefore an HTTP or FTP URL
  575. function getSize(url) {
  576.     try
  577.     {
  578.         var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  579.         if(cacheEntryDescriptor)
  580.           return cacheEntryDescriptor.dataSize;
  581.     }
  582.     catch(ex) {}
  583.     try
  584.     {
  585.         cacheEntryDescriptor = ftpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
  586.         if (cacheEntryDescriptor)
  587.             return cacheEntryDescriptor.dataSize;
  588.     }
  589.     catch(ex) {}
  590.     return -1;
  591. }
  592.  
  593. function setAlt(elem) {
  594.     var altText = document.getElementById("image-alt-text");
  595.     if (elem.hasAttribute("alt")) {
  596.         if (elem.alt != "") {
  597.             altText.value = elem.alt;
  598.             altText.setAttribute("style","font-style:inherit");
  599.         } else {
  600.             altText.value = gMetadataBundle.getString("altTextBlank");
  601.             altText.setAttribute("style","font-style:italic");
  602.         }
  603.     } else {
  604.         altText.value = gMetadataBundle.getString("altTextMissing");
  605.         altText.setAttribute("style","font-style:italic");
  606.     }
  607.  
  608. }
  609.