/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1997-1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Norris Boyd * Roland Pennings * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ /** * Process a JavaScript source file and process special comments * to produce an HTML file of documentation, similar to javadoc. * @author Norris Boyd * @see rhinotip.jar * @lastmodified xx * @version 1.2 Roland Pennings: Allow multiple files for a function. * @version 1.3 Roland Pennings: Removes ../.. from the input directory name */ defineClass("File") var functionDocArray = []; var inputDirName = ""; var indexFileArray = []; var indexFile = ""; var indexFileName = "index_files"; var indexFunctionArray = []; var indexFunction = ""; var indexFunctionName = "index_functions"; var FileList = []; var DirList = []; var outputdir = null; var debug = 0; /** * Process JavaScript source file f, writing jsdoc to * file out. * @param f input file * @param fname name of the input file (without the path) * @param inputdir directory of the input file * @param out output file */ function processFile(f, fname, inputdir, out) { var s; var firstLine = true; indexFileArray[fname] = ""; // write the header of the output file out.writeLine('
' + fname + ''); if (inputdir != null) { outstr = '
Index Files ';
	  outstr += 'Index Functions

'; out.writeLine(outstr); } // process the input file var comment = ""; while ((s = f.readLine()) != null) { var m = s.match(/\/\*\*(.*)/); if (m != null) { // Found a comment start. s = "*" + m[1]; do { m = s.match(/(.*)\*\//); if (m != null) { // Found end of comment. comment += m[1]; break; } // Strip leading whitespace and "*". comment += s.replace(/^\s*\*/, ""); s = f.readLine(); } while (s != null); if (debug) print("Found comment " + comment); if (firstLine) { // We have a comment for the whole file. out.writeLine('

File ' + fname + '

'); out.writeLine(processComment(comment,firstLine,fname)); out.writeLine('
'); firstLine = false; comment = ""; continue; } } // match the beginning of the function // NB we also match functions without a comment! // if we have two comments one after another only the last one will be taken m = s.match(/^\s*function\s+((\w+)|(\w+)(\s+))\(([^)]*)\)/); if (m != null) { // Found a function start var htmlText = processFunction(m[1], m[5], comment); // sjm changed from 2nd to 5th arg // Save the text in a global variable, so we // can write out a table of contents first. functionDocArray[functionDocArray.length] = {name:m[1], text:htmlText}; // Store the function also in the indexFunctionArray // so we can have a separate file with the function table of contents if (indexFunctionArray[m[1]]) { // print("ERROR: function: " + m[1] + " is defined more than once!"); // Allow multiple files for a function with (indexFunctionArray[m[1]]) { filename = filename + "|" + fname; // print("filename = " + filename); } } else { indexFunctionArray[m[1]] = {filename:fname}; } //reset comment comment = ""; } // match a method being bound to a prototype m = s.match(/^\s*(\w*)\.prototype\.(\w*)\s*=\s*function\s*\(([^)]*)\)/); if (m != null) { // Found a method being bound to a prototype. var htmlText = processPrototypeMethod(m[1], m[2], m[3], comment); // Save the text in a global variable, so we // can write out a table of contents first. functionDocArray[functionDocArray.length] = {name:m[1]+".prototype."+m[2], text:htmlText}; // Store the function also in the indexFunctionArray // so we can have a separate file with the function table of contents if (indexFunctionArray[m[1]]) { // print("ERROR: function: " + m[1] + " is defined more than once!"); // Allow multiple files for a function with (indexFunctionArray[m[1]]) { filename = filename + "|" + fname; // print("filename = " + filename); } } else { indexFunctionArray[m[1]] = {filename:fname}; } //reset comment comment = ""; } firstLine = false; } // Write table of contents. for (var i=0; i < functionDocArray.length; i++) { with (functionDocArray[i]) { out.writeLine('function ' + name + '
'); } } out.writeLine('
'); // Now write the saved function documentation. for (i=0; i < functionDocArray.length; i++) { with (functionDocArray[i]) { out.writeLine(''); out.writeLine(text); } } out.writeLine(''); // Now clean up the doc array functionDocArray = []; } /** * Process function and associated comment. * @param name the name of the function * @param args the args of the function as a single string * @param comment the text of the comment * @return a string for the HTML text of the documentation */ function processFunction(name, args, comment) { if (debug) print("Processing " + name + " " + args + " " + comment); return "

Function " + name + "

" + "
" +
		"function " + name + "(" + args + ")" +
		"
" + processComment(comment,0,name) + "



"; } /** * Process a method being bound to a prototype. * @param proto the name of the prototype * @param name the name of the function * @param args the args of the function as a single string * @param comment the text of the comment * @return a string for the HTML text of the documentation */ function processPrototypeMethod(proto, name, args, comment) { if (debug) print("Processing " + proto + ".prototype." + name + " " + args + " " + comment); return "

Method " + proto + ".prototype." + name + "

" + "
" +
		proto + ".prototype." + name + " = function(" + args + ")" +
		"
" + processComment(comment,0,name) + "



"; } /** * Process comment. * @param comment the text of the comment * @param firstLine shows if comment is at the beginning of the file * @param fname name of the file (without path) * @return a string for the HTML text of the documentation */ function processComment(comment,firstLine,fname) { var tags = {}; // Use the "lambda" form of regular expression replace, // where the replacement object is a function rather // than a string. The function is called with the // matched text and any parenthetical matches as // arguments, and the result of the function used as the // replacement text. // Here we use the function to build up the "tags" object, // which has a property for each "@" tag that is the name // of the tag, and whose value is an array of the // text following that tag. comment = comment.replace(/@(\w+)\s+([^@]*)/g, function (s, name, text) { var a = tags[name] || []; a.push(text); tags[name] = a; return ""; }); // if we have a comment at the beginning of a file // store the comment for the index file if (firstLine) { indexFileArray[fname] = comment; } var out = comment + '

'; if (tags["param"]) { // Create a table of parameters and their descriptions. var array = tags["param"]; var params = ""; for (var i=0; i < array.length; i++) { var m = array[i].match(/(\w+)\s+(.*)/); params += ''+m[1]+'' + ''+m[2]+''; } out += ''; out += ''; out += ''; out += ''; out += params; out += '
ParameterDescription

'; } if (tags["return"]) { out += "

Returns:
"; out += tags["return"][0] + "

"; } if (tags["author"]) { // List the authors together, separated by commas. out += '

Author:
'; var array = tags["author"]; for (var i=0; i < array.length; i++) { out += array[i]; if (i+1 < array.length) out += ", "; } out += '

'; } if (tags["version"]) { // Show the version. out += '

Version:
'; var array = tags["version"]; for (var i=0; i < array.length; i++) { out += array[i]; if (i+1 < array.length) out += "
"; } out += '

'; } if (tags["see"]) { // List the see modules together, separated by
. out += '

Dependencies:
'; var array = tags["see"]; for (var i=0; i < array.length; i++) { out += array[i]; if (i+1 < array.length) out += "
"; } out += '

'; } if (tags["lastmodified"]) { // Shows a last modified description with client-side js. out += '

Last modified:
'; out += '\n'; out += '

'; } // additional tags can be added here (i.e., "if (tags["see"])...") return out; } /** * Create an html output file * @param outputdir directory to put the file * @param htmlfile name of the file */ function CreateOutputFile(outputdir,htmlfile) { if (outputdir==null) { var outname = htmlfile; } else { var separator = Packages.java.io.File.separator; var outname = outputdir + separator + htmlfile.substring(htmlfile.lastIndexOf(separator),htmlfile.length); } print("output file: " + outname); return new File(outname); } /** * Process a javascript file. Puts the generated HTML file in the outdir * @param filename name of the javascript file * @inputdir input directory of the file (default null) */ function processJSFile(filename,inputdir) { if (debug) print("filename = " + filename + " inputdir = " + inputdir); if (!filename.match(/\.js$/)) { print("Expected filename to end in '.js'; had instead " + filename + ". I don't treat the file."); } else { if (inputdir==null) { var inname = filename; } else { var separator = Packages.java.io.File.separator; var inname = inputdir + separator + filename; } print("Processing file " + inname); var f = new File(inname); // create the output file var htmlfile = filename.replace(/\.js$/, ".html"); var out = CreateOutputFile(outputdir,htmlfile); processFile(f, filename, inputdir, out); out.close(); } } /** * Generate index files containing links to the processed javascript files * and the generated functions */ function GenerateIndex(dirname) { // construct the files index file var out = CreateOutputFile(outputdir,indexFile); // write the beginning of the file out.writeLine('

File Index - directory: ' + dirname + ''); out.writeLine('

File Index - directory: ' + dirname + '

\n'); out.writeLine(''); out.writeLine(''); out.writeLine(''); out.writeLine(''); var separator = Packages.java.io.File.separator; // sort the index file array var SortedFileArray = []; for (var fname in indexFileArray) SortedFileArray.push(fname); SortedFileArray.sort(); for (var i=0; i < SortedFileArray.length; i++) { var fname = SortedFileArray[i]; var htmlfile = fname.replace(/\.js$/, ".html"); out.writeLine('\n'); } out.writeLine('
FileDescription
' + fname + ''); if (indexFileArray[fname]) out.writeLine(indexFileArray[fname]); else out.writeLine('No comments'); out.writeLine('
'); out.close(); // construct the functions index file var out = CreateOutputFile(outputdir,indexFunction); // write the beginning of the file out.writeLine('
Function Index - directory: ' + dirname + ''); out.writeLine('

Function Index - directory: ' + dirname + '

\n'); out.writeLine(''); out.writeLine(''); out.writeLine(''); out.writeLine(''); // sort the function array var SortedFunctionArray = []; for (var functionname in indexFunctionArray) SortedFunctionArray.push(functionname); SortedFunctionArray.sort(); for (var j=0; j < SortedFunctionArray.length; j++) { var funcname = SortedFunctionArray[j]; with (indexFunctionArray[funcname]) { var outstr = ''; out.writeLine(outstr); } } out.writeLine('
FunctionFiles
' + funcname + ''; var filelst = filename.split("|"); for (var i in filelst) { var htmlfile = filelst[i].replace(/\.js$/, ".html"); outstr += '' + filelst[i] + ' '; } outstr += '
'); out.close(); } /** * prints the options for JSDoc */ function PrintOptions() { print("You can use the following options:\n"); print("-d: specify an output directory for the generated html files\n"); print("-i: processes all files in an input directory (you can specify several directories)\n"); quit(); } // Main Script // first read the arguments if (! arguments) PrintOptions(); for (var i=0; i < arguments.length; i++) { if (debug) print("argument: + \'" + arguments[i] + "\'"); if (arguments[i].match(/^\-/)) { if (String(arguments[i])=="-d"){ // output directory for the generated html files outputdir = String(arguments[i+1]); if (debug) print("outputdir: + \'" + outputdir + "\'"); i++; } else if (String(arguments[i])=="-i"){ // process all files in an input directory DirList.push(String(arguments[i+1])); if (debug) print("inputdir: + \'" + arguments[i+1] + "\'"); i++; } else { print("Unknown option: " + arguments[i] + "\n"); PrintOptions(); } } else { // we have a single file if (debug) print("file: + \'" + arguments[i] + "\'"); FileList.push(String(arguments[i])); } } // first handle the single files for (var i in FileList) processJSFile(FileList[i],null); // then handle the input directories for (var j in DirList) { var inputdir = String(DirList[j]); print("Process input directory: " + inputdir); // clean up index arrays var indexFileArray = []; var indexFunctionArray = []; // for the directory name get rid of ../../ or ..\..\ inputDirName = inputdir.replace(/\.\.\/|\.\.\\/g,""); indexFile = indexFileName + "_" + inputDirName + ".html"; indexFunction = indexFunctionName + "_" + inputDirName + ".html"; print("indexFile = " + indexFile); print("indexFunction = " + indexFunction); // read the files in the directory var DirFile = new java.io.File(inputdir); var lst = DirFile.list(); var separator = Packages.java.io.File.separator; for (var i=0; i < lst.length; i++) { processJSFile(String(lst[i]),inputdir); } // generate the index files for the input directory GenerateIndex(inputDirName); }