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