957d6f530efc7f2a795fe3359acf67aec81f7269
[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         //print(JSON.stringify(this, null,4));
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.tokenize(txs), null, 4));
303                 
304                 
305                 var sn = new imports.ScopeNamer.ScopeNamer({
306                         tokens : tr.tokenize(txs),
307                         filename : _this.srcFileRelName(srcFile)
308                 });
309                 sn.buildSymbols();
310                 return;
311                 Seed.quit();
312                 
313                 
314                 // old way..
315                 
316                  
317                 if (cacheFile) {
318                     File.write(cacheFile,
319                       JSON.stringify(
320                         _this.parser.symbolsToObject(srcFile),
321                         null,2
322                       )
323                     );
324                 
325                 }
326                  
327             });
328             
329             print(JSON.stringify(
330                 XObject.keys(imports.ScopeNamer.ScopeNamer.symbols._index),
331             null, 4));
332             
333             imports.ScopeNamer.ScopeNamer.symbols.relate();
334             
335             //print(JSON.stringify(
336             //    XObject.keys(imports.ScopeNamer.ScopeNamer.symbols._index),
337             //null, 4));
338             
339              
340              //print(JSON.stringify(imports.ScopeNamer.ScopeNamer.symbols._index, null,4));
341             //this.parser.finish();
342         },
343         
344          /**
345          * publish the parsed data into  html, js etc.
346          */
347             
348         publish  : function() {
349             this.LOG.inform("Publishing");
350              
351             // link!!!
352             
353             var _this = this;
354             this.LOG.inform("Making directories:" + this.target);
355             if (!File.isDirectory(this.target))
356                 File.mkdir(this.target);
357                 
358             if (!File.isDirectory(this.target+"/symbols"))
359                 File.mkdir(this.target+"/symbols");
360             if (!File.isDirectory(this.target+"/symbols/src"))
361                 File.mkdir(this.target+"/symbols/src");
362             
363             if (!File.isDirectory(this.target +"/json")) {
364                 File.mkdir(this.target +"/json");
365             }
366             
367             this.LOG.inform("Copying files from static: " +this.templateDir);
368             // copy everything in 'static' into 
369             File.list(this.templateDir + '/static').forEach(function (f) {
370                 _this.LOG.inform("Copy " + _this.templateDir + '/static/' + f + ' to  ' + _this.target + '/' + f);
371                 File.copyFile(_this.templateDir + '/static/' + f, _this.target + '/' + f,  Gio.FileCopyFlags.OVERWRITE);
372             });
373             
374             
375             this.LOG.inform("Setting up templates");
376             
377             
378             // used to check the details of things being linked to
379             Link.symbolSet = this.symbolSet;
380             Link.base = "../";
381             
382             Link.srcFileFlatName = XObject.createDelegate(this.srcFileFlatName, this);
383             Link.srcFileRelName = XObject.createDelegate(this.srcFileRelName, this);
384             
385             var classTemplate = new Template({
386                  templateFile : this.templateDir  + "/class.html",
387                  Link : Link
388             });
389             var classesTemplate = new Template({
390                 templateFile : this.templateDir +"/allclasses.html",
391                 Link : Link
392             });
393             var classesindexTemplate = new Template({
394                 templateFile : this.templateDir +"/index.html",
395                 Link : Link
396             });
397             var fileindexTemplate = new Template({   
398                 templateFile : this.templateDir +"/allfiles.html",
399                 Link: Link
400             });
401     
402             
403             classTemplate.symbolSet = this.symbolSet;
404             
405             
406             function hasNoParent($) {
407                 return ($.memberOf == "")
408             }
409             function isaFile($) {
410                 return ($.is("FILE"))
411             }
412             function isaClass($) { 
413                 //return ($.is("CONSTRUCTOR") || $.isNamespace); 
414                 return ($.is("CONSTRUCTOR") || $.is("OBJECT")); 
415             }
416             
417             
418             
419             
420             
421             
422             
423             
424             
425             
426             var symbols = this.symbolSet.toArray();
427             
428             var files = this.srcFiles;
429             
430             for (var i = 0, l = files.length; i < l; i++) {
431                 var file = files[i];
432                 var targetDir = _this.target + "/symbols/src/";
433                 this.makeSrcFile(file, targetDir);
434             }
435             var makeSortby = imports.JsTemplate.Template.Template.prototype.makeSortby;
436             //print(JSON.stringify(symbols,null, 4));
437             var classes = symbols.filter(isaClass).sort(makeSortby("alias"));
438              
439             
440             this.LOG.inform("iterate classes");
441             
442             var jsonAll = {}; 
443             
444             for (var i = 0, l = classes.length; i < l; i++) {
445                 var symbol = classes[i];
446                 var output = "";
447                 
448                 
449                 
450                 //symbol.ignoreNamespace = this.ignoreNamespace;
451                 
452                 var ns = File.dirname(symbol.srcFile).replace(/\//g,'.');
453                 if (symbol.alias.substring(0, ns.length) == ns) {
454                     ns = '';
455                 }
456                 ns += ns.length ? '.' : ''; 
457                 
458                 
459                 this.LOG.inform("classTemplate Process : " +
460                                 this.target+"/symbols/" + ns + symbol.alias+'.' + this.publishExt);
461                 
462                 File.write(this.target+"/symbols/" + ns + symbol.alias+'.' + this.publishExt ,
463                         classTemplate.process(symbol));
464                 
465                 jsonAll[symbol.alias] = this.publishJSON(symbol);
466                 
467                 
468                 
469             }
470             
471             File.write(this.target+"/json/roodata.json",
472                     JSON.stringify({
473                         success : true,
474                         data : jsonAll
475                     }, null, 1)
476             );
477             
478             
479             // regenrate the index with different relative links
480             Link.base = "";
481              
482             this.LOG.inform("build index");
483             
484             File.write(this.target +  "/index."+ this.publishExt, 
485                 classesindexTemplate.process(classes)
486             );
487             
488             // blank everything???? classesindexTemplate = classesIndex = classes = null;
489             
490      
491             
492             var documentedFiles = symbols.filter(function ($) {
493                 return ($.is("FILE"))
494             });
495             
496             var allFiles = [];
497             
498             for (var i = 0; i < files.length; i++) {
499                 allFiles.push(new  imports.Symbol.Symbol(files[i], [], "FILE", new imports.DocComment.DocComment("/** */")));
500             }
501             
502             for (var i = 0; i < documentedFiles.length; i++) {
503                 var offset = files.indexOf(documentedFiles[i].alias);
504                 allFiles[offset] = documentedFiles[i];
505             }
506                 
507             allFiles = allFiles.sort(makeSortby("name"));
508             this.LOG.inform("write files index");
509             
510             File.write(this.target + "/files."+this.publishExt, 
511                 fileindexTemplate.process(allFiles)
512             );
513             
514             
515             
516             
517         },
518         /**
519          * JSON files are lookup files for the documentation
520          * - can be used by IDE's or AJAX based doc tools
521          * @param {Symbol} data The symbol to publish
522          * 
523          */
524         publishJSON : function(data)
525         {
526             // what we need to output to be usefull...
527             // a) props..
528             var makeSortby = imports.JsTemplate.Template.Template.prototype.makeSortby;
529    
530             var cfgProperties = [];
531             if (!data.comment.getTag('singleton').length) {
532                 cfgProperties = data.configToArray();
533                 cfgProperties = cfgProperties.sort(makeSortby("alias"));
534                 
535             }
536             var props = []; 
537             //println(cfgProperties.toSource());
538             var p ='';
539             for(var i =0; i < cfgProperties.length;i++) {
540                 p = cfgProperties[i];
541                 props.push( {
542                     name : p.name,
543                     type : p.type,
544                     desc : p.desc,
545                     memberOf : p.memberOf == data.alias ? '' : p.memberOf
546                 });
547             }
548             
549              
550             var ownEvents = data.methods.filter( function(e){
551                     return e.isEvent && !e.comment.getTag('hide').length;
552                 }).sort(makeSortby("name"));
553                  
554             
555             var events = [];
556             var m;
557             for(var i =0; i < ownEvents.length;i++) {
558                 m = ownEvents[i];
559                 events.push( {
560                     name : m.name.substring(1),
561                     sig : this.makeFuncSkel(m.params),
562                     type : 'function',
563                     desc : m.desc
564                 });
565             }
566             //println(props.toSource());
567             // we need to output:
568             //classname => {
569             //    propname => 
570             //        type=>
571             //        desc=>
572             //    }
573     
574             var ret = {
575                 props : props,
576                 events: events
577             };
578             return ret;
579             
580             
581             
582             // b) methods
583             // c) events
584             
585             
586         },
587         srcFileRelName : function(sourceFile)
588         {
589            // print(JSON.stringify([ sourceFile, this.baseDir ] ));
590             return sourceFile.substring(this.baseDir.length+1);
591         },
592         srcFileFlatName: function(sourceFile)
593         {
594             var name = sourceFile; //this.srcFileRelName(sourceFile);
595            // print("NAME: " + name);
596             name = name.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
597             return name.replace(/\:/g, "_") + '.html'; //??;
598             
599         },
600         
601         makeSrcFile: function(sourceFile) 
602         {
603             // this stuff works...
604          
605             
606             var name = this.srcFileFlatName(this.srcFileRelName(sourceFile)) ;
607             
608             this.LOG.inform("Write Source file : " + this.target+"/symbols/src/" + name + " FROM: "  + sourceFile);
609             
610             var pretty = imports.PrettyPrint.toPretty(File.read(  sourceFile));
611             
612             File.write(this.target+"/symbols/src/" + name, 
613                 '<html><head>' +
614                 '<title>' + sourceFile + '</title>' +
615                 '<link rel="stylesheet" type="text/css" href="' + this.roojs + '/css/highlight-js.css"/>' + 
616                 '</head><body class="highlightpage">' +
617                 pretty +
618                 '</body></html>');
619         },
620         /**
621          * used by JSON output to generate a function skeleton
622          */
623         makeFuncSkel :function(params) {
624             if (!params) return "function ()\n{\n\n}";
625             return "function (" +
626                 params.filter(
627                     function($) {
628                         return $.name.indexOf(".") == -1; // don't show config params in signature
629                     }
630                 ).map( function($) { return $.name == 'this' ? '_self' : $.name; } ).join(", ") +
631             ")\n{\n\n}";
632         }
633         
634      
635         
636     }
637 );
638   
639
640
641
642
643
644  
645
646
647
648