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