JSDOC/BuildDocs.js
[app.jsdoc] / JSDOC / BuildDocs.js
1 //<script type="text/javascript">
2 /**
3  *
4  * This is the main container for the JSDOC application.
5  *
6  *usage::
7     
8     BuildDocs = imports.JSDOC.BuildDocs.BuildDocs
9     
10     new BuildDocs(opts)
11     
12     ..
13     
14     or command line:
15     
16     new ArgsParser(argv, imports.BuildDocs.BuildDocs )
17  *
18  * 
19  */
20         
21          
22 Gio = imports.gi.Gio;
23
24 XObject      = imports.XObject.XObject;
25 File        = imports.File.File;
26
27
28
29 Template     = imports.JsTemplate.Template.Template;
30 Link        = imports.JsTemplate.Link.Link; // ?? fixme!??
31
32       
33  
34 // should not realy be here -- or anywhere...??
35
36 function makeSortby(attribute) {
37     return function(a, b) {
38         if (a[attribute] != undefined && b[attribute] != undefined) {
39             a = a[attribute]; //.toLowerCase();
40             b = b[attribute];//.toLowerCase();
41             if (a < b) return -1;
42             if (a > b) return 1;
43             return 0;
44         }
45         return 0;
46     };
47 }
48
49  
50 /**
51  * @namespace JSDOC
52  */
53
54 /**
55  * 
56  * @class JSDOC/BuildDocs
57  *
58  * Main class used to build documentation
59  */
60 BuildDocs = XObject.define(
61     function(opts) {
62         XObject.extend(this, opts); // overlay cfg..
63         
64         if (!this.LOG) {
65             this.LOG = imports.JSDOC.Log.Log;
66         }
67         
68         this.parser = new imports.JSDOC.Parser.Parser({
69             LOG : this.LOG,
70             docPrivate : this.docPrivate
71
72         });
73         
74          
75         
76         this.build();
77     
78     },
79     Object,
80     {
81         
82         
83         /**
84          * @cfg {Object} LOG  an implementation of the log interface..
85          */
86         LOG: false,
87         
88          /**
89          * @cfg  {String} baseDir root directory (for relative publishing?)
90          */
91         baseDir: false,
92         
93         
94          /**
95          * @cfg {String} cacheDirectory  where to store the cached parse.
96          */
97         cacheDirectory: false,
98         
99         /**
100          * @cfg {String} ext  comma-delimited list of extensions to handle..
101          */
102         ext: 'js' ,
103         
104         
105         /**
106          * @cfg  {Array} src base source files/directories.
107          */
108         src : false,
109         
110         /**
111          * @cfg  {Array} excludeSrc base source files/directories.
112          */
113         excludeSrc : false,
114          /**
115          * @cfg  {String} target where to write the output to.
116          */
117         
118         target : false,
119         
120         
121         
122         /**
123          * @cfg  {String} templateDir where get the templates from.
124          */
125         templateDir : false,
126          /**
127          * @cfg {String} publishExt  output extenstion.
128          */
129         
130         publishExt : 'html',
131         
132         
133           /**
134          * @cfg {Boolean} docPrivate  document private variables.
135          */
136         
137         docPrivate : false,
138         
139           /**
140          * @cfg {String}  roojs Path to roojs installation.
141          */
142         roojs : 'http://roojs.com/roojs1/',
143         
144         
145           /**
146          * @cfg {Boolean} ignoreNamespace  should the memberOf be stripped for seed classes
147          */
148         ignoreNamespace : false,
149         
150         
151         
152         
153         
154         /**
155          * Full list of source files.
156          * @private
157          */
158         srcFiles : false,
159         
160         
161         /**
162          * the JSDOC.parser instance.
163          * @private
164          */
165         parser: false,
166         
167         /**
168          * the symbolset
169          * @private
170          */
171         
172         symbolSet: false,
173         /**
174          * FIXME
175          * @private
176          */
177         
178         VERSION : "2.0.0",
179         
180         
181         /**
182          * start the build process
183          */
184         build : function ()
185         {
186             
187             
188             this.LOG.inform("JsDoc Toolkit main() running at "+new Date()+".");
189            
190             
191             if (this.cacheDirectory.length && !File.isDirectory(this.cacheDirectory)) {   
192                 File.mkdir(this.cacheDirectory)
193             }
194             
195             this.srcFiles = this._getSrcFiles();
196             this._parseSrcFiles();
197              this.symbolSet  = imports.ScopeNamer.ScopeNamer.symbols;
198             //this.symbolSet = this.parser.symbols;
199             
200             // this currently uses the concept of publish.js...
201             
202             this.publish();
203              
204             
205             
206         },
207         /**
208          * create a list of files in this.srcFiles using list of directories / files in Options.src
209          * @private
210          */
211         
212         _getSrcFiles : function() 
213         {
214              var _this = this;
215             var ret= [];
216             
217             var ext = this.ext.split(",").map(function($) {return $.toLowerCase()});
218             
219             this.src.forEach(function(src_in) {
220                 // add to sourcefiles..
221                 var src = File.join(_this.baseDir ,  src_in);
222                 
223                 
224                 if (!File.isDirectory(src)) {
225                     ret.push(src);
226                     return;
227                 }
228                 
229                 File.list(src ).forEach(function($) {
230                     if (_this.excludeSrc.indexOf($) > -1) {
231                         return;
232                     }
233                     var thisExt = $.split(".").pop().toLowerCase();
234                     if (ext.indexOf(thisExt) < 0) {
235                         return;
236                     }
237                     ret.push( File.join(src,  $));
238                 });
239                     
240             });
241             //Seed.print(JSON.stringify(this.srcFiles, null,4));Seed.quit();
242             return ret;
243         },
244         /**
245          * Parse the source files.
246          * @private
247          */
248     
249         _parseSrcFiles : function() 
250         {
251             
252             var _this = this;
253             this.srcFiles.forEach(function(srcFile) {
254                 
255              
256                 var cacheFile = !_this.cacheDirectory.length ? false : 
257                     _this.cacheDirectory + srcFile.replace(/\//g, '_') + ".cache";
258                 
259                 //print(cacheFile);
260                 // disabled at present!@!!
261                 
262                 if (cacheFile  && File.exists(cacheFile)) {
263                     // check filetime?
264                     
265                     var c_mt = File.mtime(cacheFile);
266                     var o_mt = File.mtime(srcFile);
267                     //println(c_mt.toSource());
268                    // println(o_mt.toSource());
269                    
270                     // this check does not appear to work according to the doc's - need to check it out.
271                    
272                     if (c_mt > o_mt) { // cached time  > original time!
273                         // use the cached mtimes..
274                         print("Read " + cacheFile);
275                         
276                         var syms =  JSON.parse(File.read(cacheFile), function(k, v) {
277                             //print(k);
278                             if (typeof(v) != 'object') {
279                                 return v;
280                             }
281                             if (typeof(v['*object']) == 'undefined') {
282                                 return v;
283                             }
284                             
285                             var cls = imports[v['*object']][v['*object']];
286                             //print(v['*object']);
287                             delete v['*object'];
288                             var ret = new cls();
289                             XObject.extend(ret, v);
290                             return ret; //????
291                             
292                         });
293                         //print("Add sybmols " + cacheFile); 
294                         for (var sy in syms._index) {
295                           //  print("ADD:" + sy );
296                            _this.parser.symbols.addSymbol(syms._index[sy]);
297                         }
298                         return;
299                     }
300                 }
301                 
302                 var src = ''
303                 try {
304                     _this.LOG.inform("reading : " + srcFile);
305                     src = File.read(srcFile);
306                 }
307                 catch(e) {
308                     _this.LOG.warn("Can't read source file '"+srcFile+"': "+e.message);
309                     return;
310                 }
311     
312                 var txs = new imports.TextStream.TextStream(src);
313                 
314                 var tr = new imports.TokenReader.TokenReader({
315                         keepComments : true,
316                         keepWhite : true ,
317                         sepIdents: false,
318                         filename : srcFile
319                     });
320                 
321                 
322                 var col = new imports.Collapse.Collapse(tr.tokenize(txs), srcFile);
323                 // print(JSON.stringify(col, null,4));
324                 new imports.ScopeNamer.ScopeNamer(col.tokens, srcFile);
325                 return;
326                 Seed.quit();
327                 
328                 
329                 // old way..
330                 
331                 
332                 var ts = new imports.TokenStream.TokenStream(tr.tokenize(txs), srcFile);
333                  
334                 _this.parser.parse(ts, srcFile);
335                 
336                 if (cacheFile) {
337                     File.write(cacheFile,
338                       JSON.stringify(
339                         _this.parser.symbolsToObject(srcFile),
340                         null,2
341                       )
342                     );
343                 
344                 }
345                 
346                     
347        
348             });
349             
350             imports.ScopeNamer.ScopeNamer.symbols.relate();     
351             
352             //this.parser.finish();
353         },
354         
355          /**
356          * publish the parsed data into  html, js etc.
357          */
358             
359         publish  : function() {
360             this.LOG.inform("Publishing");
361              
362             // link!!!
363             
364             var _this = this;
365             this.LOG.inform("Making directories");
366             if (!File.isDirectory(this.target))
367                 File.mkdir(this.target);
368             if (!File.isDirectory(this.target+"/symbols"))
369                 File.mkdir(this.target+"/symbols");
370             if (!File.isDirectory(this.target+"/symbols/src"))
371                 File.mkdir(this.target+"/symbols/src");
372             
373             if (!File.isDirectory(this.target +"/json")) {
374                 File.mkdir(this.target +"/json");
375             }
376             
377             this.LOG.inform("Copying files from static: " +this.templateDir);
378             // copy everything in 'static' into 
379             File.list(this.templateDir + '/static').forEach(function (f) {
380                 _this.LOG.inform("Copy " + _this.templateDir + '/static/' + f + ' to  ' + _this.target + '/' + f);
381                 File.copyFile(_this.templateDir + '/static/' + f, _this.target + '/' + f,  Gio.FileCopyFlags.OVERWRITE);
382             });
383             
384             
385             this.LOG.inform("Setting up templates");
386             
387             
388             // used to check the details of things being linked to
389             Link.symbolSet = this.symbolSet;
390             Link.base = "../";
391             
392             Link.srcFileFlatName = XObject.createDelegate(this.srcFileFlatName, this);
393             Link.srcFileRelName = XObject.createDelegate(this.srcFileRelName, this);
394             
395             var classTemplate = new Template({
396                  templateFile : this.templateDir  + "/class.html",
397                  Link : Link
398             });
399             var classesTemplate = new Template({
400                 templateFile : this.templateDir +"/allclasses.html",
401                 Link : Link
402             });
403             var classesindexTemplate = new Template({
404                 templateFile : this.templateDir +"/index.html",
405                 Link : Link
406             });
407             var fileindexTemplate = new Template({   
408                 templateFile : this.templateDir +"/allfiles.html",
409                 Link: Link
410             });
411     
412             
413             classTemplate.symbolSet = this.symbolSet;
414             
415             
416             function hasNoParent($) {
417                 return ($.memberOf == "")
418             }
419             function isaFile($) {
420                 return ($.is("FILE"))
421             }
422             function isaClass($) { 
423                 return ($.is("CONSTRUCTOR") || $.isNamespace); 
424             }
425             
426             
427             
428             
429             
430             
431             
432             
433             
434             
435             var symbols = this.symbolSet.toArray();
436             
437             var files = this.srcFiles;
438             
439             for (var i = 0, l = files.length; i < l; i++) {
440                 var file = files[i];
441                 var targetDir = _this.target + "/symbols/src/";
442                 this.makeSrcFile(file, targetDir);
443             }
444             //print(JSON.stringify(symbols,null, 4));
445             var classes = symbols.filter(isaClass).sort(makeSortby("alias"));
446              
447             
448             this.LOG.inform("iterate classes");
449             
450             var jsonAll = {}; 
451             
452             for (var i = 0, l = classes.length; i < l; i++) {
453                 var symbol = classes[i];
454                 var output = "";
455                 
456                 this.LOG.inform("classTemplate Process : " + this.target+"/symbols/" +symbol.alias+'.' + this.publishExt);
457                 
458                 
459                 symbol.ignoreNamespace = this.ignoreNamespace;
460                 
461                 File.write(this.target+"/symbols/" +symbol.alias+'.' + this.publishExt ,
462                         classTemplate.process(symbol));
463                 
464                 jsonAll[symbol.alias] = this.publishJSON(symbol);
465                 
466                 
467                 
468             }
469             
470             File.write(this.target+"/json/roodata.json",
471                     JSON.stringify({
472                         success : true,
473                         data : jsonAll
474                     }, null, 1)
475             );
476             
477             
478             // regenrate the index with different relative links
479             Link.base = "";
480              
481             this.LOG.inform("build index");
482             
483             File.write(this.target +  "/index."+ this.publishExt, 
484                 classesindexTemplate.process(classes)
485             );
486             
487             // blank everything???? classesindexTemplate = classesIndex = classes = null;
488             
489      
490             
491             var documentedFiles = symbols.filter(function ($) {
492                 return ($.is("FILE"))
493             });
494             
495             var allFiles = [];
496             
497             for (var i = 0; i < files.length; i++) {
498                 allFiles.push(new  imports.Symbol.Symbol(files[i], [], "FILE", new imports.DocComment.DocComment("/** */")));
499             }
500             
501             for (var i = 0; i < documentedFiles.length; i++) {
502                 var offset = files.indexOf(documentedFiles[i].alias);
503                 allFiles[offset] = documentedFiles[i];
504             }
505                 
506             allFiles = allFiles.sort(makeSortby("name"));
507             this.LOG.inform("write files index");
508             
509             File.write(this.target + "/files."+this.publishExt, 
510                 fileindexTemplate.process(allFiles)
511             );
512             
513             
514             
515             
516         },
517         /**
518          * JSON files are lookup files for the documentation
519          * - can be used by IDE's or AJAX based doc tools
520          * 
521          * 
522          */
523         publishJSON : function(data)
524         {
525             // what we need to output to be usefull...
526             // a) props..
527             var cfgProperties = [];
528             if (!data.comment.getTag('singleton').length) {
529                 cfgProperties = data.configToArray();
530                 cfgProperties = cfgProperties.sort(makeSortby("alias"));
531                 
532             }
533             var props = []; 
534             //println(cfgProperties.toSource());
535             var p ='';
536             for(var i =0; i < cfgProperties.length;i++) {
537                 p = cfgProperties[i];
538                 props.push( {
539                     name : p.name,
540                     type : p.type,
541                     desc : p.desc,
542                     memberOf : p.memberOf == data.alias ? '' : p.memberOf
543                 });
544             }
545             
546              
547             var ownEvents = data.methods.filter( function(e){
548                     return e.isEvent && !e.comment.getTag('hide').length;
549                 }).sort(makeSortby("name"));
550                  
551             
552             var events = [];
553             var m;
554             for(var i =0; i < ownEvents.length;i++) {
555                 m = ownEvents[i];
556                 events.push( {
557                     name : m.name.substring(1),
558                     sig : this.makeFuncSkel(m.params),
559                     type : 'function',
560                     desc : m.desc
561                 });
562             }
563             //println(props.toSource());
564             // we need to output:
565             //classname => {
566             //    propname => 
567             //        type=>
568             //        desc=>
569             //    }
570     
571             var ret = {
572                 props : props,
573                 events: events
574             };
575             return ret;
576             
577             
578             
579             // b) methods
580             // c) events
581             
582             
583         },
584         srcFileRelName : function(sourceFile)
585         {
586            // print(JSON.stringify([ sourceFile, this.baseDir ] ));
587             return sourceFile.substring(this.baseDir.length+1);
588         },
589         srcFileFlatName: function(sourceFile)
590         {
591             var name = this.srcFileRelName(sourceFile);
592            // print("NAME: " + name);
593             name = name.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
594             return name.replace(/\:/g, "_") + '.html'; //??;
595             
596         },
597         
598         makeSrcFile: function(sourceFile) 
599         {
600             // this stuff works...
601          
602             
603             var name = this.srcFileFlatName(sourceFile);
604             
605             this.LOG.inform("Write Source file : " + this.target+"/symbols/src/" + name + " FROM: "  + sourceFile);
606             
607             var pretty = imports.PrettyPrint.toPretty(File.read(  sourceFile));
608             
609             File.write(this.target+"/symbols/src/" + name, 
610                 '<html><head>' +
611                 '<title>' + sourceFile + '</title>' +
612                 '<link rel="stylesheet" type="text/css" href="' + this.roojs + '/css/highlight-js.css"/>' + 
613                 '</head><body class="highlightpage">' +
614                 pretty +
615                 '</body></html>');
616         },
617         /**
618          * used by JSON output to generate a function skeleton
619          */
620         makeFuncSkel :function(params) {
621             if (!params) return "function ()\n{\n\n}";
622             return "function (" +
623                 params.filter(
624                     function($) {
625                         return $.name.indexOf(".") == -1; // don't show config params in signature
626                     }
627                 ).map( function($) { return $.name == 'this' ? '_self' : $.name; } ).join(", ") +
628             ")\n{\n\n}";
629         }
630         
631      
632         
633     }
634 );
635   
636
637
638
639
640
641  
642
643
644
645