~ubuntu-branches/ubuntu/karmic/rhino/karmic

« back to all changes in this revision

Viewing changes to examples/jsdoc.js

  • Committer: Bazaar Package Importer
  • Author(s): Jerry Haltom
  • Date: 2005-03-19 16:56:07 UTC
  • mto: (11.1.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20050319165607-geu3j3fnqlkpqkh1
Tags: upstream-1.6.R1
ImportĀ upstreamĀ versionĀ 1.6.R1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 
 *
3
 
 * The contents of this file are subject to the Netscape Public
4
 
 * License Version 1.1 (the "License"); you may not use this file
5
 
 * except in compliance with the License. You may obtain a copy of
6
 
 * the License at http://www.mozilla.org/NPL/
7
 
 *
8
 
 * Software distributed under the License is distributed on an "AS
9
 
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10
 
 * implied. See the License for the specific language governing
11
 
 * rights and limitations under the License.
12
 
 *
13
 
 * The Original Code is Rhino code, released
14
 
 * May 6, 1999.
15
 
 *
16
 
 * The Initial Developer of the Original Code is Netscape
17
 
 * Communications Corporation.  Portions created by Netscape are
18
 
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
19
 
 * Rights Reserved.
20
 
 *
21
 
 * Contributor(s):
22
 
 * Norris Boyd
23
 
 * Roland Pennings
24
 
 *
25
 
 * Alternatively, the contents of this file may be used under the
26
 
 * terms of the GNU Public License (the "GPL"), in which case the
27
 
 * provisions of the GPL are applicable instead of those above.
28
 
 * If you wish to allow use of your version of this file only
29
 
 * under the terms of the GPL and not to allow others to use your
30
 
 * version of this file under the NPL, indicate your decision by
31
 
 * deleting the provisions above and replace them with the notice
32
 
 * and other provisions required by the GPL.  If you do not delete
33
 
 * the provisions above, a recipient may use your version of this
34
 
 * file under either the NPL or the GPL.
35
 
 */
36
 
 
37
 
/**
38
 
 * Process a JavaScript source file and process special comments
39
 
 * to produce an HTML file of documentation, similar to javadoc.
40
 
 * @author Norris Boyd
41
 
 * @see rhinotip.jar
42
 
 * @lastmodified xx
43
 
 * @version 1.2 Roland Pennings: Allow multiple files for a function. 
44
 
 * @version 1.3 Roland Pennings: Removes ../.. from the input directory name
45
 
 */
46
 
defineClass("File")
47
 
 
48
 
var functionDocArray = [];
49
 
var inputDirName = "";
50
 
var indexFileArray = [];
51
 
var indexFile = "";
52
 
var indexFileName = "index_files";
53
 
var indexFunctionArray = [];
54
 
var indexFunction = "";
55
 
var indexFunctionName = "index_functions";
56
 
var FileList = [];
57
 
var DirList = [];
58
 
var outputdir = null;
59
 
var debug = 0;
60
 
 
61
 
 
62
 
 
63
 
/**
64
 
 * Process JavaScript source file <code>f</code>, writing jsdoc to 
65
 
 * file <code>out</code>.
66
 
 * @param f input file
67
 
 * @param fname name of the input file (without the path)
68
 
 * @param inputdir directory of the input file
69
 
 * @param out output file
70
 
 */
71
 
function processFile(f, fname, inputdir, out) {
72
 
        var s;
73
 
        var firstLine = true;
74
 
        indexFileArray[fname] = "";
75
 
        
76
 
    // write the header of the output file
77
 
        out.writeLine('<HTML><HEADER><TITLE>' + fname + '</TITLE><BODY>');
78
 
        if (inputdir != null) {
79
 
          outstr = '<a name=\"_top_\"></a><pre><a href=\"' + indexFile + '\">Index Files</a> '; 
80
 
          outstr += '<a href=\"' + indexFunction + '\">Index Functions</a></pre><hr>';
81
 
      out.writeLine(outstr);
82
 
        }
83
 
 
84
 
    // process the input file
85
 
        var comment = "";
86
 
        while ((s = f.readLine()) != null) {
87
 
      var m = s.match(/\/\*\*(.*)/);
88
 
          if (m != null) {
89
 
                  // Found a comment start.
90
 
                  s = "*" + m[1];
91
 
                  do {
92
 
                        m = s.match(/(.*)\*\//);
93
 
                        if (m != null) {
94
 
                          // Found end of comment.
95
 
                          comment += m[1];
96
 
                          break;
97
 
                        }
98
 
                        // Strip leading whitespace and "*".
99
 
                        comment += s.replace(/^\s*\*/, "");
100
 
                        s = f.readLine();
101
 
                  } while (s != null);  
102
 
 
103
 
          if (debug)
104
 
          print("Found comment " + comment);
105
 
 
106
 
                  if (firstLine) {
107
 
                        // We have a comment for the whole file.
108
 
                        out.writeLine('<H1>File ' + fname + '</H1>');
109
 
                        out.writeLine(processComment(comment,firstLine,fname));
110
 
                        out.writeLine('<HR>');
111
 
                        firstLine = false;
112
 
                        comment = "";
113
 
                        continue;
114
 
                  }
115
 
          }
116
 
          // match the beginning of the function
117
 
          // NB we also match functions without a comment!
118
 
          // if we have two comments one after another only the last one will be taken
119
 
          m = s.match(/^\s*function\s+((\w+)|(\w+)(\s+))\(([^)]*)\)/); 
120
 
          if (m != null)
121
 
          {
122
 
                        // Found a function start
123
 
                        var htmlText = processFunction(m[1], m[5], comment); // sjm changed from 2nd to 5th arg
124
 
 
125
 
                        // Save the text in a global variable, so we
126
 
                        // can write out a table of contents first.
127
 
                        functionDocArray[functionDocArray.length] = {name:m[1], text:htmlText};
128
 
 
129
 
                        // Store the function also in the indexFunctionArray 
130
 
                        // so we can have a seperate file with the function table of contents
131
 
                        if (indexFunctionArray[m[1]]) {
132
 
                                //  print("ERROR: function: " + m[1] + " is defined more than once!");
133
 
                                // Allow multiple files for a function
134
 
                                with (indexFunctionArray[m[1]]) {
135
 
                                        filename = filename + "|" + fname;
136
 
                                        // print("filename = " + filename);
137
 
                                }
138
 
                        } 
139
 
                        else {
140
 
                                indexFunctionArray[m[1]] = {filename:fname};
141
 
                        }
142
 
                        //reset comment 
143
 
                        comment = "";
144
 
                }  
145
 
                // match a method being bound to a prototype
146
 
          m = s.match(/^\s*(\w*)\.prototype\.(\w*)\s*=\s*function\s*\(([^)]*)\)/);
147
 
          if (m != null)
148
 
          {
149
 
                        // Found a method being bound to a prototype. 
150
 
                        var htmlText = processPrototypeMethod(m[1], m[2], m[3], comment);
151
 
 
152
 
                        // Save the text in a global variable, so we
153
 
                        // can write out a table of contents first.
154
 
                        functionDocArray[functionDocArray.length] = {name:m[1]+".prototype."+m[2], text:htmlText};
155
 
 
156
 
                        // Store the function also in the indexFunctionArray 
157
 
                        // so we can have a seperate file with the function table of contents
158
 
                        if (indexFunctionArray[m[1]]) {
159
 
                                //  print("ERROR: function: " + m[1] + " is defined more than once!");
160
 
                                // Allow multiple files for a function
161
 
                                with (indexFunctionArray[m[1]]) {
162
 
                                        filename = filename + "|" + fname;
163
 
                                        // print("filename = " + filename);
164
 
                                }
165
 
                        } 
166
 
                        else {
167
 
                                indexFunctionArray[m[1]] = {filename:fname};
168
 
                        }
169
 
                        //reset comment 
170
 
                        comment = "";
171
 
                }  
172
 
                
173
 
                
174
 
                firstLine = false;
175
 
        }
176
 
 
177
 
        // Write table of contents.
178
 
        for (var i=0; i < functionDocArray.length; i++) {
179
 
                with (functionDocArray[i]) {
180
 
                        out.writeLine('function <A HREF=#' + name +
181
 
                                      '>' + name + '</A><BR>');
182
 
                }
183
 
        }
184
 
        out.writeLine('<HR>');
185
 
 
186
 
        // Now write the saved function documentation.
187
 
        for (i=0; i < functionDocArray.length; i++) {
188
 
                with (functionDocArray[i]) {
189
 
                        out.writeLine('<A NAME=' + name + '>');
190
 
                        out.writeLine(text);
191
 
                }
192
 
        }
193
 
        out.writeLine('</BODY></HTML>');
194
 
 
195
 
        // Now clean up the doc array
196
 
        functionDocArray = [];  
197
 
}
198
 
 
199
 
/**
200
 
 * Process function and associated comment.
201
 
 * @param name the name of the function
202
 
 * @param args the args of the function as a single string
203
 
 * @param comment the text of the comment
204
 
 * @return a string for the HTML text of the documentation
205
 
 */
206
 
function processFunction(name, args, comment) {
207
 
   if (debug)
208
 
    print("Processing " + name + " " + args + " " + comment);
209
 
        return "<H2>Function " + name + "</H2>" +
210
 
                "<PRE>" +
211
 
                "function " + name + "(" + args + ")" +
212
 
                "</PRE>" +
213
 
                processComment(comment,0,name) +
214
 
                "<P><BR><BR>";
215
 
}
216
 
 
217
 
/**
218
 
 * Process a method being bound to a prototype. 
219
 
 * @param proto the name of the prototype
220
 
 * @param name the name of the function
221
 
 * @param args the args of the function as a single string
222
 
 * @param comment the text of the comment
223
 
 * @return a string for the HTML text of the documentation
224
 
 */
225
 
function processPrototypeMethod(proto, name, args, comment) {
226
 
   if (debug)
227
 
    print("Processing " + proto + ".prototype." + name + " " + args + " " + comment);
228
 
        return "<H2> Method " + proto + ".prototype." + name + "</H2>" +
229
 
                "<PRE>" +
230
 
                proto + ".prototype." + name + " = function(" + args + ")" +
231
 
                "</PRE>" +
232
 
                processComment(comment,0,name) +
233
 
                "<P><BR><BR>";
234
 
}
235
 
 
236
 
 
237
 
/**
238
 
 * Process comment.
239
 
 * @param comment the text of the comment
240
 
 * @param firstLine shows if comment is at the beginning of the file
241
 
 * @param fname name of the file (without path)
242
 
 * @return a string for the HTML text of the documentation
243
 
 */
244
 
function processComment(comment,firstLine,fname) {
245
 
        var tags = {};
246
 
        // Use the "lambda" form of regular expression replace,
247
 
        // where the replacement object is a function rather 
248
 
        // than a string. The function is called with the 
249
 
        // matched text and any parenthetical matches as
250
 
        // arguments, and the result of the function used as the
251
 
        // replacement text. 
252
 
        // Here we use the function to build up the "tags" object,
253
 
        // which has a property for each "@" tag that is the name
254
 
        // of the tag, and whose value is an array of the
255
 
        // text following that tag.
256
 
        comment = comment.replace(/@(\w+)\s+([^@]*)/g,
257
 
                                  function (s, name, text) {
258
 
                                        var a = tags[name] || [];
259
 
                                        a.push(text);
260
 
                                        tags[name] = a;
261
 
                                        return "";
262
 
                                  });
263
 
                                  
264
 
        // if we have a comment at the beginning of a file
265
 
        // store the comment for the index file                           
266
 
        if (firstLine) {
267
 
          indexFileArray[fname] = comment;
268
 
        }
269
 
 
270
 
        var out = comment + '<P>';
271
 
        if (tags["param"]) {
272
 
                // Create a table of parameters and their descriptions.
273
 
                var array = tags["param"];
274
 
                var params = "";
275
 
                for (var i=0; i < array.length; i++) {
276
 
                        var m = array[i].match(/(\w+)\s+(.*)/);
277
 
                        params += '<TR><TD><I>'+m[1]+'</I></TD>' + 
278
 
                                  '<TD>'+m[2]+'</TD></TR>';
279
 
                }
280
 
                out += '<TABLE WIDTH="90%" BORDER=1>';
281
 
                out += '<TR BGCOLOR=0xdddddddd>';
282
 
                out += '<TD><B>Parameter</B></TD>';
283
 
                out += '<TD><B>Description</B></TD></TR>';
284
 
                out += params;
285
 
                out += '</TABLE><P>';
286
 
        }
287
 
        if (tags["return"]) {
288
 
                out += "<DT><B>Returns:</B><DD>";
289
 
                out += tags["return"][0] + "</DL><P>";
290
 
        }
291
 
        if (tags["author"]) {
292
 
                // List the authors together, separated by commas.
293
 
                out += '<DT><B>Author:</B><DD>';
294
 
                var array = tags["author"];
295
 
                for (var i=0; i < array.length; i++) {
296
 
                        out += array[i];
297
 
                        if (i+1 < array.length)
298
 
                                out += ", ";
299
 
                }
300
 
                out += '</DL><P>';
301
 
        }
302
 
        if (tags["version"]) {
303
 
            // Show the version.
304
 
            out += '<DT><B>Version:</B><DD>';
305
 
            var array = tags["version"];
306
 
            for (var i=0; i < array.length; i++) {
307
 
                   out += array[i];
308
 
                   if (i+1 < array.length)
309
 
                           out += "<BR><DD>";
310
 
                }
311
 
                out += '</DL><P>';
312
 
        }
313
 
        if (tags["see"]) {
314
 
            // List the see modules together, separated by <BR>.
315
 
            out += '<DT><B>Dependencies:</B><DD>';
316
 
            var array = tags["see"];
317
 
            for (var i=0; i < array.length; i++) {
318
 
                   out += array[i];
319
 
                   if (i+1 < array.length)
320
 
                           out += "<BR><DD>";
321
 
                }
322
 
                out += '</DL><P>';
323
 
        }
324
 
        if (tags["lastmodified"]) {
325
 
            // Shows a last modified description with client-side js.
326
 
            out += '<DT><B>Last modified:</B><DD>';
327
 
                out += '<script><!--\n';
328
 
                out += 'document.writeln(document.lastModified);\n';
329
 
                out += '// ---></script>\n';            
330
 
                out += '</DL><P>';
331
 
        }
332
 
 
333
 
        // additional tags can be added here (i.e., "if (tags["see"])...")
334
 
        return out;
335
 
}
336
 
 
337
 
/**
338
 
 * Create an html output file
339
 
 * @param outputdir directory to put the file
340
 
 * @param htmlfile name of the file
341
 
*/ 
342
 
function CreateOutputFile(outputdir,htmlfile)
343
 
{        
344
 
  if (outputdir==null)
345
 
  {
346
 
    var outname = htmlfile;
347
 
  }
348
 
  else
349
 
  {
350
 
    var separator = Packages.java.io.File.separator;
351
 
    var outname = outputdir + separator + htmlfile.substring(htmlfile.lastIndexOf(separator),htmlfile.length);
352
 
  }
353
 
  print("output file: " + outname);
354
 
  return new File(outname);
355
 
}
356
 
 
357
 
/**
358
 
 * Process a javascript file. Puts the generated HTML file in the outdir  
359
 
 * @param filename name of the javascript file
360
 
 * @inputdir input directory of the file (default null)
361
 
 */
362
 
function processJSFile(filename,inputdir) 
363
 
{
364
 
  if (debug) print("filename = " + filename + " inputdir = " + inputdir);
365
 
 
366
 
  if (!filename.match(/\.js$/)) { 
367
 
        print("Expected filename to end in '.js'; had instead " + 
368
 
          filename + ". I don't treat the file.");
369
 
  } else { 
370
 
    if (inputdir==null)
371
 
        {
372
 
          var inname = filename;
373
 
    }
374
 
        else
375
 
        {
376
 
      var separator = Packages.java.io.File.separator;
377
 
      var inname = inputdir + separator + filename;
378
 
    }
379
 
    print("Processing file " + inname);
380
 
 
381
 
        var f = new File(inname);
382
 
         
383
 
    // create the output file
384
 
    var htmlfile = filename.replace(/\.js$/, ".html");
385
 
 
386
 
        var out = CreateOutputFile(outputdir,htmlfile);
387
 
        
388
 
    processFile(f, filename, inputdir, out);
389
 
        out.close();
390
 
  }
391
 
}
392
 
 
393
 
/**
394
 
 * Generate index files containing links to the processed javascript files
395
 
 * and the generated functions
396
 
 */
397
 
function GenerateIndex(dirname)
398
 
{
399
 
  // construct the files index file
400
 
  var out = CreateOutputFile(outputdir,indexFile);
401
 
  
402
 
  // write the beginning of the file
403
 
  out.writeLine('<HTML><HEADER><TITLE>File Index - directory: ' + dirname + '</TITLE><BODY>');
404
 
  out.writeLine('<H1>File Index - directory: ' + dirname + '</H1>\n');
405
 
  out.writeLine('<TABLE WIDTH="90%" BORDER=1>');
406
 
  out.writeLine('<TR BGCOLOR=0xdddddddd>');
407
 
  out.writeLine('<TD><B>File</B></TD>');
408
 
  out.writeLine('<TD><B>Description</B></TD></TR>');
409
 
 
410
 
  var separator = Packages.java.io.File.separator;
411
 
 
412
 
  // sort the index file array
413
 
  var SortedFileArray = [];
414
 
  for (var fname in indexFileArray)
415
 
    SortedFileArray.push(fname);  
416
 
  SortedFileArray.sort();
417
 
 
418
 
  for (var i=0; i < SortedFileArray.length; i++) {
419
 
    var fname = SortedFileArray[i];
420
 
        var htmlfile = fname.replace(/\.js$/, ".html");
421
 
    out.writeLine('<TR><TD><A HREF=\"' + htmlfile + '\">' + fname + '</A></TD></TD><TD>');
422
 
        if (indexFileArray[fname])
423
 
          out.writeLine(indexFileArray[fname]);
424
 
        else
425
 
          out.writeLine('No comments');
426
 
        out.writeLine('</TD></TR>\n');
427
 
  }
428
 
  out.writeLine('</TABLE></BODY></HTML>');
429
 
  out.close();
430
 
  
431
 
  // construct the functions index file
432
 
  var out = CreateOutputFile(outputdir,indexFunction);
433
 
    
434
 
  // write the beginning of the file
435
 
  out.writeLine('<HTML><HEADER><TITLE>Function Index - directory: ' + dirname + '</TITLE><BODY>');
436
 
  out.writeLine('<H1>Function Index - directory: ' + dirname + '</H1>\n');
437
 
  out.writeLine('<TABLE WIDTH="90%" BORDER=1>');
438
 
  out.writeLine('<TR BGCOLOR=0xdddddddd>');
439
 
  out.writeLine('<TD><B>Function</B></TD>');
440
 
  out.writeLine('<TD><B>Files</B></TD></TR>');
441
 
  
442
 
  // sort the function array
443
 
  var SortedFunctionArray = [];
444
 
  for (var functionname in indexFunctionArray)
445
 
    SortedFunctionArray.push(functionname);  
446
 
  SortedFunctionArray.sort();
447
 
 
448
 
  for (var j=0; j < SortedFunctionArray.length; j++) {
449
 
    var funcname = SortedFunctionArray[j];
450
 
    with (indexFunctionArray[funcname]) {
451
 
         var outstr = '<TR><TD>' + funcname + '</TD><TD>';
452
 
         var filelst = filename.split("|");
453
 
         for (var i in filelst) {
454
 
           var htmlfile = filelst[i].replace(/\.js$/, ".html");
455
 
           outstr += '<A HREF=\"' + htmlfile + '#' + funcname + '\">' + filelst[i] + '</A>&nbsp;';
456
 
         }
457
 
         outstr += '</TD></TR>';
458
 
         out.writeLine(outstr);
459
 
    }
460
 
  }
461
 
  out.writeLine('</TABLE></BODY></HTML>');
462
 
  out.close();
463
 
}
464
 
 
465
 
 
466
 
/**
467
 
 * prints the options for JSDoc
468
 
*/
469
 
function PrintOptions()
470
 
{
471
 
  print("You can use the following options:\n");
472
 
  print("-d: specify an output directory for the generated html files\n");
473
 
  print("-i: processes all files in an input directory (you can specify several directories)\n");
474
 
  quit();
475
 
}
476
 
 
477
 
 
478
 
// Main Script
479
 
// first read the arguments 
480
 
if (! arguments)
481
 
  PrintOptions();
482
 
  
483
 
for (var i=0; i < arguments.length; i++) {
484
 
  if (debug) print("argument: + \'" + arguments[i] + "\'");
485
 
  if (arguments[i].match(/^\-/)) {
486
 
   if (String(arguments[i])=="-d"){
487
 
    // output directory for the generated html files
488
 
 
489
 
    outputdir = String(arguments[i+1]);
490
 
        if (debug) print("outputdir: + \'" + outputdir + "\'");
491
 
 
492
 
    i++;  
493
 
   }
494
 
   else if (String(arguments[i])=="-i"){
495
 
    // process all files in an input directory
496
 
 
497
 
    DirList.push(String(arguments[i+1]));
498
 
if (debug) print("inputdir: + \'" + arguments[i+1] + "\'");
499
 
     i++;
500
 
   }
501
 
   else {
502
 
    print("Unknown option: " + arguments[i] + "\n");
503
 
        PrintOptions();
504
 
   }
505
 
  }
506
 
  else
507
 
  {
508
 
    // we have a single file
509
 
        if (debug) print("file: + \'" + arguments[i] + "\'");
510
 
 
511
 
        FileList.push(String(arguments[i]));
512
 
  }
513
 
}  
514
 
        
515
 
// first handle the single files
516
 
for (var i in FileList) 
517
 
  processJSFile(FileList[i],null);
518
 
 
519
 
// then handle the input directories
520
 
for (var j in DirList) {
521
 
  var inputdir = String(DirList[j]);
522
 
  
523
 
  print("Process input directory: " + inputdir);
524
 
 
525
 
  // clean up index arrays
526
 
  var indexFileArray = [];
527
 
  var indexFunctionArray = [];
528
 
 
529
 
  // for the directory name get rid of ../../ or ..\..\
530
 
  inputDirName = inputdir.replace(/\.\.\/|\.\.\\/g,"");
531
 
  
532
 
  indexFile = indexFileName + "_" + inputDirName + ".html";
533
 
  indexFunction = indexFunctionName + "_" + inputDirName + ".html";
534
 
  
535
 
print("indexFile = " + indexFile);
536
 
print("indexFunction = " + indexFunction);
537
 
        
538
 
  // read the files in the directory
539
 
  var DirFile = new java.io.File(inputdir);
540
 
  var lst = DirFile.list();
541
 
  var separator = Packages.java.io.File.separator;
542
 
 
543
 
  for (var i=0; i < lst.length; i++)
544
 
  {
545
 
    processJSFile(String(lst[i]),inputdir);
546
 
  }  
547
 
 
548
 
  // generate the index files for the input directory
549
 
  GenerateIndex(inputDirName);
550
 
}
551
 
 
552
 
 
553
 
 
 
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * The contents of this file are subject to the Netscape Public
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/NPL/
 
7
 *
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 *
 
13
 * The Original Code is Rhino code, released
 
14
 * May 6, 1999.
 
15
 *
 
16
 * The Initial Developer of the Original Code is Netscape
 
17
 * Communications Corporation.  Portions created by Netscape are
 
18
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 
19
 * Rights Reserved.
 
20
 *
 
21
 * Contributor(s):
 
22
 * Norris Boyd
 
23
 * Roland Pennings
 
24
 *
 
25
 * Alternatively, the contents of this file may be used under the
 
26
 * terms of the GNU Public License (the "GPL"), in which case the
 
27
 * provisions of the GPL are applicable instead of those above.
 
28
 * If you wish to allow use of your version of this file only
 
29
 * under the terms of the GPL and not to allow others to use your
 
30
 * version of this file under the NPL, indicate your decision by
 
31
 * deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL.  If you do not delete
 
33
 * the provisions above, a recipient may use your version of this
 
34
 * file under either the NPL or the GPL.
 
35
 */
 
36
 
 
37
/**
 
38
 * Process a JavaScript source file and process special comments
 
39
 * to produce an HTML file of documentation, similar to javadoc.
 
40
 * @author Norris Boyd
 
41
 * @see rhinotip.jar
 
42
 * @lastmodified xx
 
43
 * @version 1.2 Roland Pennings: Allow multiple files for a function.
 
44
 * @version 1.3 Roland Pennings: Removes ../.. from the input directory name
 
45
 */
 
46
defineClass("File")
 
47
 
 
48
var functionDocArray = [];
 
49
var inputDirName = "";
 
50
var indexFileArray = [];
 
51
var indexFile = "";
 
52
var indexFileName = "index_files";
 
53
var indexFunctionArray = [];
 
54
var indexFunction = "";
 
55
var indexFunctionName = "index_functions";
 
56
var FileList = [];
 
57
var DirList = [];
 
58
var outputdir = null;
 
59
var debug = 0;
 
60
 
 
61
 
 
62
 
 
63
/**
 
64
 * Process JavaScript source file <code>f</code>, writing jsdoc to
 
65
 * file <code>out</code>.
 
66
 * @param f input file
 
67
 * @param fname name of the input file (without the path)
 
68
 * @param inputdir directory of the input file
 
69
 * @param out output file
 
70
 */
 
71
function processFile(f, fname, inputdir, out) {
 
72
        var s;
 
73
        var firstLine = true;
 
74
        indexFileArray[fname] = "";
 
75
 
 
76
    // write the header of the output file
 
77
        out.writeLine('<HTML><HEADER><TITLE>' + fname + '</TITLE><BODY>');
 
78
        if (inputdir != null) {
 
79
          outstr = '<a name=\"_top_\"></a><pre><a href=\"' + indexFile + '\">Index Files</a> ';
 
80
          outstr += '<a href=\"' + indexFunction + '\">Index Functions</a></pre><hr>';
 
81
      out.writeLine(outstr);
 
82
        }
 
83
 
 
84
    // process the input file
 
85
        var comment = "";
 
86
        while ((s = f.readLine()) != null) {
 
87
      var m = s.match(/\/\*\*(.*)/);
 
88
          if (m != null) {
 
89
                  // Found a comment start.
 
90
                  s = "*" + m[1];
 
91
                  do {
 
92
                        m = s.match(/(.*)\*\//);
 
93
                        if (m != null) {
 
94
                          // Found end of comment.
 
95
                          comment += m[1];
 
96
                          break;
 
97
                        }
 
98
                        // Strip leading whitespace and "*".
 
99
                        comment += s.replace(/^\s*\*/, "");
 
100
                        s = f.readLine();
 
101
                  } while (s != null);
 
102
 
 
103
          if (debug)
 
104
          print("Found comment " + comment);
 
105
 
 
106
                  if (firstLine) {
 
107
                        // We have a comment for the whole file.
 
108
                        out.writeLine('<H1>File ' + fname + '</H1>');
 
109
                        out.writeLine(processComment(comment,firstLine,fname));
 
110
                        out.writeLine('<HR>');
 
111
                        firstLine = false;
 
112
                        comment = "";
 
113
                        continue;
 
114
                  }
 
115
          }
 
116
          // match the beginning of the function
 
117
          // NB we also match functions without a comment!
 
118
          // if we have two comments one after another only the last one will be taken
 
119
          m = s.match(/^\s*function\s+((\w+)|(\w+)(\s+))\(([^)]*)\)/);
 
120
          if (m != null)
 
121
          {
 
122
                        // Found a function start
 
123
                        var htmlText = processFunction(m[1], m[5], comment); // sjm changed from 2nd to 5th arg
 
124
 
 
125
                        // Save the text in a global variable, so we
 
126
                        // can write out a table of contents first.
 
127
                        functionDocArray[functionDocArray.length] = {name:m[1], text:htmlText};
 
128
 
 
129
                        // Store the function also in the indexFunctionArray
 
130
                        // so we can have a seperate file with the function table of contents
 
131
                        if (indexFunctionArray[m[1]]) {
 
132
                                //  print("ERROR: function: " + m[1] + " is defined more than once!");
 
133
                                // Allow multiple files for a function
 
134
                                with (indexFunctionArray[m[1]]) {
 
135
                                        filename = filename + "|" + fname;
 
136
                                        // print("filename = " + filename);
 
137
                                }
 
138
                        }
 
139
                        else {
 
140
                                indexFunctionArray[m[1]] = {filename:fname};
 
141
                        }
 
142
                        //reset comment
 
143
                        comment = "";
 
144
                }
 
145
                // match a method being bound to a prototype
 
146
          m = s.match(/^\s*(\w*)\.prototype\.(\w*)\s*=\s*function\s*\(([^)]*)\)/);
 
147
          if (m != null)
 
148
          {
 
149
                        // Found a method being bound to a prototype.
 
150
                        var htmlText = processPrototypeMethod(m[1], m[2], m[3], comment);
 
151
 
 
152
                        // Save the text in a global variable, so we
 
153
                        // can write out a table of contents first.
 
154
                        functionDocArray[functionDocArray.length] = {name:m[1]+".prototype."+m[2], text:htmlText};
 
155
 
 
156
                        // Store the function also in the indexFunctionArray
 
157
                        // so we can have a seperate file with the function table of contents
 
158
                        if (indexFunctionArray[m[1]]) {
 
159
                                //  print("ERROR: function: " + m[1] + " is defined more than once!");
 
160
                                // Allow multiple files for a function
 
161
                                with (indexFunctionArray[m[1]]) {
 
162
                                        filename = filename + "|" + fname;
 
163
                                        // print("filename = " + filename);
 
164
                                }
 
165
                        }
 
166
                        else {
 
167
                                indexFunctionArray[m[1]] = {filename:fname};
 
168
                        }
 
169
                        //reset comment
 
170
                        comment = "";
 
171
                }
 
172
 
 
173
 
 
174
                firstLine = false;
 
175
        }
 
176
 
 
177
        // Write table of contents.
 
178
        for (var i=0; i < functionDocArray.length; i++) {
 
179
                with (functionDocArray[i]) {
 
180
                        out.writeLine('function <A HREF=#' + name +
 
181
                                      '>' + name + '</A><BR>');
 
182
                }
 
183
        }
 
184
        out.writeLine('<HR>');
 
185
 
 
186
        // Now write the saved function documentation.
 
187
        for (i=0; i < functionDocArray.length; i++) {
 
188
                with (functionDocArray[i]) {
 
189
                        out.writeLine('<A NAME=' + name + '>');
 
190
                        out.writeLine(text);
 
191
                }
 
192
        }
 
193
        out.writeLine('</BODY></HTML>');
 
194
 
 
195
        // Now clean up the doc array
 
196
        functionDocArray = [];
 
197
}
 
198
 
 
199
/**
 
200
 * Process function and associated comment.
 
201
 * @param name the name of the function
 
202
 * @param args the args of the function as a single string
 
203
 * @param comment the text of the comment
 
204
 * @return a string for the HTML text of the documentation
 
205
 */
 
206
function processFunction(name, args, comment) {
 
207
   if (debug)
 
208
    print("Processing " + name + " " + args + " " + comment);
 
209
        return "<H2>Function " + name + "</H2>" +
 
210
                "<PRE>" +
 
211
                "function " + name + "(" + args + ")" +
 
212
                "</PRE>" +
 
213
                processComment(comment,0,name) +
 
214
                "<P><BR><BR>";
 
215
}
 
216
 
 
217
/**
 
218
 * Process a method being bound to a prototype.
 
219
 * @param proto the name of the prototype
 
220
 * @param name the name of the function
 
221
 * @param args the args of the function as a single string
 
222
 * @param comment the text of the comment
 
223
 * @return a string for the HTML text of the documentation
 
224
 */
 
225
function processPrototypeMethod(proto, name, args, comment) {
 
226
   if (debug)
 
227
    print("Processing " + proto + ".prototype." + name + " " + args + " " + comment);
 
228
        return "<H2> Method " + proto + ".prototype." + name + "</H2>" +
 
229
                "<PRE>" +
 
230
                proto + ".prototype." + name + " = function(" + args + ")" +
 
231
                "</PRE>" +
 
232
                processComment(comment,0,name) +
 
233
                "<P><BR><BR>";
 
234
}
 
235
 
 
236
 
 
237
/**
 
238
 * Process comment.
 
239
 * @param comment the text of the comment
 
240
 * @param firstLine shows if comment is at the beginning of the file
 
241
 * @param fname name of the file (without path)
 
242
 * @return a string for the HTML text of the documentation
 
243
 */
 
244
function processComment(comment,firstLine,fname) {
 
245
        var tags = {};
 
246
        // Use the "lambda" form of regular expression replace,
 
247
        // where the replacement object is a function rather
 
248
        // than a string. The function is called with the
 
249
        // matched text and any parenthetical matches as
 
250
        // arguments, and the result of the function used as the
 
251
        // replacement text.
 
252
        // Here we use the function to build up the "tags" object,
 
253
        // which has a property for each "@" tag that is the name
 
254
        // of the tag, and whose value is an array of the
 
255
        // text following that tag.
 
256
        comment = comment.replace(/@(\w+)\s+([^@]*)/g,
 
257
                                  function (s, name, text) {
 
258
                                        var a = tags[name] || [];
 
259
                                        a.push(text);
 
260
                                        tags[name] = a;
 
261
                                        return "";
 
262
                                  });
 
263
 
 
264
        // if we have a comment at the beginning of a file
 
265
        // store the comment for the index file
 
266
        if (firstLine) {
 
267
          indexFileArray[fname] = comment;
 
268
        }
 
269
 
 
270
        var out = comment + '<P>';
 
271
        if (tags["param"]) {
 
272
                // Create a table of parameters and their descriptions.
 
273
                var array = tags["param"];
 
274
                var params = "";
 
275
                for (var i=0; i < array.length; i++) {
 
276
                        var m = array[i].match(/(\w+)\s+(.*)/);
 
277
                        params += '<TR><TD><I>'+m[1]+'</I></TD>' +
 
278
                                  '<TD>'+m[2]+'</TD></TR>';
 
279
                }
 
280
                out += '<TABLE WIDTH="90%" BORDER=1>';
 
281
                out += '<TR BGCOLOR=0xdddddddd>';
 
282
                out += '<TD><B>Parameter</B></TD>';
 
283
                out += '<TD><B>Description</B></TD></TR>';
 
284
                out += params;
 
285
                out += '</TABLE><P>';
 
286
        }
 
287
        if (tags["return"]) {
 
288
                out += "<DT><B>Returns:</B><DD>";
 
289
                out += tags["return"][0] + "</DL><P>";
 
290
        }
 
291
        if (tags["author"]) {
 
292
                // List the authors together, separated by commas.
 
293
                out += '<DT><B>Author:</B><DD>';
 
294
                var array = tags["author"];
 
295
                for (var i=0; i < array.length; i++) {
 
296
                        out += array[i];
 
297
                        if (i+1 < array.length)
 
298
                                out += ", ";
 
299
                }
 
300
                out += '</DL><P>';
 
301
        }
 
302
        if (tags["version"]) {
 
303
            // Show the version.
 
304
            out += '<DT><B>Version:</B><DD>';
 
305
            var array = tags["version"];
 
306
            for (var i=0; i < array.length; i++) {
 
307
                   out += array[i];
 
308
                   if (i+1 < array.length)
 
309
                           out += "<BR><DD>";
 
310
                }
 
311
                out += '</DL><P>';
 
312
        }
 
313
        if (tags["see"]) {
 
314
            // List the see modules together, separated by <BR>.
 
315
            out += '<DT><B>Dependencies:</B><DD>';
 
316
            var array = tags["see"];
 
317
            for (var i=0; i < array.length; i++) {
 
318
                   out += array[i];
 
319
                   if (i+1 < array.length)
 
320
                           out += "<BR><DD>";
 
321
                }
 
322
                out += '</DL><P>';
 
323
        }
 
324
        if (tags["lastmodified"]) {
 
325
            // Shows a last modified description with client-side js.
 
326
            out += '<DT><B>Last modified:</B><DD>';
 
327
                out += '<script><!--\n';
 
328
                out += 'document.writeln(document.lastModified);\n';
 
329
                out += '// ---></script>\n';
 
330
                out += '</DL><P>';
 
331
        }
 
332
 
 
333
        // additional tags can be added here (i.e., "if (tags["see"])...")
 
334
        return out;
 
335
}
 
336
 
 
337
/**
 
338
 * Create an html output file
 
339
 * @param outputdir directory to put the file
 
340
 * @param htmlfile name of the file
 
341
*/
 
342
function CreateOutputFile(outputdir,htmlfile)
 
343
{
 
344
  if (outputdir==null)
 
345
  {
 
346
    var outname = htmlfile;
 
347
  }
 
348
  else
 
349
  {
 
350
    var separator = Packages.java.io.File.separator;
 
351
    var outname = outputdir + separator + htmlfile.substring(htmlfile.lastIndexOf(separator),htmlfile.length);
 
352
  }
 
353
  print("output file: " + outname);
 
354
  return new File(outname);
 
355
}
 
356
 
 
357
/**
 
358
 * Process a javascript file. Puts the generated HTML file in the outdir
 
359
 * @param filename name of the javascript file
 
360
 * @inputdir input directory of the file (default null)
 
361
 */
 
362
function processJSFile(filename,inputdir)
 
363
{
 
364
  if (debug) print("filename = " + filename + " inputdir = " + inputdir);
 
365
 
 
366
  if (!filename.match(/\.js$/)) {
 
367
        print("Expected filename to end in '.js'; had instead " +
 
368
          filename + ". I don't treat the file.");
 
369
  } else {
 
370
    if (inputdir==null)
 
371
        {
 
372
          var inname = filename;
 
373
    }
 
374
        else
 
375
        {
 
376
      var separator = Packages.java.io.File.separator;
 
377
      var inname = inputdir + separator + filename;
 
378
    }
 
379
    print("Processing file " + inname);
 
380
 
 
381
        var f = new File(inname);
 
382
 
 
383
    // create the output file
 
384
    var htmlfile = filename.replace(/\.js$/, ".html");
 
385
 
 
386
        var out = CreateOutputFile(outputdir,htmlfile);
 
387
 
 
388
    processFile(f, filename, inputdir, out);
 
389
        out.close();
 
390
  }
 
391
}
 
392
 
 
393
/**
 
394
 * Generate index files containing links to the processed javascript files
 
395
 * and the generated functions
 
396
 */
 
397
function GenerateIndex(dirname)
 
398
{
 
399
  // construct the files index file
 
400
  var out = CreateOutputFile(outputdir,indexFile);
 
401
 
 
402
  // write the beginning of the file
 
403
  out.writeLine('<HTML><HEADER><TITLE>File Index - directory: ' + dirname + '</TITLE><BODY>');
 
404
  out.writeLine('<H1>File Index - directory: ' + dirname + '</H1>\n');
 
405
  out.writeLine('<TABLE WIDTH="90%" BORDER=1>');
 
406
  out.writeLine('<TR BGCOLOR=0xdddddddd>');
 
407
  out.writeLine('<TD><B>File</B></TD>');
 
408
  out.writeLine('<TD><B>Description</B></TD></TR>');
 
409
 
 
410
  var separator = Packages.java.io.File.separator;
 
411
 
 
412
  // sort the index file array
 
413
  var SortedFileArray = [];
 
414
  for (var fname in indexFileArray)
 
415
    SortedFileArray.push(fname);
 
416
  SortedFileArray.sort();
 
417
 
 
418
  for (var i=0; i < SortedFileArray.length; i++) {
 
419
    var fname = SortedFileArray[i];
 
420
        var htmlfile = fname.replace(/\.js$/, ".html");
 
421
    out.writeLine('<TR><TD><A HREF=\"' + htmlfile + '\">' + fname + '</A></TD></TD><TD>');
 
422
        if (indexFileArray[fname])
 
423
          out.writeLine(indexFileArray[fname]);
 
424
        else
 
425
          out.writeLine('No comments');
 
426
        out.writeLine('</TD></TR>\n');
 
427
  }
 
428
  out.writeLine('</TABLE></BODY></HTML>');
 
429
  out.close();
 
430
 
 
431
  // construct the functions index file
 
432
  var out = CreateOutputFile(outputdir,indexFunction);
 
433
 
 
434
  // write the beginning of the file
 
435
  out.writeLine('<HTML><HEADER><TITLE>Function Index - directory: ' + dirname + '</TITLE><BODY>');
 
436
  out.writeLine('<H1>Function Index - directory: ' + dirname + '</H1>\n');
 
437
  out.writeLine('<TABLE WIDTH="90%" BORDER=1>');
 
438
  out.writeLine('<TR BGCOLOR=0xdddddddd>');
 
439
  out.writeLine('<TD><B>Function</B></TD>');
 
440
  out.writeLine('<TD><B>Files</B></TD></TR>');
 
441
 
 
442
  // sort the function array
 
443
  var SortedFunctionArray = [];
 
444
  for (var functionname in indexFunctionArray)
 
445
    SortedFunctionArray.push(functionname);
 
446
  SortedFunctionArray.sort();
 
447
 
 
448
  for (var j=0; j < SortedFunctionArray.length; j++) {
 
449
    var funcname = SortedFunctionArray[j];
 
450
    with (indexFunctionArray[funcname]) {
 
451
         var outstr = '<TR><TD>' + funcname + '</TD><TD>';
 
452
         var filelst = filename.split("|");
 
453
         for (var i in filelst) {
 
454
           var htmlfile = filelst[i].replace(/\.js$/, ".html");
 
455
           outstr += '<A HREF=\"' + htmlfile + '#' + funcname + '\">' + filelst[i] + '</A>&nbsp;';
 
456
         }
 
457
         outstr += '</TD></TR>';
 
458
         out.writeLine(outstr);
 
459
    }
 
460
  }
 
461
  out.writeLine('</TABLE></BODY></HTML>');
 
462
  out.close();
 
463
}
 
464
 
 
465
 
 
466
/**
 
467
 * prints the options for JSDoc
 
468
*/
 
469
function PrintOptions()
 
470
{
 
471
  print("You can use the following options:\n");
 
472
  print("-d: specify an output directory for the generated html files\n");
 
473
  print("-i: processes all files in an input directory (you can specify several directories)\n");
 
474
  quit();
 
475
}
 
476
 
 
477
 
 
478
// Main Script
 
479
// first read the arguments
 
480
if (! arguments)
 
481
  PrintOptions();
 
482
 
 
483
for (var i=0; i < arguments.length; i++) {
 
484
  if (debug) print("argument: + \'" + arguments[i] + "\'");
 
485
  if (arguments[i].match(/^\-/)) {
 
486
   if (String(arguments[i])=="-d"){
 
487
    // output directory for the generated html files
 
488
 
 
489
    outputdir = String(arguments[i+1]);
 
490
        if (debug) print("outputdir: + \'" + outputdir + "\'");
 
491
 
 
492
    i++;
 
493
   }
 
494
   else if (String(arguments[i])=="-i"){
 
495
    // process all files in an input directory
 
496
 
 
497
    DirList.push(String(arguments[i+1]));
 
498
if (debug) print("inputdir: + \'" + arguments[i+1] + "\'");
 
499
     i++;
 
500
   }
 
501
   else {
 
502
    print("Unknown option: " + arguments[i] + "\n");
 
503
        PrintOptions();
 
504
   }
 
505
  }
 
506
  else
 
507
  {
 
508
    // we have a single file
 
509
        if (debug) print("file: + \'" + arguments[i] + "\'");
 
510
 
 
511
        FileList.push(String(arguments[i]));
 
512
  }
 
513
}
 
514
 
 
515
// first handle the single files
 
516
for (var i in FileList)
 
517
  processJSFile(FileList[i],null);
 
518
 
 
519
// then handle the input directories
 
520
for (var j in DirList) {
 
521
  var inputdir = String(DirList[j]);
 
522
 
 
523
  print("Process input directory: " + inputdir);
 
524
 
 
525
  // clean up index arrays
 
526
  var indexFileArray = [];
 
527
  var indexFunctionArray = [];
 
528
 
 
529
  // for the directory name get rid of ../../ or ..\..\
 
530
  inputDirName = inputdir.replace(/\.\.\/|\.\.\\/g,"");
 
531
 
 
532
  indexFile = indexFileName + "_" + inputDirName + ".html";
 
533
  indexFunction = indexFunctionName + "_" + inputDirName + ".html";
 
534
 
 
535
print("indexFile = " + indexFile);
 
536
print("indexFunction = " + indexFunction);
 
537
 
 
538
  // read the files in the directory
 
539
  var DirFile = new java.io.File(inputdir);
 
540
  var lst = DirFile.list();
 
541
  var separator = Packages.java.io.File.separator;
 
542
 
 
543
  for (var i=0; i < lst.length; i++)
 
544
  {
 
545
    processJSFile(String(lst[i]),inputdir);
 
546
  }
 
547
 
 
548
  // generate the index files for the input directory
 
549
  GenerateIndex(inputDirName);
 
550
}
 
551
 
 
552
 
 
553