PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/apidoc/generators/jsduck_generator.py

http://github.com/appcelerator/titanium_mobile
Python | 638 lines | 597 code | 15 blank | 26 comment | 25 complexity | a235811e7165b59aa36e0225bf33726d MD5 | raw file
Possible License(s): MIT, JSON, Apache-2.0, 0BSD, CC-BY-SA-3.0, BSD-2-Clause, MPL-2.0-no-copyleft-exception, BSD-3-Clause, CC-BY-3.0, Unlicense
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (c) 2011 Appcelerator, Inc. All Rights Reserved.
  4. # Licensed under the Apache Public License (version 2)
  5. import os, sys, re
  6. this_dir = os.path.dirname(os.path.abspath(__file__))
  7. sys.path.append(os.path.abspath(os.path.join(this_dir, "..")))
  8. from common import dict_has_non_empty_member
  9. # We package the python markdown module already in /support/module/support/markdown.
  10. module_support_dir = os.path.abspath(os.path.join(this_dir, "..", "..", "support", "module", "support"))
  11. sys.path.append(module_support_dir)
  12. import markdown
  13. android_support_dir = os.path.abspath(os.path.join(this_dir, "..", "..", "support", "android"))
  14. sys.path.append(android_support_dir)
  15. from tilogger import *
  16. log = TiLogger(None)
  17. from string import Template
  18. all_annotated_apis = None
  19. apis = None
  20. # These top-level namespaces are added for documentation purposes
  21. special_toplevel_types = [ "Global", "Modules" ]
  22. # Avoid obliterating our four spaces pattern with a careless %s:/ /^I/
  23. FOUR_SPACES=' ' + ' '
  24. # compiling REs ahead of time, since we use them heavily.
  25. link_parts_re = re.compile(r"(?:\[([^\]]+?)\]\(([^\)\s]+?)\)|\<([^\s]+)\>)", re.MULTILINE)
  26. # To add Alloy tags in the description, use backticks around the tag (`<Button>`, e.g.).
  27. find_links_re = re.compile(r"(\[[^\]]+?\]\([^\)\s]+?\)|(?!`)\<[^\s]+\>(?!`))", re.MULTILINE)
  28. html_scheme_re = re.compile(r"^http:|^https:")
  29. doc_site_url_re = re.compile(r"http://docs.appcelerator.com/titanium/.*(#!.*)")
  30. # we use this to distinguish inline HTML tags from Markdown links. Not foolproof, and a
  31. # we should probably find a better technique in the long run.
  32. html_element_re = re.compile("([a-z]|\/)")
  33. try:
  34. from pygments import highlight
  35. from pygments.formatters import HtmlFormatter
  36. from pygments.lexers import get_lexer_by_name
  37. except:
  38. print >> sys.stderr, "You don't have Pygments!\n"
  39. print >> sys.stderr, "You can install it with:\n"
  40. print >> sys.stderr, "> easy_install Pygments"
  41. print ""
  42. sys.exit(1)
  43. # write unicode strings safely
  44. def write_utf8(file, string):
  45. file.write(string.encode('utf8', 'replace'))
  46. def convert_string_to_jsduck_link(obj_specifier):
  47. global all_annotated_apis
  48. if obj_specifier in all_annotated_apis:
  49. return obj_specifier
  50. else:
  51. # Maybe a method, property or event
  52. parts = obj_specifier.split(".")
  53. if len(parts) > 1:
  54. parent = ".".join(parts[:-1])
  55. member_name = parts[-1]
  56. if parent in all_annotated_apis:
  57. obj = all_annotated_apis[parent]
  58. list_names = {
  59. "methods": 'method-',
  60. "properties": 'property-',
  61. "events": 'event-'
  62. }
  63. for list_name in list_names.keys():
  64. if hasattr(obj, list_name) and type(getattr(obj, list_name)) == list:
  65. for m in getattr(obj, list_name):
  66. if m.name == member_name:
  67. return parent + '#' + list_names[list_name] + member_name
  68. else:
  69. return "#" + obj_specifier
  70. return obj_specifier
  71. def process_markdown_links(s):
  72. new_string = s
  73. skip_flag = False
  74. results = find_links_re.findall(new_string)
  75. if results is not None and len(results) > 0:
  76. for link in results:
  77. match = link_parts_re.match(link)
  78. if match == None:
  79. print "no match:" + link
  80. continue
  81. # Process links with a defined name [foo](url)
  82. if match.group(1) != None and match.group(2)!= None:
  83. url = match.group(2)
  84. name = match.group(1)
  85. # Ignore things enclosed with Alloy tags
  86. elif match.group(3) == "Alloy":
  87. skip_flag = True
  88. continue
  89. elif match.group(3) == "/Alloy":
  90. skip_flag = False
  91. continue
  92. # For simple markdown links, such as <Titanium.Analytics> or <www.google.com>
  93. # skip links that look like HTML elements (<span>).
  94. elif match.group(3) != None and not html_element_re.match(link, 1) and not skip_flag:
  95. url = match.group(3)
  96. name = None
  97. # Otherwise, our "link" was probably an HTML tag, so we leave it alone
  98. else:
  99. continue
  100. # Process URLs
  101. docs_site_link = False
  102. api_link = False
  103. # For links back to the doc site -- guides pages, videos, etc.
  104. # extract just the part following the hash, to avoid re-loading the site
  105. # [Quick Start](http://docs.appcelerator.com/titanium/2.1/index.html#!/guide/Quick_Start) ->
  106. # [Quick Start](#!/guide/Quick_Start Quick Start)
  107. #
  108. # Generic absolute URLs pass through unchanged
  109. # [Facebook Graph API](http://developers.facebook.com/docs/reference/api/) -> unchanged
  110. if url.startswith("http"):
  111. url_match = doc_site_url_re.match(url)
  112. if url_match:
  113. url = url_match.group(1)
  114. docs_site_link = True
  115. if not name:
  116. name = url
  117. new_string = new_string.replace(link, "[%s](%s)" % (name, url))
  118. else:
  119. # Reformat API object links so jsduck can process them.
  120. # [systemId](Titanium.XML.Entity.systemId -> {@link Titanium.XML.Entity#systemId systemId}
  121. url = convert_string_to_jsduck_link(url)
  122. if name:
  123. new_string = new_string.replace(link, "{@link %s %s}" % (url, name))
  124. else:
  125. new_string = new_string.replace(link, "{@link %s}" % url)
  126. return new_string
  127. def markdown_to_html(s, obj=None):
  128. if s is None or len(s) == 0:
  129. return ""
  130. if "<" in s or "[" in s:
  131. s = process_markdown_links(s)
  132. return markdown.markdown(s)
  133. # remove <p> and </p> if a string is enclosed with them
  134. def remove_p_tags(str):
  135. if str is None or len(str) == 0:
  136. return ""
  137. if str.startswith("<p>"):
  138. str = str[3:]
  139. if str.endswith("</p>"):
  140. str = str[:-4]
  141. return str
  142. # Print two digit version if third digit is 0.
  143. def format_version(version_str):
  144. digits = version_str.split(".")
  145. if len(digits) <= 2:
  146. return version_str
  147. else:
  148. if digits[2] == '0':
  149. return ".".join(digits[0:2])
  150. else:
  151. return ".".join(digits)
  152. def output_properties_for_obj(annotated_obj):
  153. obj = annotated_obj.api_obj
  154. res = []
  155. # Only output platforms if platforms or since versions are different from
  156. # containing object.
  157. if obj.has_key("platforms") or obj.has_key("since"):
  158. for platform in annotated_obj.platforms:
  159. res.append("@platform %s %s" % (platform["name"], format_version(platform["since"])))
  160. if obj.has_key("availability") and obj['availability'] == 'creation':
  161. res.append("@creationOnly")
  162. if obj.has_key("availability") and obj['availability'] == 'not-creation':
  163. res.append("@nonCreation")
  164. if obj.has_key("extends"):
  165. res.append("@extends %s" % (obj["extends"]))
  166. if(len(res) == 0):
  167. return ""
  168. return "\t * " + "\n\t * ".join(res) + "\n"
  169. # @deprecated and @removed are multi-line tags, so this must be
  170. # inserted after the summary and description, or the summary will get
  171. # included as part of the deprecation.
  172. def output_deprecation_for_obj(annotated_obj):
  173. obj = annotated_obj.api_obj
  174. if obj.has_key("deprecated"):
  175. if obj["deprecated"].has_key("removed"):
  176. str = "@removed %s" % (obj["deprecated"]["removed"])
  177. else:
  178. str = "@deprecated %s" % (obj["deprecated"]["since"])
  179. if obj["deprecated"].has_key("notes"):
  180. str += " %s" % markdown_to_html(obj["deprecated"]["notes"])
  181. str = str.replace("\n", "\n\t * ")
  182. return "\t * %s\n" % str
  183. else:
  184. return ""
  185. def output_example(desc, code, convert_empty_code):
  186. if len(desc) == 0 and len(code) == 0:
  187. return None
  188. # sometimes if there is only one example
  189. if len(code) == 0 and convert_empty_code == True:
  190. # no code? probably desc contains the code
  191. code = desc
  192. desc = []
  193. # determine if we need t remove leading spaces from all code lines
  194. need_strip = True
  195. for line in code:
  196. if len(line) > 0 and line[0:4] != FOUR_SPACES:
  197. need_strip = False
  198. break
  199. if need_strip:
  200. stripped_code = []
  201. for line in code:
  202. stripped_code.append(line[4:])
  203. code = stripped_code
  204. # hack - insert &shy; to avoid having closing comment sign within JSDUck markup
  205. code = "\n".join(code).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("*/", "*&shy;/")
  206. desc = "\n".join(desc)
  207. if len(desc) > 0 and len(code) > 0:
  208. return "<p>%s</p><pre>%s</pre>" % (markdown_to_html(desc), code)
  209. elif len(desc) == 0 and len(code) > 0:
  210. return "<pre>%s</pre>" % (code)
  211. elif len(desc) > 0 and len(code) == 0:
  212. return "<p>%s</p>" % markdown_to_html(desc)
  213. def output_examples_for_obj(obj):
  214. res = []
  215. if obj.has_key("examples"):
  216. if len(obj['examples']) == 1:
  217. res.append("<h3>Example</h3>")
  218. else:
  219. res.append("<h3>Examples</h3>")
  220. for example in obj['examples']:
  221. res.append("<h4>%s</h4>" % (example['title']))
  222. body = example['example']
  223. code = []
  224. desc = []
  225. desc_finished = False
  226. prev_line_empty = False
  227. first_code_block = True
  228. for line in body.splitlines():
  229. # parse description part until code starts
  230. # skip empty string between desc and code
  231. if not desc_finished:
  232. if prev_line_empty == True and (line.find(FOUR_SPACES) == 0 or line.find('\t') == 0):
  233. desc_finished = True
  234. else:
  235. # parsing code until code finishes or another description starts
  236. if line.find(FOUR_SPACES) != 0 and line.find('\t') != 0 and len(line) != 0:
  237. # code block finished - another description started - flush content
  238. desc_finished = False
  239. res.append(output_example(desc, code, first_code_block))
  240. first_code_block = False
  241. code = []
  242. desc = []
  243. if not desc_finished:
  244. desc.append(line)
  245. else:
  246. code.append(line)
  247. prev_line_empty = len(line.strip()) == 0
  248. res.append(output_example(desc, code, first_code_block))
  249. res = filter(None, res)
  250. if(len(res) == 0):
  251. return ""
  252. return "\t * " + "\n\t * ".join(res) + "\n"
  253. def transform_type(type):
  254. if isinstance(type, list):
  255. # type consist of more then one type
  256. return "/".join(map((lambda typ: transform_type(typ)), type))
  257. if type.startswith("Array<"):
  258. type = re.sub(r'Array<(.*?)>', r'\1', type)
  259. type = transform_type(type) + "[]"
  260. elif type == "Dictionary":
  261. type = "Dictionary"
  262. elif type.startswith("Dictionary<"):
  263. type = re.sub(r'Dictionary<(.*?)>', r'\1', type)
  264. type = "Dictionary<%s>" % (type)
  265. elif type == 'Callback':
  266. type = "Function"
  267. elif type.startswith("Callback<"):
  268. type = re.sub(r'Callback<(.*?)>', r'\1', type)
  269. type = "Callback<%s>" % (type)
  270. return type
  271. def is_special_toplevel_type(one_type):
  272. for special_type in special_toplevel_types:
  273. if one_type["name"].find(special_type) == 0:
  274. return True
  275. return False
  276. def get_summary_and_description(api_obj):
  277. summary = None
  278. desc = None
  279. if api_obj.has_key("summary"):
  280. summary = markdown_to_html(api_obj["summary"])
  281. if api_obj.has_key("description"):
  282. desc = markdown_to_html(api_obj["description"])
  283. res = u""
  284. if summary != None:
  285. res = u"\t * " + summary + "\n"
  286. if desc != None:
  287. res += u"\t * @description " + desc + "\n"
  288. elif desc != None:
  289. # use description if there is no summary
  290. res = u"\t * " + desc
  291. return res
  292. def get_edit_url(filepath):
  293. res = ""
  294. basePath = ""
  295. isTiDoc = 0
  296. isAppCModuleDoc = 0
  297. isTizenDoc = 0
  298. isTiModuleDoc = 0
  299. url =''
  300. # Some module are in private repos and can't be edited.
  301. module_black_list = ['ti.geofence', 'appcelerator.https']
  302. # Identify object type by path. These should always be mutually exclusive.
  303. isTiDoc = filepath.find('titanium_mobile/')
  304. isTiModuleDoc = filepath.find('titanium_modules/')
  305. isAppCModuleDoc = filepath.find('appc_modules/')
  306. isTizenDoc = filepath.find('titanium_mobile_tizen/')
  307. if isTiDoc != -1:
  308. basePath = "https://github.com/appcelerator/titanium_mobile/edit/master/"
  309. index = filepath.find('apidoc/')
  310. path = filepath[index:]
  311. url += basePath + path
  312. res = "\t * @editurl " + url + "\n"
  313. elif isAppCModuleDoc != -1 or isTiModuleDoc !=-1:
  314. s = Template('https://github.com/appcelerator-modules/$module/edit/master/$path')
  315. index = filepath.find('apidoc/')
  316. modulepath = filepath[index:]
  317. match = re.search('titanium_modules|appc_modules\/(.+)\/apidoc', filepath)
  318. if match:
  319. modulename = match.group(1)
  320. if modulename not in module_black_list:
  321. url = s.substitute(module=modulename, path=modulepath)
  322. res = "\t * @editurl " + url + "\n"
  323. elif isTizenDoc != -1:
  324. basePath = "https://github.com/appcelerator/titanium_mobile_tizen/edit/master/modules/tizen/"
  325. index = filepath.find('apidoc/')
  326. path = filepath[index:]
  327. url += basePath + path
  328. res = "\t * @editurl " + url + "\n"
  329. return res
  330. # Side effect of hiding properties is that the accessors do not get hidden
  331. # Explicitly hide accessors for JSDuck
  332. def hide_accessors(parent_name, property_name):
  333. res = ""
  334. parent_obj = all_annotated_apis[parent_name].api_obj
  335. if "properties" in parent_obj:
  336. parent_properties = parent_obj["properties"]
  337. property_dict = dict((p["name"], p) for p in parent_properties)
  338. if property_name in property_dict:
  339. setter = True;
  340. getter = True;
  341. if "accessors" in property_dict[property_name] and not property_dict[property_name]["accessors"]:
  342. return res
  343. if "availability" in property_dict[property_name] and property_dict[property_name]["availability"] == "creation":
  344. setter = False;
  345. if "permission" in property_dict[property_name]:
  346. if property_dict[property_name]["permission"] == "read-only":
  347. setter = False;
  348. elif property_dict[property_name]["permission"] == "write-only":
  349. getter = False;
  350. upperFirst = property_name[0].upper() + property_name[1:]
  351. if getter:
  352. getter = "get" + upperFirst
  353. res += "/**\n\t * @method " + getter + " \n\t * @hide\n*/\n"
  354. if setter:
  355. setter = "set" + upperFirst
  356. res += "/**\n\t * @method " + setter + " \n\t * @hide\n*/\n"
  357. if "extends" in parent_obj:
  358. parent_name = parent_obj["extends"]
  359. return res + hide_accessors(parent_name, property_name)
  360. else:
  361. return res
  362. def get_constants(constants_list, raw_apis, api_type="props"):
  363. if api_type == "params":
  364. rv = "\nThis parameter accepts the following constants:\n\n"
  365. elif api_type == "returns":
  366. rv = "\nCan return one of the following constants:\n\n"
  367. else:
  368. rv = "\nThis property can be assigned the following constants:\n\n"
  369. if type(constants_list) is not list:
  370. a = [constants_list]
  371. constants_list = a
  372. for item in constants_list:
  373. namespace = item.rsplit('.', 1)[0]
  374. token = item.rsplit('.', 1)[-1]
  375. if item[-1] == '*':
  376. token = token[:-1]
  377. if namespace in raw_apis:
  378. for property in raw_apis[namespace]["properties"]:
  379. if (token and property["name"].startswith(token)) or (not token and re.match(r"[_A-Z]+", property["name"])):
  380. prop = namespace + "." + property["name"]
  381. rv += " * [" + prop + "](" + prop + ")\n"
  382. if property["name"] == token:
  383. break
  384. return rv
  385. def generate(raw_apis, annotated_apis, options):
  386. global all_annotated_apis, apis
  387. all_annotated_apis = annotated_apis
  388. apis = raw_apis
  389. if options is not None and (not hasattr(options, "output") or options.output is None or len(options.output) == 0):
  390. log.error ("'output' option not provided")
  391. if options is not None and not os.path.exists(options.output):
  392. os.makedirs(options.output)
  393. # Write the output files
  394. if options is not None:
  395. log.info("Creating titanium.js in %s" % options.output)
  396. output = open(os.path.join(options.output, "titanium.js"), "w")
  397. for name in annotated_apis:
  398. annotated_obj = annotated_apis[name]
  399. write_utf8(output, "/**\n\t * @class %s\n" % (annotated_obj.name))
  400. if annotated_obj.typestr == "module" and annotated_obj.parent is None:
  401. write_utf8(output, '\t * @typestr Module\n')
  402. else:
  403. typestr = ''
  404. if annotated_obj.typestr == "module":
  405. typestr = "Submodule"
  406. elif annotated_obj.typestr == "proxy":
  407. typestr = "Object"
  408. elif annotated_obj.typestr == "method":
  409. typestr = "Function"
  410. elif annotated_obj.typestr == "property":
  411. typestr = "Property"
  412. elif annotated_obj.typestr == "event":
  413. typestr = "Event"
  414. elif annotated_obj.typestr == "parameter":
  415. typestr = "Parameter"
  416. if len(typestr) > 0 and annotated_obj.parent is not None:
  417. write_utf8(output, '\t * @typestr %s of %s\n' % (typestr, annotated_obj.parent.name))
  418. else:
  419. write_utf8(output, '\t * @typestr %s\n' % (typestr))
  420. if annotated_obj.is_pseudotype and not is_special_toplevel_type(annotated_obj.api_obj):
  421. write_utf8(output, "\t * @pseudo\n")
  422. write_utf8(output, output_properties_for_obj(annotated_obj))
  423. write_utf8(output, get_edit_url(raw_apis[name]['filepath']))
  424. write_utf8(output, get_summary_and_description(annotated_obj.api_obj))
  425. write_utf8(output, output_examples_for_obj(annotated_obj.api_obj))
  426. write_utf8(output, output_deprecation_for_obj(annotated_obj))
  427. write_utf8(output, "\t */\n\n")
  428. p = annotated_obj.properties
  429. for k in p:
  430. # Do not insert records for inherited members
  431. if k.inherited_from:
  432. continue
  433. obj = k.api_obj
  434. getter_ok = True
  435. setter_ok = True
  436. if k.permission == "read-only" or k.availability == "creation":
  437. setter_ok = False
  438. if k.permission == "write-only":
  439. getter_ok = False
  440. if "accessors" in obj and not obj["accessors"]:
  441. getter_ok = setter_ok = False
  442. if k.default is not None:
  443. default_val = remove_p_tags(markdown_to_html(str(k.default)))
  444. write_utf8(output, '/**\n\t * @property [%s=%s]\n' % (k.name, default_val))
  445. else:
  446. write_utf8(output, "/**\n\t * @property %s\n" % (k.name))
  447. if obj.has_key('type'):
  448. write_utf8(output, "\t * @type %s\n" % (transform_type(obj["type"])))
  449. if obj.has_key('permission'):
  450. if obj["permission"] == "read-only":
  451. write_utf8(output, "\t * @readonly\n")
  452. elif obj["permission"] == "write-only":
  453. write_utf8(output, "\t * @writeonly\n")
  454. write_utf8(output, output_properties_for_obj(k))
  455. write_utf8(output, get_summary_and_description(obj))
  456. if obj.has_key('constants'):
  457. write_utf8(output, markdown_to_html(get_constants(obj["constants"], raw_apis)))
  458. write_utf8(output, output_examples_for_obj(obj))
  459. write_utf8(output, output_deprecation_for_obj(k))
  460. write_utf8(output, " */\n\n")
  461. p = annotated_obj.methods
  462. for k in p:
  463. # Do not insert records for inherited members
  464. if k.inherited_from:
  465. continue
  466. obj = k.api_obj
  467. write_utf8(output, "/**\n\t * @method %s\n" % (k.name))
  468. write_utf8(output, get_summary_and_description(obj))
  469. write_utf8(output, output_examples_for_obj(obj))
  470. write_utf8(output, output_deprecation_for_obj(k))
  471. if obj.has_key("parameters"):
  472. for param in obj["parameters"]:
  473. if "summary" in param:
  474. summary = param["summary"]
  475. if "repeatable" in param and param["repeatable"]:
  476. repeatable = "..."
  477. else:
  478. repeatable = ""
  479. if "constants" in param:
  480. summary += get_constants(param["constants"], raw_apis, "params")
  481. type = "{" + transform_type(param["type"]) + repeatable + "}" if param.has_key("type") else ""
  482. optional = "(optional)" if param.has_key('optional') and param["optional"] == True else ""
  483. if param.has_key('default'):
  484. default_val = remove_p_tags(markdown_to_html(str(param['default'])))
  485. write_utf8(output, "\t * @param %s [%s=%s] %s\n\t * %s\n" % (type, param['name'], default_val, optional, markdown_to_html(summary)))
  486. else:
  487. write_utf8(output, "\t * @param %s %s %s\n\t * %s\n" % (type, param['name'], optional, markdown_to_html(summary)))
  488. if obj.has_key("returns"):
  489. returntypes = obj["returns"]
  490. summary = ""
  491. # check for the object form first
  492. if "type" in returntypes:
  493. type = "{" + transform_type(returntypes["type"]) + "}"
  494. summary = returntypes["summary"] if "summary" in returntypes else ""
  495. else:
  496. # could be an array, check if it's iterable
  497. if hasattr(returntypes, "__getitem__") or hasattr(returntypes, "__iter__"):
  498. type = ""
  499. for one_returntype in returntypes:
  500. if type == "":
  501. type = "{" + transform_type(one_returntype["type"])
  502. else:
  503. type = type + "/" + transform_type(one_returntype["type"])
  504. # Can't handle multiple summaries, only take one.
  505. if summary == "" and summary in one_returntype:
  506. summary = one_returntype["summary"]
  507. type = type + "}"
  508. else:
  509. log.warn("returns for %s should be an array or a dict." % obj["name"]);
  510. if "constants" in returntypes:
  511. summary += get_constants(returntypes["constants"], raw_apis, "returns")
  512. write_utf8(output, "\t * @return %s %s\n" % (type, markdown_to_html(summary)))
  513. else:
  514. write_utf8(output, "\t * @return {void}\n")
  515. write_utf8(output, output_properties_for_obj(k))
  516. write_utf8(output, "\t*/\n\n")
  517. p = annotated_obj.events
  518. for k in p:
  519. # Do not insert records for inherited members
  520. if k.inherited_from:
  521. continue
  522. obj = k.api_obj
  523. write_utf8(output, "/**\n\t * @event %s\n" % (k.name))
  524. write_utf8(output, get_summary_and_description(obj))
  525. write_utf8(output, output_examples_for_obj(obj))
  526. write_utf8(output, output_deprecation_for_obj(k))
  527. if k.properties is not None:
  528. for param in k.properties:
  529. if "deprecated" in param.api_obj:
  530. deprecated = "(deprecated)"
  531. else:
  532. deprecated = ""
  533. platforms = "("+" ".join(param.api_obj['platforms'])+")" if param.api_obj.has_key('platforms') and param.api_obj["platforms"] else ""
  534. if param.api_obj.has_key('type'):
  535. write_utf8(output, "\t * @param {%s} %s %s %s\n" % (transform_type(param.api_obj['type']), deprecated, platforms, param.name))
  536. else:
  537. write_utf8(output, "\t * @param %s %s %s\n" % (deprecated, platforms, param.name))
  538. write_utf8(output, get_summary_and_description(param.api_obj))
  539. if "constants" in param.api_obj:
  540. write_utf8(output, markdown_to_html(get_constants(param.api_obj["constants"], raw_apis)))
  541. write_utf8(output, output_properties_for_obj(k))
  542. write_utf8(output, "\t*/\n\n")
  543. # handle excluded members
  544. api_obj = annotated_obj.api_obj
  545. if "excludes" in api_obj:
  546. for member_type in [ "properties", "methods", "events" ]:
  547. if member_type in api_obj["excludes"]:
  548. annotation_string = { "properties":"@property", "methods":"@method",
  549. "events":"@event" }[member_type]
  550. excluded_members = api_obj["excludes"][member_type]
  551. for one_member in excluded_members:
  552. write_utf8(output, "/**\n\t * %s %s \n\t * @hide\n*/\n" % (annotation_string, one_member))
  553. # Explicitly hide accessors
  554. if member_type == "properties" and "extends" in api_obj:
  555. parent_name = api_obj["extends"]
  556. hide_methods = hide_accessors(parent_name, one_member)
  557. if hide_methods:
  558. write_utf8(output, "%s" % (hide_methods))
  559. output.close()