Recent Changes - Search:

edit SideBar

JSDoc

JSDoc Public Output

JSDoc output, updated every 5 minutes

See Also

JSDoc

JSDoc Summary

  1. TerraSwarm accessors repo
    • Location in the ptII tree: $PTII/org/terraswarm/accessor/accessors/web/doc/jsdoc/index.html
    • Automatically updated location: JSDoc output for accessors (accessors workgroup)
    • org/terraswarm/accessor/accessors/web/build.xml contains the jsdoc target that creates the output
      • The custom jsdoc branch is downloaded automatically by JSAccessor.getAccessorsRepository() to $PTII/org/terraswarm/accessor/accessors/web/vendors/jsdoc/
  2. PtII repo that contains docs for actor/lib/jjs
    • Location in the ptII tree: $PTII/doc/codeDoc/js/index.html
    • Automatically updated location: JSDoc output for ptII accessors (ptII repo)
    • $PTII/build.xml contains the jsdoc target that creates the output
      • The custom jsdoc branch is also downloaded to $PTII/doc/vendors/jsdoc
        • We download the custom jsdoc branch twice because we want each repo to be standalone

Problems and solutions for JSDoc writers

The message "Unable to parse a tag's type expression for source file" appears

See https://www.terraswarm.org/accessors/wiki/Main/JSDoc#JSDocArrays

JSDoc Implementation Details

How JSDoc creates the PtDoc.xml files

Summary: running (cd $PTII/org/terraswarm/accessor/accessors/web; ant) or reloading an accessor runs a custom version of JSDoc that reads org/terraswarm/accessor/accessors/web/jsdoc/templates/ptdoc/publish.js and generates PtDoc.xml files like $PTII/org/terraswarm/accessor/accessors/web/audio/AudioCapturePtDoc.xml

Details:

Keep in mind that we can run jsdoc in two locations, the terraswarm accessors repo and the ptII repo.

Here, we discuss how files like $PTII/org/terraswarm/accessor/accessors/web/audio/AudioCapturePtDoc.xml are created, see https://www.terraswarm.org/accessors/audio/AudioCapturePtDoc.xml for example:

<property name="documentation" class="ptolemy.vergil.basic.DocAttribute"><property name="author" class="ptolemy.kernel.util.StringAttribute" value="Ilge Akkya">
    </property><property name="description" class="ptolemy.kernel.util.StringAttribute" value="<p>Capture audio from the microphone. </p>
<p> This accessor requires the optional 'audio' module, which may or may
 not be provided by an accessor host.</p>"
>
    </property><property name="trigger (port)" class="ptolemy.kernel.util.StringAttribute" value="undefined Input that triggers recording.">
    </property><property name="signal (port)" class="ptolemy.kernel.util.StringAttribute" value="({names:["number"]}) A sequence of numbers representing the captured audio signal.">
    </property><property name="version" class="ptolemy.kernel.util.StringAttribute" value="$$Id: AudioCapture.js 342 2015-10-31 15:48:43Z cxh $$">
    </property></property>

The *PtDoc.xml files for the TerraSwarm accessors repo are created by running ant in accessors/web, which is in the ptII tree as $PTII/org/terraswarm/accessor/accessors/web/build.xml. The JSAccessor.getAccessorsRepository() method runs ant for us when the accessors are reloaded. The terraswarm website gets updated via continuous integration.

The way ant works is that it reads in accessors/web/build.xml, which names the default target as being the build target. The build target has two dependency targets: jsdoc and ptdoc that get invoked.

The ptdoc target looks like:

    <target name="ptdoc"
          depends="vendors-jsdoc"
          description="Invoke jsdoc to read *.js files and generate *PtDoc.xml files suitable for Ptolemy"
          >
    <echo>Invoke jsdoc to read *.js files and generate *PtDoc.xml files suitable for Ptolemy.                  
    See https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSDocSystems                                      
    See jsdoc/jsdoc.json                                                                                      
    See jsdoc/plugins/accessorJSDocTags.js                                                                    
    See jsdoc/templates/ptdoc/publish.js                                                                      
    </echo>
    <exec executable="${jsdoc.home}/jsdoc">
      <arg value="--configure" />
      <arg value="jsdoc/jsdoc.json" />
      <arg value="--recurse" />
      <arg value="--template" />
      <arg value="jsdoc/templates/ptdoc" />
      <arg value="--verbose" />
      <arg value="." />
    </exec>
  </target>

The ptdoc target has a dependency target vendors-jsdoc which downloads our custom branch of jsdoc.

To invoke the command by hand:

bash-3.2$ cd $PTII/org/terraswarm/accessor/accessors/web
bash-3.2$ ant ptdoc
Buildfile: /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/build.xml

-check-vendors-jsdoc:

vendors-jsdoc:

ptdoc:
     [echo] Invoke jsdoc to read *.js files and generate *PtDoc.xml files suitable for Ptolemy.
     [echo]     See https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSDocSystems
     [echo]     See jsdoc/jsdoc.json
     [echo]     See jsdoc/plugins/accessorJSDocTags.js
     [echo]     See jsdoc/templates/ptdoc/publish.js
     [echo]
     [exec] Parsing /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/audio/AudioCapture.js ...
     [exec] Parsing /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/audio/AudioPlayer.js ...
...

To see what command is invoked:

bash-3.2$ cd $PTII/org/terraswarm/accessor/accessors/web
bash-3.2$ ant -v ptdoc
Apache Ant(TM) version 1.9.4 compiled on April 29 2014
Trying the default build file: build.xml
Buildfile: /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/build.xml
...
ptdoc:
     [echo] Invoke jsdoc to read *.js files and generate *PtDoc.xml files suitable for Ptolemy.
     [echo]     See https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSDocSystems
     [echo]     See jsdoc/jsdoc.json
     [echo]     See jsdoc/plugins/accessorJSDocTags.js
     [echo]     See jsdoc/templates/ptdoc/publish.js
     [echo]
     [exec] Current OS is Mac OS X
     [exec] Executing 'vendors/jsdoc/jsdoc' with arguments:
     [exec] '--configure'
     [exec] 'jsdoc/jsdoc.json'
     [exec] '--recurse'
     [exec] '--template'
     [exec] 'jsdoc/templates/ptdoc'
     [exec] '--verbose'
     [exec] '.'

To run jsdoc by hand to build ptdoc files:

bash-3.2$ cd $PTII/org/terraswarm/accessor/accessors/web
bash-3.2$ ./vendors/jsdoc/jsdoc --configure jsdoc/jsdoc.json --recurse --template jsdoc/templates/ptdoc --verbose .
./vendors/jsdoc/jsdoc --configure jsdoc/jsdoc.json --recurse --template jsdoc/templates/ptdoc --verbose .
Parsing /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/audio/AudioCapture.js ...
Parsing /Users/cxh/ptII/org/terraswarm/accessor/accessors/web/audio/AudioPlayer.js ...
...
 

The template directory is jsdoc/templates/ptdoc, which maps to org/terraswarm/accessor/accessors/web/jsdoc/templates/ptdoc/

In that directory is a publish.js file that defines methods that are PtDoc specific.

JSDoc Simple Example

This example downloads the non-accessor-specifc JSDoc code and runs it on the Ptolemy II .js files. This example is an illustration of getting started with JSDoc, instead of following this example, we now have a custom branch of JSDoc, ant rules and Java code that invokes the ant rules

  1. Download into $PTII/vendors:
    cd $PTII/vendors
    git clone https://github.com/jsdoc3/jsdoc
  2. Run jsdoc
    cd $PTII
    vendors/jsdoc/jsdoc --recurse ptolemy/actor/lib/jjs
  3. The output is in $PTII/out/index.html

JSDoc Ant Ptolemy Example

We now have an ant rule that generates JSDoc output in $PTII/doc/codeDoc/js/index.html

  1. Update your tree and regenerate $PTII/build.xml
    cd $PTII
    svn update
    ./configure
  2. Invoke ant:
    ant jsdoc
  3. View the output
    open doc/codeDoc/js/index.html

Note that the file $PTII/doc/jsdoc/topREADME.md is used to create $PTII/doc/codeDoc/js/index.html. The way this works is that we pass a -R argument to jsdoc in $PTII/build.xml.

For formatting instructions see Getting Started and Block Tags

Continuous Integration

We use continuous integration to update the websites with the documentation.

Recall from above that the docs appear on both the CHESS website and the TerraSwarm website.

How the Ptolemy JavaScript website is updated

We have a continuous integration (CI) build that updates https://chess.eecs.berkeley.edu/ptexternal/src/ptII/doc/codeDoc/js/index.html by checking the ptII svn repo for changes every 5 minutes. See http://terra.eecs.berkeley.edu:8080/job/ptIIci/

During the build, ant vendors-jsdoc-pull jsdoc is run. To see this step requires a Jenkins account with access to the ptIIci configuration. The settings are below:

The chess website is updated at the end of the build process by a Publish artifacts to SCP Repository post-build action.

To see this step requires a Jenkins account with access to the ptIIci configuration. The settings are below:

  • "Publish artifacts to SCP Repository"
    • "SCP site": moog.eecs.berkeley.edu
      • "Files to upload"
      • "Source:" doc/codeDoc/**
      • "Destination:" cvswww/chess.eecs.berkeley.edu/ptexternal/src/ptII

How the TerraSwarm website is updated

We have a continuous integration (CI) build that updates https://www.terraswarm.org/accessors/doc/jsdoc/ by checking the accessors svn repo for changes every 5 minutes. See http://terra.eecs.berkeley.edu:8080/job/accessors/

During the build, ant build is run, which creates the *PtDoc.xml files and runs JSDoc. To see this step requires a Jenkins account with access to the accessors configuration.

The TerraSwarm website is updated by a "Send build artifacts over SSH" post-build step. The settings are below:

JSDoc Custom Tags

Unfortunately, the docs for adding custom tags to JSDoc are a bit sparse.

https://github.com/jsdoc3/jsdoc says: "Templates and Build Tools"

"The JSDoc community has created numerous templates and other tools to help you generate and customize your documentation. Here are just a few: Templates"

JSDoc Custom Tag Plugin

Below is a description of what was necessary to set up the custom JSDoc Custom Tag Plugin for both the ptII and accessors repo.

There are two components: The plugin (covered here) and the JSDoc Custom Tag Fork (below).

Below are the steps that were done to set up the plugin in the ptII and accessor repos.

  1. Edit jsdoc.json and add a plugins tag and value pair. The location of the accessorJSDocTags.js file is different for the ptII repo and the accessors repo
    1. For ptII, the plugin is at $PTII/doc/jsdoc/plugins/accessorJSDocTags.js, so $PTII/doc/jsdoc/jsdoc.json looks like:
      {
        ...
          "plugins": ["plugins/markdown", "doc/jsdoc/plugins/accessorJSDocTags"],
        ...
      }
      The "plugins/markdown" enables the use of Markdown, see Using the Markdown plugin
    2. For the accessors, the plugin is at accessors/web/jsdoc/jsdoc.json, which contains:
      {
          ...
          "plugins": ["plugins/markdown", "jsdoc/plugins/accessorJSDocTags"]
          ...
      }
  2. The jsdoc.json file is read by jsdoc as follows:
    1. For ptII, after running ./configure, the ant file at $PTII/build.xml will contain:
       <target name="jsdoc"
                depends="vendors-jsdoc, jsdoc-getFromWiki"
                description="Run jsdoc to generate documentation for JavaScript files."
                >
          <echo>Invoke jsdoc to generate documentation for .js files.                            
          To set this up:                                                                        
             cd $PTII/vendors; git clone https://github.com/terraswarm/jsdoc.git                  

          $PTII/doc/jsdoc/topREADME.md will be used as the basis of the first page.              

          The output appears in doc/codeDoc/js/index.html                                        
          </echo>
          <exec executable="${jsdoc.home}/jsdoc">
            <arg value="--verbose" />
            <arg value="--recurse" />
            <arg value="-c" />
            <arg value="doc/jsdoc/jsdoc.json" />
            <arg value="-R" />
            <arg value="doc/jsdoc/topREADME.md" />
            <arg value="-d" />
            <arg value="doc/codeDoc/js" />

            <arg value="ptolemy/actor/lib/jjs"/>
            <arg value="ptolemy/actor/lib/vertx/demo"/>
            <arg value="org/terraswarm/accessor"/>
          </exec>
        </target>
      1. The depends="vendors-jsdoc" is used to check out the fork of the JSDoc repo, more on this below.
      2. The jsdoc-getFromWiki gets a file from the TerraSwarm accessors wiki, see pmWiki to JSDoc
    2. For accessors, the ant file at accessors/web/build.xml contains:
         <target name="jsdoc"
                depends="vendors-jsdoc"
                description="Run jsdoc to generate documentation for JavaScript files."
                >
          <echo>Invoke jsdoc to generate documentation for .js files.                                                                          
          The output appears in doc/jsdoc/index.html                                                                                          
          </echo>
          <exec executable="${jsdoc.home}/jsdoc"
                timeout="10000"
                >
            <arg value="--configure" />
            <arg value="jsdoc/jsdoc.json" />
            <arg value="--destination" />
            <arg value="doc/jsdoc" />
            <arg value="--readme" />
            <arg value="README.md" />
            <arg value="--verbose" />
            <arg value="." />
          </exec>
        </target>
    3. The depends="vendors-jsdoc" is used to check out the fork of the JSDoc repo, more on this below.
  3. Create $PTII/doc/jsdoc/plugins/accessorJSDocTags.js or accessors/web/jsdoc/plugins/accessorJSDocTags.js:
    //Start of text from jsdoc/lib/jsdoc/tag/dictionary/definitions.js                                                                      
    function filepathMinusPrefix(filepath) {
        var sourcePaths = getSourcePaths();
        var commonPrefix = path.commonPrefix(sourcePaths);
        var result = '';

        if (filepath) {
            filepath = path.normalize(filepath);
            // always use forward slashes in the result                                                                                      
            result = (filepath + path.sep).replace(commonPrefix, '')
                .replace(/\\/g, '/');
        }

        if (result.length > 0 && result[result.length - 1] !== '/') {
            result += '/';
        }

        return result;
    }
    //end of text from jsdoc/lib/jsdoc/tag/dictionary/definitions.js

    exports.defineTags = function(dictionary) {
        dictionary.defineTag("accessor", {
            mustHaveValue: true,
            isNamespace: true, // Needed to get "accessor-" into the file name.                                                              
            onTagged: function(doclet, tag) {
                //console.log("accessorJSDocTags.js: accessor: " + doclet + " " + tag);                                                      

                // Start of text from jsdoc/lib/jsdoc/tag/dictionary/definitions.js                                                          

                // setDocletKindToTitle is not declared here, so we do what it says:                                                        
                //setDocletKindToTitle(doclet, tag);                                                                                        
                doclet.addTag( 'kind', tag.title );

                // setDocletNameToValue(doclet, tag);                                                                                        
                if (tag.value && tag.value.description) { // as in a long tag                                                                
                    doclet.addTag('name', tag.value.description);
                }
                else if (tag.text) { // or a short tag                                                                                      
                    doclet.addTag('name', tag.text);
                }

                if (!doclet.name) {
                    // setDocletNameToFilename(doclet, tag);                                                                                
                    var name = '';

                    if (doclet.meta.path) {
                        name = filepathMinusPrefix(doclet.meta.path);
                    }
                    name += doclet.meta.filename.replace(/\.js$/i, '');

                    doclet.name = name;
                }
                // Not sure if we need this:                                                                                                
                // in case the user wrote something like `/** @accessor  accessor:foo */`:                                                  
                //doclet.name = stripModuleNamespace(doclet.name);                    

                // setDocletTypeToValueType(doclet, tag);                                                                                    
                if (tag.value && tag.value.type) {
                    // Add the type names and other type properties (such as `optional`).                                                    
                    // Don't overwrite existing properties.                                                                                  
                    Object.keys(tag.value).forEach(function(prop) {
                        if ( !hasOwnProp.call(doclet, prop) ) {
                            doclet[prop] = tag.value[prop];
                        }
                    });
                }

                // End of text from jsdoc/lib/jsdoc/tag/dictionary/definitions.js                                                            

                doclet.accessor = tag.name;
            }
        });
        dictionary.defineTag("input", {
            mustHaveValue: true,
            canHaveType: true,
            canHaveName: true,
            onTagged: function(doclet, tag) {
                if (!doclet.inputs) { doclet.inputs = []; }
                doclet.inputs.push(tag.value);
            }
        });
        dictionary.defineTag("output", {
            mustHaveValue: true,
            canHaveType: true,
            canHaveName: true,
            onTagged: function(doclet, tag) {
                if (!doclet.outputs) { doclet.outputs = []; }
                doclet.outputs.push(tag.value);
            }
        });
        dictionary.defineTag("parameter", {
            mustHaveValue: true,
            canHaveType: true,
            canHaveName: true,
            onTagged: function(doclet, tag) {
                if (!doclet.parameters) { doclet.parameters = []; }
                doclet.parameters.push(tag.value);
            }
        });
    };

What the above code does is define tags that, when invoked, update an array that contains the instances of that tag.

Now the problem is how to use those tags.

JSDoc Custom Tag Fork

After an analysis of JSDoc Custom Tags documentation above, it seems that the best way to add our tags is to fork the git hub repo. So, we have https://github.com/terraswarm/jsdoc.

When ant jsdoc is run, the build.xml files can do a git clone and git pull of the fork of the JSDoc repo.

  • If $PTII/vendors/jsdoc or accessors/web/vendors/jsdoc does not exist, then git clone https://github.com/terraswarm/jsdoc.git is invoked.
  • To update the fork of the JSDoc repo, run ant vendors-jsdoc-pull
  • To check out the repo by hand, use:
      cd vendors
      git clone https://github.com/terraswarm/jsdoc.git

The following changes were made to the JSDoc repository.

  • Added partial support for accessors.
    • lib/jsdoc/util/templateHelper.js
      •  exports.getMembers = function(data) {
             var members = {
        +        accessors: find( data, {kind: 'accessor'} ),
                 classes: find( data, {kind: 'class'} ),
                 externals: find( data, {kind: 'external'} ),
                 events: find( data, {kind: 'event'} ),
      • templates/default/publish.js
      • +    nav += buildMemberNav(members.accessors, 'Accessors', {}, linkto);
             nav += buildMemberNav(members.modules, 'Modules', {}, linkto);
             nav += buildMemberNav(members.externals, 'Externals', seen, linktoExternal);
             nav += buildMemberNav(members.classes, 'Classes', seen, linkto);
  • Now generating accessor-StockTick.html, but need to figure out links... This change had some debugging checked in that was later removed, below are the key changes:
    • templates/default/publish.js

      •      // once for all
             view.nav = buildNav(members);
        +    attachModuleSymbols( find({ longname: {left: 'accessor:'} }), members.accessors );
             attachModuleSymbols( find({ longname: {left: 'module:'} }), members.modules );

             // generate the pretty-printed source files first so other pages can link to them
      •      indexUrl);

             // set up the lists that we'll use to generate pages
        +    var accessors = taffy(members.accessors);
             var classes = taffy(members.classes);
             var modules = taffy(members.modules);
             var namespaces = taffy(members.namespaces);
      •      var interfaces = taffy(members.interfaces);

             Object.keys(helper.longnameToUrl).forEach(function(longname) {
        +        var myAccessors = helper.find(accessors, {longname: longname});
        +        if (myAccessors.length) {
        +            // FIXME: I don't understand the helper.longnameToUrl[longname] stuff, so just hardwire it.
        +            generate('Accessor: ' + myAccessors[0].name, myAccessors, "accessor-" + myAccessors[0].name + ".html");
        +        }
        +
                 var myModules = helper.find(modules, {longname: longname});
                 if (myModules.length) {
                     generate('Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]);
  • To create files with the 'accessor-' prefix, the defineTag() in the plugin needs to set isNamespace and the containers variable needs to have 'accessor' added to it.
    • lib/jsdoc/util/templateHelper.js
      •  var ids = {};

         // each container gets its own html file
        -var containers = ['class', 'module', 'external', 'namespace', 'mixin', 'interface'];
        +var containers = ['accessor', 'class', 'module', 'external', 'namespace', 'mixin', 'interface'];

         var tutorials;
         
      •  Object.keys(helper.longnameToUrl).forEach(function(longname) {
                 var myAccessors = helper.find(accessors, {longname: longname});
                 if (myAccessors.length) {
        -            // FIXME: I don't understand the helper.longnameToUrl[longname] stuff, so just hardwire it.
        -            generate('Accessor: ' + myAccessors[0].name, myAccessors, "accessor-" + myAccessors[0].name + ".html");
        +            generate('Accessor: ' + myAccessors[0].name, myAccessors, helper.longnameToUrl[longname]);
                 }

                 var myModules = helper.find(modules, {longname: longname});
  • Added support for accessors to handlers called by the parser visitors.
    • }

       function setCurrentModule(doclet) {
      -    if (doclet.kind === 'module') {
      +    if (doclet.kind === 'module' || doclet.kind === 'accessor') {
               currentModule = new CurrentModule(doclet);
           }
       }
       
    •  function setDefaultScope(doclet) {
           // module doclets don't get a default scope
      -    if (!doclet.scope && doclet.kind !== 'module') {
      +    if (!doclet.scope && doclet.kind !== 'module' && doclet.kind !== 'accessor') {
               doclet.setScope(SCOPE_NAMES.GLOBAL);
           }
       }
    • // b) the doclet represents a module
           // c) we're in a module that exports only this symbol
           if ( !newDoclet.memberof && newDoclet.kind !== 'module' &&
      -        (!currentModule || currentModule.longname !== newDoclet.name) ) {
      +         (!currentModule || currentModule.longname !== newDoclet.name)
      +         && newDoclet.kind !== 'accessor'
      +       ) {
               newDoclet.scope = SCOPE_NAMES.GLOBAL;
           }
       
  • https://github.com/terraswarm/jsdoc/commit/ba2307a2f22194926afb20a10f65099d09e1bcbd
    • At the end of the file:
      +
      +<?js if (data.inputs && data.inputs.length) { ?>
      +    <h5>Inputs:</h5>
      +    <?js= this.partial('params.tmpl', data.inputs) ?>
      +                                              <?js } ?>
      +
      +<?js if (data.outputs && data.outputs.length) { ?>
      +    <h5>Outputs:</h5>
      +    <?js= this.partial('params.tmpl', data.outputs) ?>
      +                                              <?js } ?>
      +
      +<?js if (data.parameters && data.parameters.length) { ?>
      +    <h5>Parameters:</h5>
      +    <?js= this.partial('params.tmpl', data.parameters) ?>
      +<?js } ?>

JSDoc Custom Tag Notes

Notes about how the above procedure was determined.

Unable to parse a tag's type expression.

In the accessors repo, if web/jsdoc/plugins/accessorJSDocTags.js contains:

dictionary.defineTag("input", {
        capnHaveType: true,
        canHaveName: true,
        onTagged: function(doclet, tag) {
            console.log("accessors/web/jsdoc/plugins/accessorJSDocTags.js: input tag:" + tag);
            doclet.input = tag.name;
        }
    });

Then running ant jsdoc yields:

 [exec] Parsing /Users/cxh/src/accessors/web/FFT.js ...
     [exec] accessors/web/jsdoc/plugins/accessorJSDocTags.js: input tag:[object Object]
     [exec] accessors/web/jsdoc/plugins/accessorJSDocTags.js: input tag:[object Object]
     [exec] Parsing /Users/cxh/src/accessors/web/RangeSensor.js ...ERROR: Unable to parse a tag's type expression for source file /Users\
/cxh/src/accessors/web/FFT.js with tag title "input" and text "{[number]} signalIn Input signal array": Invalid type expression "[number\
]": Expected "!", "$", "'
", "(", "*", ".", "...", "0", "?", "Function", "\"", "\\", "_", "break", "case", "catch", "class", "const", "co\
ntinue"
, "debugger", "default", "delete", "do", "else", "enum", "export", "extends", "false", "finally", "for", "function", "if", "imple\
ments"
, "import", "in", "instanceof", "interface", "let", "new", "null", "package", "private", "protected", "public", "return", "static"\
, "super", "switch", "this", "throw", "true", "try", "typeof", "undefined", "var", "void", "while", "with", "yield", "{", Unicode letter\
 number, Unicode lowercase letter, Unicode modifier letter, Unicode other letter, Unicode titlecase letter, Unicode uppercase letter or \
[1-9] but "[" found.
     [exec]
     [exec] ERROR: Unable to parse a tag's type expression for source file /Users/cxh/src/accessors/web/FFT.js with tag title "input" an\
d text "{[number]} signalIn Input signal array": Invalid type expression "[number]": Expected "!", "$", "'
", "(", "*", ".", "...", "0", \
"
?", "Function", "\"", "\\", "_", "break", "case", "catch", "class", "const", "continue", "debugger", "default", "delete", "do", "else",\
 "enum", "export", "extends", "false", "finally", "for", "function", "if", "implements", "import", "in", "instanceof", "interface", "let\
"
, "new", "null", "package", "private", "protected", "public", "return", "static", "super", "switch", "this", "throw", "true", "try", "t\
ypeof"
, "undefined", "var", "void", "while", "with", "yield", "{", Unicode letter number, Unicode lowercase letter, Unicode modifier let\
ter, Unicode other letter, Unicode titlecase letter, Unicode uppercase letter or [1-9] but "[" found.

What's happening here is that we are getting an Object.

https://github.com/jsdoc3/jsdoc/issues/498 has a link to some code:

https://github.com/AnalyticalGraphicsInc/cesium/blob/92babfac/Tools/jsdoc3/plugins/customTags.js

/**
    @overview Define custom tags for Cesium
    @module plugins/customTags
    @author Kristian Calhoun <kristian.calhoun@gmail.com>
 */


exports.defineTags = function(dictionary) {

        dictionary.defineTag('enumeration', {
                onTagged: function(doclet, tag) {
                        doclet.addTag('kind', 'enumeration');
                        doclet.filename = doclet.name;
                }
        });

        dictionary.defineTag('performance', {
                mustHaveValue: true,
                onTagged: function(doclet, tag) {
                        if (!doclet.performance) { doclet.performance = []; }
                        doclet.performance.push(tag.value);
                }
        });

        dictionary.defineTag('glsl', {
                onTagged: function(doclet, tag) {
                        doclet.addTag('kind', 'glsl');
                        doclet.filename = doclet.name;
                }
        }).synonym('glslStruct').synonym('glslUniform').synonym('glslConstant').synonym('glslFunction');

        dictionary.defineTag('demo', {
                mustHaveValue: true,
                onTagged: function(doclet, tag) {
                        if (!doclet.demo) { doclet.demo = []; }
                        doclet.demo.push(tag.value);
                }
        });
};

https://github.com/AnalyticalGraphicsInc/cesium/blob/92babfac/Tools/jsdoc3/templates/default/tmpl/details.tmpl

<?js if(typeof meta.filename === 'string' && (kind === 'class' || kind === 'module' || kind === 'enumeration' || kind === 'glsl')){?>
        <h5>Source:</h5>
                <ul class="see-list">
                        <li><a href='<?js= args[5]+args[6]+args[4]+meta.filename.substring(5) ?>' target="_blank"><?js= meta.filename.substring(5) ?></a></li>
                </ul>
        <?js }?>

So, one possibility would be to override the templates, see http://usejsdoc.org/about-configuring-default-template.html

https://code.google.com/p/jsdoc/source/browse/trunk/modules/jsdoc/tag/dictionary/definitions.js?r=19 has the following definition for param

dictionary.defineTag('param', {
            mustHaveValue: true,
            canHaveType: true,
            canHaveName: true,
            onTagged: function(doclet, tag) {
                if (!doclet.params) { doclet.params = []; }
                doclet.params.push(tag.value);
            }
        })

Using that code results in something that at least does not cause errors. accessors/web/jsdoc/plugins/accessorJSDocTags.js now contains

exports.defineTags = function(dictionary) {
    dictionary.defineTag("accessor", {
        mustHaveValue: true,
        onTagged: function(doclet, tag) {
            doclet.accessor = tag.name;
        }
    });
    dictionary.defineTag("input", {
        mustHaveValue: true,
        canHaveType: true,
        canHaveName: true,
        onTagged: function(doclet, tag) {
            if (!doclet.inputs) { doclet.inputs = []; }
            doclet.inputs.push(tag.value);
        }
    });
    dictionary.defineTag("output", {
        mustHaveValue: true,
        canHaveType: true,
        canHaveName: true,
        onTagged: function(doclet, tag) {
            if (!doclet.outputs) { doclet.outputs = []; }
            doclet.outputs.push(tag.value);
        }
    });
    dictionary.defineTag("parameter", {
        mustHaveValue: true,
        canHaveType: true,
        canHaveName: true,
        onTagged: function(doclet, tag) {
            if (!doclet.parameters) { doclet.parameters = []; }
            doclet.parameters.push(tag.value);
        }
    });
};

The next step is to modify the template files

Modifying JSDoc's Template Files

Configuring JSDoc's default template states:

"Overriding the default template's layout file"

"The default template uses a file named layout.tmpl to specify the header and footer for each page in the generated documentation. In particular, this file defines which CSS and JavaScript files are loaded for each page. In JSDoc 3.3.0 and later, you can specify your own layout.tmpl file to use, which allows you to load your own custom CSS and JavaScript files in addition to, or instead of, the standard files."

"To use this feature, set the option templates.default.layoutFile to the path to your customized layout file. Relative paths are resolved against the path to the config.json file; the current working directory; and the JSDoc directory, in that order."

To do this, jsdoc.json would have:

{
  ...
  "templates": {
    "default": {
      "layoutFile": [
            "./jsdoc/templates/accessorLayout.tmpl"
        ]
    }
  }
}

The above works, but where to insert my changes? Ideally, I would like to define an option that would invoke some JavaScript that would process my custom arguments...

Ideally, we would like to only copy a portion the default 1.1Mb of jsdoc/templates/default/

One idea is to define a method that would search our directory first.

Ant

Invoke jsdoc directly

  1. Download jsdoc into vendors:
    cd $PTII/vendors
    git clone https://github.com/jsdoc3/jsdoc.git
  2. Create $PTII/b.xml:
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <project basedir="." default="jsdocs">                                                                                            
      <property name="jsdoc.home" value="/Users/cxh/ptII/vendors/jsdoc" />

      <target name="jsdocs">
        <exec executable="${jsdoc.home}/jsdoc">
          <arg value="--verbose" />
          <arg value="--recurse" />
          <arg value="/Users/cxh/ptII/ptolemy/actor/lib/jjs"/>
        </exec>
      </target>

    </project>
  3. Run:
    ant -f b.xml
  4. See out/index.html

jsdoc3-ant-task

Summary: Do not use Instead, execute jsdoc directly as above Use https://github.com/jannon/jsdoc3-ant-task, it is for JSDoc 3

  1. Download jsdoc3 and jsdoc3-ant-task:
    cd $PTII/vendors
    git clone https://github.com/jsdoc3/jsdoc
    git clone https://github.com/jannon/jsdoc3-ant-task
  2. Build jsdoc-ant-task:
    cd jsdoc-ant-task
    ant
    sudo cp /home/cxh/src/ptII/vendors/jsdoc3-ant-task/build/jar/jsdoc3-ant-task-1.0.jar /usr/local/apache-ant/lib
  3. Create b.xml
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <project basedir="." default="jsdocs">
      <property name="jsdoc.home" value="/home/cxh/src/ptII/vendors/>" />

      <path id="jsdoc3.classpath">
        <pathelement location="/home/cxh/src/ptII/vendors/jsdoc3-ant-task/build/jar/jsdoc3-ant-task-1.0.jar" />
        <pathelement location="/home/cxh/src/ptII/vendors/jsdoc3-ant-task/lib/jsdoc3/lib/js.jar" />
      </path>

      <target name="jsdocs">
        <taskdef name="jsdoc3" classname="net.jannon.ant.tasks.JsDoc3" classpathref="jsdoc3.classpath"/>
        <jsdoc3 jsdochome="${jsdoc.home}"
                file="ptolemy/actor/lib/jjs/basicFunctions.js"
                >
                <!-- arg line="-h true" / -->
        </jsdoc3>
      </target>

    </project>
  4. Run it:
    ant -f b.xml jsdocs

However, I'm still not getting output?

It looks like this uses a custom version of jsdoc3 from jsdoc3-ant-task/lib/jsdoc3?

Also, Unknown command line option found: dirname=/home/admin/libs/jsdoc-master was generated, see https://github.com/jannon/jsdoc3-ant-task/issues/9 and https://github.com/jannon/jsdoc3-ant-task/issues/7

pmWiki to JSDoc

We have pmWiki pages at https://www.terraswarm.org/accessors/wiki/Version0/OptionalJavaScriptModules that we would like to show up at https://chess.eecs.berkeley.edu/ptexternal/src/ptII/doc/codeDoc/js/index.html

The way we do this is

  1. Set up pmWiki so that we generate MarkDown text, see http://www.pmwiki.org/wiki/Cookbook/Markdown
    1. We use markdown because $PTII/doc/codeDoc/js/index.html includes $PTII/doc/jsdoc/topREADME.md, which is a MarkDown file
  2. We download

https://www.terraswarm.org/accessors/wiki/Version0/OptionalJavaScriptModules?action=markdown using wget.

  1. To do this, we need to save our cookies and use them with wget.
    1. Under Firefox, install https://addons.mozilla.org/en-us/firefox/addon/export-cookies/

Adding Hierarchy to JSDoc

Problem:

This page still doesn't show accessors in subdirectories:

https://chess.eecs.berkeley.edu/ptexternal/src/ptII/doc/codeDoc/js/index.html

Eventually, they will all be in subdirectories. All the more mature ones are in subdirectories REST, Camera, ImageFilters, etc)

Resources

Ideas

  • Create a separate page for each directory and then link to them.
    • This makes cross directory links tricky

Invoke JSDoc on itself

#!/bin/sh

./vendors/jsdoc/jsdoc \
--verbose \
--recurse \
-c \
doc/jsdoc/jsdoc.json \
-R \
doc/jsdoc/topREADME.md \
-d \
doc/codeDoc/js \
vendors/jsdoc
{
"plugins": ["plugins/markdown", "doc/jsdoc/plugins/accessorJSDocTags"],
"markdown": {
"tags": ["accessor", "input", "output", "parameter"]
},
"source": {
"excludePattern": "(fixtures|templates|test|rhino|plugins|node_modules)"
}
}

Running doit, we get Modules listed in the right with names like jsdoc/src/filter

Modules
  jsdoc/app
  jsdoc/augment
  jsdoc/borrow
  jsdoc/config
  jsdoc/doclet
  jsdoc/env
  jsdoc/fs
  jsdoc/name
  jsdoc/opts/argparser
  jsdoc/opts/args
  jsdoc/package
  jsdoc/path
  jsdoc/readme
  jsdoc/src/astnode
  jsdoc/src/filter
  jsdoc/src/handlers

Use the directory name in the accessor tag

One possibility is to have accessors/web/camera/Camera.js use

   *  @accessor camera/Camera  

instead of

   *  @accessor Camera

If this is done, then any accessors will be listed appropriately.

The nice thing about this is that it is similar to the @module tag in https://github.com/jsdoc3/jsdoc/blob/master/lib/jsdoc/src/filter.js vendors/jsdoc/lib/jsdoc/src/filter.js:

  @module jsdoc/src/filter 

Determine the root directory automatically.

If we did not want to go with using the directory in the @accessor tag, then in theory we could try to determine it. However, as we are running jsdoc in $PTII and it is possible that the accessors repo is checked out, how would we determine that org/terraswarm/accessor/accessors/web/cameras/Camera.js is in the "cameras/" directory?

  • We could search for web, but that seems unportable
  • We could use @namespace
  • We could add another tag and use it as the directory name

Type Info as a separate Attribute

Edward wrote:

"Actually, it would be better if the type information appeared in the PtDoc XML file as a separate attribute. I.e, in RESTPtDoc.xml, instead of generating:"
     <property name="timeout (parameter)"
               class="ptolemy.kernel.util.StringAttribute"
               value="({names:[&quot;int&quot;]}) The amount of time (in milliseconds) to wait for a response&#10;  before triggering a null response and an error. This defaults to 5000.">
"we should be generating:"
     <property name="timeout (parameter)"
               class="ptolemy.kernel.util.StringAttribute"
               type="int"
               value="The amount of time (in milliseconds) to wait for a response&#10;  before triggering a null response and an error. This defaults to 5000.">

accessors/web/jsdoc/templates/ptdoc/publish.js is where we generate the output.

Here's a diff of that file

bash-3.2$ svn diff
Index: jsdoc/templates/ptdoc/publish.js
===================================================================
--- jsdoc/templates/ptdoc/publish.js    (revision 432)
+++ jsdoc/templates/ptdoc/publish.js    (working copy)
@@ -70,8 +70,8 @@
             // <property name="price (output, number)" class="ptolemy.kernel.util.StringAttribute" value="The most recent trade price\
 for the stock.">

             moml += '    <property name="
' + xmlEscape(name) + " (" + propertyName + ")"
-                + '
" class="ptolemy.kernel.util.StringAttribute" value="'
-                + xmlEscape(type) + " "
+                + '
" class="ptolemy.kernel.util.StringAttribute" type="'
+                + xmlEscape(type) + '
" value="'
                 + xmlEscape(description) + '
">\n'
                 + '    </property>\n';
         });
bash-3.2$

However, the output is not necessarily in the format we want. The type= elements look like:

bash-3.2$ find . -name "*PtDoc.xml" | xargs grep type= | awk -F \" '{for(i=1;i<=NF;i++) { if ($i ~ /type=/) {print $(i+1)}}}' | sort | uniq -c
  13 ({names:[&quot;JSON&quot;]})
   4 ({names:[&quot;Object&quot;]})
   3 ({names:[&quot;array&quot;]})
   1 ({names:[&quot;array.&lt;number&gt;&quot;]})
   1 ({names:[&quot;array.&lt;{&apos;horizontal&apos;: &apos;number&apos;, &apos;vertical&apos;: &apos;number&apos;}&gt;&quot;]})
   1 ({names:[&quot;array.&lt;{&apos;real&apos;: &apos;number&apos;, &apos;imag&apos;: &apos;number&apos;}&gt;&quot;]})
  25 ({names:[&quot;boolean&quot;]})
   1 ({names:[&quot;general&quot;]})
  33 ({names:[&quot;int&quot;]})
   9 ({names:[&quot;number&quot;]})
   1 ({names:[&quot;port&quot;]})
   2 ({names:[&quot;record&quot;]})
  66 ({names:[&quot;string&quot;]})
  70 undefined
bash-3.2$
 

For example, the type in ./image/MotionDetectorPtDoc.xml is an array:

<property name="cog (port)" class="ptolemy.kernel.util.StringAttribute" type="({names:[&quot;array.&lt;{&apos;horizontal&apos;: &apos;number&apos;, &apos;vertical&apos;: &apos;number&apos;}&gt;&quot;]}) value="The horizontal and vertical position of the center of gravity of motion, in pixels.">

We could eliminate the {names:[quot; and the trailing quot;])}, below is the complete function:

/** Parse the input, output, or parameter accessor xml and generate MoML.                                                              
 *  @param {String} propertyName Either 'port' or 'parameter'                                                                          
 *  @param elements Array of elements consisting of name, type and description fields.                                                
 *  The type field is expected to be an object.                                                                                        
 *  @return MoML representation of the accessor xml.                                                                                  
 */

function accessorPropertiesToMoML(propertyName, elements) {
    var moml = '', _debugging = false;
    elements
        .forEach(function (element) {
            var name = element.name, type = element.type, description = element.description;
            if (type !== undefined) {
                type = type.toSource();
            }
            if (_debugging) {
                console.error(propertyName + " name: " + name);
                console.error(propertyName + " type: " + type);
                console.error(propertyName + " description: " + description);
            }
            // What we want:                                                                                                          
            // <property name="price (output, number)" class="ptolemy.kernel.util.StringAttribute" type="int" value="The most recent trade price for the stock.">                                                                                                            

            simplerType = xmlEscape(type);
            if (type !== undefined) {
                simplerType = simplerType.replace(/^\({names:\[&quot;/g, '').replace(/&quot;\]}\)$/g, '');
                //console.error("simplerType: type: " + type + ", new simpleType: " + simplerType);                                    
            }
            moml += '    <property name="' + xmlEscape(name) + " (" + propertyName + ")" +
                '" class="ptolemy.kernel.util.StringAttribute" type="' +
                simplerType + '" value="' +
                xmlEscape(description) + '">\n' +
                '    </property>\n';
        });
    return moml;

Now, these are the types:

bash-3.2$ find . -name "*PtDoc.xml" | xargs grep type= | awk -F \" '{for(i=1;i<=NF;i++) { if ($i ~ /type=/) {print $(i+1)}}}' | sort |\
 uniq -c | sort -nr
  70 undefined
  66 string
  33 int
  25 boolean
  13 JSON
   9 number
   4 Object
   3 array
   2 record
   1 port
   1 general
   1 array.&lt;{&apos;real&apos;: &apos;number&apos;, &apos;imag&apos;: &apos;number&apos;}&gt;
   1 array.&lt;{&apos;horizontal&apos;: &apos;number&apos;, &apos;vertical&apos;: &apos;number&apos;}&gt;
   1 array.&lt;number&gt;
bash-3.2$

  • Back to JS
Edit - History - Print - Recent Changes - Search
Page last modified on March 04, 2016, at 11:16 PM