home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 December / PCWorld_2005-12_cd.bin / komunikace / netscape / nsb-install-8-0.exe / chrome / toolkit.jar / content / global / viewSource.js < prev    next >
Text File  |  2005-09-26  |  17KB  |  578 lines

  1.  
  2. const pageLoaderIface = Components.interfaces.nsIWebPageDescriptor;
  3. const nsISelectionPrivate = Components.interfaces.nsISelectionPrivate;
  4. const nsISelectionController = Components.interfaces.nsISelectionController;
  5. var gBrowser = null;
  6. var gViewSourceBundle = null;
  7. var gPrefs = null;
  8.  
  9. var gLastLineFound = '';
  10. var gGoToLine = 0;
  11.  
  12. try {
  13.   var prefService = Components.classes["@mozilla.org/preferences-service;1"]
  14.                               .getService(Components.interfaces.nsIPrefService);
  15.   gPrefs = prefService.getBranch(null);
  16. } catch (ex) {
  17. }
  18.  
  19. var gSelectionListener = {
  20.   timeout: 0,
  21.   notifySelectionChanged: function(doc, sel, reason)
  22.   {
  23.     // Coalesce notifications within 100ms intervals.
  24.     if (!this.timeout)
  25.       this.timeout = setTimeout(updateStatusBar, 100);
  26.   }
  27. }
  28.  
  29. function onLoadViewSource() 
  30. {
  31.   viewSource(window.arguments[0]);
  32.   document.commandDispatcher.focusedWindow = content;
  33.   initFindBar();
  34. }
  35.  
  36. function onUnloadViewSource()
  37. {
  38.   uninitFindBar();
  39. }
  40.  
  41. function getBrowser()
  42. {
  43.   if (!gBrowser)
  44.     gBrowser = document.getElementById("content");
  45.   return gBrowser;
  46. }
  47.  
  48. function getSelectionController()
  49. {
  50.   return getBrowser().docShell
  51.     .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
  52.     .getInterface(Components.interfaces.nsISelectionDisplay)
  53.     .QueryInterface(nsISelectionController);
  54.  
  55. }
  56.  
  57. function getViewSourceBundle()
  58. {
  59.   if (!gViewSourceBundle)
  60.     gViewSourceBundle = document.getElementById("viewSourceBundle");
  61.   return gViewSourceBundle;
  62. }
  63.  
  64. function viewSource(url)
  65. {
  66.   if (!url)
  67.     return false; // throw Components.results.NS_ERROR_FAILURE;
  68.  
  69.   getBrowser().addEventListener("unload", onUnloadContent, true);
  70.   getBrowser().addEventListener("load", onLoadContent, true);
  71.  
  72.   var loadFromURL = true;
  73.   //
  74.   // Parse the 'arguments' supplied with the dialog.
  75.   //    arg[0] - URL string.
  76.   //    arg[1] - Charset value in the form 'charset=xxx'.
  77.   //    arg[2] - Page descriptor used to load content from the cache.
  78.   //    arg[3] - Line number to go to.
  79.   //
  80.   if ("arguments" in window) {
  81.     var arg;
  82.     //
  83.     // Set the charset of the viewsource window...
  84.     //
  85.     if (window.arguments.length >= 2) {
  86.       arg = window.arguments[1];
  87.  
  88.       try {
  89.         if (typeof(arg) == "string" && arg.indexOf('charset=') != -1) {
  90.           var arrayArgComponents = arg.split('=');
  91.           if (arrayArgComponents) {
  92.             //we should "inherit" the charset menu setting in a new window
  93.             getMarkupDocumentViewer().defaultCharacterSet = arrayArgComponents[1];
  94.           } 
  95.         }
  96.       } catch (ex) {
  97.         // Ignore the failure and keep processing arguments...
  98.       }
  99.     }
  100.     //
  101.     // Get any specified line to jump to.
  102.     //
  103.     if (window.arguments.length >= 4) {
  104.       arg = window.arguments[3];
  105.       gGoToLine = parseInt(arg);
  106.     }
  107.     //
  108.     // Use the page descriptor to load the content from the cache (if
  109.     // available).
  110.     //
  111.     if (window.arguments.length >= 3) {
  112.       arg = window.arguments[2];
  113.  
  114.       try {
  115.         if (typeof(arg) == "object" && arg != null) {
  116.           var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface);
  117.  
  118.           //
  119.           // Load the page using the page descriptor rather than the URL.
  120.           // This allows the content to be fetched from the cache (if
  121.           // possible) rather than the network...
  122.           //
  123.           PageLoader.LoadPage(arg, pageLoaderIface.DISPLAY_AS_SOURCE);
  124.           // The content was successfully loaded from the page cookie.
  125.           loadFromURL = false;
  126.         }
  127.       } catch(ex) {
  128.         // Ignore the failure.  The content will be loaded via the URL
  129.         // that was supplied in arg[0].
  130.       }
  131.     }
  132.   }
  133.  
  134.   if (loadFromURL) {
  135.     //
  136.     // Currently, an exception is thrown if the URL load fails...
  137.     //
  138.     var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
  139.     var viewSrcUrl = "view-source:" + url;
  140.     getBrowser().webNavigation.loadURI(viewSrcUrl, loadFlags, null, null, null);
  141.   }
  142.  
  143.   //check the view_source.wrap_long_lines pref and set the menuitem's checked attribute accordingly
  144.   if (gPrefs) {
  145.     try {
  146.       var wraplonglinesPrefValue = gPrefs.getBoolPref("view_source.wrap_long_lines");
  147.  
  148.       if (wraplonglinesPrefValue)
  149.         document.getElementById('menu_wrapLongLines').setAttribute("checked", "true");
  150.     } catch (ex) {
  151.     }
  152.     try {
  153.       document.getElementById("menu_highlightSyntax").setAttribute("checked", gPrefs.getBoolPref("view_source.syntax_highlight"));
  154.     } catch (ex) {
  155.     }
  156.   } else {
  157.     document.getElementById("menu_highlightSyntax").setAttribute("hidden", "true");
  158.   }
  159.  
  160.   window._content.focus();
  161.  
  162.   return true;
  163. }
  164.  
  165. function onLoadContent()
  166. {
  167.   //
  168.   // If the view source was opened with a "go to line" argument.
  169.   //
  170.   if (gGoToLine > 0) {
  171.     goToLine(gGoToLine);
  172.     gGoToLine = 0;
  173.   }
  174.   document.getElementById('cmd_goToLine').removeAttribute('disabled');
  175.  
  176.   // Register a listener so that we can show the caret position on the status bar.
  177.   window._content.getSelection()
  178.    .QueryInterface(nsISelectionPrivate)
  179.    .addSelectionListener(gSelectionListener);
  180. }
  181.  
  182. function onUnloadContent()
  183. {
  184.   //
  185.   // Disable "go to line" while reloading due to e.g. change of charset
  186.   // or toggling of syntax highlighting.
  187.   //
  188.   document.getElementById('cmd_goToLine').setAttribute('disabled', 'true');
  189. }
  190.  
  191. function ViewSourceClose()
  192. {
  193.   window.close();
  194. }
  195.  
  196. function BrowserReload()
  197. {
  198.   // Reload will always reload from cache which is probably not what's wanted
  199.   BrowserReloadSkipCache();
  200. }
  201.  
  202. function BrowserReloadSkipCache()
  203. {
  204.   const webNavigation = getBrowser().webNavigation;
  205.   webNavigation.reload(webNavigation.LOAD_FLAGS_BYPASS_PROXY | webNavigation.LOAD_FLAGS_BYPASS_CACHE);
  206. }
  207.  
  208. // Strips the |view-source:| for editPage()
  209. function ViewSourceEditPage()
  210. {
  211.   editPage(window.content.location.href.substring(12), window, false);
  212. }
  213.  
  214. // Strips the |view-source:| for saveURL()
  215. function ViewSourceSavePage()
  216. {
  217.   saveURL(window.content.location.href.substring(12), null, "SaveLinkTitle");
  218. }
  219.  
  220. function onEnterPP()
  221. {
  222.   var toolbox = document.getElementById("viewSource-toolbox");
  223.   toolbox.hidden = true;
  224. }
  225.  
  226. function onExitPP()
  227. {
  228.   var toolbox = document.getElementById("viewSource-toolbox");
  229.   toolbox.hidden = false;
  230. }
  231.  
  232. function ViewSourceGoToLine()
  233. {
  234.   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  235.         .getService(Components.interfaces.nsIPromptService);
  236.   var viewSourceBundle = getViewSourceBundle();
  237.  
  238.   var input = {value:gLastLineFound};
  239.   for (;;) {
  240.     var ok = promptService.prompt(
  241.         window,
  242.         viewSourceBundle.getString("goToLineTitle"),
  243.         viewSourceBundle.getString("goToLineText"),
  244.         input,
  245.         null,
  246.         {value:0});
  247.  
  248.     if (!ok) return;
  249.  
  250.     var line = parseInt(input.value);
  251.  
  252.     if (!(line > 0)) {
  253.       promptService.alert(window,
  254.           viewSourceBundle.getString("invalidInputTitle"),
  255.           viewSourceBundle.getString("invalidInputText"));
  256.   
  257.       continue;
  258.     }
  259.  
  260.     var found = goToLine(line);
  261.  
  262.     if (found) {
  263.       break;
  264.     }
  265.  
  266.     promptService.alert(window,
  267.         viewSourceBundle.getString("outOfRangeTitle"),
  268.         viewSourceBundle.getString("outOfRangeText"));
  269.   }
  270. }
  271.  
  272. function goToLine(line)
  273. {
  274.   var viewsource = window._content.document.body;
  275.  
  276.   //
  277.   // The source document is made up of a number of pre elements with
  278.   // id attributes in the format <pre id="line123">, meaning that
  279.   // the first line in the pre element is number 123.
  280.   // Do binary search to find the pre element containing the line.
  281.   //
  282.   var pre;
  283.   for (var lbound = 0, ubound = viewsource.childNodes.length; ; ) {
  284.     var middle = (lbound + ubound) >> 1;
  285.     pre = viewsource.childNodes[middle];
  286.  
  287.     var firstLine = parseInt(pre.id.substring(4));
  288.  
  289.     if (lbound == ubound - 1) {
  290.       break;
  291.     }
  292.  
  293.     if (line >= firstLine) {
  294.       lbound = middle;
  295.     } else {
  296.       ubound = middle;
  297.     }
  298.   }
  299.  
  300.   var result = {};
  301.   var found = findLocation(pre, line, null, -1, false, result);
  302.  
  303.   if (!found) {
  304.     return false;
  305.   }
  306.  
  307.   var selection = window._content.getSelection();
  308.   selection.removeAllRanges();
  309.  
  310.   // In our case, the range's startOffset is after "\n" on the previous line.
  311.   // Tune the selection at the beginning of the next line and do some tweaking
  312.   // to position the focusNode and the caret at the beginning of the line.
  313.  
  314.   selection.QueryInterface(nsISelectionPrivate)
  315.     .interlinePosition = true;    
  316.  
  317.   selection.addRange(result.range);
  318.  
  319.   if (!selection.isCollapsed) {
  320.     selection.collapseToEnd();
  321.  
  322.     var offset = result.range.startOffset;
  323.     var node = result.range.startContainer;
  324.     if (offset < node.data.length) {
  325.       // The same text node spans across the "\n", just focus where we were.
  326.       selection.extend(node, offset);
  327.     }
  328.     else {
  329.       // There is another tag just after the "\n", hook there. We need
  330.       // to focus a safe point because there are edgy cases such as
  331.       // <span>...\n</span><span>...</span> vs.
  332.       // <span>...\n<span>...</span></span><span>...</span>
  333.       node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
  334.       selection.extend(node, 0);
  335.     }
  336.   }
  337.  
  338.   var selCon = getSelectionController();
  339.   selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
  340.   selCon.setCaretEnabled(true);
  341.   selCon.setCaretVisibilityDuringSelection(true);
  342.  
  343.   // Scroll the beginning of the line into view.
  344.   selCon.scrollSelectionIntoView(
  345.     nsISelectionController.SELECTION_NORMAL,
  346.     nsISelectionController.SELECTION_FOCUS_REGION,
  347.     true);
  348.  
  349.   gLastLineFound = line;
  350.  
  351.   //pch: don't update the status bar for now
  352.   //document.getElementById("statusbar-line-col").label = getViewSourceBundle()
  353.   //    .getFormattedString("statusBarLineCol", [line, 1]);
  354.  
  355.   return true;
  356. }
  357.  
  358. function updateStatusBar()
  359. {
  360.   // Reset the coalesce flag.
  361.   gSelectionListener.timeout = 0;
  362.  
  363.   var statusBarField = document.getElementById("statusbar-line-col");
  364.  
  365.   var selection = window._content.getSelection();
  366.   if (!selection.focusNode) {
  367.     statusBarField.label = '';
  368.     return;
  369.   }
  370.   if (selection.focusNode.nodeType != Node.TEXT_NODE) {
  371.     return;
  372.   }
  373.  
  374.   var selCon = getSelectionController();
  375.   selCon.setDisplaySelection(nsISelectionController.SELECTION_ON);
  376.   selCon.setCaretEnabled(true);
  377.   selCon.setCaretVisibilityDuringSelection(true);
  378.  
  379.   var interlinePosition = selection
  380.       .QueryInterface(nsISelectionPrivate).interlinePosition;
  381.  
  382.   var result = {};
  383.   findLocation(null, -1, 
  384.       selection.focusNode, selection.focusOffset, interlinePosition, result);
  385.  
  386.   //pch no status bar for now
  387.   return;
  388.   statusBarField.label = getViewSourceBundle()
  389.       .getFormattedString("statusBarLineCol", [result.line, result.col]);
  390. }
  391.  
  392. //
  393. // Loops through the text lines in the pre element. The arguments are either
  394. // (pre, line) or (node, offset, interlinePosition). result is an out
  395. // argument. If (pre, line) are specified (and node == null), result.range is
  396. // a range spanning the specified line. If the (node, offset,
  397. // interlinePosition) are specified, result.line and result.col are the line
  398. // and column number of the specified offset in the specified node relative to
  399. // the whole file.
  400. //
  401. function findLocation(pre, line, node, offset, interlinePosition, result)
  402. {
  403.   if (node && !pre) {
  404.     //
  405.     // Look upwards to find the current pre element.
  406.     //
  407.     for (pre = node;
  408.          pre.nodeName != "PRE";
  409.          pre = pre.parentNode);
  410.   }
  411.  
  412.   //
  413.   // The source document is made up of a number of pre elements with
  414.   // id attributes in the format <pre id="line123">, meaning that
  415.   // the first line in the pre element is number 123.
  416.   //
  417.   var curLine = parseInt(pre.id.substring(4));
  418.  
  419.   //
  420.   // Walk through each of the text nodes and count newlines.
  421.   //
  422.   var treewalker = window._content.document
  423.       .createTreeWalker(pre, NodeFilter.SHOW_TEXT, null, false);
  424.  
  425.   //
  426.   // The column number of the first character in the current text node.
  427.   //
  428.   var firstCol = 1;
  429.  
  430.   var found = false;
  431.   for (var textNode = treewalker.firstChild();
  432.        textNode && !found;
  433.        textNode = treewalker.nextNode()) {
  434.  
  435.     //
  436.     // \r is not a valid character in the DOM, so we only check for \n.
  437.     //
  438.     var lineArray = textNode.data.split(/\n/);
  439.     var lastLineInNode = curLine + lineArray.length - 1;
  440.  
  441.     //
  442.     // Check if we can skip the text node without further inspection.
  443.     //
  444.     if (node ? (textNode != node) : (lastLineInNode < line)) {
  445.       if (lineArray.length > 1) {
  446.         firstCol = 1;
  447.       }
  448.       firstCol += lineArray[lineArray.length - 1].length;
  449.       curLine = lastLineInNode;
  450.       continue;
  451.     }
  452.  
  453.     //
  454.     // curPos is the offset within the current text node of the first
  455.     // character in the current line.
  456.     //
  457.     for (var i = 0, curPos = 0;
  458.          i < lineArray.length;
  459.          curPos += lineArray[i++].length + 1) {
  460.  
  461.       if (i > 0) {
  462.         curLine++;
  463.       }
  464.  
  465.       if (node) {
  466.         if (offset >= curPos && offset <= curPos + lineArray[i].length) {
  467.           //
  468.           // If we are right after the \n of a line and interlinePosition is
  469.           // false, the caret looks as if it were at the end of the previous
  470.           // line, so we display that line and column instead.
  471.           //
  472.           if (i > 0 && offset == curPos && !interlinePosition) {
  473.             result.line = curLine - 1;
  474.             var prevPos = curPos - lineArray[i - 1].length;
  475.             result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
  476.  
  477.           } else {
  478.             result.line = curLine;
  479.             result.col = (i == 0 ? firstCol : 1) + offset - curPos;
  480.           }
  481.           found = true;
  482.  
  483.           break;
  484.         }
  485.  
  486.       } else {
  487.         if (curLine == line && !("range" in result)) {
  488.           result.range = document.createRange();
  489.           result.range.setStart(textNode, curPos);
  490.  
  491.           //
  492.           // This will always be overridden later, except when we look for
  493.           // the very last line in the file (this is the only line that does
  494.           // not end with \n).
  495.           //
  496.           result.range.setEndAfter(pre.lastChild);
  497.  
  498.         } else if (curLine == line + 1) {
  499.           result.range.setEnd(textNode, curPos - 1);
  500.           found = true;
  501.           break;
  502.         }
  503.       }
  504.     }
  505.   }
  506.  
  507.   return found || ("range" in result);
  508. }
  509.  
  510. //function to toggle long-line wrapping and set the view_source.wrap_long_lines 
  511. //pref to persist the last state
  512. function wrapLongLines()
  513. {
  514.   var myWrap = window._content.document.body;
  515.  
  516.   if (myWrap.className == '')
  517.     myWrap.className = 'wrap';
  518.   else myWrap.className = '';
  519.  
  520.   //since multiple viewsource windows are possible, another window could have 
  521.   //affected the pref, so instead of determining the new pref value via the current
  522.   //pref value, we use myWrap.className  
  523.   if (gPrefs){
  524.     try {
  525.       if (myWrap.className == '') {
  526.         gPrefs.setBoolPref("view_source.wrap_long_lines", false);
  527.       }
  528.       else {
  529.         gPrefs.setBoolPref("view_source.wrap_long_lines", true);
  530.       }
  531.     } catch (ex) {
  532.     }
  533.   }
  534. }
  535.  
  536. //function to toggle syntax highlighting and set the view_source.syntax_highlight
  537. //pref to persist the last state
  538. function highlightSyntax()
  539. {
  540.   var highlightSyntaxMenu = document.getElementById("menu_highlightSyntax");
  541.   var highlightSyntax = (highlightSyntaxMenu.getAttribute("checked") == "true");
  542.   gPrefs.setBoolPref("view_source.syntax_highlight", highlightSyntax);
  543.  
  544.   var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface);
  545.   PageLoader.LoadPage(PageLoader.currentDescriptor, pageLoaderIface.DISPLAY_NORMAL);
  546. }
  547.  
  548. // Fix for bug 136322: this function overrides the function in
  549. // browser.js to call PageLoader.LoadPage() instead of BrowserReloadWithFlags()
  550. function BrowserSetForcedCharacterSet(aCharset)
  551. {
  552.   var docCharset = getBrowser().docShell.QueryInterface(
  553.                             Components.interfaces.nsIDocCharset);
  554.   docCharset.charset = aCharset;
  555.   var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface);
  556.   PageLoader.LoadPage(PageLoader.currentDescriptor, pageLoaderIface.DISPLAY_NORMAL);
  557. }
  558.  
  559. // fix for bug #229503
  560. // we need to define BrowserSetForcedDetector() so that we can
  561. // change auto-detect options in the "View | Character Encoding" menu.
  562. // As with BrowserSetForcedCharacterSet(), call PageLoader.LoadPage() 
  563. // instead of BrowserReloadWithFlags()
  564. function BrowserSetForcedDetector(doReload)
  565. {
  566.   getBrowser().documentCharsetInfo.forcedDetector = true; 
  567.   if (doReload)
  568.   {
  569.     var PageLoader = getBrowser().webNavigation.QueryInterface(pageLoaderIface);
  570.     PageLoader.LoadPage(PageLoader.currentDescriptor, pageLoaderIface.DISPLAY_NORMAL);
  571.   }
  572. }
  573.  
  574. function getMarkupDocumentViewer()
  575. {
  576.   return gBrowser.markupDocumentViewer;
  577. }
  578.