added documentation for ng:include and ng:widget and test for doc collector.

This commit is contained in:
Misko Hevery
2010-11-04 14:24:31 -07:00
parent c0d30aedfc
commit 47066e70e1
12 changed files with 374 additions and 31 deletions

View File

@@ -1,8 +1,10 @@
require.paths.push("./lib");
require.paths.push(__dirname);
var fs = require('fs'),
spawn = require('child_process').spawn,
mustache = require('../lib/mustache'),
callback = require('./callback'),
markdown = require('../lib/markdown');
mustache = require('mustache'),
callback = require('callback'),
markdown = require('markdown');
var documentation = {
section:{},
@@ -44,7 +46,7 @@ var work = callback.chain(function () {
mergeTemplate('wiki_widgets.js', 'wiki_widgets.js', documentation, callback.chain());
console.log('DONE');
});
work();
if (!this.testmode) work();
////////////////////
function noop(){}
@@ -78,6 +80,10 @@ function mergeTemplate(template, output, doc, callback){
}
function trim(string) {
return string.replace(/^[\s\n\r]+/g, '').replace(/[\s\n\r]+$/g, '');
}
function unknownTag(doc, name) {
var error = "[" + doc.raw.file + ":" + doc.raw.line + "]: unknown tag: " + name;
console.log(error);
@@ -93,7 +99,7 @@ function escapedHtmlTag(doc, name, value) {
}
function markdownTag(doc, name, value) {
doc[name] = markdown.toHTML(value);
doc[name] = markdown.toHTML(value.replace(/^#/gm, '##'));
}
var TAG = {
@@ -103,8 +109,10 @@ var TAG = {
namespace: valueTag,
css: valueTag,
see: valueTag,
usageContent: valueTag,
'function': valueTag,
description: markdownTag,
TODO: markdownTag,
returns: markdownTag,
name: function(doc, name, value) {
doc.name = value;
@@ -113,13 +121,13 @@ var TAG = {
param: function(doc, name, value){
doc.param = doc.param || [];
doc.paramRest = doc.paramRest || [];
var match = value.match(/^({([^\s=]+)(=)?}\s*)?([^\s]+|\[(\S+)+=([^\]]+)\])\s+(.*)/);
var match = value.match(/^({([^\s=]+)(=)?}\s*)?(([^\s=]+)|\[(\S+)+=([^\]]+)\])\s+(.*)/);
if (match) {
var param = {
type: match[2],
name: match[4] || match[5],
'default':match[6],
description:match[7]};
name: match[6] || match[5],
'default':match[7],
description:match[8]};
doc.param.push(param);
if (!doc.paramFirst) {
doc.paramFirst = param;
@@ -138,11 +146,11 @@ function parseNgDoc(doc){
var atText;
var match;
doc.raw.text.split(/\n/).forEach(function(line, lineNumber){
if (match = line.match(/^@(\w+)(\s+(.*))?/)) {
if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {
// we found @name ...
// if we have existing name
if (atName) {
(TAG[atName] || unknownTag)(doc, atName, atText.join('\n'));
(TAG[atName] || unknownTag)(doc, atName, trim(atText.join('\n')));
}
atName = match[1];
atText = [];
@@ -178,8 +186,9 @@ function findNgDoc(file, callback) {
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/))
if (doc.raw.text.match(/@ngdoc/)){
callback(doc);
}
doc = null;
inDoc = false;
}

View File

@@ -15,7 +15,7 @@ angular.filter.{{shortName}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/param
<h3>Parameters</h3>
<ul>
{{#param}}
<li><tt>{{name}}{{#type}}({{type}}){{/type}}</tt>: {{description}}</li>
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}{{^type}}:*{{/type}}{{#default}}={{default}}{{/default}}</tt>: {{{description}}}</li>
{{/param}}
</ul>

View File

@@ -6,19 +6,29 @@
<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" />
<script type="text/javascript">
function DocsController() {
this.docs = NG_DOC;
window.$root = this.$root;
this.getUrl = function(page){
return '#' + encodeURIComponent(page.name);
}
}
</script>
</head>
<body ng:init="docs=$window.NG_DOC; $window.$root = $root">
<body ng:controller="DocsController">
<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>
<a href="{{getUrl(page)}}"><tt>{{page.shortName}}</tt></a>
</div>
</div>
</td>
<td valign="top"><ng:include src="$location.hashPath + '.html' "></ng:include></td>
<td valign="top"><ng:include src=" './' + $location.hashPath + '.html' "></ng:include></td>
</tr>
</table>
</body>

View File

@@ -1 +1,6 @@
{{{description}}}
<h1><tt>{{name}}</tt></h1>
{{{description}}}
<WIKI:SOURCE style="display:block;">
{{{example}}}
</WIKI:SOURCE>

47
docs/spec/collectSpec.js Normal file
View File

@@ -0,0 +1,47 @@
console.log(__dirname);
require.paths.push(__dirname + "/../");
require.paths.push(__dirname + "/../../");
var fs = require('fs');
var Script = process.binding('evals').Script;
var collect = load('docs/collect.js');
describe('collect', function(){
describe('TAG', function(){
var TAG = collect.TAG;
describe('@param', function(){
var doc;
beforeEach(function(){
doc = {};
});
it('should parse with no default', function(){
TAG.param(doc, 'param',
'{(number|string)} number Number to format.');
expect(doc.param).toEqual([{
type : '(number|string)',
name : 'number',
'default' : undefined,
description : 'Number to format.' }]);
});
it('should parse with default', function(){
TAG.param(doc, 'param',
'{(number|string)=} [fractionSize=2] desc');
expect(doc.param).toEqual([{
type : '(number|string)',
name : 'fractionSize',
'default' : '2',
description : 'desc' }]);
});
});
});
});
function load(path){
var sandbox = {
require: require,
console: console,
__dirname: __dirname,
testmode: true
};
Script.runInNewContext(fs.readFileSync(path), sandbox, path);
return sandbox;
}

21
docs/specs.js Normal file
View File

@@ -0,0 +1,21 @@
require.paths.push("./lib");
var jasmine = require('jasmine-1.0.1');
var sys = require('sys');
for(var key in jasmine) {
global[key] = jasmine[key];
}
var isVerbose = false;
var showColors = true;
process.argv.forEach(function(arg){
switch(arg) {
case '--color': showColors = true; break;
case '--noColor': showColors = false; break;
case '--verbose': isVerbose = true; break;
}
});
jasmine.executeSpecsInFolder(__dirname + '/spec', function(runner, log){
process.exit(runner.results().failedCount);
}, isVerbose, showColors);

28
docs/widget.template Normal file
View File

@@ -0,0 +1,28 @@
<h1><tt>{{name}}</tt></h1>
<h2>Usage</h2>
<h3>In HTML Template Binding</h3>
<tt>
<pre>
&lt;{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}&gt;{{#usageContent}}
{{usageContent}}
{{/usageContent}}&lt;/{{shortName}}&gt;
</pre>
</tt>
<h3>Parameters</h3>
<ul>
{{#param}}
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}{{^type}}:*{{/type}}{{#default}}={{default}}{{/default}}</tt>: {{{description}}}</li>
{{/param}}
</ul>
<h3>CSS</h3>
{{{css}}}
<h2>Description</h2>
{{{description}}}
<WIKI:SOURCE style="display:block;">
{{{example}}}
</WIKI:SOURCE>

View File

@@ -1,3 +1,3 @@
#!/bin/sh
/usr/local/bin/node docs/collect.js
/usr/local/bin/node docs/specs.js --noColor && /usr/local/bin/node docs/collect.js

180
lib/jasmine-1.0.1/index.js Normal file
View File

@@ -0,0 +1,180 @@
var fs = require('fs');
var sys = require('sys');
var path = require('path');
var filename = __dirname + '/jasmine.js';
global.window = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval
};
var src = fs.readFileSync(filename);
var jasmine = process.compile(src + '\njasmine;', filename);
delete global.window;
function noop(){}
jasmine.executeSpecsInFolder = function(folder, done, isVerbose, showColors, matcher){
var log = [];
var columnCounter = 0;
var start = 0;
var elapsed = 0;
var verbose = isVerbose || false;
var fileMatcher = new RegExp(matcher || "\.js$");
var colors = showColors || false;
var specs = jasmine.getAllSpecFiles(folder, fileMatcher);
var ansi = {
green: '\033[32m',
red: '\033[31m',
yellow: '\033[33m',
none: '\033[0m'
};
for (var i = 0, len = specs.length; i < len; ++i){
var filename = specs[i];
require(filename.replace(/\.*$/, ""));
}
var jasmineEnv = jasmine.getEnv();
jasmineEnv.reporter = {
log: function(str){
},
reportSpecStarting: function(runner) {
},
reportRunnerStarting: function(runner) {
sys.puts('Started');
start = Number(new Date);
},
reportSuiteResults: function(suite) {
var specResults = suite.results();
var path = [];
while(suite) {
path.unshift(suite.description);
suite = suite.parentSuite;
}
var description = path.join(' ');
if (verbose)
log.push('Spec ' + description);
specResults.items_.forEach(function(spec){
if (spec.failedCount > 0 && spec.description) {
if (!verbose)
log.push(description);
log.push(' it ' + spec.description);
spec.items_.forEach(function(result){
log.push(' ' + result.trace.stack + '\n');
});
}
});
},
reportSpecResults: function(spec) {
var result = spec.results();
var msg = '';
if (result.passed())
{
msg = (colors) ? (ansi.green + '.' + ansi.none) : '.';
// } else if (result.skipped) { TODO: Research why "result.skipped" returns false when "xit" is called on a spec?
// msg = (colors) ? (ansi.yellow + '*' + ansi.none) : '*';
} else {
msg = (colors) ? (ansi.red + 'F' + ansi.none) : 'F';
}
sys.print(msg);
if (columnCounter++ < 50) return;
columnCounter = 0;
sys.print('\n');
},
reportRunnerResults: function(runner) {
elapsed = (Number(new Date) - start) / 1000;
sys.puts('\n');
log.forEach(function(log){
sys.puts(log);
});
sys.puts('Finished in ' + elapsed + ' seconds');
var summary = jasmine.printRunnerResults(runner);
if(colors)
{
if(runner.results().failedCount === 0 )
sys.puts(ansi.green + summary + ansi.none);
else
sys.puts(ansi.red + summary + ansi.none);
} else {
sys.puts(summary);
}
(done||noop)(runner, log);
}
};
jasmineEnv.execute();
};
jasmine.getAllSpecFiles = function(dir, matcher){
var specs = [];
if (fs.statSync(dir).isFile() && dir.match(matcher)) {
specs.push(dir);
} else {
var files = fs.readdirSync(dir);
for (var i = 0, len = files.length; i < len; ++i){
var filename = dir + '/' + files[i];
if (fs.statSync(filename).isFile() && filename.match(matcher)){
specs.push(filename);
}else if (fs.statSync(filename).isDirectory()){
var subfiles = this.getAllSpecFiles(filename, matcher);
subfiles.forEach(function(result){
specs.push(result);
});
}
}
}
return specs;
};
jasmine.printRunnerResults = function(runner){
var results = runner.results();
var suites = runner.suites();
var msg = '';
msg += suites.length + ' test' + ((suites.length === 1) ? '' : 's') + ', ';
msg += results.totalCount + ' assertion' + ((results.totalCount === 1) ? '' : 's') + ', ';
msg += results.failedCount + ' failure' + ((results.failedCount === 1) ? '' : 's') + '\n';
return msg;
};
function now(){
return new Date().getTime();
}
jasmine.asyncSpecWait = function(){
var wait = jasmine.asyncSpecWait;
wait.start = now();
wait.done = false;
(function innerWait(){
waits(10);
runs(function() {
if (wait.start + wait.timeout < now()) {
expect('timeout waiting for spec').toBeNull();
} else if (wait.done) {
wait.done = false;
} else {
innerWait();
}
});
})();
};
jasmine.asyncSpecWait.timeout = 4 * 1000;
jasmine.asyncSpecDone = function(){
jasmine.asyncSpecWait.done = true;
};
for ( var key in jasmine) {
exports[key] = jasmine[key];
}

View File

@@ -144,13 +144,6 @@ var _undefined = undefined,
*
*
* @example
* //TODO this example current doesn't show up anywhere because the overview template doesn't
* // render it.
*
* The following example filter reverses a text string. In addition, it conditionally makes the
* text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
* modification).
<script type="text/javascript">
angular.filter.reverse = function(input, uppercase, color) {
var out = "";
@@ -166,12 +159,17 @@ var _undefined = undefined,
return out;
};
</script>
The following example filter reverses a text string. In addition, it conditionally makes the
text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
modification).
<hr/>
<span ng:non-bindable="true">{{"hello"|reverse}}</span>: {{"hello"|reverse}}<br>
<span ng:non-bindable="true">{{"hello"|reverse:true}}</span>: {{"hello"|reverse:true}}<br>
<span ng:non-bindable="true">{{"hello"|reverse:true:"blue"}}</span>:
{{"hello"|reverse:true:"blue"}}
* //TODO: I completely dropped a mention of using the other option (setter method), it's
* @TODO: I completely dropped a mention of using the other option (setter method), it's
* confusing to have two ways to do the same thing. I just wonder if we should prefer using the
* setter way over direct assignment because in the future we might want to be able to intercept
* filter registrations for some reason.

View File

@@ -1,4 +1,4 @@
foreach({
extend(angularValidator, {
'noop': function() { return _null; },
'regexp': function(value, regexp, msg) {
@@ -132,4 +132,4 @@ foreach({
return inputState.error;
}
}, function(v,k) {angularValidator[k] = v;});
});

View File

@@ -244,13 +244,28 @@ angularWidget('option', function(){
});
/*ng:doc
* @type widget
* @name ng:include
/**
* @ngdoc widget
* @name angular.widget.ng:include
*
* @description
* Include external HTML fragment.
*
* Keep in mind that Same Origin Policy applies to included resources
* (e.g. ng:include won't work for file:// access).
*
* @param {string} src expression evaluating to URL.
* @param {Scope=} [scope=new_child_scope] expression evaluating to angular.scope
*
* @example
* <select name="url">
* <option value="angular.filter.date.html">date filter</option>
* <option value="angular.filter.html.html">html filter</option>
* <option value="">(blank)</option>
* </select>
* <tt>url = <a href="{{url}}">{{url}}</a></tt>
* <hr/>
* <ng:include src="url"></ng:include>
*
* @scenario
*/
@@ -299,6 +314,36 @@ angularWidget('ng:include', function(element){
}
});
/**
* @ngdoc widget
* @name angular.widget.ng:switch
*
* @description
* Conditionally change the DOM structure.
*
* @usageContent
* <any ng:switch-when="matchValue1"/>...</any>
* <any ng:switch-when="matchValue2"/>...</any>
* ...
* <any ng:switch-when="matchValueN"/>...</any>
*
* @param {*} on expression to match against <tt>ng:switch-when</tt>.
*
* @example
<select name="switch">
<option>settings</option>
<option>home</option>
</select>
<tt>switch={{switch}}</tt>
</hr>
<ng:switch on="switch" >
<div ng:switch-when="settings">Settings Div</div>
<span ng:switch-when="home">Home Span</span>
</ng:switch>
</code>
*
* @scenario
*/
var ngSwitch = angularWidget('ng:switch', function (element){
var compiler = this,
watchExpr = element.attr("on"),