summaryrefslogtreecommitdiffstats
path: root/comm/suite/browser/metadata.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/browser/metadata.js')
-rw-r--r--comm/suite/browser/metadata.js526
1 files changed, 526 insertions, 0 deletions
diff --git a/comm/suite/browser/metadata.js b/comm/suite/browser/metadata.js
new file mode 100644
index 0000000000..604ef54bb5
--- /dev/null
+++ b/comm/suite/browser/metadata.js
@@ -0,0 +1,526 @@
+/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const MathMLNS = "http://www.w3.org/1998/Math/MathML";
+const XLinkNS = "http://www.w3.org/1999/xlink";
+const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const XMLNS = "http://www.w3.org/XML/1998/namespace";
+const XHTMLNS = "http://www.w3.org/1999/xhtml";
+var gMetadataBundle;
+var gLangBundle;
+var gRegionBundle;
+var nodeView;
+var htmlMode = false;
+
+var onLink = false;
+var onImage = false;
+var onInsDel = false;
+var onQuote = false;
+var onMisc = false;
+var onTable = false;
+var onTitle = false;
+var onLang = false;
+
+function onLoad()
+{
+ gMetadataBundle = document.getElementById("bundle_metadata");
+ gLangBundle = document.getElementById("bundle_languages");
+ gRegionBundle = document.getElementById("bundle_regions");
+
+ showMetadataFor(window.arguments[0]);
+
+ nodeView = window.arguments[0].ownerDocument.defaultView;
+}
+
+function showMetadataFor(elem)
+{
+ // skip past non-element nodes
+ while (elem && elem.nodeType != Node.ELEMENT_NODE)
+ elem = elem.parentNode;
+
+ if (!elem) {
+ alert(gMetadataBundle.getString("unableToShowProps"));
+ window.close();
+ }
+
+ if (elem.ownerDocument.getElementsByName && !elem.ownerDocument.namespaceURI)
+ htmlMode = true;
+
+ // htmllocalname is "" if it's not an html tag, or the name of the tag if it is.
+ var htmllocalname = "";
+ if (isHTMLElement(elem,"")) {
+ htmllocalname = elem.localName.toLowerCase();
+ }
+
+ // We only look for images once
+ checkForImage(elem, htmllocalname);
+
+ // Walk up the tree, looking for elements of interest.
+ // Each of them could be at a different level in the tree, so they each
+ // need their own boolean to tell us to stop looking.
+ while (elem && elem.nodeType == Node.ELEMENT_NODE) {
+ htmllocalname = "";
+ if (isHTMLElement(elem,"")) {
+ htmllocalname = elem.localName.toLowerCase();
+ }
+
+ if (!onLink) checkForLink(elem, htmllocalname);
+ if (!onInsDel) checkForInsDel(elem, htmllocalname);
+ if (!onQuote) checkForQuote(elem, htmllocalname);
+ if (!onTable) checkForTable(elem, htmllocalname);
+ if (!onTitle) checkForTitle(elem, htmllocalname);
+ if (!onLang) checkForLang(elem, htmllocalname);
+
+ elem = elem.parentNode;
+ }
+
+ // Decide which sections to show
+ var onMisc = onTable || onTitle || onLang;
+ if (!onMisc) hideNode("misc-sec");
+ if (!onLink) hideNode("link-sec");
+ if (!onImage) hideNode("image-sec");
+ if (!onInsDel) hideNode("insdel-sec");
+ if (!onQuote) hideNode("quote-sec");
+
+ // Fix the Misc section visibilities
+ if (onMisc) {
+ if (!onTable) hideNode("misc-tblsummary");
+ if (!onLang) hideNode("misc-lang");
+ if (!onTitle) hideNode("misc-title");
+ }
+
+ // Get rid of the "No properties" message. This is a backstop -
+ // it should really never show, as long as nsContextMenu.js's
+ // checking doesn't get broken.
+ if (onLink || onImage || onInsDel || onQuote || onMisc)
+ hideNode("no-properties")
+}
+
+var cacheListener = {
+ onCacheEntryAvailable: function onCacheEntryAvailable(descriptor) {
+ if (descriptor) {
+ var kbSize = descriptor.dataSize / 1024;
+ kbSize = Math.round(kbSize * 100) / 100;
+ setInfo("image-filesize", gMetadataBundle.getFormattedString("imageSize",
+ [formatNumber(kbSize),
+ formatNumber(descriptor.dataSize)]));
+ } else {
+ setInfo("image-filesize", gMetadataBundle.getString("imageSizeUnknown"));
+ }
+ },
+ onCacheEntryCheck: function onCacheEntryCheck() {
+ return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
+ }
+};
+
+function checkForImage(elem, htmllocalname)
+{
+ var img;
+ var imgType; // "img" = <img>
+ // "object" = <object>
+ // "input" = <input type=image>
+ // "background" = css background (to be added later)
+ var ismap = false;
+
+ if (htmllocalname === "img") {
+ img = elem;
+ imgType = "img";
+
+ } else if (htmllocalname === "object" &&
+ elem.type.substring(0,6) == "image/" &&
+ elem.data) {
+ img = elem;
+ imgType = "object";
+
+ } else if (htmllocalname === "input" &&
+ elem.type.toUpperCase() == "IMAGE") {
+ img = elem;
+ imgType = "input";
+
+ } else if (htmllocalname === "area" || htmllocalname === "a") {
+
+ // Clicked in image map?
+ var map = elem;
+ ismap = true;
+ setAlt(map);
+
+ while (map && map.nodeType == Node.ELEMENT_NODE && !isHTMLElement(map,"map") )
+ map = map.parentNode;
+
+ if (map && map.nodeType == Node.ELEMENT_NODE) {
+ img = getImageForMap(map);
+ var imgLocalName = img && img.localName.toLowerCase();
+ if (imgLocalName == "img" || imgLocalName == "object") {
+ imgType = imgLocalName;
+ }
+ }
+
+ }
+
+ if (img) {
+
+ var imgURL = imgType == "object" ? img.data : img.src;
+ setInfo("image-url", imgURL);
+
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ const LoadContextInfo = Services.loadContextInfo;
+ var loadContextInfo = opener.gPrivate ? LoadContextInfo.private :
+ LoadContextInfo.default;
+ const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+ Services.cache2
+ .getService(Ci.nsICacheStorageService)
+ .diskCacheStorage(loadContextInfo, false)
+ .asyncOpenURI(NetUtil.newURI(imgURL), null,
+ Ci.nsICacheStorage.OPEN_READONLY, cacheListener);
+
+ if ("width" in img && img.width != "") {
+ setInfo("image-width", gMetadataBundle.getFormattedString("imageWidth", [formatNumber(img.width)]));
+ setInfo("image-height", gMetadataBundle.getFormattedString("imageHeight", [formatNumber(img.height)]));
+ }
+ else {
+ setInfo("image-width", "");
+ setInfo("image-height", "");
+ }
+
+ if (imgType == "img") {
+ setInfo("image-desc", img.longDesc);
+ } else {
+ setInfo("image-desc", "");
+ }
+
+ onImage = true;
+ }
+
+ if (!ismap) {
+ if (imgType == "img" || imgType == "input") {
+ setAlt(img);
+ } else {
+ hideNode("image-alt");
+ }
+ }
+}
+
+function checkForLink(elem, htmllocalname)
+{
+ if ((htmllocalname === "a" && elem.href != "") ||
+ htmllocalname === "area") {
+
+ setLink(elem.href, elem.getAttribute("type"),
+ convertLanguageCode(elem.getAttribute("hreflang")),
+ elem.getAttribute("rel"), elem.getAttribute("rev"));
+
+ var target = elem.target;
+
+ switch (target) {
+ case "_top":
+ setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
+ break;
+ case "_parent":
+ setInfo("link-target", gMetadataBundle.getString("parentFrameText"));
+ break;
+ case "_blank":
+ setInfo("link-target", gMetadataBundle.getString("newWindowText"));
+ break;
+ case "":
+ case "_self":
+ if (elem.ownerDocument.defaultView) {
+ if (elem.ownerDocument != elem.ownerDocument.defaultView.content.document)
+ setInfo("link-target", gMetadataBundle.getString("sameFrameText"));
+ else
+ setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
+ } else {
+ hideNode("link-target");
+ }
+ break;
+ default:
+ setInfo("link-target", "\"" + target + "\"");
+ }
+
+ onLink = true;
+ }
+ else if (elem.namespaceURI == MathMLNS && elem.hasAttribute("href")) {
+ setLink(makeHrefAbsolute(elem.getAttribute("href"), elem));
+
+ setInfo("link-target", "");
+
+ onLink = true;
+ }
+ else if (elem.getAttributeNS(XLinkNS, "href")) {
+ setLink(makeHrefAbsolute(elem.getAttributeNS(XLinkNS, "href"), elem));
+
+ switch (elem.getAttributeNS(XLinkNS,"show")) {
+ case "embed":
+ setInfo("link-target", gMetadataBundle.getString("embeddedText"));
+ break;
+ case "new":
+ setInfo("link-target", gMetadataBundle.getString("newWindowText"));
+ break;
+ case null:
+ case "":
+ case "replace":
+ if (elem.ownerDocument != elem.ownerDocument.defaultView.content.document)
+ setInfo("link-target", gMetadataBundle.getString("sameFrameText"));
+ else
+ setInfo("link-target", gMetadataBundle.getString("sameWindowText"));
+ break;
+ default:
+ setInfo("link-target", "");
+ break;
+ }
+
+ onLink = true;
+ }
+}
+
+function checkForInsDel(elem, htmllocalname)
+{
+ if ((htmllocalname === "ins" || htmllocalname === "del") &&
+ (elem.cite || elem.dateTime)) {
+ setInfo("insdel-cite", elem.cite);
+ setInfo("insdel-date", elem.dateTime);
+ onInsDel = true;
+ }
+}
+
+
+function checkForQuote(elem, htmllocalname)
+{
+ if ((htmllocalname === "q" || htmllocalname === "blockquote") && elem.cite) {
+ setInfo("quote-cite", elem.cite);
+ onQuote = true;
+ }
+}
+
+function checkForTable(elem, htmllocalname)
+{
+ if (htmllocalname === "table" && elem.summary) {
+ setInfo("misc-tblsummary", elem.summary);
+ onTable = true;
+ }
+}
+
+function checkForLang(elem, htmllocalname)
+{
+ if ((htmllocalname && elem.lang) || elem.getAttributeNS(XMLNS, "lang")) {
+ var abbr;
+ if (htmllocalname && elem.lang)
+ abbr = elem.lang;
+ else
+ abbr = elem.getAttributeNS(XMLNS, "lang");
+
+ setInfo("misc-lang", convertLanguageCode(abbr));
+ onLang = true;
+ }
+}
+
+function checkForTitle(elem, htmllocalname)
+{
+ if (htmllocalname && elem.title) {
+ setInfo("misc-title", elem.title);
+ onTitle = true;
+ }
+}
+
+/*
+ * Set five link properties at once.
+ * All parameters are optional.
+ */
+function setLink(url, lang, type, rel, rev)
+{
+ setInfo("link-url", url);
+ setInfo("link-type", type);
+ setInfo("link-lang", lang);
+ setInfo("link-rel", rel);
+ setInfo("link-rev", rev);
+}
+
+/*
+ * Set text of node id to value
+ * if value="" the node with specified id is hidden.
+ * Node should be have one of these forms
+ * <xul:label id="id-text" value=""/>
+ * <xul:description id="id-text"/>
+ */
+function setInfo(id, value)
+{
+ if (!value) {
+ hideNode(id);
+ return;
+ }
+
+ var node = document.getElementById(id+"-text");
+
+ if (node.namespaceURI == XULNS && node.localName == "label" ||
+ (node.namespaceURI == XULNS && node.localName == "textbox")) {
+ node.setAttribute("value",value);
+
+ } else if (node.namespaceURI == XULNS && node.localName == "description") {
+ node.textContent = value;
+ }
+}
+
+// Hide node with specified id
+function hideNode(id)
+{
+ var style = document.getElementById(id).getAttribute("style");
+ document.getElementById(id).setAttribute("style", "display:none;" + style);
+}
+
+/*
+ * Find <img> or <object> which uses an imagemap.
+ * If more then one object is found we can't determine which one
+ * was clicked.
+ *
+ * This code has to be changed once bug 1882 is fixed.
+ * Once bug 72527 is fixed this code should use the .images collection.
+ */
+function getImageForMap(map)
+{
+ var mapuri = "#" + map.getAttribute("name");
+ var multipleFound = false;
+ var img;
+
+ var list = getHTMLElements(map.ownerDocument, "img");
+ for (var i=0; i < list.length; i++) {
+ if (list.item(i).getAttribute("usemap") == mapuri) {
+ if (img) {
+ multipleFound = true;
+ break;
+ } else {
+ img = list.item(i);
+ imgType = "img";
+ }
+ }
+ }
+
+ list = getHTMLElements(map.ownerDocument, "object");
+ for (i = 0; i < list.length; i++) {
+ if (list.item(i).getAttribute("usemap") == mapuri) {
+ if (img) {
+ multipleFound = true;
+ break;
+ } else {
+ img = list.item(i);
+ imgType = "object";
+ }
+ }
+ }
+
+ if (multipleFound)
+ img = null;
+
+ return img;
+}
+
+function getHTMLElements(node, name)
+{
+ if (htmlMode)
+ return node.getElementsByTagName(name);
+ return node.getElementsByTagNameNS(XHTMLNS, name);
+}
+
+// name should be in lower case
+function isHTMLElement(node, name)
+{
+ if (node.nodeType != Node.ELEMENT_NODE)
+ return false;
+
+ if (htmlMode)
+ return !name || node.localName.toLowerCase() == name;
+
+ return (!name || node.localName == name) && node.namespaceURI == XHTMLNS;
+}
+
+// This function coded according to the spec at:
+// http://www.bath.ac.uk/~py8ieh/internet/discussion/metadata.txt
+function convertLanguageCode(abbr)
+{
+ if (!abbr) return "";
+ var result;
+ var region = "";
+ var tokens = abbr.split("-");
+ var language = tokens.shift();
+
+ if (language == "x" || language == "i")
+ {
+ // x and i prefixes mean unofficial ones. So we proper-case the next
+ // word and leave the rest.
+ if (tokens.length > 0)
+ {
+ // Upper-case first letter
+ language = tokens[0].substr(0, 1).toUpperCase() + tokens[0].substr(1);
+ tokens.shift();
+
+ // Add on the rest as space-separated strings inside the brackets
+ region = tokens.join(" ");
+ }
+ }
+ else
+ {
+ // Otherwise we treat the first as a lang, the second as a region
+ // and the rest as strings.
+ try
+ {
+ language = gLangBundle.getString(language.toLowerCase());
+ }
+ catch (e)
+ {
+ }
+
+ if (tokens.length > 0)
+ {
+ try
+ {
+ tokens[0] = gRegionBundle.getString(tokens[0].toLowerCase());
+ }
+ catch (e)
+ {
+ }
+ region = tokens.join(" ");
+ }
+ }
+
+ if (region) {
+ result = gMetadataBundle.getFormattedString("languageRegionFormat",
+ [language, region]);
+ } else {
+ result = language;
+ }
+ return result;
+}
+
+function setAlt(elem) {
+ var altText = document.getElementById("image-alt-text");
+ if (elem.hasAttribute("alt")) {
+ if (elem.alt != "") {
+ altText.value = elem.alt;
+ altText.setAttribute("style","font-style:inherit");
+ } else {
+ altText.value = gMetadataBundle.getString("altTextBlank");
+ altText.setAttribute("style","font-style:italic");
+ }
+ } else {
+ altText.value = gMetadataBundle.getString("altTextMissing");
+ altText.setAttribute("style","font-style:italic");
+ }
+
+}
+
+function formatNumber(number)
+{
+ return (+number).toLocaleString(); // coerce number to a numeric value before calling toLocaleString()
+}
+
+function makeHrefAbsolute(href, elem)
+{
+ const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+ try {
+ var baseURI = NetUtil.newURI(elem.baseURI, elem.ownerDocument.characterSet);
+ href = NetUtil.newURI(href, elem.ownerDocument.characterSet, baseURI).spec;
+ } catch (e) {
+ }
+ return href;
+}