|
1 if (typeof JSDOC == "undefined") JSDOC = {}; |
|
2 |
|
3 /** |
|
4 @constructor |
|
5 */ |
|
6 JSDOC.DocTag = function(src) { |
|
7 this.init(); |
|
8 if (typeof src != "undefined") { |
|
9 this.parse(src); |
|
10 } |
|
11 } |
|
12 |
|
13 /** |
|
14 Create and initialize the properties of this. |
|
15 */ |
|
16 JSDOC.DocTag.prototype.init = function() { |
|
17 this.title = ""; |
|
18 this.type = ""; |
|
19 this.name = ""; |
|
20 this.isOptional = false; |
|
21 this.defaultValue = ""; |
|
22 this.desc = ""; |
|
23 |
|
24 return this; |
|
25 } |
|
26 |
|
27 /** |
|
28 Populate the properties of this from the given tag src. |
|
29 @param {string} src |
|
30 */ |
|
31 JSDOC.DocTag.prototype.parse = function(src) { |
|
32 if (typeof src != "string") throw "src must be a string not "+(typeof src); |
|
33 |
|
34 try { |
|
35 src = this.nibbleTitle(src); |
|
36 if (JSDOC.PluginManager) { |
|
37 JSDOC.PluginManager.run("onDocTagSynonym", this); |
|
38 } |
|
39 |
|
40 src = this.nibbleType(src); |
|
41 |
|
42 // only some tags are allowed to have names. |
|
43 if (this.title == "param" || this.title == "property" || this.title == "config") { // @config is deprecated |
|
44 src = this.nibbleName(src); |
|
45 } |
|
46 } |
|
47 catch(e) { |
|
48 if (LOG) LOG.warn(e); |
|
49 else throw e; |
|
50 } |
|
51 this.desc = src; // whatever is left |
|
52 |
|
53 // example tags need to have whitespace preserved |
|
54 if (this.title != "example") this.desc = this.desc.trim(); |
|
55 |
|
56 if (JSDOC.PluginManager) { |
|
57 JSDOC.PluginManager.run("onDocTag", this); |
|
58 } |
|
59 } |
|
60 |
|
61 /** |
|
62 Automatically called when this is stringified. |
|
63 */ |
|
64 JSDOC.DocTag.prototype.toString = function() { |
|
65 return this.desc; |
|
66 } |
|
67 |
|
68 /*t: |
|
69 plan(1, "testing JSDOC.DocTag#toString"); |
|
70 |
|
71 var tag = new JSDOC.DocTag("param {object} date A valid date."); |
|
72 is(""+tag, "A valid date.", "stringifying a tag returns the desc."); |
|
73 */ |
|
74 |
|
75 /** |
|
76 Find and shift off the title of a tag. |
|
77 @param {string} src |
|
78 @return src |
|
79 */ |
|
80 JSDOC.DocTag.prototype.nibbleTitle = function(src) { |
|
81 if (typeof src != "string") throw "src must be a string not "+(typeof src); |
|
82 |
|
83 var parts = src.match(/^\s*(\S+)(?:\s([\s\S]*))?$/); |
|
84 |
|
85 if (parts && parts[1]) this.title = parts[1]; |
|
86 if (parts && parts[2]) src = parts[2]; |
|
87 else src = ""; |
|
88 |
|
89 return src; |
|
90 } |
|
91 |
|
92 /*t: |
|
93 plan(8, "testing JSDOC.DocTag#nibbleTitle"); |
|
94 |
|
95 var tag = new JSDOC.DocTag(); |
|
96 |
|
97 tag.init().nibbleTitle("aTitleGoesHere"); |
|
98 is(tag.title, "aTitleGoesHere", "a title can be found in a single-word string."); |
|
99 |
|
100 var src = tag.init().nibbleTitle("aTitleGoesHere and the rest"); |
|
101 is(tag.title, "aTitleGoesHere", "a title can be found in a multi-word string."); |
|
102 is(src, "and the rest", "the rest is returned when the title is nibbled off."); |
|
103 |
|
104 src = tag.init().nibbleTitle(""); |
|
105 is(tag.title, "", "given an empty string the title is empty."); |
|
106 is(src, "", "the rest is empty when the tag is empty."); |
|
107 |
|
108 var src = tag.init().nibbleTitle(" aTitleGoesHere\n a description"); |
|
109 is(tag.title, "aTitleGoesHere", "leading and trailing spaces are not part of the title."); |
|
110 is(src, " a description", "leading spaces (less one) are part of the description."); |
|
111 |
|
112 tag.init().nibbleTitle("a.Title::Goes_Here foo"); |
|
113 is(tag.title, "a.Title::Goes_Here", "titles with punctuation are allowed."); |
|
114 */ |
|
115 |
|
116 /** |
|
117 Find and shift off the type of a tag. |
|
118 @requires frame/String.js |
|
119 @param {string} src |
|
120 @return src |
|
121 */ |
|
122 JSDOC.DocTag.prototype.nibbleType = function(src) { |
|
123 if (typeof src != "string") throw "src must be a string not "+(typeof src); |
|
124 |
|
125 if (src.match(/^\s*\{/)) { |
|
126 var typeRange = src.balance("{", "}"); |
|
127 if (typeRange[1] == -1) { |
|
128 throw "Malformed comment tag ignored. Tag type requires an opening { and a closing }: "+src; |
|
129 } |
|
130 this.type = src.substring(typeRange[0]+1, typeRange[1]).trim(); |
|
131 this.type = this.type.replace(/\s*,\s*/g, "|"); // multiples can be separated by , or | |
|
132 src = src.substring(typeRange[1]+1); |
|
133 } |
|
134 |
|
135 return src; |
|
136 } |
|
137 |
|
138 /*t: |
|
139 plan(5, "testing JSDOC.DocTag.parser.nibbleType"); |
|
140 requires("../frame/String.js"); |
|
141 |
|
142 var tag = new JSDOC.DocTag(); |
|
143 |
|
144 tag.init().nibbleType("{String[]} aliases"); |
|
145 is(tag.type, "String[]", "type can have non-alpha characters."); |
|
146 |
|
147 tag.init().nibbleType("{ aTypeGoesHere } etc etc"); |
|
148 is(tag.type, "aTypeGoesHere", "type is trimmed."); |
|
149 |
|
150 tag.init().nibbleType("{ oneType, twoType ,\n threeType } etc etc"); |
|
151 is(tag.type, "oneType|twoType|threeType", "multiple types can be separated by commas."); |
|
152 |
|
153 var error; |
|
154 try { tag.init().nibbleType("{widget foo"); } |
|
155 catch(e) { error = e; } |
|
156 is(typeof error, "string", "malformed tag type throws error."); |
|
157 isnt(error.indexOf("Malformed"), -1, "error message tells tag is malformed."); |
|
158 */ |
|
159 |
|
160 /** |
|
161 Find and shift off the name of a tag. |
|
162 @requires frame/String.js |
|
163 @param {string} src |
|
164 @return src |
|
165 */ |
|
166 JSDOC.DocTag.prototype.nibbleName = function(src) { |
|
167 if (typeof src != "string") throw "src must be a string not "+(typeof src); |
|
168 |
|
169 src = src.trim(); |
|
170 |
|
171 // is optional? |
|
172 if (src.charAt(0) == "[") { |
|
173 var nameRange = src.balance("[", "]"); |
|
174 if (nameRange[1] == -1) { |
|
175 throw "Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: "+src; |
|
176 } |
|
177 this.name = src.substring(nameRange[0]+1, nameRange[1]).trim(); |
|
178 this.isOptional = true; |
|
179 |
|
180 src = src.substring(nameRange[1]+1); |
|
181 |
|
182 // has default value? |
|
183 var nameAndValue = this.name.split("="); |
|
184 if (nameAndValue.length) { |
|
185 this.name = nameAndValue.shift().trim(); |
|
186 this.defaultValue = nameAndValue.join("="); |
|
187 } |
|
188 } |
|
189 else { |
|
190 var parts = src.match(/^(\S+)(?:\s([\s\S]*))?$/); |
|
191 if (parts) { |
|
192 if (parts[1]) this.name = parts[1]; |
|
193 if (parts[2]) src = parts[2].trim(); |
|
194 else src = ""; |
|
195 } |
|
196 } |
|
197 |
|
198 return src; |
|
199 } |
|
200 |
|
201 /*t: |
|
202 requires("../frame/String.js"); |
|
203 plan(9, "testing JSDOC.DocTag.parser.nibbleName"); |
|
204 |
|
205 var tag = new JSDOC.DocTag(); |
|
206 |
|
207 tag.init().nibbleName("[foo] This is a description."); |
|
208 is(tag.isOptional, true, "isOptional syntax is detected."); |
|
209 is(tag.name, "foo", "optional param name is found."); |
|
210 |
|
211 tag.init().nibbleName("[foo] This is a description."); |
|
212 is(tag.isOptional, true, "isOptional syntax is detected when no type."); |
|
213 is(tag.name, "foo", "optional param name is found when no type."); |
|
214 |
|
215 tag.init().nibbleName("[foo=7] This is a description."); |
|
216 is(tag.name, "foo", "optional param name is found when default value."); |
|
217 is(tag.defaultValue, 7, "optional param default value is found when default value."); |
|
218 |
|
219 //tag.init().nibbleName("[foo= a value] This is a description."); |
|
220 //is(tag.defaultValue, " a value", "optional param default value is found when default value has spaces (issue #112)."); |
|
221 |
|
222 tag.init().nibbleName("[foo=[]] This is a description."); |
|
223 is(tag.defaultValue, "[]", "optional param default value is found when default value is [] (issue #95)."); |
|
224 |
|
225 tag.init().nibbleName("[foo=a=b] This is a description."); |
|
226 is(tag.name, "foo", "optional param name is found when default value is a=b."); |
|
227 is(tag.defaultValue, "a=b", "optional param default value is found when default value is a=b.") |
|
228 */ |
|
229 |
|
230 /*t: |
|
231 plan(32, "Testing JSDOC.DocTag.parser."); |
|
232 requires("../frame/String.js"); |
|
233 |
|
234 var tag = new JSDOC.DocTag(); |
|
235 |
|
236 is(typeof tag, "object", "JSDOC.DocTag.parser with an empty string returns an object."); |
|
237 is(typeof tag.title, "string", "returned object has a string property 'title'."); |
|
238 is(typeof tag.type, "string", "returned object has a string property 'type'."); |
|
239 is(typeof tag.name, "string", "returned object has a string property 'name'."); |
|
240 is(typeof tag.defaultValue, "string", "returned object has a string property 'defaultValue'."); |
|
241 is(typeof tag.isOptional, "boolean", "returned object has a boolean property 'isOptional'."); |
|
242 is(typeof tag.desc, "string", "returned object has a string property 'desc'."); |
|
243 |
|
244 tag = new JSDOC.DocTag("param {widget} foo"); |
|
245 is(tag.title, "param", "param title is found."); |
|
246 is(tag.name, "foo", "param name is found when desc is missing."); |
|
247 is(tag.desc, "", "param desc is empty when missing."); |
|
248 |
|
249 tag = new JSDOC.DocTag("param {object} date A valid date."); |
|
250 is(tag.name, "date", "param name is found with a type."); |
|
251 is(tag.type, "object", "param type is found."); |
|
252 is(tag.desc, "A valid date.", "param desc is found with a type."); |
|
253 |
|
254 tag = new JSDOC.DocTag("param aName a description goes\n here."); |
|
255 is(tag.name, "aName", "param name is found without a type."); |
|
256 is(tag.desc, "a description goes\n here.", "param desc is found without a type."); |
|
257 |
|
258 tag = new JSDOC.DocTag("param {widget}"); |
|
259 is(tag.name, "", "param name is empty when it is not given."); |
|
260 |
|
261 tag = new JSDOC.DocTag("param {widget} [foo] This is a description."); |
|
262 is(tag.name, "foo", "optional param name is found."); |
|
263 |
|
264 tag = new JSDOC.DocTag("return {aType} This is a description."); |
|
265 is(tag.type, "aType", "when return tag has no name, type is found."); |
|
266 is(tag.desc, "This is a description.", "when return tag has no name, desc is found."); |
|
267 |
|
268 tag = new JSDOC.DocTag("author Joe Coder <jcoder@example.com>"); |
|
269 is(tag.title, "author", "author tag has a title."); |
|
270 is(tag.type, "", "the author tag has no type."); |
|
271 is(tag.name, "", "the author tag has no name."); |
|
272 is(tag.desc, "Joe Coder <jcoder@example.com>", "author tag has desc."); |
|
273 |
|
274 tag = new JSDOC.DocTag("private \t\n "); |
|
275 is(tag.title, "private", "private tag has a title."); |
|
276 is(tag.type, "", "the private tag has no type."); |
|
277 is(tag.name, "", "the private tag has no name."); |
|
278 is(tag.desc, "", "private tag has no desc."); |
|
279 |
|
280 tag = new JSDOC.DocTag("example\n example(code);\n more();"); |
|
281 is(tag.desc, " example(code);\n more();", "leading whitespace (less one) in examples code is preserved."); |
|
282 |
|
283 tag = new JSDOC.DocTag("param theName \n"); |
|
284 is(tag.name, "theName", "name only is found."); |
|
285 |
|
286 tag = new JSDOC.DocTag("type theDesc \n"); |
|
287 is(tag.desc, "theDesc", "desc only is found."); |
|
288 |
|
289 tag = new JSDOC.DocTag("type {theType} \n"); |
|
290 is(tag.type, "theType", "type only is found."); |
|
291 |
|
292 tag = new JSDOC.DocTag(""); |
|
293 is(tag.title, "", "title is empty when tag is empty."); |
|
294 */ |