JSDOC/DocTag.js
[app.jsdoc] / JSDOC / DocTag.js
1 //<script  type="text/javascript">
2  
3  
4 XObject = imports.XObject.XObject;
5
6 Options = imports.Options.Options;
7
8  
9 /**
10  * DocTag - represents a single A=b tag.
11  * @class DocTag
12  * @scope JSDOC
13  */
14   
15  
16 DocTag = XObject.define(
17
18 /**
19  * @constructor
20  * @arg {String} src
21  */
22
23     function(src) {
24         this.title        = "";
25         this.type         = "";
26         this.name         = "";
27         this.isOptional   = false;
28         this.defaultValue = "";
29         this.desc         = "";
30         if (typeof src != "undefined") {
31             this.parse(src);
32         }
33     }, 
34     Object,
35     {
36         
37         title: '',
38         type: '',
39         name : '',
40         isOptional : false,
41         defaultValue : '',
42         desc : '',
43         /**
44          * serialize..
45          */
46         toJSON :function(t)
47         {
48             var ret = { '*object' : 'DocTag' };
49             
50             for (var i in this) {
51                 switch (typeof(this[i])) {
52                     case 'function':
53                        continue;
54                        continue;
55                         
56                     case 'string':
57                     case 'number':
58                     case 'boolean':                    
59                         ret[i] = this[i]; continue;
60                     default:
61                         print("unknown type:" + typeof(this[i]));
62                         Seed.quit();
63                    }
64             }
65             return ret;
66         },
67         
68
69
70         /**
71             Populate the properties of this from the given tag src.
72             @param {string} src
73          */
74         parse : function(src) {
75             if (typeof src != "string") throw "src must be a string not "+(typeof src);
76
77             try {
78                 src = this.nibbleTitle(src);
79                 //if (JSDOC.PluginManager) {
80                 //    JSDOC.PluginManager.run("onDocTagSynonym", this);
81                // }
82                 
83                 src = this.nibbleType(src);
84                 
85                 // only some tags are allowed to have names.
86                 if (this.title == "param" ||
87                     this.title == "property" ||
88                     this.title == "cfg") {
89                     // @config is deprecated
90                     
91                     src = this.nibbleName(src);
92                     // support @param name {Type} ...
93                     if (!this.type) {
94                         src = this.nibbleType(src);
95                     }
96                 }
97             }
98             catch(e) {
99                 if (Options.LOG) Options.LOG.warn(e);
100                 else throw e;
101             }
102             this.desc = src; // whatever is left
103             
104             // example tags need to have whitespace preserved
105             if (this.title != "example") this.desc = this.desc.trim();
106             
107             //if (JSDOC.PluginManager) {
108             //    JSDOC.PluginManager.run("onDocTag", this);
109             //}
110         },
111
112         /**
113             Automatically called when this is stringified.
114          */
115         toString : function() {
116             return this.desc;
117         },
118          
119
120         /**
121             Find and shift off the title of a tag.
122             @param {string} src
123             @return src
124          */
125         nibbleTitle : function(src) {
126             if (typeof src != "string") throw "src must be a string not "+(typeof src);
127             
128             var parts = src.match(/^\s*(\S+)(?:\s([\s\S]*))?$/);
129
130             if (parts && parts[1]) this.title = parts[1];
131             if (parts && parts[2]) src = parts[2];
132             else src = "";
133             
134             return src;
135         },
136          
137         /**
138             Find and shift off the type of a tag.
139             @requires frame/String.js
140             @param {string} src
141             @return src
142          */
143         nibbleType : function(src) 
144         {
145             if (typeof src != "string") throw "src must be a string not "+(typeof src);
146             
147             if (src.match(/^\s*\{/)) {
148                 var typeRange = this.balance(src,"{", "}");
149                 if (typeRange[1] == -1) {
150                     throw "Malformed comment tag ignored. Tag type requires an opening { and a closing }: "+src;
151                 }
152                 this.type = src.substring(typeRange[0]+1, typeRange[1]).trim();
153                 this.type = this.type.replace(/\s*,\s*/g, "|"); // multiples can be separated by , or |
154                 src = src.substring(typeRange[1]+1);
155             }
156             
157             return src;
158         },
159          
160
161         /**
162             Find and shift off the name of a tag.
163             @requires frame/String.js
164             @param {string} src
165             @return src
166          */
167         nibbleName : function(src) {
168             if (typeof src != "string") throw "src must be a string not "+(typeof src);
169             
170             src = src.trim();
171             
172             // is optional?
173             if (src.charAt(0) == "[") {
174                 var nameRange = this.balance(src,"[", "]");
175                 if (nameRange[1] == -1) {
176                     throw "Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: "+src;
177                 }
178                 this.name = src.substring(nameRange[0]+1, nameRange[1]).trim();
179                 this.isOptional = true;
180                 
181                 src = src.substring(nameRange[1]+1);
182                 
183                 // has default value?
184                 var nameAndValue = this.name.split("=");
185                 if (nameAndValue.length) {
186                     this.name = nameAndValue.shift().trim();
187                     this.defaultValue = nameAndValue.join("=");
188                 }
189             }
190             else {
191                 var parts = src.match(/^(\S+)(?:\s([\s\S]*))?$/);
192                 if (parts) {
193                     if (parts[1]) this.name = parts[1];
194                     if (parts[2]) src = parts[2].trim();
195                     else src = "";
196                 }
197             }   
198
199             return src;
200         },
201         
202         balance : function(str, open, close) {
203             var i = 0;
204             while (str.charAt(i) != open) {
205                 if (i == str.length) return [-1, -1];
206                 i++;
207             }
208             
209             var j = i+1;
210             var balance = 1;
211             while (j < str.length) {
212                 if (str.charAt(j) == open) balance++;
213                 if (str.charAt(j) == close) balance--;
214                 if (balance == 0) break;
215                 j++;
216                 if (j == str.length) return [-1, -1];
217             }
218             
219             return [i, j];
220 }
221
222         
223         
224 });
225
226 // cached support?
227 DocTag.fromDump = function(t)
228 {
229     var ns = new DocTag();
230     for (var i in t) {
231         if (typeof(ns[i]) == "undefined") {
232             println("ERR:no default for DocTag:"+ i);
233         }
234        ns[i] = t[i];
235     }
236     return ns;
237 }