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