aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/python-lib/cuddlefish/docs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.12/python-lib/cuddlefish/docs')
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py392
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apirenderer.py302
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py124
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py199
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py78
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/renderapi.readme.md210
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py106
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