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