diff options
| -rw-r--r-- | docs/collect.js | 33 | ||||
| -rw-r--r-- | docs/filter.template | 2 | ||||
| -rw-r--r-- | docs/index.html | 16 | ||||
| -rw-r--r-- | docs/overview.template | 7 | ||||
| -rw-r--r-- | docs/spec/collectSpec.js | 47 | ||||
| -rw-r--r-- | docs/specs.js | 21 | ||||
| -rw-r--r-- | docs/widget.template | 28 | ||||
| -rwxr-xr-x | gen_docs.sh | 2 | ||||
| -rw-r--r-- | lib/jasmine-1.0.1/index.js | 180 | ||||
| -rw-r--r-- | src/Angular.js | 14 | ||||
| -rw-r--r-- | src/validators.js | 4 | ||||
| -rw-r--r-- | src/widgets.js | 51 |
12 files changed, 374 insertions, 31 deletions
diff --git a/docs/collect.js b/docs/collect.js index f85f2c6b..c16366ab 100644 --- a/docs/collect.js +++ b/docs/collect.js @@ -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; } diff --git a/docs/filter.template b/docs/filter.template index f9005782..0602aff9 100644 --- a/docs/filter.template +++ b/docs/filter.template @@ -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> diff --git a/docs/index.html b/docs/index.html index f61893a4..a2cad87e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -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> diff --git a/docs/overview.template b/docs/overview.template index dbb91e2a..c4c899e2 100644 --- a/docs/overview.template +++ b/docs/overview.template @@ -1 +1,6 @@ -{{{description}}}
\ No newline at end of file +<h1><tt>{{name}}</tt></h1> +{{{description}}} + +<WIKI:SOURCE style="display:block;"> +{{{example}}} +</WIKI:SOURCE>
\ No newline at end of file diff --git a/docs/spec/collectSpec.js b/docs/spec/collectSpec.js new file mode 100644 index 00000000..2d1b559c --- /dev/null +++ b/docs/spec/collectSpec.js @@ -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; +} diff --git a/docs/specs.js b/docs/specs.js new file mode 100644 index 00000000..5f1cd153 --- /dev/null +++ b/docs/specs.js @@ -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);
\ No newline at end of file diff --git a/docs/widget.template b/docs/widget.template new file mode 100644 index 00000000..cf82eac9 --- /dev/null +++ b/docs/widget.template @@ -0,0 +1,28 @@ +<h1><tt>{{name}}</tt></h1> +<h2>Usage</h2> +<h3>In HTML Template Binding</h3> +<tt> + <pre> +<{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}>{{#usageContent}} + + {{usageContent}} +{{/usageContent}}</{{shortName}}> + </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>
\ No newline at end of file diff --git a/gen_docs.sh b/gen_docs.sh index d883fd34..575287e9 100755 --- a/gen_docs.sh +++ b/gen_docs.sh @@ -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 diff --git a/lib/jasmine-1.0.1/index.js b/lib/jasmine-1.0.1/index.js new file mode 100644 index 00000000..6c57d20a --- /dev/null +++ b/lib/jasmine-1.0.1/index.js @@ -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]; +}
\ No newline at end of file diff --git a/src/Angular.js b/src/Angular.js index 8997045b..0e26a829 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -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. diff --git a/src/validators.js b/src/validators.js index 3eb11f78..fd18d66c 100644 --- a/src/validators.js +++ b/src/validators.js @@ -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;}); +}); diff --git a/src/widgets.js b/src/widgets.js index 5eda5345..7a06fd6c 100644 --- a/src/widgets.js +++ b/src/widgets.js @@ -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"), |
