~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/editor/ui/dialogs/content/EdInsertTOC.js

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}