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 / editor / EdInsertTOC.js < prev    next >
Text File  |  2003-06-08  |  15KB  |  413 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is TOCMaker.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Daniel Glazman.
  18.  * Portions created by the Initial Developer are Copyright (C) 2002
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *   Daniel Glazman <daniel@glazman.org> (Original author)
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. // tocHeadersArray is the array containing the pairs tag/class
  39. // defining TOC entries
  40. var tocHeadersArray = new Array(6);
  41.  
  42. // a global used when building the TOC
  43. var currentHeaderLevel = 0;
  44.  
  45. // a global set to true if the TOC is to be readonly
  46. var readonly = false;
  47.  
  48. // a global set to true if user wants indexes in the TOC
  49. var orderedList = true;
  50.  
  51. // constants
  52. const kMozToc                  = "mozToc";
  53. const kMozTocLength            = 6;
  54. const kMozTocIdPrefix          = "mozTocId";
  55. const kMozTocIdPrefixLength    = 8;
  56. const kMozTocClassPrefix       = "mozToc";
  57. const kMozTocClassPrefixLength = 6;
  58.  
  59. // Startup() is called when EdInsertTOC.xul is opened
  60. function Startup()
  61. {
  62.   // early way out if if we have no editor
  63.   if (!GetCurrentEditor()) {
  64.     window.close();
  65.     return;
  66.   }
  67.  
  68.   var i, j;
  69.   // clean the table of tag/class pairs we look for
  70.   for (i = 0; i < 6; ++i)
  71.     tocHeadersArray[i] = [ "", "" ];
  72.  
  73.   // reset all settings
  74.   for (i = 1; i < 7; ++i) {
  75.     var menulist = document.getElementById("header" + i + "Menulist");
  76.     var menuitem = document.getElementById("header" + i + "none");
  77.     var textbox  = document.getElementById("header" + i + "Class");
  78.     menulist.selectedItem = menuitem;
  79.     textbox.setAttribute("disabled", "true");
  80.   }
  81.  
  82.   var theDocument = GetCurrentEditor().document;
  83.  
  84.   // do we already have a TOC in the document ? It should have "mozToc" ID
  85.   var toc = theDocument.getElementById(kMozToc);
  86.  
  87.   // default TOC definition, use h1-h6 for TOC entry levels 1-6
  88.   var headers = "h1 1 h2 2 h3 3 h4 4 h5 5 h6 6";
  89.  
  90.   var orderedListCheckbox = document.getElementById("orderedListCheckbox");
  91.   orderedListCheckbox.checked = true;
  92.  
  93.   if (toc) {
  94.     // man, there is already a TOC here
  95.  
  96.     if (toc.getAttribute("class") == "readonly") {
  97.       // and it's readonly
  98.       var checkbox = document.getElementById("readOnlyCheckbox");
  99.       checkbox.checked = true;
  100.       readonly = true;
  101.     }
  102.  
  103.     // let's see if it's an OL or an UL
  104.     orderedList = (toc.nodeName.toLowerCase() == "ol");
  105.     orderedListCheckbox.checked = orderedList;
  106.  
  107.     var nodeList = toc.childNodes;
  108.     // let's look at the children of the TOC ; if we find a comment beginning
  109.     // with "mozToc", it contains the TOC definition
  110.     for (i = 0; i< nodeList.length; ++i) {
  111.       if (nodeList.item(i).nodeType == Node.COMMENT_NODE &&
  112.           nodeList.item(i).data.substr(0, kMozTocLength) == kMozToc) {
  113.         // yep, there is already a definition here; parse it !
  114.         headers = nodeList.item(i).data.substr(kMozTocLength + 1,
  115.                                     nodeList.item(i).length - kMozTocLength - 1);
  116.         break;
  117.       }
  118.     }
  119.   }
  120.  
  121.   // let's get an array filled with the (tag.class, index level) pairs
  122.   var headersArray = headers.split(" ");
  123.  
  124.   for (i = 0; i < headersArray.length; i += 2) {
  125.     var tag = headersArray[i], className = "";
  126.     var index = headersArray[i + 1];
  127.     menulist = document.getElementById("header" + index + "Menulist");
  128.     if (menulist) {
  129.       var sep = tag.indexOf(".");
  130.       if (sep != -1) {
  131.         // the tag variable contains in fact "tag.className", let's parse
  132.         // the class and get the real tag name
  133.         var tmp   = tag.substr(0, sep);
  134.         className = tag.substr(sep + 1, tag.length - sep - 1);
  135.         tag = tmp;
  136.       }
  137.  
  138.       // update the dialog
  139.       menuitem = document.getElementById("header" + index +
  140.                                          tag.toUpperCase());
  141.       textbox  = document.getElementById("header" + index + "Class");
  142.       menulist.selectedItem = menuitem;
  143.       if (tag != "") {
  144.         textbox.removeAttribute("disabled");
  145.       }
  146.       if (className != "") {
  147.         textbox.value = className;
  148.       }
  149.       tocHeadersArray[index - 1] = [ tag, className ];
  150.     }
  151.   }
  152. }
  153.  
  154.  
  155. function BuildTOC(update)
  156. {
  157.   // controlClass() is a node filter that accepts a node if
  158.   // (a) we don't look for a class (b) we look for a class and
  159.   // node has it
  160.   function controlClass(node, index)
  161.   {
  162.     currentHeaderLevel = index + 1;
  163.     if (tocHeadersArray[index][1] == "") {
  164.       // we are not looking for a specific class, this node is ok
  165.       return NodeFilter.FILTER_ACCEPT;
  166.     }
  167.     if (node.getAttribute("class")) {
  168.       // yep, we look for a class, let's look at all the classes
  169.       // the node has
  170.       var classArray = node.getAttribute("class").split(" ");
  171.       for (var j = 0; j < classArray.length; j++) {
  172.         if (classArray[j] == tocHeadersArray[index][1]) {
  173.           // hehe, we found it...
  174.           return NodeFilter.FILTER_ACCEPT;
  175.         }
  176.       }
  177.     }
  178.     return NodeFilter.FILTER_SKIP;
  179.   }
  180.  
  181.   // the main node filter for our node iterator
  182.   // it selects the tag names as specified in the dialog
  183.   // then calls the controlClass filter above
  184.   function acceptNode(node)
  185.   {
  186.     switch (node.nodeName.toLowerCase())
  187.     {
  188.       case tocHeadersArray[0][0]:
  189.         return controlClass(node, 0);
  190.         break;
  191.       case tocHeadersArray[1][0]:
  192.         return controlClass(node, 1);
  193.         break;
  194.       case tocHeadersArray[2][0]:
  195.         return controlClass(node, 2);
  196.         break;
  197.       case tocHeadersArray[3][0]:
  198.         return controlClass(node, 3);
  199.         break;
  200.       case tocHeadersArray[4][0]:
  201.         return controlClass(node, 4);
  202.         break;
  203.       case tocHeadersArray[5][0]:
  204.         return controlClass(node, 5);
  205.         break;
  206.       default:
  207.         return NodeFilter.FILTER_SKIP;
  208.         break;
  209.     }
  210.     return NodeFilter.FILTER_SKIP;   // placate the js compiler
  211.   }
  212.  
  213.   var editor = GetCurrentEditor();
  214.   var theDocument = editor.document;
  215.   // let's create a TreeWalker to look for our nodes
  216.   var treeWalker = theDocument.createTreeWalker(theDocument.documentElement,
  217.                                                 NodeFilter.SHOW_ELEMENT,
  218.                                                 acceptNode,
  219.                                                 true);
  220.   // we need an array to store all TOC entries we find in the document
  221.   var tocArray = new Array();
  222.   if (treeWalker) {
  223.     var tocSourceNode = treeWalker.nextNode();
  224.     while (tocSourceNode) {
  225.       var headerIndex = currentHeaderLevel;
  226.  
  227.       // we have a node, we need to get all its textual contents
  228.       var textTreeWalker = theDocument.createTreeWalker(tocSourceNode,
  229.                                                         NodeFilter.SHOW_TEXT,
  230.                                                         null,
  231.                                                         true);
  232.       var textNode = textTreeWalker.nextNode(), headerText = "";
  233.       while (textNode) {
  234.         headerText += textNode.data;
  235.         textNode = textTreeWalker.nextNode();
  236.       }
  237.  
  238.       var anchor = tocSourceNode.firstChild, id;
  239.       // do we have a named anchor as 1st child of our node ?
  240.       if (anchor.nodeName.toLowerCase() == "a" &&
  241.           anchor.hasAttribute("name") &&
  242.           anchor.getAttribute("name").substr(0, kMozTocIdPrefixLength) == kMozTocIdPrefix) {
  243.         // yep, get its name
  244.         id = anchor.getAttribute("name");
  245.       }
  246.       else {
  247.         // no we don't and we need to create one
  248.         anchor = theDocument.createElement("a");
  249.         tocSourceNode.insertBefore(anchor, tocSourceNode.firstChild);
  250.         // let's give it a random ID
  251.         var c = 1000000 * Math.random();
  252.         id = kMozTocIdPrefix + Math.round(c);
  253.         anchor.setAttribute("name",  id);
  254.         anchor.setAttribute("class", kMozTocClassPrefix +
  255.                                      tocSourceNode.nodeName.toUpperCase());
  256.       }
  257.       // and store that new entry in our array
  258.       tocArray.push(headerIndex, headerText, id);
  259.       tocSourceNode = treeWalker.nextNode();
  260.     }
  261.   }
  262.  
  263.   /* generate the TOC itself */
  264.   headerIndex = 0;
  265.   var item, toc;
  266.   for (var i = 0; i < tocArray.length; i += 3) {
  267.     if (!headerIndex) {
  268.       // do we need to create an ol/ul container for the first entry ?
  269.       ++headerIndex;
  270.       toc = theDocument.getElementById(kMozToc);
  271.       if (!toc || !update) {
  272.         // we need to create a list container for the table of contents
  273.         toc = GetCurrentEditor().createElementWithDefaults(orderedList ? "ol" : "ul");
  274.         // grrr, we need to create a LI inside the list otherwise
  275.         // Composer will refuse an empty list and will remove it !
  276.         var pit = theDocument.createElement("li");
  277.         toc.appendChild(pit);
  278.         GetCurrentEditor().insertElementAtSelection(toc, true);
  279.         // ah, now it's inserted so let's remove the useless list item...
  280.         toc.removeChild(pit);
  281.         // we need to recognize later that this list is our TOC
  282.         toc.setAttribute("id", kMozToc);
  283.       }
  284.       else {
  285.         // we have to update an existing TOC, is the existing TOC of the
  286.         // desired type (ordered or not) ?
  287.         if (orderedList != (toc.nodeName.toLowerCase() == "ol")) {
  288.           // nope, we have to recreate the list
  289.           var newToc = GetCurrentEditor().createElementWithDefaults(orderedList ? "ol" : "ul");
  290.           toc.parentNode.insertBefore(newToc, toc);
  291.           // and remove the old one
  292.           toc.parentNode.removeChild(toc);
  293.           toc = newToc;
  294.           toc.setAttribute("id", kMozToc);
  295.         }
  296.         else {
  297.           // we can keep the list itself but let's get rid of the TOC entries
  298.           var nodeList = toc.childNodes, l = nodeList.length;
  299.           for (j = l - 1; j >= 0; --j)
  300.             toc.removeChild(nodeList.item(j));
  301.         }
  302.       }
  303.       var commentText = "mozToc ";
  304.       for (var j = 0; j < 6; j++) {
  305.         if (tocHeadersArray[j][0] != "") {
  306.           commentText += tocHeadersArray[j][0];
  307.           if (tocHeadersArray[j][1] != "") {
  308.             commentText += "." + tocHeadersArray[j][1];
  309.           }
  310.           commentText += " " + (j + 1) + " ";
  311.         }
  312.       }
  313.       // important, we have to remove trailing spaces
  314.       commentText = TrimStringRight(commentText);
  315.  
  316.       // forge a comment we'll insert in the TOC ; that comment will hold
  317.       // the TOC definition for us
  318.       var ct = theDocument.createComment(commentText);
  319.       toc.appendChild(ct);
  320.  
  321.       // assign a special class to the TOC top element if the TOC is readonly
  322.       // the definition of this class is in EditorOverride.css
  323.       if (readonly) {
  324.         toc.setAttribute("class", "readonly");
  325.       }
  326.       else {
  327.         toc.removeAttribute("class");
  328.       }
  329.  
  330.       // We need a new variable to hold the local ul/ol container
  331.       // The toplevel TOC element is not the parent element of a
  332.       // TOC entry if its depth is > 1...
  333.       var tocList = toc;
  334.       // create a list item
  335.       var tocItem = theDocument.createElement("li");
  336.       // and an anchor in this list item
  337.       var tocAnchor = theDocument.createElement("a");
  338.       // make it target the source of the TOC entry
  339.       tocAnchor.setAttribute("href", "#" + tocArray[i + 2]);
  340.       // and put the textual contents of the TOC entry in that anchor
  341.       var tocEntry = theDocument.createTextNode(tocArray[i + 1]);
  342.       // now, insert everything where it has to be inserted
  343.       tocAnchor.appendChild(tocEntry);
  344.       tocItem.appendChild(tocAnchor);
  345.       tocList.appendChild(tocItem);
  346.       item = tocList;
  347.     }
  348.     else {
  349.       if (tocArray[i] < headerIndex) {
  350.         // if the depth of the new TOC entry is less than the depth of the
  351.         // last entry we created, find the good ul/ol ancestor
  352.         for (j = headerIndex - tocArray[i]; j > 0; --j) {
  353.           if (item != toc) {
  354.             item = item.parentNode.parentNode;
  355.           }
  356.         }
  357.         tocItem = theDocument.createElement("li");
  358.       }
  359.       else if (tocArray[i] > headerIndex) {
  360.         // to the contrary, it's deeper than the last one
  361.         // we need to create sub ul/ol's and li's
  362.         for (j = tocArray[i] - headerIndex; j > 0; --j) {
  363.           tocList = theDocument.createElement(orderedList ? "ol" : "ul");
  364.           item.lastChild.appendChild(tocList);
  365.           tocItem = theDocument.createElement("li");
  366.           tocList.appendChild(tocItem);
  367.           item = tocList;
  368.         }
  369.       }
  370.       else {
  371.         tocItem = theDocument.createElement("li");
  372.       }
  373.       tocAnchor = theDocument.createElement("a");
  374.       tocAnchor.setAttribute("href", "#" + tocArray[i + 2]);
  375.       tocEntry = theDocument.createTextNode(tocArray[i + 1]);
  376.       tocAnchor.appendChild(tocEntry);
  377.       tocItem.appendChild(tocAnchor);
  378.       item.appendChild(tocItem);
  379.       headerIndex = tocArray[i];
  380.     }
  381.   }
  382.   SaveWindowLocation();
  383.   return true;
  384. }
  385.  
  386. function selectHeader(elt, index)
  387. {
  388.   var tag = elt.value;
  389.   tocHeadersArray[index - 1][0] = tag;
  390.   var textbox = document.getElementById("header" + index + "Class");
  391.   if (tag == "") {
  392.     textbox.setAttribute("disabled", "true");
  393.   }
  394.   else {
  395.     textbox.removeAttribute("disabled");
  396.   }
  397. }
  398.  
  399. function changeClass(elt, index)
  400. {
  401.   tocHeadersArray[index - 1][1] = elt.value;
  402. }
  403.  
  404. function ToggleReadOnlyToc(elt)
  405. {
  406.   readonly = elt.checked;
  407. }
  408.  
  409. function ToggleOrderedList(elt)
  410. {
  411.   orderedList = elt.checked;
  412. }
  413.