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 = this.parser.symbols;
198             
199             // this currently uses the concept of publish.js...
200             
201             this.publish();
202              
203             
204             
205         },
206         /**
207          * create a list of files in this.srcFiles using list of directories / files in Options.src
208          * @private
209          */
210         
211         _getSrcFiles : function() 
212         {
213              var _this = this;
214             var ret= [];
215             
216             var ext = this.ext.split(",").map(function($) {return $.toLowerCase()});
217             
218             this.src.forEach(function(src_in) {
219                 // add to sourcefiles..
220                 var src = File.join(_this.baseDir ,  src_in);
221                 
222                 
223                 if (!File.isDirectory(src)) {
224                     ret.push(src);
225                     return;
226                 }
227                 
228                 File.list(src ).forEach(function($) {
229                     if (_this.excludeSrc.indexOf($) > -1) {
230                         return;
231                     }
232                     var thisExt = $.split(".").pop().toLowerCase();
233                     if (ext.indexOf(thisExt) < 0) {
234                         return;
235                     }
236                     ret.push( File.join(src,  $));
237                 });
238                     
239             });
240             //Seed.print(JSON.stringify(this.srcFiles, null,4));Seed.quit();
241             return ret;
242         },
243         /**
244          * Parse the source files.
245          * @private
246          */
247     
248         _parseSrcFiles : function() 
249         {
250             
251             var _this = this;
252             this.srcFiles.forEach(function(srcFile) {
253                 
254              
255                 var cacheFile = !_this.cacheDirectory.length ? false : 
256                     _this.cacheDirectory + srcFile.replace(/\//g, '_') + ".cache";
257                 
258                 //print(cacheFile);
259                 // disabled at present!@!!
260                 
261                 if (cacheFile  && File.exists(cacheFile)) {
262                     // check filetime?
263                     
264                     var c_mt = File.mtime(cacheFile);
265                     var o_mt = File.mtime(srcFile);
266                     //println(c_mt.toSource());
267                    // println(o_mt.toSource());
268                    
269                     // this check does not appear to work according to the doc's - need to check it out.
270                    
271                     if (c_mt > o_mt) { // cached time  > original time!
272                         // use the cached mtimes..
273                         print("Read " + cacheFile);
274                         
275                         var syms =  JSON.parse(File.read(cacheFile), function(k, v) {
276                             //print(k);
277                             if (typeof(v) != 'object') {
278                                 return v;
279                             }
280                             if (typeof(v['*object']) == 'undefined') {
281                                 return v;
282                             }
283                             
284                             var cls = imports[v['*object']][v['*object']];
285                             //print(v['*object']);
286                             delete v['*object'];
287                             var ret = new cls();
288                             XObject.extend(ret, v);
289                             return ret; //????
290                             
291                         });
292                         //print("Add sybmols " + cacheFile); 
293                         for (var sy in syms._index) {
294                           //  print("ADD:" + sy );
295                            _this.parser.symbols.addSymbol(syms._index[sy]);
296                         }
297                         return;
298                     }
299                 }
300                 
301                 var src = ''
302                 try {
303                     _this.LOG.inform("reading : " + srcFile);
304                     src = File.read(srcFile);
305                 }
306                 catch(e) {
307                     _this.LOG.warn("Can't read source file '"+srcFile+"': "+e.message);
308                     return;
309                 }
310     
311                 var txs = new imports.TextStream.TextStream(src);
312                 
313                 var tr = new imports.TokenReader.TokenReader({
314                         keepComments : true,
315                         keepWhite : true ,
316                         sepIdents: false,
317                         filename : srcFile
318                     });
319                 
320                 
321                 var col = new imports.Collapse.Collapse(tr.tokenize(txs), srcFile);
322                 print(JSON.stringify(col, null,4));
323                 new imports.ScopeNamer.ScopeNamer(col.tokens);
324                 Seed.quit();
325                 
326                 
327                 // old way..
328                 
329                 
330                 var ts = new imports.TokenStream.TokenStream(tr.tokenize(txs), srcFile);
331                  
332                 _this.parser.parse(ts, srcFile);
333                 
334                 if (cacheFile) {
335                     File.write(cacheFile,
336                       JSON.stringify(
337                         _this.parser.symbolsToObject(srcFile),
338                         null,2
339                       )
340                     );
341                 
342                 }
343                 
344                     
345        
346             });
347             
348             
349             
350             this.parser.finish();
351         },
352         
353          /**
354          * publish the parsed data into  html, js etc.
355          */
356             
357         publish  : function() {
358             this.LOG.inform("Publishing");
359              
360             // link!!!
361             
362             var _this = this;
363             this.LOG.inform("Making directories");
364             if (!File.isDirectory(this.target))
365                 File.mkdir(this.target);
366             if (!File.isDirectory(this.target+"/symbols"))
367                 File.mkdir(this.target+"/symbols");
368             if (!File.isDirectory(this.target+"/symbols/src"))
369                 File.mkdir(this.target+"/symbols/src");
370             
371             if (!File.isDirectory(this.target +"/json")) {
372                 File.mkdir(this.target +"/json");
373             }
374             
375             this.LOG.inform("Copying files from static: " +this.templateDir);
376             // copy everything in 'static' into 
377             File.list(this.templateDir + '/static').forEach(function (f) {
378                 _this.LOG.inform("Copy " + _this.templateDir + '/static/' + f + ' to  ' + _this.target + '/' + f);
379                 File.copyFile(_this.templateDir + '/static/' + f, _this.target + '/' + f,  Gio.FileCopyFlags.OVERWRITE);
380             });
381             
382             
383             this.LOG.inform("Setting up templates");
384             
385             
386             // used to check the details of things being linked to
387             Link.symbolSet = this.symbolSet;
388             Link.base = "../";
389             
390             Link.srcFileFlatName = XObject.createDelegate(this.srcFileFlatName, this);
391             Link.srcFileRelName = XObject.createDelegate(this.srcFileRelName, this);
392             
393             var classTemplate = new Template({
394                  templateFile : this.templateDir  + "/class.html",
395                  Link : Link
396             });
397             var classesTemplate = new Template({
398                 templateFile : this.templateDir +"/allclasses.html",
399                 Link : Link
400             });
401             var classesindexTemplate = new Template({
402                 templateFile : this.templateDir +"/index.html",
403                 Link : Link
404             });
405             var fileindexTemplate = new Template({   
406                 templateFile : this.templateDir +"/allfiles.html",
407                 Link: Link
408             });
409     
410             
411             classTemplate.symbolSet = this.symbolSet;
412             
413             
414             function hasNoParent($) {
415                 return ($.memberOf == "")
416             }
417             function isaFile($) {
418                 return ($.is("FILE"))
419             }
420             function isaClass($) { 
421                 return ($.is("CONSTRUCTOR") || $.isNamespace); 
422             }
423             
424             
425             
426             
427             
428             
429             
430             
431             
432             
433             var symbols = this.symbolSet.toArray();
434             
435             var files = this.srcFiles;
436             
437             for (var i = 0, l = files.length; i < l; i++) {
438                 var file = files[i];
439                 var targetDir = _this.target + "/symbols/src/";
440                 this.makeSrcFile(file, targetDir);
441             }
442             //print(JSON.stringify(symbols,null, 4));
443             var classes = symbols.filter(isaClass).sort(makeSortby("alias"));
444              
445             
446             this.LOG.inform("iterate classes");
447             
448             var jsonAll = {}; 
449             
450             for (var i = 0, l = classes.length; i < l; i++) {
451                 var symbol = classes[i];
452                 var output = "";
453                 
454                 this.LOG.inform("classTemplate Process : " + this.target+"/symbols/" +symbol.alias+'.' + this.publishExt);
455                 
456                 
457                 symbol.ignoreNamespace = this.ignoreNamespace;
458                 
459                 File.write(this.target+"/symbols/" +symbol.alias+'.' + this.publishExt ,
460                         classTemplate.process(symbol));
461                 
462                 jsonAll[symbol.alias] = this.publishJSON(symbol);
463                 
464                 
465                 
466             }
467             
468             File.write(this.target+"/json/roodata.json",
469                     JSON.stringify({
470                         success : true,
471                         data : jsonAll
472                     }, null, 1)
473             );
474             
475             
476             // regenrate the index with different relative links
477             Link.base = "";
478              
479             this.LOG.inform("build index");
480             
481             File.write(this.target +  "/index."+ this.publishExt, 
482                 classesindexTemplate.process(classes)
483             );
484             
485             // blank everything???? classesindexTemplate = classesIndex = classes = null;
486             
487      
488             
489             var documentedFiles = symbols.filter(function ($) {
490                 return ($.is("FILE"))
491             });
492             
493             var allFiles = [];
494             
495             for (var i = 0; i < files.length; i++) {
496                 allFiles.push(new  imports.Symbol.Symbol(files[i], [], "FILE", new imports.DocComment.DocComment("/** */")));
497             }
498             
499             for (var i = 0; i < documentedFiles.length; i++) {
500                 var offset = files.indexOf(documentedFiles[i].alias);
501                 allFiles[offset] = documentedFiles[i];
502             }
503                 
504             allFiles = allFiles.sort(makeSortby("name"));
505             this.LOG.inform("write files index");
506             
507             File.write(this.target + "/files."+this.publishExt, 
508                 fileindexTemplate.process(allFiles)
509             );
510             
511             
512             
513             
514         },
515         /**
516          * JSON files are lookup files for the documentation
517          * - can be used by IDE's or AJAX based doc tools
518          * 
519          * 
520          */
521         publishJSON : function(data)
522         {
523             // what we need to output to be usefull...
524             // a) props..
525             var cfgProperties = [];
526             if (!data.comment.getTag('singleton').length) {
527                 cfgProperties = data.configToArray();
528                 cfgProperties = cfgProperties.sort(makeSortby("alias"));
529                 
530             }
531             var props = []; 
532             //println(cfgProperties.toSource());
533             var p ='';
534             for(var i =0; i < cfgProperties.length;i++) {
535                 p = cfgProperties[i];
536                 props.push( {
537                     name : p.name,
538                     type : p.type,
539                     desc : p.desc,
540                     memberOf : p.memberOf == data.alias ? '' : p.memberOf
541                 });
542             }
543             
544              
545             var ownEvents = data.methods.filter( function(e){
546                     return e.isEvent && !e.comment.getTag('hide').length;
547                 }).sort(makeSortby("name"));
548                  
549             
550             var events = [];
551             var m;
552             for(var i =0; i < ownEvents.length;i++) {
553                 m = ownEvents[i];
554                 events.push( {
555                     name : m.name.substring(1),
556                     sig : this.makeFuncSkel(m.params),
557                     type : 'function',
558                     desc : m.desc
559                 });
560             }
561             //println(props.toSource());
562             // we need to output:
563             //classname => {
564             //    propname => 
565             //        type=>
566             //        desc=>
567             //    }
568     
569             var ret = {
570                 props : props,
571                 events: events
572             };
573             return ret;
574             
575             
576             
577             // b) methods
578             // c) events
579             
580             
581         },
582         srcFileRelName : function(sourceFile)
583         {
584            // print(JSON.stringify([ sourceFile, this.baseDir ] ));
585             return sourceFile.substring(this.baseDir.length+1);
586         },
587         srcFileFlatName: function(sourceFile)
588         {
589             var name = this.srcFileRelName(sourceFile);
590            // print("NAME: " + name);
591             name = name.replace(/\.\.?[\\\/]/g, "").replace(/[\\\/]/g, "_");
592             return name.replace(/\:/g, "_") + '.html'; //??;
593             
594         },
595         
596         makeSrcFile: function(sourceFile) 
597         {
598             // this stuff works...
599          
600             
601             var name = this.srcFileFlatName(sourceFile);
602             
603             this.LOG.inform("Write Source file : " + this.target+"/symbols/src/" + name + " FROM: "  + sourceFile);
604             
605             var pretty = imports.PrettyPrint.toPretty(File.read(  sourceFile));
606             
607             File.write(this.target+"/symbols/src/" + name, 
608                 '<html><head>' +
609                 '<title>' + sourceFile + '</title>' +
610                 '<link rel="stylesheet" type="text/css" href="' + this.roojs + '/css/highlight-js.css"/>' + 
611                 '</head><body class="highlightpage">' +
612                 pretty +
613                 '</body></html>');
614         },
615         /**
616          * used by JSON output to generate a function skeleton
617          */
618         makeFuncSkel :function(params) {
619             if (!params) return "function ()\n{\n\n}";
620             return "function (" +
621                 params.filter(
622                     function($) {
623                         return $.name.indexOf(".") == -1; // don't show config params in signature
624                     }
625                 ).map( function($) { return $.name == 'this' ? '_self' : $.name; } ).join(", ") +
626             ")\n{\n\n}";
627         }
628         
629      
630         
631     }
632 );
633   
634
635
636
637
638
639  
640
641
642
643