# HG changeset patch # User Sandrine Ribeau # Date 1273070642 -7200 # Node ID fb004819cab4484668e5779a296d76afeefa9349 # Parent 7199fddc0a88eb8a9cc6caaa1616b0e0864033d1 [book] add generated js documentation to cw book diff -r 7199fddc0a88 -r fb004819cab4 doc/book/en/annexes/docstrings-conventions.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/annexes/docstrings-conventions.rst Wed May 05 16:44:02 2010 +0200 @@ -0,0 +1,106 @@ +Javascript docstrings +===================== + +Whereas in Python source code we only need to include a module docstrings +using the directive `.. automodule:: mypythonmodule`, we will have to +explicitely define Javascript modules and functions in the doctrings since +there is no native directive to include Javascript files. + +Rest generation +--------------- + +`pyjsrest` is a small utility parsing Javascript doctrings and generating the +corresponding Restructured file used by Sphinx to generate HTML documentation. +This script will have the following structure:: + + =========== + filename.js + =========== + .. module:: filename.js + +We use the `.. module::` directive to register a javascript library +as a Python module for Sphinx. This provides an entry in the module index. + +The contents of the docstring found in the javascript file will be added as is +following the module declaration. No treatment will be done on the doctring. +All the documentation structure will be in the docstrings and will comply +with the following rules. + +Docstring structure +------------------- + +Basically we document javascript with RestructuredText docstring +following the same convention as documenting Python code. + +The doctring in Javascript files must be contained in standard +Javascript comment signs, starting with `/**` and ending with `*/`, +such as:: + + /** + * My comment starts here. + * This is the second line prefixed with a `*`. + * ... + * ... + * All the follwing line will be prefixed with a `*` followed by a space. + * ... + * ... + */ + + +Comments line prefixed by `//` will be ignored. They are reserved for source +code comments dedicated to developers. + + +Javscript functions docstring +----------------------------- + +By default, the `function` directive describes a module-level function. + +`function` directive +~~~~~~~~~~~~~~~~~~~~ + +Its purpose is to define the function prototype such as:: + + .. function:: loadxhtml(url, data, reqtype, mode) + +If any namespace is used, we should add it in the prototype for now, +until we define an appropriate directive. +:: + .. function:: jQuery.fn.loadxhtml(url, data, reqtype, mode) + +Function parameters +~~~~~~~~~~~~~~~~~~~ + +We will define function parameters as a bulleted list, where the +parameter name will be backquoted and followed by its description. + +Example of a javascript function docstring:: + + .. function:: loadxhtml(url, data, reqtype, mode) + + cubicweb loadxhtml plugin to make jquery handle xhtml response + + fetches `url` and replaces this's content with the result + + Its arguments are: + + * `url` + + * `mode`, how the replacement should be done (default is 'replace') + Possible values are : + - 'replace' to replace the node's content with the generated HTML + - 'swap' to replace the node itself with the generated HTML + - 'append' to append the generated HTML to the node's content + + +Optional parameter specification +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Javascript functions handle arguments not listed in the function signature. +In the javascript code, they will be flagged using `/* ... */`. In the docstring, +we flag those optional arguments the same way we would define it in +Python:: + + .. function:: asyncRemoteExec(fname, arg1=None, arg2=None) + + diff -r 7199fddc0a88 -r fb004819cab4 doc/book/en/annexes/index.rst --- a/doc/book/en/annexes/index.rst Wed May 05 11:19:58 2010 +0200 +++ b/doc/book/en/annexes/index.rst Wed May 05 16:44:02 2010 +0200 @@ -17,6 +17,8 @@ rql/index mercurial depends + javascript-api + docstrings-conventions (X)HTML tricks to apply ----------------------- diff -r 7199fddc0a88 -r fb004819cab4 doc/book/en/devweb/js.rst --- a/doc/book/en/devweb/js.rst Wed May 05 11:19:58 2010 +0200 +++ b/doc/book/en/devweb/js.rst Wed May 05 16:44:02 2010 +0200 @@ -353,3 +353,11 @@ There is also javascript support for massmailing, gmap (google maps), fckcwconfig (fck editor), timeline, calendar, goa (CubicWeb over AppEngine), flot (charts drawing), tabs and bookmarks. + +API +~~~ + +.. toctree:: + :maxdepth: 1 + + js_api/index diff -r 7199fddc0a88 -r fb004819cab4 doc/book/en/makefile --- a/doc/book/en/makefile Wed May 05 11:19:58 2010 +0200 +++ b/doc/book/en/makefile Wed May 05 16:44:02 2010 +0200 @@ -11,6 +11,10 @@ PAPER = #BUILDDIR = build BUILDDIR = ~/tmp/cwdoc +CWDIR = ../../.. +JSDIR = ${CWDIR}/web/data +JSTORST = ${CWDIR}/doc/tools/pyjsrest.py +BUILDJS = devweb/js_api # Internal variables for sphinx PAPEROPT_a4 = -D latex_paper_size=a4 @@ -18,6 +22,7 @@ ALLSPHINXOPTS = -d ${BUILDDIR}/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + .PHONY: help clean html web pickle htmlhelp latex changes linkcheck help: @@ -36,6 +41,7 @@ rm -rf apidoc/ rm -f *.html -rm -rf ${BUILDDIR}/* + -rm -rf ${BUILDJS} all: ${TARGET} apidoc html @@ -48,12 +54,16 @@ epydoc --html -o apidoc -n "cubicweb" --exclude=setup --exclude=__pkginfo__ ../../../ # run sphinx ### -html: +html: js mkdir -p ${BUILDDIR}/html ${BUILDDIR}/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) ${BUILDDIR}/html @echo @echo "Build finished. The HTML pages are in ${BUILDDIR}/html." +js: + mkdir -p ${BUILDJS} + $(JSTORST) -p ${JSDIR} -o ${BUILDJS} + pickle: mkdir -p ${BUILDDIR}/pickle ${BUILDDIR}/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) ${BUILDDIR}/pickle diff -r 7199fddc0a88 -r fb004819cab4 doc/tools/pyjsrest.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/tools/pyjsrest.py Wed May 05 16:44:02 2010 +0200 @@ -0,0 +1,110 @@ +#!/usr/bin/env python +""" +Parser for Javascript comments. +""" +from __future__ import with_statement + +import sys, os, getopt, re + +def clean_comment(match): + comment = match.group() + comment = strip_stars(comment) + return comment + +# Rest utilities +def rest_title(title, level, level_markups=['=', '=', '-', '~', '+', '`']): + size = len(title) + if level == 0: + return '\n'.join((level_markups[level] * size, title, level_markups[0] * size)) + '\n' + return '\n'.join(('\n' + title, level_markups[level] * size)) + '\n' + +def get_doc_comments(text): + """ + Return a list of all documentation comments in the file text. Each + comment is a pair, with the first element being the comment text and + the second element being the line after it, which may be needed to + guess function & arguments. + + >>> get_doc_comments(read_file('examples/module.js'))[0][0][:40] + '/**\n * This is the module documentation.' + >>> get_doc_comments(read_file('examples/module.js'))[1][0][7:50] + 'This is documentation for the first method.' + >>> get_doc_comments(read_file('examples/module.js'))[1][1] + 'function the_first_function(arg1, arg2) ' + >>> get_doc_comments(read_file('examples/module.js'))[2][0] + '/** This is the documentation for the second function. */' + + """ + return [clean_comment(match) for match in re.finditer('/\*\*.*?\*/', + text, re.DOTALL|re.MULTILINE)] + +RE_STARS = re.compile('^\s*?\* ?', re.MULTILINE) + + +def strip_stars(doc_comment): + """ + Strip leading stars from a doc comment. + + >>> strip_stars('/** This is a comment. */') + 'This is a comment.' + >>> strip_stars('/**\n * This is a\n * multiline comment. */') + 'This is a\n multiline comment.' + >>> strip_stars('/** \n\t * This is a\n\t * multiline comment. \n*/') + 'This is a\n multiline comment.' + + """ + return RE_STARS.sub('', doc_comment[3:-2]).strip() + +def parse_js_files(args=sys.argv): + """ + Main command-line invocation. + """ + try: + opts, args = getopt.gnu_getopt(args[1:], 'p:o:h', [ + 'jspath=', 'output=', 'help']) + opts = dict(opts) + except getopt.GetoptError: + usage() + sys.exit(2) + + rst_dir = opts.get('--output') or opts.get('-o') + if rst_dir is None and len(args) != 1: + rst_dir = 'apidocs' + js_dir = opts.get('--jspath') or opts.get('-p') + if not os.path.exists(os.path.join(rst_dir)): + os.makedirs(os.path.join(rst_dir)) + + f_index = open(os.path.join(rst_dir, 'index.rst'), 'wb') + f_index.write(''' +.. toctree:: + :maxdepth: 1 + +''' +) + for js_path, js_dirs, js_files in os.walk(js_dir): + rst_path = re.sub('%s%s*' % (js_dir, os.path.sep), '', js_path) + for js_file in js_files: + if not js_file.endswith('.js'): + continue + if not os.path.exists(os.path.join(rst_dir, rst_path)): + os.makedirs(os.path.join(rst_dir, rst_path)) + rst_content = extract_rest(js_path, js_file) + filename = os.path.join(rst_path, js_file[:-3]) + # add to index + f_index.write(' %s\n' % filename) + # save rst file + with open(os.path.join(rst_dir, filename) + '.rst', 'wb') as f_rst: + f_rst.write(rst_content) + f_index.close() + +def extract_rest(js_dir, js_file): + js_filepath = os.path.join(js_dir, js_file) + filecontent = open(js_filepath, 'U').read() + comments = get_doc_comments(filecontent) + rst = rest_title(js_file, 0) + rst += '.. module:: %s\n\n' % js_file + rst += '\n\n'.join(comments) + return rst + +if __name__ == '__main__': + parse_js_files() diff -r 7199fddc0a88 -r fb004819cab4 web/data/cubicweb.timeline-bundle.js --- a/web/data/cubicweb.timeline-bundle.js Wed May 05 11:19:58 2010 +0200 +++ b/web/data/cubicweb.timeline-bundle.js Wed May 05 16:44:02 2010 +0200 @@ -1,14 +1,14 @@ var SimileAjax_urlPrefix = baseuri() + 'data/'; var Timeline_urlPrefix = baseuri() + 'data/'; -/*================================================== +/* * Simile Ajax API * * Include this file in your HTML file as follows: * * * - *================================================== + * */ if (typeof SimileAjax == "undefined") { @@ -213,9 +213,9 @@ SimileAjax.loaded = true; })(); } -/*================================================== +/* * Platform Utility Functions and Constants - *================================================== + * */ /* This must be called after our jQuery has been loaded @@ -319,9 +319,10 @@ SimileAjax.Platform.getDefaultLocale = function() { return SimileAjax.Platform.clientLocale; -};/*================================================== +}; +/* * Debug Utility Functions - *================================================== + * */ SimileAjax.Debug = { @@ -678,9 +679,9 @@ } }; })(); -/*================================================== +/* * DOM Utility Functions - *================================================== + * */ SimileAjax.DOM = new Object(); @@ -1040,9 +1041,9 @@ SimileAjax.includeCssFile(document, SimileAjax.urlPrefix + "styles/graphics-ie6.css"); } -/*================================================== +/* * Opacity, translucency - *================================================== + * */ SimileAjax.Graphics._createTranslucentImage1 = function(url, verticalAlign) { var elmt = document.createElement("img"); @@ -1119,9 +1120,9 @@ } }; -/*================================================== +/* * Bubble - *================================================== + * */ SimileAjax.Graphics.bubbleConfig = { @@ -1479,9 +1480,9 @@ }; }; -/*================================================== +/* * Animation - *================================================== + * */ /** @@ -1549,11 +1550,11 @@ } }; -/*================================================== +/* * CopyPasteButton * * Adapted from http://spaces.live.com/editorial/rayozzie/demo/liveclip/liveclipsample/techPreview.html. - *================================================== + * */ /** @@ -1606,9 +1607,9 @@ return div; }; -/*================================================== +/* * getWidthHeight - *================================================== + * */ SimileAjax.Graphics.getWidthHeight = function(el) { // RETURNS hash {width: w, height: h} in pixels @@ -1633,9 +1634,9 @@ }; -/*================================================== +/* * FontRenderingContext - *================================================== + * */ SimileAjax.Graphics.getFontRenderingContext = function(elmt, width) { return new SimileAjax.Graphics._FontRenderingContext(elmt, width); @@ -2127,9 +2128,9 @@ var d = new Date().getTimezoneOffset(); return d / -60; }; -/*================================================== +/* * String Utility Functions and Constants - *================================================== + * */ String.prototype.trim = function() { @@ -2170,9 +2171,9 @@ } return result; }; -/*================================================== +/* * HTML Utility Functions - *================================================== + * */ SimileAjax.HTML = new Object(); @@ -2655,9 +2656,9 @@ return (this._a.length > 0) ? this._a[this._a.length - 1] : null; }; -/*================================================== +/* * Event Index - *================================================== + * */ SimileAjax.EventIndex = function(unit) { @@ -2889,9 +2890,9 @@ return this._index < this._events.length() ? this._events.elementAt(this._index++) : null; } -};/*================================================== +};/* * Default Unit - *================================================== + * */ SimileAjax.NativeDateUnit = new Object(); @@ -2953,9 +2954,9 @@ return new Date(v.getTime() + n); }; -/*================================================== +/* * General, miscellaneous SimileAjax stuff - *================================================== + * */ SimileAjax.ListenerQueue = function(wildcardHandlerName) { @@ -2998,7 +2999,7 @@ } }; -/*====================================================================== +/* * History * * This is a singleton that keeps track of undoable user actions and @@ -3020,7 +3021,7 @@ * * An iframe is inserted into the document's body element to track * onload events. - *====================================================================== + * */ SimileAjax.History = { @@ -3632,7 +3633,7 @@ } return elmt; }; -/*================================================== +/* * Timeline API * * This file will load all the Javascript files @@ -3696,7 +3697,7 @@ * Note that the Ajax version is usually NOT the same as the Timeline version. * See variable simile_ajax_ver below for the current version * - *================================================== + * */ (function() { @@ -3928,7 +3929,7 @@ loadMe(); } })(); -/*================================================= +/* * * Coding standards: * @@ -3950,14 +3951,14 @@ * We also want to use jslint: http://www.jslint.com/ * * - *================================================== + * */ -/*================================================== +/* * Timeline VERSION - *================================================== + * */ // Note: version is also stored in the build.xml file Timeline.version = 'pre 2.4.0'; // use format 'pre 1.2.3' for trunk versions @@ -3965,9 +3966,9 @@ Timeline.display_version = Timeline.version + ' (with Ajax lib ' + Timeline.ajax_lib_version + ')'; // cf method Timeline.writeVersion -/*================================================== +/* * Timeline - *================================================== + * */ Timeline.strings = {}; // localization string tables Timeline.HORIZONTAL = 0; @@ -4183,9 +4184,9 @@ -/*================================================== +/* * Timeline Implementation object - *================================================== + * */ Timeline._Impl = function(elmt, bandInfos, orientation, unit, timelineID) { SimileAjax.WindowManager.initialize(); @@ -4585,7 +4586,7 @@ this.paint(); }; -/*================================================= +/* * * Coding standards: * @@ -4607,14 +4608,14 @@ * We also want to use jslint: http://www.jslint.com/ * * - *================================================== + * */ -/*================================================== +/* * Band - *================================================== + * */ Timeline._Band = function(timeline, bandInfo, index) { // hack for easier subclassing @@ -5344,9 +5345,9 @@ Timeline._Band.prototype.closeBubble = function() { SimileAjax.WindowManager.cancelPopups(); }; -/*================================================== +/* * Classic Theme - *================================================== + * */ @@ -5523,14 +5524,14 @@ }; this.mouseWheel = 'scroll'; // 'default', 'zoom', 'scroll' -};/*================================================== +};/* * An "ether" is a object that maps date/time to pixel coordinates. - *================================================== + * */ -/*================================================== +/* * Linear Ether - *================================================== + * */ Timeline.LinearEther = function(params) { @@ -5601,9 +5602,9 @@ }; -/*================================================== +/* * Hot Zone Ether - *================================================== + * */ Timeline.HotZoneEther = function(params) { @@ -5828,9 +5829,9 @@ Timeline.HotZoneEther.prototype._getScale = function() { return this._interval / this._pixelsPerInterval; }; -/*================================================== +/* * Gregorian Ether Painter - *================================================== + * */ Timeline.GregorianEtherPainter = function(params) { @@ -5919,9 +5920,9 @@ }; -/*================================================== +/* * Hot Zone Gregorian Ether Painter - *================================================== + * */ Timeline.HotZoneGregorianEtherPainter = function(params) { @@ -6080,9 +6081,9 @@ } }; -/*================================================== +/* * Year Count Ether Painter - *================================================== + * */ Timeline.YearCountEtherPainter = function(params) { @@ -6169,9 +6170,9 @@ Timeline.YearCountEtherPainter.prototype.softPaint = function() { }; -/*================================================== +/* * Quarterly Ether Painter - *================================================== + * */ Timeline.QuarterlyEtherPainter = function(params) { @@ -6257,9 +6258,9 @@ Timeline.QuarterlyEtherPainter.prototype.softPaint = function() { }; -/*================================================== +/* * Ether Interval Marker Layout - *================================================== + * */ Timeline.EtherIntervalMarkerLayout = function(timeline, band, theme, align, showLine) { @@ -6363,9 +6364,9 @@ }; }; -/*================================================== +/* * Ether Highlight Layout - *================================================== + * */ Timeline.EtherHighlight = function(timeline, band, theme, backgroundLayer) { @@ -6404,9 +6405,9 @@ } } }; -/*================================================== +/* * Event Utils - *================================================== + * */ Timeline.EventUtils = {}; @@ -6421,7 +6422,7 @@ }; Timeline.EventUtils.decodeEventElID = function(elementID) { - /*================================================== + /* * * Use this function to decode an event element's id on a band (label div, * tape div or icon img). @@ -6447,7 +6448,7 @@ * by using Timeline.getTimeline, Timeline.getBand, or * Timeline.getEvent and passing in the element's id * - *================================================== + * */ var parts = elementID.split('-'); @@ -6467,9 +6468,9 @@ // elType should be one of {label | icon | tapeN | highlightN} return elType + "-tl-" + timeline.timelineID + "-" + band.getIndex() + "-" + evt.getID(); -};/*================================================== +};/* * Gregorian Date Labeller - *================================================== + * */ Timeline.GregorianDateLabeller = function(locale, timeZone) { @@ -6558,9 +6559,9 @@ return { text: text, emphasized: emphasized }; } -/*================================================== +/* * Default Event Source - *================================================== + * */ @@ -7125,12 +7126,12 @@ }; -/*================================================== +/* * Original Event Painter - *================================================== + * */ -/*================================================== +/* * * To enable a single event listener to monitor everything * on a Timeline, we need a way to map from an event's icon, @@ -7152,7 +7153,7 @@ * You can then retrieve the band/timeline objects and event object * by using Timeline.EventUtils.decodeEventElID * - *================================================== + * */ /* @@ -7818,9 +7819,9 @@ this._eventPaintListeners[i](this._band, op, evt, els); } }; -/*================================================== +/* * Detailed Event Painter - *================================================== + * */ // Note: a number of features from original-painter @@ -8509,9 +8510,9 @@ this._onSelectListeners[i](eventID); } }; -/*================================================== +/* * Overview Event Painter - *================================================== + * */ Timeline.OverviewEventPainter = function(params) { @@ -8767,9 +8768,9 @@ Timeline.OverviewEventPainter.prototype.showBubble = function(evt) { // not implemented }; -/*================================================== +/* * Compact Event Painter - *================================================== + * */ Timeline.CompactEventPainter = function(params) { @@ -9831,9 +9832,9 @@ this._onSelectListeners[i](eventIDs); } }; -/*================================================== +/* * Span Highlight Decorator - *================================================== + * */ Timeline.SpanHighlightDecorator = function(params) { @@ -9948,9 +9949,9 @@ Timeline.SpanHighlightDecorator.prototype.softPaint = function() { }; -/*================================================== +/* * Point Highlight Decorator - *================================================== + * */ Timeline.PointHighlightDecorator = function(params) { @@ -10015,9 +10016,9 @@ Timeline.PointHighlightDecorator.prototype.softPaint = function() { }; -/*================================================== +/* * Default Unit - *================================================== + * */ Timeline.NativeDateUnit = new Object(); @@ -10083,35 +10084,35 @@ return new Date(v.getTime() + n); }; -/*================================================== +/* * Common localization strings - *================================================== + * */ Timeline.strings["fr"] = { wikiLinkLabel: "Discute" }; -/*================================================== +/* * Localization of labellers.js - *================================================== + * */ Timeline.GregorianDateLabeller.monthNames["fr"] = [ "jan", "fev", "mar", "avr", "mai", "jui", "jui", "aou", "sep", "oct", "nov", "dec" ]; -/*================================================== +/* * Common localization strings - *================================================== + * */ Timeline.strings["en"] = { wikiLinkLabel: "Discuss" }; -/*================================================== +/* * Localization of labellers.js - *================================================== + * */ Timeline.GregorianDateLabeller.monthNames["en"] = [