diff options
Diffstat (limited to 'tools/addon-sdk-1.12/python-lib/cuddlefish/docs')
8 files changed, 1415 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py new file mode 100644 index 0000000..5501cd4 --- /dev/null +++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py @@ -0,0 +1,4 @@ +# 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/. + diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py new file mode 100644 index 0000000..b6ccf22 --- /dev/null +++ b/tools/addon-sdk-1.12/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.12/python-lib/cuddlefish/docs/apirenderer.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apirenderer.py new file mode 100644 index 0000000..36e46ef --- /dev/null +++ b/tools/addon-sdk-1.12/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.12/python-lib/cuddlefish/docs/documentationitem.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py new file mode 100755 index 0000000..c72133a --- /dev/null +++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py @@ -0,0 +1,124 @@ +# 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, re + +class DocumentationItemInfo(object): + def __init__(self, env_root, md_path, filename): + self.env_root = env_root + # full path to MD file, without filename + self.source_path = md_path + # MD filename + self.source_filename = filename + + def env_root(self): + return self.env_root + + def source_path(self): + return self.source_path + + def source_filename(self): + return self.source_filename + + def base_filename(self): + return self.source_filename[:-len(".md")] + + def source_path_and_filename(self): + return os.sep.join([self.source_path, self.source_filename]) + + def source_path_relative_from_env_root(self): + return self.source_path[len(self.env_root) + 1:] + +class DevGuideItemInfo(DocumentationItemInfo): + def __init__(self, env_root, devguide_root, md_path, filename): + DocumentationItemInfo.__init__(self, env_root, md_path, filename) + self.devguide_root = devguide_root + + def source_path_relative_from_devguide_root(self): + return self.source_path[len(self.devguide_root) + 1:] + + def destination_path(self): + root_pieces = self.devguide_root.split(os.sep) + root_pieces[-1] = "dev-guide" + return os.sep.join([os.sep.join(root_pieces), self.source_path_relative_from_devguide_root()]) + +class ModuleInfo(DocumentationItemInfo): + def __init__(self, env_root, module_root, md_path, filename): + DocumentationItemInfo.__init__(self, env_root, md_path, filename) + self.module_root = module_root + + def js_module_path(self): + return os.path.join(self.env_root, "lib", self.source_path_relative_from_module_root(), self.source_filename[:-len(".md")] + ".js") + + def source_path_relative_from_module_root(self): + return self.source_path[len(self.module_root) + 1:] + + def destination_path(self): + if self.level() == "third-party": + return os.sep.join([self.env_root, "doc", "modules", "packages"]) + root_pieces = self.module_root.split(os.sep) + root_pieces[-1] = "modules" + relative_pieces = self.source_path_relative_from_module_root().split(os.sep) + return os.sep.join(root_pieces + relative_pieces) + + def relative_url(self): + if self.level() == "third-party": + relative_pieces = ["packages"] + else: + relative_pieces = self.source_path_relative_from_module_root().split(os.sep) + return "/".join(relative_pieces) + "/" + self.base_filename() + ".html" + + def name(self): + if os.sep.join([self.module_root, "sdk"]) == self.source_path or self.level() == "third-party": + return self.source_filename[:-3] + else: + path_from_root_pieces = self.source_path_relative_from_module_root().split(os.sep) + return "/".join(["/".join(path_from_root_pieces[1:]), self.source_filename[:-len(".md")]]) + + def level(self): + if self.source_path_relative_from_env_root().startswith("packages"): + return "third-party" + else: + if os.sep.join([self.module_root, "sdk"]) == self.source_path: + return "high" + else: + return "low" + +def get_modules_in_package(env_root, package_docs_dir, module_list, ignore_files_in_root): + for (dirpath, dirnames, filenames) in os.walk(package_docs_dir): + for filename in filenames: + # ignore files in the root + if ignore_files_in_root and package_docs_dir == dirpath: + continue + if filename.endswith(".md"): + module_list.append(ModuleInfo(env_root, package_docs_dir, dirpath, filename)) + +def get_module_list(env_root): + module_list = [] + # get the built-in modules + module_root = os.sep.join([env_root, "doc", "module-source"]) + get_modules_in_package(env_root, module_root, module_list, True) + # get the third-party modules + packages_root = os.sep.join([env_root, "packages"]) + if os.path.exists(packages_root): + for entry in os.listdir(packages_root): + if os.path.isdir(os.sep.join([packages_root, entry])): + package_docs = os.sep.join([packages_root, entry, "docs"]) + if os.path.exists(package_docs): + get_modules_in_package(env_root, package_docs, module_list, False) + module_list.sort(key=lambda x: x.name()) + return module_list + +def get_devguide_list(env_root): + devguide_list = [] + devguide_root = os.sep.join([env_root, "doc", "dev-guide-source"]) + for (dirpath, dirnames, filenames) in os.walk(devguide_root): + for filename in filenames: + if filename.endswith(".md"): + devguide_list.append(DevGuideItemInfo(env_root, devguide_root, dirpath, filename)) + return devguide_list + +if __name__ == "__main__": + module_list = get_module_list(sys.argv[1]) + print [module_info.name for module_info in module_list] diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py new file mode 100755 index 0000000..b498f00 --- /dev/null +++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py @@ -0,0 +1,199 @@ +# 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 + +from cuddlefish._version import get_versions +from cuddlefish.docs import apiparser +from cuddlefish.docs import apirenderer +from cuddlefish.docs import webdocs +from documentationitem import get_module_list +from documentationitem import get_devguide_list +from documentationitem import ModuleInfo +from documentationitem import DevGuideItemInfo +from linkrewriter import rewrite_links +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, "modules") + if os.path.exists(api_doc_dir): + shutil.rmtree(api_doc_dir) + +def generate_static_docs(env_root, override_version=get_versions()["version"]): + clean_generated_docs(get_sdk_docs_path(env_root)) + generate_docs(env_root, override_version, 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_versions()["version"], get_base_url(env_root)) + +def generate_named_file(env_root, filename_and_path): + web_docs = webdocs.WebDocs(env_root, get_versions()["version"], get_base_url(env_root)) + abs_path = os.path.abspath(filename_and_path) + path, filename = os.path.split(abs_path) + if abs_path.startswith(os.path.join(env_root, 'doc', 'module-source')): + module_root = os.sep.join([env_root, "doc", "module-source"]) + module_info = ModuleInfo(module_root, path, filename) + write_module_doc(env_root, web_docs, module_info, False) + elif abs_path.startswith(os.path.join(get_sdk_docs_path(env_root), 'dev-guide-source')): + devguide_root = os.sep.join([env_root, "doc", "dev-guide-source"]) + devguideitem_info = DevGuideItemInfo(devguide_root, path, filename) + write_devguide_doc(env_root, web_docs, devguideitem_info, False) + else: + raise ValueError("Not a valid path to a documentation file") + +def generate_docs(env_root, version=get_versions()["version"], 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, version, 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, version, 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 "doc/sdk" which ends in ".md" +# * every file in "doc/dev-guide-source" which ends in ".md" +# * every file in "doc/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() + module_src_dir = os.path.join(env_root, "doc", "module-source") + for (dirpath, dirnames, filenames) in os.walk(module_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)))) + package_dir = os.path.join(env_root, "packages") + for (dirpath, dirnames, filenames) in os.walk(package_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, version, base_url): + docs_dir = get_sdk_docs_path(env_root) + web_docs = webdocs.WebDocs(env_root, version, 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 for all modules + if not os.path.exists(os.path.join(docs_dir, "modules")): + os.mkdir(os.path.join(docs_dir, "modules")) + module_root = os.sep.join([env_root, "doc", "module-source"]) + module_list = get_module_list(env_root) + [write_module_doc(env_root, web_docs, module_info, must_rewrite_links) for module_info in module_list] + + # generate third-party module index + third_party_index_file = os.sep.join([env_root, "doc", "module-source", "third-party-modules.md"]) + third_party_module_list = [module_info for module_info in module_list if module_info.level() == "third-party"] + write_module_index(env_root, web_docs, third_party_index_file, third_party_module_list, must_rewrite_links) + + + # generate high-level module index + high_level_index_file = os.sep.join([env_root, "doc", "module-source", "high-level-modules.md"]) + high_level_module_list = [module_info for module_info in module_list if module_info.level() == "high"] + write_module_index(env_root, web_docs, high_level_index_file, high_level_module_list, must_rewrite_links) + + # generate low-level module index + low_level_index_file = os.sep.join([env_root, "doc", "module-source", "low-level-modules.md"]) + low_level_module_list = [module_info for module_info in module_list if module_info.level() == "low"] + write_module_index(env_root, web_docs, low_level_index_file, low_level_module_list, must_rewrite_links) + + # generate dev-guide docs + devguide_list = get_devguide_list(env_root) + [write_devguide_doc(env_root, web_docs, devguide_info, must_rewrite_links) for devguide_info in devguide_list] + + # make /md/dev-guide/welcome.html the top level index file + doc_html = web_docs.create_guide_page(os.path.join(docs_dir, 'dev-guide-source', 'index.md')) + write_file(env_root, doc_html, docs_dir, 'index', False) + +def write_module_index(env_root, web_docs, source_file, module_list, must_rewrite_links): + doc_html = web_docs.create_module_index(source_file, module_list) + base_filename, extension = os.path.splitext(os.path.basename(source_file)) + destination_path = os.sep.join([env_root, "doc", "modules"]) + write_file(env_root, doc_html, destination_path, base_filename, must_rewrite_links) + +def write_module_doc(env_root, web_docs, module_info, must_rewrite_links): + doc_html = web_docs.create_module_page(module_info.source_path_and_filename()) + write_file(env_root, doc_html, module_info.destination_path(), module_info.base_filename(), must_rewrite_links) + +def write_devguide_doc(env_root, web_docs, devguide_info, must_rewrite_links): + doc_html = web_docs.create_guide_page(devguide_info.source_path_and_filename()) + write_file(env_root, doc_html, devguide_info.destination_path(), devguide_info.base_filename(), must_rewrite_links) + +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, get_sdk_docs_path(env_root), file_contents, dest_path) + open(dest_path, "w").write(file_contents) + diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py new file mode 100644 index 0000000..11b0f11 --- /dev/null +++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py @@ -0,0 +1,78 @@ +# 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 HTMLParser +import urlparse + +def rewrite_links(env_root, sdk_docs_path, page, dest_path): + dest_path_depth = len(dest_path.split(os.sep)) -1 # because dest_path includes filename + docs_root_depth = len(sdk_docs_path.split(os.sep)) + relative_depth = dest_path_depth - docs_root_depth + linkRewriter = LinkRewriter("../" * relative_depth) + return linkRewriter.rewrite_links(page) + +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): + self.stack.append(self.__html_start_tag(tag, self._rewrite_link(attrs))) + + def handle_entityref(self, name): + self.stack.append("&" + name + ";") + + 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, self._rewrite_link(attrs))) + + def _update_attribute(self, attr_name, attrs): + attr_value = attrs.get(attr_name, '') + if attr_value: + parsed = urlparse.urlparse(attr_value) + if not parsed.scheme: + attrs[attr_name] = self.link_prefix + attr_value + + def _rewrite_link(self, attrs): + attrs = dict(attrs) + self._update_attribute('href', attrs) + self._update_attribute('src', attrs) + self._update_attribute('action', attrs) + return 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.12/python-lib/cuddlefish/docs/renderapi.readme.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/renderapi.readme.md new file mode 100644 index 0000000..627c2a6 --- /dev/null +++ b/tools/addon-sdk-1.12/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.12/python-lib/cuddlefish/docs/webdocs.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py new file mode 100755 index 0000000..f761282 --- /dev/null +++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py @@ -0,0 +1,106 @@ +# 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, re, errno +import markdown +import cgi + +from cuddlefish import packaging +from cuddlefish.docs import apirenderer +from cuddlefish._version import get_versions +from documentationitem import get_module_list + +INDEX_PAGE = '/doc/static-files/base.html' +BASE_URL_INSERTION_POINT = '<base ' +VERSION_INSERTION_POINT = '<div id="version">' +MODULE_INDEX_INSERTION_POINT = '<ul id="module-index">' +THIRD_PARTY_MODULE_SUMMARIES = '<ul id="third-party-module-summaries">' +HIGH_LEVEL_MODULE_SUMMARIES = '<ul id="high-level-module-summaries">' +LOW_LEVEL_MODULE_SUMMARIES = '<ul id="low-level-module-summaries">' +CONTENT_ID = '<div id="main-content">' +TITLE_ID = '<title>' +DEFAULT_TITLE = 'Add-on SDK Documentation' + +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 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, version=get_versions()["version"], base_url = None): + self.root = root + self.version = version + 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): + md_content = unicode(open(path, 'r').read(), 'utf8') + guide_content = markdown.markdown(md_content) + return self._create_page(guide_content) + + def create_module_index(self, path, module_list): + md_content = unicode(open(path, 'r').read(), 'utf8') + index_content = markdown.markdown(md_content) + module_list_content = self._make_module_text(module_list) + index_content = insert_after(index_content, MODULE_INDEX_INSERTION_POINT, module_list_content) + return self._create_page(index_content) + + def create_module_page(self, path): + module_content = apirenderer.md_to_div(path) + return self._create_page(module_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 _make_module_text(self, module_list): + module_text = '' + for module in module_list: + module_link = tag_wrap(module.name(), 'a', \ + {'href': "/".join(["modules", module.relative_url()])}) + module_list_item = tag_wrap(module_link, "li") + module_text += module_list_item + return module_text + + 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) + base_page = insert_after(base_page, VERSION_INSERTION_POINT, "Version " + self.version) + module_list = get_module_list(root) + + third_party_module_list = [module_info for module_info in module_list if module_info.level() == "third-party"] + third_party_module_text = self._make_module_text(third_party_module_list) + base_page = insert_after(base_page, \ + THIRD_PARTY_MODULE_SUMMARIES, third_party_module_text) + + high_level_module_list = [module_info for module_info in module_list if module_info.level() == "high"] + high_level_module_text = self._make_module_text(high_level_module_list) + base_page = insert_after(base_page, \ + HIGH_LEVEL_MODULE_SUMMARIES, high_level_module_text) + + low_level_module_list = [module_info for module_info in module_list if module_info.level() == "low"] + low_level_module_text = self._make_module_text(low_level_module_list) + base_page = insert_after(base_page, \ + LOW_LEVEL_MODULE_SUMMARIES, low_level_module_text) + return base_page + + 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 |