diff options
| author | Misko Hevery | 2010-10-27 15:31:10 -0700 |
|---|---|---|
| committer | Igor Minar | 2010-11-03 09:47:22 -0700 |
| commit | 659af29adbd041fbbbaf041ead53266210a61f4e (patch) | |
| tree | 32a9ecff4482ae883321e2975fe74057795b21d1 /docs | |
| parent | 1fe7e3a1302e948a31ab80d02ede6975c3bddd58 (diff) | |
| download | angular.js-659af29adbd041fbbbaf041ead53266210a61f4e.tar.bz2 | |
jsdoc parser + generator + viewer + scenario runner
- parse jsdocs from source code
- generate prerendered (markdown + mustache) partials
- generate json
- generate scenario runner for examples in docs
- basic angular doc viewer
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/callback.js | 66 | ||||
| -rw-r--r-- | docs/collect.js | 206 | ||||
| -rw-r--r-- | docs/docs-data.js | 1 | ||||
| -rw-r--r-- | docs/docs-scenario.html | 9 | ||||
| -rw-r--r-- | docs/docs-scenario.js | 9 | ||||
| -rw-r--r-- | docs/docs.js | 7 | ||||
| -rw-r--r-- | docs/filter.template | 33 | ||||
| -rw-r--r-- | docs/filter:currency.html | 21 | ||||
| -rw-r--r-- | docs/filter:currency.md | 20 | ||||
| -rw-r--r-- | docs/index.html | 25 | ||||
| -rw-r--r-- | docs/overview.template | 1 | ||||
| -rw-r--r-- | docs/wiki_widgets.css | 58 | ||||
| -rw-r--r-- | docs/wiki_widgets.js | 51 |
13 files changed, 466 insertions, 41 deletions
diff --git a/docs/callback.js b/docs/callback.js new file mode 100644 index 00000000..0d0669d1 --- /dev/null +++ b/docs/callback.js @@ -0,0 +1,66 @@ +function noop(){} + +function chain(delegateFn, explicitDone){ + var onDoneFn = noop; + var onErrorFn = noop; + var waitForCount = 1; + delegateFn = delegateFn || noop; + var stackError = new Error('capture stack'); + + function decrementWaitFor() { + waitForCount--; + if (waitForCount == 0) + onDoneFn(); + } + + function self(){ + try { + return delegateFn.apply(self, arguments); + } catch (error) { + self.error(error); + } finally { + if (!explicitDone) + decrementWaitFor(); + } + }; + self.onDone = function(callback){ + onDoneFn = callback; + return self; + }; + self.onError = function(callback){ + onErrorFn = callback; + return self; + }; + self.waitFor = function(callback){ + if (waitForCount == 0) + throw new Error("Can not wait on already called callback."); + waitForCount++; + return chain(callback).onDone(decrementWaitFor).onError(self.error); + }; + + self.waitMany = function(callback){ + if (waitForCount == 0) + throw new Error("Can not wait on already called callback."); + waitForCount++; + return chain(callback, true).onDone(decrementWaitFor).onError(self.error); + }; + + self.done = function(callback){ + decrementWaitFor(); + }; + + self.error = function(error) { + var stack = stackError.stack.split(/\n\r?/).splice(2); + var nakedStack = []; + stack.forEach(function(frame){ + if (!frame.match(/callback\.js:\d+:\d+\)$/)) + nakedStack.push(frame); + }); + error.stack = error.stack + '\nCalled from:\n' + nakedStack.join('\n'); + onErrorFn(error); + }; + + return self; +} + +exports.chain = chain; diff --git a/docs/collect.js b/docs/collect.js new file mode 100644 index 00000000..a3c2a25c --- /dev/null +++ b/docs/collect.js @@ -0,0 +1,206 @@ +var fs = require('fs'), + spawn = require('child_process').spawn, + mustache = require('../lib/mustache'), + callback = require('./callback'), + markdown = require('../lib/markdown'); + +var documentation = { + section:{}, + all:[] +}; + +var SRC_DIR = "docs/"; +var OUTPUT_DIR = "build/docs/"; + +var work = callback.chain(function () { + console.log('Parsing Angular Reference Documentation'); + mkdirPath(OUTPUT_DIR, work.waitFor(function(){ + findJsFiles('src', work.waitMany(function(file) { + //console.log('reading', file, '...'); + findNgDoc(file, work.waitMany(function(doc) { + parseNgDoc(doc); + if (doc.ngdoc) { + delete doc.raw.text; + var section = documentation.section; + (section[doc.ngdoc] = section[doc.ngdoc] || []).push(doc); + documentation.all.push(doc); + console.log('Found:', doc.ngdoc + ':' + doc.shortName); + mergeTemplate( + doc.ngdoc + '.template', + doc.name + '.html', doc, work.waitFor()); + } + })); + })); + })); +}).onError(function(err){ + console.log('ERROR:', err.stack || err); +}).onDone(function(){ + mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(documentation)}, callback.chain()); + mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain()); + copy('docs-scenario.html', callback.chain()); + copy('index.html', callback.chain()); + mergeTemplate('docs.js', 'docs.js', documentation, callback.chain()); + mergeTemplate('wiki_widgets.css', 'wiki_widgets.css', documentation, callback.chain()); + mergeTemplate('wiki_widgets.js', 'wiki_widgets.js', documentation, callback.chain()); + console.log('DONE'); +}); +work(); +//////////////////// + +function noop(){} +function mkdirPath(path, callback) { + var parts = path.split(/\//); + path = '.'; + (function next(){ + if (parts.length) { + path += '/' + parts.shift(); + fs.mkdir(path, 0777, next); + } else { + callback(); + } + })(); +} + +function copy(name, callback){ + fs.readFile(SRC_DIR + name, callback.waitFor(function(err, content){ + if (err) return this.error(err); + fs.writeFile(OUTPUT_DIR + name, content, callback); + })); +} + +function mergeTemplate(template, output, doc, callback){ + fs.readFile(SRC_DIR + template, + callback.waitFor(function(err, template){ + if (err) return this.error(err); + var content = mustache.to_html(template.toString(), doc); + fs.writeFile(OUTPUT_DIR + output, content, callback); + })); +} + + +function unknownTag(doc, name) { + var error = "[" + doc.raw.file + ":" + doc.raw.line + "]: unknown tag: " + name; + console.log(error); + throw new Error(error); +} + +function valueTag(doc, name, value) { + doc[name] = value; +} + +function markdownTag(doc, name, value) { + doc[name] = markdown.toHTML(value); +} + +var TAG = { + ngdoc: valueTag, + example: valueTag, + scenario: valueTag, + namespace: valueTag, + css: valueTag, + see: valueTag, + 'function': valueTag, + description: markdownTag, + returns: markdownTag, + name: function(doc, name, value) { + doc.name = value; + doc.shortName = value.split(/\./).pop(); + }, + param: function(doc, name, value){ + doc.param = doc.param || []; + doc.paramRest = doc.paramRest || []; + var match = value.match(/^({([^\s=]+)(=([^\s]+))?}\s*)?([^\s]+)\s*(.*)/); + if (match) { + var param = { + type: match[2], + 'default':match[4], + name: match[5], + description:match[6]}; + doc.param.push(param); + if (!doc.paramFirst) { + doc.paramFirst = param; + } else { + doc.paramRest.push(param); + } + } else { + throw "[" + doc.raw.file + ":" + doc.raw.line + + "]: @param must be in format '{type} name=value description' got: " + value; + } + } +}; + +function parseNgDoc(doc){ + var atName; + var atText; + var match; + doc.raw.text.split(/\n/).forEach(function(line, lineNumber){ + if (match = line.match(/^@(\w+)(\s+(.*))?/)) { + // we found @name ... + // if we have existing name + if (atName) { + (TAG[atName] || unknownTag)(doc, atName, atText.join('\n')); + } + atName = match[1]; + atText = []; + if(match[3]) atText.push(match[3]); + } else { + if (atName) { + atText.push(line); + } else { + // ignore + } + } + }); + if (atName) { + (TAG[atName] || unknownTag)(doc, atName, atText.join('\n')); + } +} + +function findNgDoc(file, callback) { + fs.readFile(file, callback.waitFor(function(err, content){ + var lines = content.toString().split(/\n\r?/); + var doc; + var match; + var inDoc = false; + lines.forEach(function(line, lineNumber){ + lineNumber++; + // is the comment starting? + if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) { + line = match[1]; + inDoc = true; + doc = {raw:{file:file, line:lineNumber, text:[]}}; + } + // are we done? + if (inDoc && line.match(/\*\//)) { + doc.raw.text = doc.raw.text.join('\n'); + doc.raw.text = doc.raw.text.replace(/^\n/, ''); + if (doc.raw.text.match(/@ngdoc/)) + callback(doc); + doc = null; + inDoc = false; + } + // is the comment add text + if (inDoc){ + doc.raw.text.push(line.replace(/^\s*\*\s?/, '')); + } + }); + callback.done(); + })); +} + +function findJsFiles(dir, callback){ + fs.readdir(dir, callback.waitFor(function(err, files){ + if (err) return this.error(err); + files.forEach(function(file){ + var path = dir + '/' + file; + fs.lstat(path, callback.waitFor(function(err, stat){ + if (err) return this.error(err); + if (stat.isDirectory()) + findJsFiles(path, callback.waitMany(callback)); + else if (/\.js$/.test(path)) + callback(path); + })); + }); + callback.done(); + })); +} diff --git a/docs/docs-data.js b/docs/docs-data.js new file mode 100644 index 00000000..27b3fab5 --- /dev/null +++ b/docs/docs-data.js @@ -0,0 +1 @@ +NG_DOC={{{JSON}}};
\ No newline at end of file diff --git a/docs/docs-scenario.html b/docs/docs-scenario.html new file mode 100644 index 00000000..83ca6fdf --- /dev/null +++ b/docs/docs-scenario.html @@ -0,0 +1,9 @@ +<!DOCTYPE HTML> +<html xmlns:ng="http://angularjs.org" wiki:ng="http://angularjs.org"> +<head> + <script type="text/javascript" src="../angular-scenario.js" ng:autobind></script> + <script type="text/javascript" src="docs-scenario.js"></script> +</head> +<body> +</body> +</html>
\ No newline at end of file diff --git a/docs/docs-scenario.js b/docs/docs-scenario.js new file mode 100644 index 00000000..f0c6edb9 --- /dev/null +++ b/docs/docs-scenario.js @@ -0,0 +1,9 @@ +{{#all}} +describe('{{name}}', function(){ + beforeEach(function(){ + navigateTo('index.html#{{name}}'); + }); + // {{raw.file}}:{{raw.line}} +{{{scenario}}} +}); +{{/all}}
\ No newline at end of file diff --git a/docs/docs.js b/docs/docs.js new file mode 100644 index 00000000..6f5c5034 --- /dev/null +++ b/docs/docs.js @@ -0,0 +1,7 @@ +function DocController($resource, $location){ + this.docs = $resource('documentation.json').get(); + this.getPartialDoc = function(){ + return encodeURIComponent($location.hashPath) + '.html'; + }; +} +DocController.$inject=['$resource', '$location'];
\ No newline at end of file diff --git a/docs/filter.template b/docs/filter.template new file mode 100644 index 00000000..677a8785 --- /dev/null +++ b/docs/filter.template @@ -0,0 +1,33 @@ +<h1><tt>{{name}}</tt></h1> +<h2>Usage</h2> +<h3>In HTML Template Binding</h3> +<tt> + <span>{{</span> + {{paramFirst.name}}_expression + | {{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}={{default}}]</i>{{/default}}{{/paramRest}} + <span> }}</span> +</tt> +<h3>In JavaScript</h3> +<tt ng:non-bindable> +angular.filter.{{shortName}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/paramRest}} ); +</tt> + +<h3>Parameters</h3> +<ul> + {{#param}} + <li><tt>{{name}}{{#type}}({{type}}){{/type}}</tt>: {{description}}</li> + {{/param}} +</ul> + +<h3>Returns</h3> +{{{returns}}} + +<h3>CSS</h3> +{{{css}}} + +<h2>Description</h2> +{{{description}}} + +<WIKI:SOURCE style="display:block;"> +{{example}} +</WIKI:SOURCE>
\ No newline at end of file diff --git a/docs/filter:currency.html b/docs/filter:currency.html deleted file mode 100644 index bd277bb8..00000000 --- a/docs/filter:currency.html +++ /dev/null @@ -1,21 +0,0 @@ -<h1>filter:currency</h1> - -<p>formats a number as a currency (ie $1,234.56)</p> - -<p>@format <em>expression</em> | currency</p> - -<p><example> -<input type="text" name="amount" value="1234.56"/> <br/> -{{amount | currency}} -</example></p> - -<p><test> - it('should init with 1234.56', function(){ - expect(bind('amount')).toEqual('$1,234.56'); - }); - it('should update', function(){ - element(':input[name=amount]').value('-1234'); - expect(bind('amount')).toEqual('-$1,234.00'); - expect(bind('amount')).toHaveColor('red'); - }); -</test></p> diff --git a/docs/filter:currency.md b/docs/filter:currency.md deleted file mode 100644 index 44687a91..00000000 --- a/docs/filter:currency.md +++ /dev/null @@ -1,20 +0,0 @@ -# filter:currency -formats a number as a currency (ie $1,234.56) - -@format _expression_ | currency - -<example> -<input type="text" name="amount" value="1234.56"/> <br/> -{{amount | currency}} -</example> - -<test> - it('should init with 1234.56', function(){ - expect(bind('amount')).toEqual('$1,234.56'); - }); - it('should update', function(){ - element(':input[name=amount]').value('-1234'); - expect(bind('amount')).toEqual('-$1,234.00'); - expect(bind('amount')).toHaveColor('red'); - }); -</test>
\ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..f61893a4 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html xmlns:ng="http://angularjs.org" wiki:ng="http://angularjs.org"> +<head> + <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script> + <script type="text/javascript" src="docs-data.js"></script> + <script type="text/javascript" src="../angular.min.js" ng:autobind></script> + <script type="text/javascript" src="http://angularjs.org/extensions/wiki_widgets.js"></script> + <link rel="stylesheet" href="http://angularjs.org/extensions/wiki_widgets.css" type="text/css" media="screen" /> +</head> +<body ng:init="docs=$window.NG_DOC; $window.$root = $root"> + <table> + <tr> + <td valign="top"> + <div ng:repeat="(name, type) in docs.section"> + <b>{{name}}</b> + <div ng:repeat="page in type"> + <a href="#{{page.name}}"><tt>{{page.shortName}}</tt></a> + </div> + </div> + </td> + <td valign="top"><ng:include src="$location.hashPath + '.html' "></ng:include></td> + </tr> + </table> +</body> +</html>
\ No newline at end of file diff --git a/docs/overview.template b/docs/overview.template new file mode 100644 index 00000000..dbb91e2a --- /dev/null +++ b/docs/overview.template @@ -0,0 +1 @@ +{{{description}}}
\ No newline at end of file diff --git a/docs/wiki_widgets.css b/docs/wiki_widgets.css new file mode 100644 index 00000000..a1f6e939 --- /dev/null +++ b/docs/wiki_widgets.css @@ -0,0 +1,58 @@ +WIKI\:SOURCE, +WIKI\:SOURCE>ul.tabs, +WIKI\:SOURCE>ul.tabs>li { + display: block; + margin: 0; + padding: 0; +} +WIKI\:SOURCE>ul.tabs { + margin: 0 !important; + padding: 0 !important; +} + +WIKI\:SOURCE > ul.tabs > li.tab { + margin: 0 0 0 .8em !important; + display: inline-block; + border: 1px solid gray; + padding: .1em .8em .4em .8em !important; + -moz-border-radius-topleft: 5px; + -moz-border-radius-topright: 5px; + -webkit-border-top-left-radius: 5px; + -webkit-border-top-right-radius: 5px; + border-bottom: none; + cursor: pointer; + background-color: lightgray; + margin-bottom: -2px; + position: relative; + z-index: 0; +} + +WIKI\:SOURCE > ul.tabs > li.tab.selected { + z-index: 2; + background-color: white; + font-weight: bold; + border-width: 2px; +} + +WIKI\:SOURCE > ul.tabs > li.pane { + border: 2px solid gray; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + background-color: white; + padding: 5px !important; +} + +WIKI\:SOURCE > ul.tabs > li.pane { + display:none; +} + +WIKI\:SOURCE > ul.tabs > li.pane.selected { + position: relative; + z-index: 1; + display:block; +} +WIKI\:SOURCE > ul.tabs > li.pane.source { + font-size: .8em !important; +} +//////////////////
\ No newline at end of file diff --git a/docs/wiki_widgets.js b/docs/wiki_widgets.js new file mode 100644 index 00000000..0147536b --- /dev/null +++ b/docs/wiki_widgets.js @@ -0,0 +1,51 @@ +(function(){ + var HTML_TEMPLATE = + '<!DOCTYPE HTML>\n' + + '<html xmlns:ng="http://angularjs.org">\n' + + ' <head>\n' + + ' <script type="text/javascript"\n' + + ' src="http://angularjs.org/ng/js/angular-debug.js" ng:autobind></script>\n' + + ' </head>\n' + + ' <body>\n' + + '_HTML_SOURCE_\n' + + ' </body>\n' + + '</html>'; + + angular.widget('WIKI:SOURCE', function(element){ + this.descend(true); + var html = element.text(); + element.show(); + var tabs = angular.element( + '<ul class="tabs">' + + '<li class="tab selected" to="angular"><angular/></li>' + + '<li class="tab" to="plain">plain</li>' + + '<li class="tab" to="source">source</li>' + + '<li class="pane selected angular">' + html + '</li>' + + '<li class="pane plain" ng:non-bindable>' + html + '</li>' + + '<li class="pane source" ng:non-bindable><pre class="brush: js; html-script: true"></pre></li>' + + '</ul>'); + var pre = tabs. + find('>li.source>pre'). + text(HTML_TEMPLATE.replace('_HTML_SOURCE_', html)); + var color = element.attr('color') || 'white'; + element.html(''); + element.append(tabs); + element.find('>ul.tabs>li.pane').css('background-color', color); + var script = (html.match(/<script[^\>]*>([\s\S]*)<\/script>/) || [])[1] || ''; + try { + eval(script); + } catch (e) { + alert(e); + } + return function(element){ + element.find('>ul.tabs>li.tab').click(function(){ + if ($(this).is(".selected")) return; + element. + find('>ul.tabs>li.selected'). + add(this). + add(element.find('>ul>li.pane.' + angular.element(this).attr('to'))). + toggleClass('selected'); + }); + }; + }); +})();
\ No newline at end of file |
