aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.5/python-lib/cuddlefish/docs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.5/python-lib/cuddlefish/docs')
-rw-r--r--tools/addon-sdk-1.5/python-lib/cuddlefish/docs/__init__.py0
-rw-r--r--tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apiparser.py392
-rw-r--r--tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apirenderer.py302
-rw-r--r--tools/addon-sdk-1.5/python-lib/cuddlefish/docs/generate.py283
-rw-r--r--tools/addon-sdk-1.5/python-lib/cuddlefish/docs/renderapi.readme.md210
-rw-r--r--tools/addon-sdk-1.5/python-lib/cuddlefish/docs/webdocs.py198
6 files changed, 1385 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/__init__.py b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/__init__.py
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apiparser.py b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apiparser.py
new file mode 100644
index 0000000..b6ccf22
--- /dev/null
+++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apiparser.py
@@ -0,0 +1,392 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys, re, textwrap
+
+VERSION = 4
+
+class ParseError(Exception):
+ # args[1] is the line number that caused the problem
+ def __init__(self, why, lineno):
+ self.why = why
+ self.lineno = lineno
+ def __str__(self):
+ return ("ParseError: the JS API docs were unparseable on line %d: %s" %
+ (self.lineno, self.why))
+
+class Accumulator:
+ def __init__(self, holder, firstline):
+ self.holder = holder
+ self.firstline = firstline
+ self.otherlines = []
+ def addline(self, line):
+ self.otherlines.append(line)
+ def finish(self):
+ # take a list of strings like:
+ # "initial stuff" (this is in firstline)
+ # " more stuff" (this is in lines[0])
+ # " yet more stuff"
+ # " indented block"
+ # " indented block"
+ # " nonindented stuff" (lines[-1])
+ #
+ # calculate the indentation level by looking at all but the first
+ # line, and removing the whitespace they all have in common. Then
+ # join the results with newlines and return a single string.
+ pieces = []
+ if self.firstline:
+ pieces.append(self.firstline)
+ if self.otherlines:
+ pieces.append(textwrap.dedent("\n".join(self.otherlines)))
+ self.holder["description"] = "\n".join(pieces)
+
+
+class APIParser:
+ def parse(self, lines, lineno):
+ api = {"line_number": lineno + 1}
+# assign the name from the first line, of the form "<api name="API_NAME">"
+ title_line = lines[lineno].rstrip("\n")
+ api["name"] = self._parse_title_line(title_line, lineno + 1)
+ lineno += 1
+# finished with the first line, assigned the name
+ working_set = self._initialize_working_set()
+ props = []
+ currentPropHolder = api
+# fetch the next line, of the form "@tag [name] {datatype} description"
+# and parse it into tag, info, description
+ tag, info, firstline = self._parseTypeLine(lines[lineno], lineno + 1)
+ api["type"] = tag
+# if this API element is a property then datatype must be set
+ if tag == 'property':
+ api['datatype'] = info['datatype']
+ # info is ignored
+ currentAccumulator = Accumulator(api, firstline)
+ lineno += 1
+ while (lineno) < len(lines):
+ line = lines[lineno].rstrip("\n")
+ # accumulate any multiline descriptive text belonging to
+ # the preceding "@" section
+ if self._is_description_line(line):
+ currentAccumulator.addline(line)
+ else:
+ currentAccumulator.finish()
+ if line.startswith("<api"):
+ # then we should recursively handle a nested element
+ nested_api, lineno = self.parse(lines, lineno)
+ self._update_working_set(nested_api, working_set)
+ elif line.startswith("</api"):
+ # then we have finished parsing this api element
+ currentAccumulator.finish()
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ self._assemble_api_element(api, working_set)
+ return api, lineno
+ else:
+ # then we are looking at a subcomponent of an <api> element
+ tag, info, desc = self._parseTypeLine(line, lineno + 1)
+ currentAccumulator = Accumulator(info, desc)
+ if tag == "prop":
+ # build up props[]
+ props.append(info)
+ elif tag == "returns":
+ # close off the @prop list
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ props = []
+ api["returns"] = info
+ currentPropHolder = info
+ elif tag == "param":
+ # close off the @prop list
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ props = []
+ working_set["params"].append(info)
+ currentPropHolder = info
+ elif tag == "argument":
+ # close off the @prop list
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ props = []
+ working_set["arguments"].append(info)
+ currentPropHolder = info
+ else:
+ raise ParseError("unknown '@' section header %s in \
+ '%s'" % (tag, line), lineno + 1)
+ lineno += 1
+ raise ParseError("closing </api> tag not found for <api name=\"" +
+ api["name"] + "\">", lineno + 1)
+
+ def _parse_title_line(self, title_line, lineno):
+ if "name" not in title_line:
+ raise ParseError("Opening <api> tag must have a name attribute.",
+ lineno)
+ m = re.search("name=['\"]{0,1}([-\w\.]*?)['\"]", title_line)
+ if not m:
+ raise ParseError("No value for name attribute found in "
+ "opening <api> tag.", lineno)
+ return m.group(1)
+
+ def _is_description_line(self, line):
+ return not ( (line.lstrip().startswith("@")) or
+ (line.lstrip().startswith("<api")) or
+ (line.lstrip().startswith("</api")) )
+
+ def _initialize_working_set(self):
+ # working_set accumulates api elements
+ # that might belong to a parent api element
+ working_set = {}
+ working_set["constructors"] = []
+ working_set["methods"] = []
+ working_set["properties"] = []
+ working_set["params"] = []
+ working_set["events"] = []
+ working_set["arguments"] = []
+ return working_set
+
+ def _update_working_set(self, nested_api, working_set):
+ # add this api element to whichever list is appropriate
+ if nested_api["type"] == "constructor":
+ working_set["constructors"].append(nested_api)
+ if nested_api["type"] == "method":
+ working_set["methods"].append(nested_api)
+ if nested_api["type"] == "property":
+ working_set["properties"].append(nested_api)
+ if nested_api["type"] == "event":
+ working_set["events"].append(nested_api)
+
+ def _assemble_signature(self, api_element, params):
+ signature = api_element["name"] + "("
+ if len(params) > 0:
+ signature += params[0]["name"]
+ for param in params[1:]:
+ signature += ", " + param["name"]
+ signature += ")"
+ api_element["signature"] = signature
+
+ def _assemble_api_element(self, api_element, working_set):
+ # if any of this working set's lists are non-empty,
+ # add it to the current api element
+ if (api_element["type"] == "constructor") or \
+ (api_element["type"] == "function") or \
+ (api_element["type"] == "method"):
+ self._assemble_signature(api_element, working_set["params"])
+ if len(working_set["params"]) > 0:
+ api_element["params"] = working_set["params"]
+ if len(working_set["properties"]) > 0:
+ api_element["properties"] = working_set["properties"]
+ if len(working_set["constructors"]) > 0:
+ api_element["constructors"] = working_set["constructors"]
+ if len(working_set["methods"]) > 0:
+ api_element["methods"] = working_set["methods"]
+ if len(working_set["events"]) > 0:
+ api_element["events"] = working_set["events"]
+ if len(working_set["arguments"]) > 0:
+ api_element["arguments"] = working_set["arguments"]
+
+ def _validate_info(self, tag, info, line, lineno):
+ if tag == 'property':
+ if not 'datatype' in info:
+ raise ParseError("No type found for @property.", lineno)
+ elif tag == "param":
+ if info.get("required", False) and "default" in info:
+ raise ParseError(
+ "required parameters should not have defaults: '%s'"
+ % line, lineno)
+ elif tag == "prop":
+ if "datatype" not in info:
+ raise ParseError("@prop lines must include {type}: '%s'" %
+ line, lineno)
+ if "name" not in info:
+ raise ParseError("@prop lines must provide a name: '%s'" %
+ line, lineno)
+
+ def _parseTypeLine(self, line, lineno):
+ # handle these things:
+ # @method
+ # @returns description
+ # @returns {string} description
+ # @param NAME {type} description
+ # @param NAME
+ # @prop NAME {type} description
+ # @prop NAME
+ # returns:
+ # tag: type of api element
+ # info: linenumber, required, default, name, datatype
+ # description
+
+ info = {"line_number": lineno}
+ line = line.rstrip("\n")
+ pieces = line.split()
+
+ if not pieces:
+ raise ParseError("line is too short: '%s'" % line, lineno)
+ if not pieces[0].startswith("@"):
+ raise ParseError("type line should start with @: '%s'" % line,
+ lineno)
+ tag = pieces[0][1:]
+ skip = 1
+
+ expect_name = tag in ("param", "prop")
+
+ if len(pieces) == 1:
+ description = ""
+ else:
+ if pieces[1].startswith("{"):
+ # NAME is missing, pieces[1] is TYPE
+ pass
+ else:
+ if expect_name:
+ info["required"] = not pieces[1].startswith("[")
+ name = pieces[1].strip("[ ]")
+ if "=" in name:
+ name, info["default"] = name.split("=")
+ info["name"] = name
+ skip += 1
+
+ if len(pieces) > skip and pieces[skip].startswith("{"):
+ info["datatype"] = pieces[skip].strip("{ }")
+ skip += 1
+
+ # we've got the metadata, now extract the description
+ pieces = line.split(None, skip)
+ if len(pieces) > skip:
+ description = pieces[skip]
+ else:
+ description = ""
+ self._validate_info(tag, info, line, lineno)
+ return tag, info, description
+
+def parse_hunks(text):
+ # return a list of tuples. Each is one of:
+ # ("raw", string) : non-API blocks
+ # ("api-json", dict) : API blocks
+ yield ("version", VERSION)
+ lines = text.splitlines(True)
+ line_number = 0
+ markdown_string = ""
+ while line_number < len(lines):
+ line = lines[line_number]
+ if line.startswith("<api"):
+ if len(markdown_string) > 0:
+ yield ("markdown", markdown_string)
+ markdown_string = ""
+ api, line_number = APIParser().parse(lines, line_number)
+ # this business with 'leftover' is a horrible thing to do,
+ # and exists only to collect the \n after the closing /api tag.
+ # It's not needed probably, except to help keep compatibility
+ # with the previous behaviour
+ leftover = lines[line_number].lstrip("</api>")
+ if len(leftover) > 0:
+ markdown_string += leftover
+ line_number = line_number + 1
+ yield ("api-json", api)
+ else:
+ markdown_string += line
+ line_number = line_number + 1
+ if len(markdown_string) > 0:
+ yield ("markdown", markdown_string)
+
+class TestRenderer:
+ # render docs for test purposes
+
+ def getm(self, d, key):
+ return d.get(key, "<MISSING>")
+
+ def join_lines(self, text):
+ return " ".join([line.strip() for line in text.split("\n")])
+
+ def render_prop(self, p):
+ s = "props[%s]: " % self.getm(p, "name")
+ pieces = []
+ for k in ("type", "description", "required", "default"):
+ if k in p:
+ pieces.append("%s=%s" % (k, self.join_lines(str(p[k]))))
+ return s + ", ".join(pieces)
+
+ def render_param(self, p):
+ pieces = []
+ for k in ("name", "type", "description", "required", "default"):
+ if k in p:
+ pieces.append("%s=%s" % (k, self.join_lines(str(p[k]))))
+ yield ", ".join(pieces)
+ for prop in p.get("props", []):
+ yield " " + self.render_prop(prop)
+
+ def render_method(self, method):
+ yield "name= %s" % self.getm(method, "name")
+ yield "type= %s" % self.getm(method, "type")
+ yield "description= %s" % self.getm(method, "description")
+ signature = method.get("signature")
+ if signature:
+ yield "signature= %s" % self.getm(method, "signature")
+ params = method.get("params", [])
+ if params:
+ yield "parameters:"
+ for p in params:
+ for pline in self.render_param(p):
+ yield " " + pline
+ r = method.get("returns", None)
+ if r:
+ yield "returns:"
+ if "type" in r:
+ yield " type= %s" % r["type"]
+ if "description" in r:
+ yield " description= %s" % self.join_lines(r["description"])
+ props = r.get("props", [])
+ for p in props:
+ yield " " + self.render_prop(p)
+
+ def format_api(self, api):
+ for mline in self.render_method(api):
+ yield mline
+ constructors = api.get("constructors", [])
+ if constructors:
+ yield "constructors:"
+ for m in constructors:
+ for mline in self.render_method(m):
+ yield " " + mline
+ methods = api.get("methods", [])
+ if methods:
+ yield "methods:"
+ for m in methods:
+ for mline in self.render_method(m):
+ yield " " + mline
+ properties = api.get("properties", [])
+ if properties:
+ yield "properties:"
+ for p in properties:
+ yield " " + self.render_prop(p)
+
+ def render_docs(self, docs_json, outf=sys.stdout):
+
+ for (t,data) in docs_json:
+ if t == "api-json":
+ for line in self.format_api(data):
+ line = line.rstrip("\n")
+ outf.write("API: " + line + "\n")
+ else:
+ for line in str(data).split("\n"):
+ outf.write("MD :" + line + "\n")
+
+def hunks_to_dict(docs_json):
+ exports = {}
+ for (t,data) in docs_json:
+ if t != "api-json":
+ continue
+ if data["name"]:
+ exports[data["name"]] = data
+ return exports
+
+if __name__ == "__main__":
+ json = False
+ if sys.argv[1] == "--json":
+ json = True
+ del sys.argv[1]
+ docs_text = open(sys.argv[1]).read()
+ docs_parsed = list(parse_hunks(docs_text))
+ if json:
+ import simplejson
+ print simplejson.dumps(docs_parsed, indent=2)
+ else:
+ TestRenderer().render_docs(docs_parsed)
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apirenderer.py b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apirenderer.py
new file mode 100644
index 0000000..36e46ef
--- /dev/null
+++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/apirenderer.py
@@ -0,0 +1,302 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys, os
+import markdown
+import apiparser
+
+# list of all the 'class' and 'id' attributes assigned to
+# <div> and <span> tags by the renderer.
+API_REFERENCE = 'api_reference'
+MODULE_API_DOCS_CLASS = 'module_api_docs'
+MODULE_API_DOCS_ID = '_module_api_docs'
+API_HEADER = 'api_header'
+API_NAME = 'api_name'
+API_COMPONENT_GROUP = 'api_component_group'
+API_COMPONENT = 'api_component'
+DATATYPE = 'datatype'
+RETURNS = 'returns'
+PARAMETER_SET = 'parameter_set'
+MODULE_DESCRIPTION = 'module_description'
+
+HTML_HEADER = '''
+<!DOCTYPE html>\n
+<html>\n
+<head>\n
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\n
+ <base target="_blank"/>\n
+ <link rel="stylesheet" type="text/css" media="all"\n
+ href="../../../css/base.css" />\n
+ <link rel="stylesheet" type="text/css" media="all"\n
+ href="../../../css/apidocs.css" />\n
+ <title>Add-on SDK Documentation</title>\n
+ <style type="text/css">\n
+ body {\n
+ border: 50px solid #FFFFFF;\n
+ }\n
+ </style>\n
+\n
+ <script type="text/javascript">\n
+ function rewrite_links() {\n
+ var images = document.getElementsByTagName("img");\n
+ for (var i = 0; i < images.length; i++) {\n
+ var before = images[i].src.split("packages/")[0];\n
+ var after = images[i].src.split("/docs")[1];\n
+ images[i].src = before + after;\n
+ }\n
+ }\n
+ </script>\n
+</head>\n
+\n
+<body onload = "rewrite_links()">\n'''
+
+HTML_FOOTER = '''
+</body>\n
+\n
+</html>\n'''
+
+def indent(text_in):
+ text_out = ''
+ lines = text_in.splitlines(True)
+ indentation_level = 0
+ indentation_depth = 2
+ for line in lines:
+ if (line.startswith('<div')):
+ text_out += ((' ' * indentation_depth) * indentation_level) + line
+ if not '</div>' in line:
+ indentation_level += 1
+ else:
+ if (line.startswith('</div>')):
+ indentation_level -= 1
+ text_out += ((' ' * indentation_depth) * indentation_level) + line
+ return text_out
+
+def tag_wrap_id(text, classname, id, tag = 'div'):
+ return ''.join(['\n<'+ tag + ' id="', id, '" class="', \
+ classname, '">\n', text + '\n</' + tag +'>\n'])
+
+def tag_wrap(text, classname, tag = 'div', inline = False):
+ if inline:
+ return ''.join(['\n<' + tag + ' class="', classname, '">', \
+ text, '</'+ tag + '>\n'])
+ else:
+ return ''.join(['\n<' + tag + ' class="', classname, '">', \
+ text, '\n</'+ tag + '>\n'])
+
+def tag_wrap_inline(text, classname, tag = 'div'):
+ return ''.join(['\n<' + tag + ' class="', classname, '">', \
+ text, '</'+ tag + '>\n'])
+
+def span_wrap(text, classname):
+ return ''.join(['<span class="', classname, '">', \
+ text, '</span>'])
+
+class API_Renderer(object):
+ def __init__(self, json, tag):
+ self.name = json.get('name', None)
+ self.tag = tag
+ self.description = json.get('description', '')
+ self.json = json
+
+ def render_name(self):
+ raise Exception('not implemented in this class')
+
+ def render_description(self):
+ return markdown.markdown(self.description)
+
+ def render_subcomponents(self):
+ raise Exception('not implemented in this class')
+
+ def get_tag(self):
+ return self.tag
+
+class Class_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+
+ def render_name(self):
+ return self.name
+
+ def render_subcomponents(self):
+ return render_object_contents(self.json, 'h5', 'h6')
+
+class Event_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.arguments_json = json.get('arguments', None)
+
+ def render_name(self):
+ return self.name
+
+ def render_subcomponents(self):
+ if not self.arguments_json:
+ return ''
+ text = ''.join([render_comp(Argument_Doc(argument_json, 'div')) \
+ for argument_json in self.arguments_json])
+ return tag_wrap(text, PARAMETER_SET)
+
+class Argument_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.datatype = json.get('datatype', None)
+
+ def render_name(self):
+ return span_wrap(self.datatype, DATATYPE)
+
+ def render_subcomponents(self):
+ return ''
+
+class Function_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.signature = json['signature']
+ self.returns = json.get('returns', None)
+ self.parameters_json = json.get('params', None)
+
+ def render_name(self):
+ return self.signature
+
+ def render_subcomponents(self):
+ return self._render_parameters() + self._render_returns()
+
+ def _render_parameters(self):
+ if not self.parameters_json:
+ return ''
+ text = ''.join([render_comp(Parameter_Doc(parameter_json, 'div')) \
+ for parameter_json in self.parameters_json])
+ return tag_wrap(text, PARAMETER_SET)
+
+ def _render_returns(self):
+ if not self.returns:
+ return ''
+ text = 'Returns: ' + span_wrap(self.returns['datatype'], DATATYPE)
+ text += markdown.markdown(self.returns['description'])
+ return tag_wrap(text, RETURNS)
+
+class Property_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.datatype = json.get('datatype', None)
+ self.required = json.get('required', True)
+ self.default = json.get('default', False)
+
+ def render_name(self):
+ rendered = self.name
+ if self.default:
+ rendered = rendered + " = " + self.default
+ if self.datatype:
+ rendered = rendered + ' : ' + span_wrap(self.datatype, DATATYPE)
+ if not self.required:
+ rendered = '[ ' + rendered + ' ]'
+ return rendered
+
+ def render_subcomponents(self):
+ return render_object_contents(self.json)
+
+class Parameter_Doc(Property_Doc):
+ def __init__(self, json, tag):
+ Property_Doc.__init__(self, json, tag)
+ self.properties_json = json.get('props', None)
+
+ def render_subcomponents(self):
+ if not self.properties_json:
+ return ''
+ text = ''.join([render_comp(Property_Doc(property_json, 'div')) \
+ for property_json in self.properties_json])
+ return text
+
+def render_object_contents(json, tag = 'div', comp_tag = 'div'):
+ ctors = json.get('constructors', None)
+ text = render_comp_group(ctors, 'Constructors', Function_Doc, tag, comp_tag)
+ methods = json.get('methods', None)
+ text += render_comp_group(methods, 'Methods', Function_Doc, tag, comp_tag)
+ properties = json.get('properties', None)
+ text += render_comp_group(properties, 'Properties', Property_Doc, tag, comp_tag)
+ events = json.get('events', None)
+ text += render_comp_group(events, 'Events', Event_Doc, tag, comp_tag)
+ return text
+
+def render_comp(component):
+ # a component is wrapped inside a single div marked 'API_COMPONENT'
+ # containing:
+ # 1) the component name, marked 'API_NAME'
+ text = tag_wrap(component.render_name(), API_NAME, component.get_tag(), True)
+ # 2) the component description
+ text += component.render_description()
+ # 3) the component contents
+ text += component.render_subcomponents()
+ return tag_wrap(text, API_COMPONENT)
+
+def render_comp_group(group, group_name, ctor, tag = 'div', comp_tag = 'div'):
+ if not group:
+ return ''
+ # component group is a list of components in a single div called
+ # 'API_COMPONENT_GROUP' containing:
+ # 1) a title for the group marked with 'API_HEADER'
+ text = tag_wrap(group_name, API_HEADER, tag, True)
+ # 2) each component
+ text += ''.join([render_comp(ctor(api, comp_tag)) for api in group])
+ return tag_wrap(text, API_COMPONENT_GROUP)
+
+def render_descriptions(descriptions_md):
+ text = ''.join([description_md for description_md in descriptions_md])
+ return tag_wrap(markdown.markdown(text), MODULE_DESCRIPTION)
+
+def render_api_reference(api_docs):
+ if (len(api_docs) == 0):
+ return ''
+ # at the top level api reference is in a single div marked 'API_REFERENCE',
+ # containing:
+ # 1) a title 'API Reference' marked with 'API_HEADER'
+ text = tag_wrap('API Reference', API_HEADER, 'h2', True)
+ # 2) a component group called 'Classes' containing any class elements
+ classes = [api for api in api_docs if api['type'] == 'class']
+ text += render_comp_group(classes, 'Classes', Class_Doc, 'h3', 'h4')
+ # 3) a component group called 'Functions' containing any global functions
+ functions = [api for api in api_docs if api['type'] == 'function']
+ text += render_comp_group(functions, 'Functions', Function_Doc, 'h3', 'h4')
+ # 4) a component group called 'Properties' containing any global properties
+ properties = [api for api in api_docs if api['type'] == 'property']
+ text += render_comp_group(properties, 'Properties', Property_Doc, 'h3', 'h4')
+ # 5) a component group called 'Events' containing any global events
+ events = [api for api in api_docs if api['type'] == 'event']
+ text += render_comp_group(events, 'Events', Event_Doc, 'h3', 'h4')
+ return tag_wrap(text, API_REFERENCE)
+
+# take the JSON output of apiparser
+# return the HTML DIV containing the rendered component
+def json_to_div(json, markdown_filename):
+ module_name, ext = os.path.splitext(os.path.basename(markdown_filename))
+ descriptions = [hunk[1] for hunk in json if hunk[0]=='markdown']
+ api_docs = [hunk[1] for hunk in json if hunk[0]=='api-json']
+ text = "<h1>" + module_name + "</h1>"
+ text += render_descriptions(descriptions)
+ text += render_api_reference(api_docs)
+ text = tag_wrap_id(text, MODULE_API_DOCS_CLASS, \
+ module_name + MODULE_API_DOCS_ID)
+ return text.encode('utf8')
+
+# take the JSON output of apiparser
+# return standalone HTML containing the rendered component
+def json_to_html(json, markdown_filename):
+ return indent(HTML_HEADER + \
+ json_to_div(json, markdown_filename) + HTML_FOOTER)
+
+# take the name of a Markdown file
+# return the HTML DIV containing the rendered component
+def md_to_div(markdown_filename):
+ markdown_contents = open(markdown_filename).read().decode('utf8')
+ json = list(apiparser.parse_hunks(markdown_contents))
+ return json_to_div(json, markdown_filename)
+
+# take the name of a Markdown file
+# return standalone HTML containing the rendered component
+def md_to_html(markdown_filename):
+ return indent(HTML_HEADER + md_to_div(markdown_filename) + HTML_FOOTER)
+
+if __name__ == '__main__':
+ if (len(sys.argv) == 0):
+ print 'Supply the name of a docs file to parse'
+ else:
+ print md_to_html(sys.argv[1])
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/generate.py b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/generate.py
new file mode 100644
index 0000000..789a8dd
--- /dev/null
+++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/generate.py
@@ -0,0 +1,283 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+import shutil
+import hashlib
+import tarfile
+import StringIO
+import HTMLParser
+import urlparse
+
+from cuddlefish import packaging
+from cuddlefish.docs import apiparser
+from cuddlefish.docs import apirenderer
+from cuddlefish.docs import webdocs
+import simplejson as json
+
+DIGEST = "status.md5"
+TGZ_FILENAME = "addon-sdk-docs.tgz"
+
+def get_sdk_docs_path(env_root):
+ return os.path.join(env_root, "doc")
+
+def get_base_url(env_root):
+ sdk_docs_path = get_sdk_docs_path(env_root).lstrip("/")
+ return "file://"+"/"+"/".join(sdk_docs_path.split(os.sep))+"/"
+
+def clean_generated_docs(docs_dir):
+ status_file = os.path.join(docs_dir, "status.md5")
+ if os.path.exists(status_file):
+ os.remove(status_file)
+ index_file = os.path.join(docs_dir, "index.html")
+ if os.path.exists(index_file):
+ os.remove(index_file)
+ dev_guide_dir = os.path.join(docs_dir, "dev-guide")
+ if os.path.exists(dev_guide_dir):
+ shutil.rmtree(dev_guide_dir)
+ api_doc_dir = os.path.join(docs_dir, "packages")
+ if os.path.exists(api_doc_dir):
+ shutil.rmtree(api_doc_dir)
+
+def generate_static_docs(env_root):
+ clean_generated_docs(get_sdk_docs_path(env_root))
+ generate_docs(env_root, stdout=StringIO.StringIO())
+ tgz = tarfile.open(TGZ_FILENAME, 'w:gz')
+ tgz.add(get_sdk_docs_path(env_root), "doc")
+ tgz.close()
+ return TGZ_FILENAME
+
+def generate_local_docs(env_root):
+ return generate_docs(env_root, get_base_url(env_root))
+
+def generate_named_file(env_root, filename):
+ web_docs = webdocs.WebDocs(env_root, get_base_url(env_root))
+ # next, generate api doc or guide doc
+ abs_path = os.path.abspath(filename)
+ if abs_path.startswith(os.path.join(env_root, 'packages')):
+ doc_html, dest_dir, filename = generate_api_doc(env_root, abs_path, web_docs)
+ write_file(env_root, doc_html, dest_dir, filename)
+ elif abs_path.startswith(os.path.join(get_sdk_docs_path(env_root), 'dev-guide-source')):
+ doc_html, dest_dir, filename = generate_guide_doc(env_root, abs_path, web_docs)
+ write_file(env_root, doc_html, dest_dir, filename, False)
+ else:
+ raise ValueError("Not a valid path to a documentation file")
+
+def generate_docs(env_root, base_url=None, stdout=sys.stdout):
+ docs_dir = get_sdk_docs_path(env_root)
+ # if the generated docs don't exist, generate everything
+ if not os.path.exists(os.path.join(docs_dir, "dev-guide")):
+ print >>stdout, "Generating documentation..."
+ generate_docs_from_scratch(env_root, base_url)
+ current_status = calculate_current_status(env_root)
+ open(os.path.join(docs_dir, DIGEST), "w").write(current_status)
+ else:
+ current_status = calculate_current_status(env_root)
+ previous_status_file = os.path.join(docs_dir, DIGEST)
+ docs_are_up_to_date = False
+ if os.path.exists(previous_status_file):
+ docs_are_up_to_date = current_status == open(previous_status_file, "r").read()
+ # if the docs are not up to date, generate everything
+ if not docs_are_up_to_date:
+ print >>stdout, "Regenerating documentation..."
+ generate_docs_from_scratch(env_root, base_url)
+ open(os.path.join(docs_dir, DIGEST), "w").write(current_status)
+ return get_base_url(env_root) + "index.html"
+
+# this function builds a hash of the name and last modification date of:
+# * every file in "packages" which ends in ".md"
+# * every file in "static-files" which does not start with "."
+def calculate_current_status(env_root):
+ docs_dir = get_sdk_docs_path(env_root)
+ current_status = hashlib.md5()
+ package_src_dir = os.path.join(env_root, "packages")
+ for (dirpath, dirnames, filenames) in os.walk(package_src_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ guide_src_dir = os.path.join(docs_dir, "dev-guide-source")
+ for (dirpath, dirnames, filenames) in os.walk(guide_src_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ base_html_file = os.path.join(docs_dir, "static-files", "base.html")
+ current_status.update(base_html_file)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, base_html_file))))
+ return current_status.digest()
+
+def generate_docs_from_scratch(env_root, base_url):
+ docs_dir = get_sdk_docs_path(env_root)
+ web_docs = webdocs.WebDocs(env_root, base_url)
+ must_rewrite_links = True
+ if base_url:
+ must_rewrite_links = False
+ clean_generated_docs(docs_dir)
+
+ # py2.5 doesn't have ignore=, so we delete tempfiles afterwards. If we
+ # required >=py2.6, we could use ignore=shutil.ignore_patterns("*~")
+ for (dirpath, dirnames, filenames) in os.walk(docs_dir):
+ for n in filenames:
+ if n.endswith("~"):
+ os.unlink(os.path.join(dirpath, n))
+
+ # generate api docs from all packages
+ os.mkdir(os.path.join(docs_dir, "packages"))
+ # create the index file and save that
+ pkg_cfg = packaging.build_pkg_cfg(env_root)
+ index = json.dumps(packaging.build_pkg_index(pkg_cfg))
+ index_path = os.path.join(docs_dir, "packages", 'index.json')
+ open(index_path, 'w').write(index)
+
+ # for each package, generate its docs
+ for pkg_name, pkg in pkg_cfg['packages'].items():
+ src_dir = pkg.root_dir
+ package_dirname = os.path.basename(src_dir)
+ dest_dir = os.path.join(docs_dir, "packages", package_dirname)
+ os.mkdir(dest_dir)
+
+ src_readme = os.path.join(src_dir, "README.md")
+ if os.path.exists(src_readme):
+ shutil.copyfile(src_readme,
+ os.path.join(dest_dir, "README.md"))
+
+ # create the package page
+ package_filename = os.path.join(dest_dir, pkg_name + ".html")
+ if not os.path.exists(package_filename):
+ package_doc_html = web_docs.create_package_page(pkg_name)
+ replace_file(env_root, package_filename, package_doc_html, must_rewrite_links)
+
+ # generate all the API docs
+ docs_src_dir = os.path.join(src_dir, "doc")
+ if os.path.isdir(os.path.join(src_dir, "docs")):
+ docs_src_dir = os.path.join(src_dir, "docs")
+ generate_file_tree(env_root, docs_src_dir, web_docs, generate_api_doc, must_rewrite_links)
+
+ # generate all the guide docs
+ dev_guide_src = os.path.join(docs_dir, "dev-guide-source")
+ generate_file_tree(env_root, dev_guide_src, web_docs, generate_guide_doc, must_rewrite_links)
+
+ # make /md/dev-guide/welcome.html the top level index file
+ doc_html, dest_dir, filename = generate_guide_doc(env_root, os.path.join(docs_dir, 'dev-guide-source', 'welcome.md'), web_docs)
+ write_file(env_root, doc_html, docs_dir, 'index', False)
+
+def generate_file_tree(env_root, src_dir, web_docs, generate_file, must_rewrite_links):
+ for (dirpath, dirnames, filenames) in os.walk(src_dir):
+ assert dirpath.startswith(src_dir) # what is this for??
+ for filename in filenames:
+ if filename.endswith("~"):
+ continue
+ src_path = os.path.join(dirpath, filename)
+ if src_path.endswith(".md"):
+ # write the standalone HTML files
+ doc_html, dest_dir, filename = generate_file(env_root, src_path, web_docs)
+ write_file(env_root, doc_html, dest_dir, filename, must_rewrite_links)
+
+def generate_api_doc(env_root, src_dir, web_docs):
+ doc_html = web_docs.create_module_page(src_dir)
+ dest_dir, filename = get_api_doc_dest_path(env_root, src_dir)
+ return doc_html, dest_dir, filename
+
+def generate_guide_doc(env_root, src_dir, web_docs):
+ doc_html = web_docs.create_guide_page(src_dir)
+ dest_dir, filename = get_guide_doc_dest_path(env_root, src_dir)
+ return doc_html, dest_dir, filename
+
+def write_file(env_root, doc_html, dest_dir, filename, must_rewrite_links):
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir)
+ dest_path_html = os.path.join(dest_dir, filename) + ".html"
+ replace_file(env_root, dest_path_html, doc_html, must_rewrite_links)
+ return dest_path_html
+
+def replace_file(env_root, dest_path, file_contents, must_rewrite_links):
+ if os.path.exists(dest_path):
+ os.remove(dest_path)
+ # before we copy the final version, we'll rewrite the links
+ # I'll do this last, just because we know definitely what the dest_path is at this point
+ if must_rewrite_links and dest_path.endswith(".html"):
+ file_contents = rewrite_links(env_root, file_contents, dest_path)
+ open(dest_path, "w").write(file_contents)
+
+def rewrite_links(env_root, page, dest_path):
+ dest_path_depth = len(dest_path.split(os.sep)) -1 # because dest_path includes filename
+ docs_root_depth = len(get_sdk_docs_path(env_root).split(os.sep))
+ relative_depth = dest_path_depth - docs_root_depth
+ linkRewriter = LinkRewriter("../" * relative_depth)
+ return linkRewriter.rewrite_links(page)
+
+# Given the full path to an API source file, and the root,
+# return a tuple of:
+# 1) the full path to the corresponding HTML file, without the filename
+# 2) the filename without the extension
+def get_guide_doc_dest_path(env_root, src_dir):
+ src_dir_relative = src_dir[len(os.path.join(get_sdk_docs_path(env_root), "dev-guide-source")) + 1:]
+ return os.path.split(os.path.join(get_sdk_docs_path(env_root), "dev-guide", src_dir_relative)[:-3])
+
+# Given the full path to a dev guide source file, and the root,
+# return a tuple of:
+# 1) the full path to the corresponding HTML file, without the filename
+# 2) the filename without the extension
+def get_api_doc_dest_path(env_root, src_dir):
+ src_dir_relative = src_dir[len(env_root) + 1:]
+ return os.path.split(os.path.join(get_sdk_docs_path(env_root), src_dir_relative)[:-3])
+
+class LinkRewriter(HTMLParser.HTMLParser):
+ def __init__(self, link_prefix):
+ HTMLParser.HTMLParser.__init__(self)
+ self.stack = []
+ self.link_prefix = link_prefix
+
+ def rewrite_links(self, page):
+ self.feed(page)
+ self.close()
+ page = ''.join(self.stack)
+ self.stack = []
+ return page
+
+ def handle_decl(self, decl):
+ self.stack.append("<!" + decl + ">")
+
+ def handle_comment(self, decl):
+ self.stack.append("<!--" + decl + "-->")
+
+ def handle_starttag(self, tag, attrs):
+ attrs = dict(attrs)
+ href = attrs.get('href', '')
+ if href:
+ parsed = urlparse.urlparse(href)
+ if not parsed.scheme:
+ attrs['href'] = self.link_prefix + href
+ src = attrs.get('src', '')
+ if src:
+ parsed = urlparse.urlparse(src)
+ if not parsed.scheme:
+ attrs['src'] = self.link_prefix + src
+ self.stack.append(self.__html_start_tag(tag, attrs))
+
+ def handle_endtag(self, tag):
+ self.stack.append(self.__html_end_tag(tag))
+
+ def handle_startendtag(self, tag, attrs):
+ self.stack.append(self.__html_startend_tag(tag, attrs))
+
+ def handle_data(self, data):
+ self.stack.append(data)
+
+ def __html_start_tag(self, tag, attrs):
+ return '<%s%s>' % (tag, self.__html_attrs(attrs))
+
+ def __html_startend_tag(self, tag, attrs):
+ return '<%s%s/>' % (tag, self.__html_attrs(attrs))
+
+ def __html_end_tag(self, tag):
+ return '</%s>' % (tag)
+
+ def __html_attrs(self, attrs):
+ _attrs = ''
+ if attrs:
+ _attrs = ' %s' % (' '.join([('%s="%s"' % (k,v)) for k,v in dict(attrs).iteritems()]))
+ return _attrs
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/renderapi.readme.md b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/renderapi.readme.md
new file mode 100644
index 0000000..627c2a6
--- /dev/null
+++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/renderapi.readme.md
@@ -0,0 +1,210 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+This document describes the structure of the HTML generated by the renderapi.py
+tool, both for use in the API docs shown by "cfx docs" and as exported by
+"cfx sdocs". The particular HTML id and class attributes embedded in the files,
+as well as their organization, represent the interface between the tool and any
+front-end code wanting to style the docs in some particular way.
+
+renderapi generates two sorts of files:
+
+- a file called "<module-name>.div": this is the contents of the parsed
+Markdown file rendered inside a well-defined DIV tag
+
+- a file called "<module-name>.html": this is the DIV from above inserted into
+a simple HTML template that references a sample CSS file which styles the
+contents of the DIV. This CSS file is the same as the one used by the SDK
+itself.
+
+DIV tags
+--------
+The following class and id attributes are used in the DIV:
+
+renderapi uses a number of class attributes and a single id attribute in the DIV:
+
+id attribute <module_name>"_module_api_docs"
+class attribute "api_reference"
+class attribute "module_api_docs"
+class attribute "api_header"
+class attribute "api_name"
+class attribute "api_component_group"
+class attribute "api_component"
+class attribute "datatype"
+class attribute "returns"
+class attribute "parameter_set"
+class attribute "module_description"
+
+DIV structure
+-------------
+The top level DIV is marked with the id attribute and the "module_api_docs" class
+attribute:
+
+ <div id='tabs_module_api_docs' class='module_api_docs'>
+ //module doc contents
+ </div>
+
+
+Inside this:
+
+- the first item is an <h1> heading containing the name of the module:
+
+- all "markdown" hunks (that is, all descriptive text not occurring
+inside <api></api> tags) are rendered inside a DIV marked with the
+"module-description" class attribute
+
+- all <api></api> content is rendered, enclosed in a single tag marked
+with the "api_reference" class attribute:
+
+ <div id='tabs_module_api_docs' class='module_api_docs'>
+ <div class='module_description'>
+ //descriptions
+ </div>
+ <div class='api_reference'>
+ //api reference
+ </div>
+ </div>
+
+If there is no <api></api> content, then the "api-reference" section is absent.
+
+### API Reference structure ###
+
+The first item in API reference is an <h2> heading title marked with the
+"api_header" attribute. This might have the text content "API Reference"
+(but you should not rely on that):
+
+ <div class='api_reference'>
+
+ <h2 class='api_header'>API Reference</h2>
+
+ //api contents
+
+ </div>
+
+After the title come one or more component groups.
+
+#### Component Group ####
+
+A component group is marked with the "api_component_group" attribute. The
+component group is a collection of some sort of component: for example, a group
+of classes, a group of functions, or a group of events.
+
+Each component group starts off with a header marked with the
+"api_header" attribute and is followed by one or more sections marked with the
+"api_component" attribute.
+At the top level (that is, when they are directly under the "API Reference"
+heading), the "api_header" items are <h3> headings, otherwise they are divs.
+
+ <div class='api_reference'>
+
+ <h2 class='api_header'>API Reference</h2>
+
+ <div class='api_component_group'>
+
+ <h3 class='api_header'>Classes</h3>
+
+ <div class='api_component'>
+ // the first class
+ </div>
+
+ <div class='api_component'>
+ // another class
+ </div>
+
+ </div>
+
+ <div class='api_component_group'>
+ //some different components
+
+ <h3 class='api_header'>Functions</h3>
+
+ <div class='api_component'>
+ the first function
+ </div>
+
+ <div class='api_component'>
+ another function
+ </div>
+
+ </div>
+
+ </div>
+
+#### Component ####
+
+API components represent actual objects in the API like classes, functions,
+properties and events.
+
+Each component starts with a section marked with the
+"api_name" tag, which includes the name of the component in the API: for
+example "postMessage(message)".
+
+Components at the top level (i.e., directly under h3 headings) are <h4>
+headings, otherwise they are divs.
+
+After the name, the component's contents are listed. Different sorts of
+components may have different sorts of contents: for example, a function might
+have parameters. If the component is composite then it may contain its own
+component group. For example, a class may contain methods and properties,
+which might be grouped together.
+
+ <div class='api_component'>
+
+ <h4 class='api_name'>Panel</h4>
+
+ <div class='api_component_group'>
+
+ <div class='api_header'>
+ Methods
+ </div>
+
+ <div class='api_component'>
+ show()
+ </div>
+
+ </div>
+
+ </div>
+
+Other attributes
+-----------------------------
+
+### Datatype ###
+All primitive data types, like "string" and "number", are marked with the
+"datatype" class attribute:
+
+ <div class="api_component">
+
+ <div class="api_name">
+ label : <span class="datatype">string</span>
+ </div>
+
+ <p>A required string description of the widget used for accessibility,
+ title bars, and error reporting.</p>
+
+ </div>
+
+### Returns ###
+
+Functions mark return values with the "returns" class attribute.
+
+ <div class="api_component">
+
+ <div class="api_name">
+ get()
+ </div>
+
+ Make a `GET` request.
+
+ <div class="returns">
+ Returns: <span class="datatype">Request</span>
+ </div>
+
+ </div>
+
+### Parameter_set ###
+
+Functions that take parameters mark them with the parameter_set class
+attribute.
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/webdocs.py b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/webdocs.py
new file mode 100644
index 0000000..44f68a5
--- /dev/null
+++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/docs/webdocs.py
@@ -0,0 +1,198 @@
+import os, re, errno
+import markdown
+import cgi
+
+from cuddlefish import packaging
+from cuddlefish.docs import apirenderer
+from cuddlefish._version import get_versions
+
+INDEX_PAGE = '/doc/static-files/base.html'
+BASE_URL_INSERTION_POINT = '<base '
+VERSION_INSERTION_POINT = '<div id="version">'
+HIGH_LEVEL_PACKAGE_SUMMARIES = '<li id="high-level-package-summaries">'
+LOW_LEVEL_PACKAGE_SUMMARIES = '<li id="low-level-package-summaries">'
+CONTENT_ID = '<div id="main-content">'
+TITLE_ID = '<title>'
+DEFAULT_TITLE = 'Add-on SDK Documentation'
+
+def get_modules(modules_json):
+ modules = []
+ for name in modules_json:
+ typ = modules_json[name][0]
+ if typ == "directory":
+ sub_modules = get_modules(modules_json[name][1])
+ for sub_module in sub_modules:
+ modules.append([name, sub_module[0]])
+ elif typ == "file":
+ if not name.startswith(".") and name.endswith('.js'):
+ modules.append([name[:-3]])
+ return modules
+
+def get_documented_modules(package_name, modules_json, doc_path):
+ modules = get_modules(modules_json)
+ documented_modules = []
+ for module in modules:
+ path = os.path.join(*module)
+ if module_md_exists(doc_path, path):
+ documented_modules.append(module)
+ if package_name == "addon-kit":
+ # hack for bug 664001, self-maker.js is in api-utils, self.md is in
+ # addon-kit. Real fix is for this function to look for all .md files,
+ # not for .js files with matching .md file in the same package.
+ documented_modules.append(["self"])
+ return documented_modules
+
+def module_md_exists(root, module_name):
+ module_md_path = os.path.join(root, module_name + '.md')
+ return os.path.exists(module_md_path)
+
+def tag_wrap(text, tag, attributes={}):
+ result = '\n<' + tag
+ for name in attributes.keys():
+ result += ' ' + name + '=' + '"' + attributes[name] + '"'
+ result +='>' + text + '</'+ tag + '>\n'
+ return result
+
+def is_high_level(package_json):
+ return not is_low_level(package_json)
+
+def is_low_level(package_json):
+ return 'jetpack-low-level' in package_json.get('keywords', [])
+
+def insert_after(target, insertion_point_id, text_to_insert):
+ insertion_point = target.find(insertion_point_id) + len(insertion_point_id)
+ return target[:insertion_point] + text_to_insert + target[insertion_point:]
+
+class WebDocs(object):
+ def __init__(self, root, base_url = None):
+ self.root = root
+ self.pkg_cfg = packaging.build_pkg_cfg(root)
+ self.packages_json = packaging.build_pkg_index(self.pkg_cfg)
+ self.base_page = self._create_base_page(root, base_url)
+
+ def create_guide_page(self, path):
+ path, ext = os.path.splitext(path)
+ md_path = path + '.md'
+ md_content = unicode(open(md_path, 'r').read(), 'utf8')
+ guide_content = markdown.markdown(md_content)
+ return self._create_page(guide_content)
+
+ def create_module_page(self, path):
+ path, ext = os.path.splitext(path)
+ md_path = path + '.md'
+ module_content = apirenderer.md_to_div(md_path)
+ return self._create_page(module_content)
+
+ def create_package_page(self, package_name):
+ package_content = self._create_package_detail(package_name)
+ return self._create_page(package_content)
+
+ def _create_page(self, page_content):
+ page = self._insert_title(self.base_page, page_content)
+ page = insert_after(page, CONTENT_ID, page_content)
+ return page.encode('utf8')
+
+ def _create_module_list(self, package_json):
+ package_name = package_json['name']
+ libs = package_json['files'][1]['lib'][1]
+ doc_path = package_json.get('doc', None)
+ if not doc_path:
+ return ''
+ modules = get_documented_modules(package_name, libs, doc_path)
+ modules.sort()
+ module_items = ''
+ relative_doc_path = doc_path[len(self.root) + 1:]
+ relative_doc_URL = "/".join(relative_doc_path.split(os.sep))
+ for module in modules:
+ module_link = tag_wrap('/'.join(module), 'a', \
+ {'href': relative_doc_URL + '/' + '/'.join(module) + '.html'})
+ module_items += tag_wrap(module_link, 'li', {'class':'module'})
+ return tag_wrap(module_items, 'ul', {'class':'modules'})
+
+ def _create_package_summaries(self, packages_json, include):
+ packages = ''
+ for package_name in packages_json.keys():
+ package_json = packages_json[package_name]
+ if not include(package_json):
+ continue
+ package_path = self.pkg_cfg["packages"][package_name]["root_dir"]
+ package_directory = package_path[len(self.root) + 1:]
+ package_directory = "/".join(package_directory.split(os.sep))
+ package_link = tag_wrap(package_name, 'a', {'href': \
+ package_directory + "/" \
+ + package_name + '.html'})
+ text = tag_wrap(package_link, 'h4')
+ text += self._create_module_list(package_json)
+ packages += tag_wrap(text, 'div', {'class':'package-summary', \
+ 'style':'display: block;'})
+ return packages
+
+ def _create_base_page(self, root, base_url):
+ base_page = unicode(open(root + INDEX_PAGE, 'r').read(), 'utf8')
+ if base_url:
+ base_tag = 'href="' + base_url + '"'
+ base_page = insert_after(base_page, BASE_URL_INSERTION_POINT, base_tag)
+ sdk_version = get_versions()["version"]
+ base_page = insert_after(base_page, VERSION_INSERTION_POINT, "Version " + sdk_version)
+ high_level_summaries = \
+ self._create_package_summaries(self.packages_json, is_high_level)
+ base_page = insert_after(base_page, \
+ HIGH_LEVEL_PACKAGE_SUMMARIES, high_level_summaries)
+ low_level_summaries = \
+ self._create_package_summaries(self.packages_json, is_low_level)
+ base_page = insert_after(base_page, \
+ LOW_LEVEL_PACKAGE_SUMMARIES, low_level_summaries)
+ return base_page
+
+ def _create_package_detail_row(self, field_value, \
+ field_descriptor, field_name):
+ meta = tag_wrap(tag_wrap(field_descriptor, 'span', \
+ {'class':'meta-header'}), 'td')
+ value = tag_wrap(tag_wrap(field_value, 'span', \
+ {'class':field_name}), 'td')
+ return tag_wrap(meta + value, 'tr')
+
+ def _create_package_detail_table(self, package_json):
+ table_contents = ''
+ if package_json.get('author', None):
+ table_contents += self._create_package_detail_row(\
+ cgi.escape(package_json['author']), 'Author', 'author')
+ if package_json.get('version', None):
+ table_contents += self._create_package_detail_row(\
+ package_json['version'], 'Version', 'version')
+ if package_json.get('license', None):
+ table_contents += self._create_package_detail_row(\
+ package_json['license'], 'License', 'license')
+ if package_json.get('dependencies', None):
+ table_contents += self._create_package_detail_row(\
+ ', '.join(package_json['dependencies']), \
+ 'Dependencies', 'dependencies')
+ table_contents += self._create_package_detail_row(\
+ self._create_module_list(package_json), 'Modules', 'modules')
+ return tag_wrap(tag_wrap(table_contents, 'tbody'), 'table', \
+ {'class':'meta-table'})
+
+ def _create_package_detail(self, package_name):
+ package_json = self.packages_json.get(package_name, None)
+ if not package_json:
+ raise IOError(errno.ENOENT, 'Package not found')
+ # pieces of the package detail: 1) title, 2) table, 3) description
+ package_title = tag_wrap(package_name, 'h1')
+ table = self._create_package_detail_table(package_json)
+ description = ''
+ if package_json.get('readme', None):
+ description += tag_wrap(tag_wrap(\
+ markdown.markdown(\
+ package_json['readme']), 'p'), 'div', {'class':'docs'})
+ return tag_wrap(package_title + table + description, 'div', \
+ {'class':'package-detail'})
+
+ def _insert_title(self, target, content):
+ match = re.search('<h1>.*</h1>', content)
+ if match:
+ title = match.group(0)[len('<h1>'):-len('</h1>')] + ' - ' + \
+ DEFAULT_TITLE
+ else:
+ title = DEFAULT_TITLE
+ target = insert_after(target, TITLE_ID, title)
+ return target