!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
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
Startup
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
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
BuildTOC
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
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
controlClass
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
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
acceptNode
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
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 while (toc.hasChildNodes())
299 toc.removeChild(toc.lastChild);
300 }
301 }
302 var commentText = "mozToc ";
303 for (var j = 0; j < 6; j++) {
304 if (tocHeadersArray[j][0] != "") {
305 commentText += tocHeadersArray[j][0];
306 if (tocHeadersArray[j][1] != "") {
307 commentText += "." + tocHeadersArray[j][1];
308 }
309 commentText += " " + (j + 1) + " ";
310 }
311 }
312 // important, we have to remove trailing spaces
313 commentText = TrimStringRight(commentText);
314
315 // forge a comment we'll insert in the TOC ; that comment will hold
316 // the TOC definition for us
317 var ct = theDocument.createComment(commentText);
318 toc.appendChild(ct);
319
320 // assign a special class to the TOC top element if the TOC is readonly
321 // the definition of this class is in EditorOverride.css
322 if (readonly) {
323 toc.setAttribute("class", "readonly");
324 }
325 else {
326 toc.removeAttribute("class");
327 }
328
329 // We need a new variable to hold the local ul/ol container
330 // The toplevel TOC element is not the parent element of a
331 // TOC entry if its depth is > 1...
332 var tocList = toc;
333 // create a list item
334 var tocItem = theDocument.createElement("li");
335 // and an anchor in this list item
336 var tocAnchor = theDocument.createElement("a");
337 // make it target the source of the TOC entry
338 tocAnchor.setAttribute("href", "#" + tocArray[i + 2]);
339 // and put the textual contents of the TOC entry in that anchor
340 var tocEntry = theDocument.createTextNode(tocArray[i + 1]);
341 // now, insert everything where it has to be inserted
342 tocAnchor.appendChild(tocEntry);
343 tocItem.appendChild(tocAnchor);
344 tocList.appendChild(tocItem);
345 item = tocList;
346 }
347 else {
348 if (tocArray[i] < headerIndex) {
349 // if the depth of the new TOC entry is less than the depth of the
350 // last entry we created, find the good ul/ol ancestor
351 for (j = headerIndex - tocArray[i]; j > 0; --j) {
352 if (item != toc) {
353 item = item.parentNode.parentNode;
354 }
355 }
356 tocItem = theDocument.createElement("li");
357 }
358 else if (tocArray[i] > headerIndex) {
359 // to the contrary, it's deeper than the last one
360 // we need to create sub ul/ol's and li's
361 for (j = tocArray[i] - headerIndex; j > 0; --j) {
362 tocList = theDocument.createElement(orderedList ? "ol" : "ul");
363 item.lastChild.appendChild(tocList);
364 tocItem = theDocument.createElement("li");
365 tocList.appendChild(tocItem);
366 item = tocList;
367 }
368 }
369 else {
370 tocItem = theDocument.createElement("li");
371 }
372 tocAnchor = theDocument.createElement("a");
373 tocAnchor.setAttribute("href", "#" + tocArray[i + 2]);
374 tocEntry = theDocument.createTextNode(tocArray[i + 1]);
375 tocAnchor.appendChild(tocEntry);
376 tocItem.appendChild(tocAnchor);
377 item.appendChild(tocItem);
378 headerIndex = tocArray[i];
379 }
380 }
381 SaveWindowLocation();
382 return true;
383 }
384
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
385 function selectHeader(elt, index)
386 {
387 var tag = elt.value;
388 tocHeadersArray[index - 1][0] = tag;
389 var textbox = document.getElementById("header" + index + "Class");
390 if (tag == "") {
391 textbox.setAttribute("disabled", "true");
392 }
393 else {
394 textbox.removeAttribute("disabled");
395 }
396 }
397
changeClass
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
398 function changeClass(elt, index)
399 {
400 tocHeadersArray[index - 1][1] = elt.value;
401 }
402
ToggleReadOnlyToc
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
403 function ToggleReadOnlyToc(elt)
404 {
405 readonly = elt.checked;
406 }
407
ToggleOrderedList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
408 function ToggleOrderedList(elt)
409 {
410 orderedList = elt.checked;
411 }