diff options
Diffstat (limited to 'docs')
27 files changed, 1256 insertions, 698 deletions
| diff --git a/docs/directive.template b/docs/directive.template deleted file mode 100644 index 07e38ea7..00000000 --- a/docs/directive.template +++ /dev/null @@ -1,58 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -<h2>Description</h2> -{{{description}}} - -<h2>Usage</h2> -<h3>In HTML Template Binding</h3> -<tt> -  <pre> -<{{element}} {{shortName}}="{{paramFirst.name}}"> -  ... -</{{element}}> -  </pre> -</tt> - -<h3>Parameters</h3> -<ul> -  {{#param}} -  <li><tt>{{name}}</tt> – -      <tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt> -      <tt>{{#default}}[{{default}}]{{/default}}</tt> -      – {{{description}}}</li> -  {{/param}} -</ul> -{{{paramDescription}}} - -{{#css}} -<h3>CSS</h3> -{{{css}}} -{{/css}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} diff --git a/docs/docs-data.js b/docs/docs-data.js deleted file mode 100644 index 62feb2e9..00000000 --- a/docs/docs-data.js +++ /dev/null @@ -1 +0,0 @@ -NG_PAGES={{{JSON}}};
\ No newline at end of file diff --git a/docs/filter.template b/docs/filter.template deleted file mode 100644 index 516957d0..00000000 --- a/docs/filter.template +++ /dev/null @@ -1,65 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -<h2>Description</h2> -{{{description}}} - -<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}}</tt> – -      <tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt> -      <tt>{{#default}}[{{default}}]{{/default}}</tt> -      – {{{description}}}</li> -  {{/param}} -</ul> - -{{#returns}} -<h3>Returns</h3> -<tt>{{{{type}}}}</tt> {{{description}}} -{{/returns}} - -{{#css}} -<h3>CSS</h3> -{{{css}}} -{{/css}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} diff --git a/docs/formatter.template b/docs/formatter.template deleted file mode 100644 index 9ccdc811..00000000 --- a/docs/formatter.template +++ /dev/null @@ -1,53 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -<h2>Description</h2> -{{{description}}} - -<h2>Usage</h2> -<h3>In HTML Template Binding</h3> -<tt> -  <input type="text" ng:format="{{shortName}}"> -</tt> -<h3>In JavaScript</h3> -<tt ng:non-bindable> -var userInputString = angular.formatter.{{shortName}}.format(modelValue);<br/> -var modelValue = angular.formatter.{{shortName}}.parse(userInputString); -</tt> - -{{#returns}} -<h3>Returns</h3> -<tt>{{{{type}}}}</tt> {{{description}}} -{{/returns}} - -{{#css}} -<h3>CSS</h3> -{{{css}}} -{{/css}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} diff --git a/docs/function.template b/docs/function.template deleted file mode 100644 index 8765277b..00000000 --- a/docs/function.template +++ /dev/null @@ -1,52 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -<h2>Description</h2> -{{{description}}} - -<h2>Usage</h2> -<tt ng:non-bindable> -{{name}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/paramRest}} ); -</tt> - -<h3>Parameters</h3> -<ul> -  {{#param}} -  <li><tt>{{name}}</tt> – -      <tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt> -      <tt>{{#default}}[{{default}}]{{/default}}</tt> -      – {{{description}}}</li> -  {{/param}} -</ul> - -{{#returns}} -<h3>Returns</h3> -<tt>{{{{type}}}}</tt> {{{description}}} -{{/returns}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} diff --git a/docs/overview.template b/docs/overview.template deleted file mode 100644 index 9902a16f..00000000 --- a/docs/overview.template +++ /dev/null @@ -1,31 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -{{{description}}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} diff --git a/docs/spec/collectSpec.js b/docs/spec/collectSpec.js deleted file mode 100644 index f0783bdf..00000000 --- a/docs/spec/collectSpec.js +++ /dev/null @@ -1,288 +0,0 @@ -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('markdown', function(){ -    it('should replace angular in markdown', function(){ -      expect(collect.markdown('<angular/>')). -        toEqual('<p><tt><angular/></tt></p>'); -    }); -     -    it('should not replace anything in <pre>', function(){ -      expect(collect.markdown('bah x\n<pre>\nangular.k\n</pre>\n asdf x')). -        toEqual( -            '<p>bah x</p>' + -            '<pre>\nangular.k\n</pre>' +  -            '<p>asdf x</p>'); -    }); -     -    it('should replace text between two <pre></pre> tags', function() { -      expect(collect.markdown('<pre>x</pre># One<pre>b</pre>')). -        toEqual('<pre>x</pre><h1>One</h1><pre>b</pre>'); -    }); -  }); -   -  describe('processNgDoc', function() { -    var processNgDoc = collect.processNgDoc, -        documentation; - -    beforeEach(function() { -      documentation = { -        pages: [], -        byName: {} -      }; -    }); -     -    it('should store references to docs by name', function() { -      var doc = {ngdoc: 'section', name: 'fake', raw: {text:''}}; -      processNgDoc(documentation, doc); -      expect(documentation.byName.fake).toBe(doc); -    }); -     -    it('should connect doc to owner (specified by @methodOf)', function() { -      var parentDoc = {ngdoc: 'section', name: 'parent', raw: {text:''}}; -      var doc = {ngdoc: 'section', name: 'child', methodOf: 'parent', raw: {text:''}}; -      processNgDoc(documentation, parentDoc); -      processNgDoc(documentation, doc); -      expect(documentation.byName.parent.method).toBeDefined(); -      expect(documentation.byName.parent.method[0]).toBe(doc); -    }); -     -    it('should not add doc to sections if @memberOf specified', function() { -      var parentDoc = {ngdoc: 'parent', name: 'parent', raw: {text:''}}; -      var doc = {ngdoc: 'child', name: 'child', methodOf: 'parent', raw: {text:''}}; -      processNgDoc(documentation, parentDoc); -      processNgDoc(documentation, doc); -      expect(documentation.pages.child).not.toBeDefined(); -    }); -     -    it('should throw exception if owner does not exist', function() { -      expect(function() { -        processNgDoc(documentation, {ngdoc: 'section', methodOf: 'not.exist', raw: {text:''}}); -      }).toThrow('Owner "not.exist" is not defined.'); -    }); -     -    it('should ignore non-ng docs', function() { -      var doc = {name: 'anything'}; -      expect(function() { -        processNgDoc(documentation, doc); -      }).not.toThrow(); -      expect(documentation.pages).not.toContain(doc); -    }); -  }); -   -  describe('TAG', function(){ -    var TAG = collect.TAG; -    var doc; -    beforeEach(function(){ -      doc = {}; -    }); -     -    describe('@param', function(){ -      it('should parse with no default', function(){ -        TAG.param(doc, 'param',  -            '{(number|string)} number Number \n to format.'); -        expect(doc.param).toEqual([{  -          type : '(number|string)',  -          name : 'number', -          optional: false, -          'default' : undefined,  -          description : 'Number \n to format.' }]); -      }); -      it('should parse with default and optional', function(){ -        TAG.param(doc, 'param',  -            '{(number|string)=} [fractionSize=2] desc'); -        expect(doc.param).toEqual([{  -          type : '(number|string)',  -          name : 'fractionSize', -          optional: true, -          'default' : '2',  -          description : 'desc' }]); -      }); -    }); -     -    describe('@requires', function() { -      it('should parse more @requires tag into array', function() { -        TAG.requires(doc, 'requires', '$service'); -        TAG.requires(doc, 'requires', '$another'); -         -        expect(doc.requires).toEqual([ -          {name: '$service'}, -          {name: '$another'} -        ]); -      }); -    }); - -    describe('@property', function() { -      it('should parse @property tags into array', function() { -        TAG.property(doc, 'property', '{type} name1 desc'); -        TAG.property(doc, 'property', '{type} name2 desc'); -        expect(doc.property.length).toEqual(2); -      }); -       -      it('should parse @property with only name', function() { -        TAG.property(doc, 'property', 'fake'); -        expect(doc.property[0].name).toEqual('fake'); -      }); -       -      it('should parse @property with optional type', function() { -        TAG.property(doc, 'property', '{string} name'); -        expect(doc.property[0].name).toEqual('name'); -        expect(doc.property[0].type).toEqual('string'); -      }); -       -      it('should parse @property with optional description', function() { -        TAG.property(doc, 'property', 'name desc rip tion'); -        expect(doc.property[0].name).toEqual('name'); -        expect(doc.property[0].description).toEqual('desc rip tion'); -      }); -       -      it('should parse @property with type and description both', function() { -        TAG.property(doc, 'property', '{bool} name desc rip tion'); -        expect(doc.property[0].name).toEqual('name'); -        expect(doc.property[0].type).toEqual('bool'); -        expect(doc.property[0].description).toEqual('desc rip tion'); -      }); -       -      /** -       * If property description is undefined, this variable is not set in the template, -       * so the whole @description tag is used instead -       */ -      it('should set undefined description to "false"', function() { -        TAG.property(doc, 'property', 'name'); -        expect(doc.property[0].description).toBe(false); -      }); -    }); -     -    describe('@methodOf', function() { -      it('should parse @methodOf tag', function() { -        expect(function() { -          TAG.methodOf(doc, 'methodOf', 'parentName'); -        }).not.toThrow(); -        expect(doc.methodOf).toEqual('parentName'); -      }); -    }); -     -    describe('@returns', function() { -      it('should not parse @returns without type', function() { -      expect(function() {TAG.returns(doc, 'returns', 'lala');}) -        .toThrow(); -      }); -       -      it('should parse @returns with type and description', function() { -        TAG.returns(doc, 'returns', '{string} descrip tion'); -        expect(doc.returns).toEqual({type: 'string', description: 'descrip tion'}); -      }); - -      it('should transform description of @returns with markdown', function() { -        TAG.returns(doc, 'returns', '{string} descrip *tion*'); -        expect(doc.returns).toEqual({type: 'string', description: 'descrip <em>tion</em>'}); -      }); - -      it('should support multiline content', function() { -        TAG.returns(doc, 'returns', '{string} description\n new line\n another line'); -        expect(doc.returns). -          toEqual({type: 'string', description: 'description\n new line\n another line'}); -      }); -    }); -     -    describe('@description', function(){ -      it('should support pre blocks', function(){ -        TAG.description(doc, 'description', '<pre>abc</pre>'); -        expect(doc.description). -          toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>'); -      }); - -      it('should support multiple pre blocks', function() { -        TAG.description(doc, 'description', 'foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>'); -        expect(doc.description). -          toBe('<p>foo </p>' + -               '<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>' + -               '<h2>bah</h2>\n\n' + -               '<p>foo </p>' + -               '<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>'); - -      }); - -      it('should support nested @link annotations with or without description', function() { -        TAG.description(doc, 'description', -            'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' + -            'dad{@link angular.foo}\n\n' + -            '{@link angular.directive.ng:foo ng:foo}'); -        expect(doc.description). -          toBe('<p>foo <a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' + -               '<p>da <a href="#!angular.foo"><code>bar foo bar</code></a> </p>\n\n' + -               '<p>dad<a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' + -               '<p><a href="#!angular.directive.ng:foo"><code>ng:foo</code></a></p>'); -      }); - -      it('should increment all headings by one', function() { -        TAG.description(doc, 'description', '# foo\nabc'); -        expect(doc.description). -          toBe('<h2>foo</h2>\n\n<p>abc</p>'); -      }); -    }); - -    describe('@example', function(){ -      it('should not remove {{}}', function(){ -        TAG.example(doc, 'example', 'text {{ abc }}'); -        expect(doc.example).toEqual('text {{ abc }}'); -      }); -    }); - -    describe('@deprecated', function() { -      it('should parse @deprecated', function() { -        TAG.deprecated(doc, 'deprecated', 'Replaced with foo.'); -        expect(doc.deprecated).toBe('Replaced with foo.'); -      }) -    }); - -    describe('@workInProgress', function() { -      it('should parse @workInProgress without a description and default to true', function() { -        TAG.workInProgress(doc, 'workInProgress', ''); -        expect(doc.workInProgress).toEqual({description: ''}); -      }); - -      it('should parse @workInProgress with a description', function() { -        TAG.workInProgress(doc, 'workInProgress', 'my description'); -        expect(doc.workInProgress).toEqual({description: '<p>my description</p>'}); -      }); -    }); - -  }); -   -  describe('trim', function(){ -    var trim = collect.trim; -    it('should remove leading/trailing space', function(){ -      expect(trim('  \nabc\n  ')).toEqual('abc'); -    }); -     -    it('should remove leading space on every line', function(){ -      expect(trim('\n 1\n  2\n   3\n')).toEqual('1\n 2\n  3'); -    }); -  }); -   -  describe('keywords', function(){ -    var keywords = collect.keywords; -    it('should collect keywords', function(){ -      expect(keywords('\nHello: World! @ignore.')).toEqual('hello world'); -      expect(keywords('The `ng:class-odd` and ')).toEqual('and ng:class-odd the'); -    }); -  }); - -}); - -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/spec/ngdocSpec.js b/docs/spec/ngdocSpec.js new file mode 100644 index 00000000..63be610b --- /dev/null +++ b/docs/spec/ngdocSpec.js @@ -0,0 +1,257 @@ +var ngdoc = require('ngdoc.js'); + +describe('ngdoc', function(){ +  var Doc = ngdoc.Doc; +  describe('Doc', function(){ +    describe('metadata', function(){ +       +      it('should find keywords', function(){ +        expect(new Doc('\nHello: World! @ignore.').keywords()).toEqual('hello world'); +        expect(new Doc('The `ng:class-odd` and').keywords()).toEqual('and ng:class-odd the'); +      }); +    }); +     +    describe('parse', function(){ +      it('should convert @names into properties', function(){ +        var doc = new Doc('\n@name name\n@desc\ndesc\ndesc2\n@dep\n'); +        doc.parse(); +        expect(doc.name).toEqual('name'); +        expect(doc.desc).toEqual('desc\ndesc2'); +        expect(doc.dep).toEqual(''); +      }); +       +      it('should parse parameters', function(){ +        var doc = new Doc( +            '@param {*} a short\n' + +            '@param {Type} b med\n' + +            '@param {Class=} [c=2] long\nline'); +        doc.parse(); +        expect(doc.param).toEqual([ +           {name:'a', description:'short', type:'*', optional:false, 'default':undefined}, +           {name:'b', description:'med', type:'Type', optional:false, 'default':undefined}, +           {name:'c', description:'long\nline', type:'Class', optional:true, 'default':'2'} +         ]); +      }); +       +      it('should parse return', function(){ +        var doc = new Doc('@returns {Type} text *bold*.'); +        doc.parse(); +        expect(doc.returns).toEqual({ +          type: 'Type', +          description: 'text <em>bold</em>.' +        }); +      }); +    }); +     +     +  }); +   +  describe('markdown', function(){ +    var markdown = ngdoc.markdown; +     +    it('should replace angular in markdown', function(){ +      expect(markdown('<angular/>')). +        toEqual('<p><tt><angular/></tt></p>'); +    }); +     +    it('should not replace anything in <pre>', function(){ +      expect(markdown('bah x\n<pre>\nangular.k\n</pre>\n asdf x')). +        toEqual( +            '<p>bah x</p>' + +            '<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' +  +            'angular.k\n' +  +            '</pre></div>' +  +            '<p>asdf x</p>'); +    }); +     +    it('should replace text between two <pre></pre> tags', function() { +      expect(markdown('<pre>x</pre># One<pre>b</pre>')). +        toMatch('</div><h3>One</h3><div'); +    }); +  }); + +  describe('trim', function(){ +    var trim = ngdoc.trim; +    it('should remove leading/trailing space', function(){ +      expect(trim('  \nabc\n  ')).toEqual('abc'); +    }); +     +    it('should remove leading space on every line', function(){ +      expect(trim('\n 1\n  2\n   3\n')).toEqual('1\n 2\n  3'); +    }); +  }); +   +  describe('merge', function(){ +    it('should merge child with parent', function(){ +      var parent = new Doc({name:'angular.service.abc'}); +      var methodA = new Doc({name:'methodA', methodOf:'angular.service.abc'}); +      var methodB = new Doc({name:'methodB', methodOf:'angular.service.abc'}); +      var propA = new Doc({name:'propA', propertyOf:'angular.service.abc'}); +      var propB = new Doc({name:'propB', propertyOf:'angular.service.abc'}); +      ;var docs = [methodB, methodA, propB, propA, parent]; // keep wrong order; +      ngdoc.merge(docs); +      expect(docs.length).toEqual(1); +      expect(docs[0].name).toEqual('angular.service.abc'); +      expect(docs[0].methods).toEqual([methodA, methodB]); +      expect(docs[0].properties).toEqual([propA, propB]); +    }); +     +  }); +   +  //////////////////////////////////////// +   +  describe('TAG', function(){ +    describe('@param', function(){ +      it('should parse with no default', function(){ +        var doc = new Doc('@param {(number|string)} number Number \n to format.'); +        doc.parse(); +        expect(doc.param).toEqual([{  +          type : '(number|string)',  +          name : 'number', +          optional: false, +          'default' : undefined,  +          description : 'Number \n to format.' }]); +      }); +       +      it('should parse with default and optional', function(){ +        var doc = new Doc('@param {(number|string)=} [fractionSize=2] desc'); +        doc.parse(); +        expect(doc.param).toEqual([{  +          type : '(number|string)',  +          name : 'fractionSize', +          optional: true, +          'default' : '2',  +          description : 'desc' }]); +      }); +    }); +     +    describe('@requires', function() { +      it('should parse more @requires tag into array', function() { +        var doc = new Doc('@requires $service\n@requires $another'); +        doc.parse(); +        expect(doc.requires).toEqual(['$service', '$another']); +      }); +    }); + +    describe('@property', function() { +      it('should parse @property tags into array', function() { +        var doc = new Doc("@property {type} name1 desc\n@property {type} name2 desc"); +        doc.parse(); +        expect(doc.properties.length).toEqual(2); +      }); +       +      it('should parse @property with only name', function() { +        var doc = new Doc("@property fake"); +        doc.parse(); +        expect(doc.properties[0].name).toEqual('fake'); +      }); +       +      it('should parse @property with optional type', function() { +        var doc = new Doc("@property {string} name"); +        doc.parse(); +        expect(doc.properties[0].name).toEqual('name'); +        expect(doc.properties[0].type).toEqual('string'); +      }); +       +      it('should parse @property with optional description', function() { +        var doc = new Doc("@property name desc rip tion"); +        doc.parse(); +        expect(doc.properties[0].name).toEqual('name'); +        expect(doc.properties[0].description).toEqual('desc rip tion'); +      }); +       +      it('should parse @property with type and description both', function() { +        var doc = new Doc("@property {bool} name desc rip tion"); +        doc.parse(); +        expect(doc.properties[0].name).toEqual('name'); +        expect(doc.properties[0].type).toEqual('bool'); +        expect(doc.properties[0].description).toEqual('desc rip tion'); +      }); +       +    }); +     +    describe('@returns', function() { +      it('should not parse @returns without type', function() { +        var doc = new Doc("@returns lala"); +        expect(doc.parse).toThrow(); +      }); +       +      it('should parse @returns with type and description', function() { +        var doc = new Doc("@returns {string} descrip tion"); +        doc.parse(); +        expect(doc.returns).toEqual({type: 'string', description: 'descrip tion'}); +      }); + +      it('should transform description of @returns with markdown', function() { +        var doc = new Doc("@returns {string} descrip *tion*"); +        doc.parse(); +        expect(doc.returns).toEqual({type: 'string', description: 'descrip <em>tion</em>'}); +      }); + +      it('should support multiline content', function() { +        var doc = new Doc("@returns {string} description\n new line\n another line"); +        doc.parse(); +        expect(doc.returns). +          toEqual({type: 'string', description: 'description\n new line\n another line'}); +      }); +    }); +     +    describe('@description', function(){ +      it('should support pre blocks', function(){ +        var doc = new Doc("@description <pre>abc</pre>"); +        doc.parse(); +        expect(doc.description). +          toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>'); +      }); + +      it('should support multiple pre blocks', function() { +        var doc = new Doc("@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>"); +        doc.parse(); +        expect(doc.description). +          toBe('<p>foo </p>' + +               '<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>' + +               '<h3>bah</h3>\n\n' + +               '<p>foo </p>' + +               '<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>'); + +      }); + +      it('should support nested @link annotations with or without description', function() { +        var doc = new Doc("@description " + +            'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' + +            'dad{@link angular.foo}\n\n' + +            '{@link angular.directive.ng:foo ng:foo}'); +        doc.parse(); +        expect(doc.description). +          toBe('<p>foo <a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' + +               '<p>da <a href="#!angular.foo"><code>bar foo bar</code></a> </p>\n\n' + +               '<p>dad<a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' + +               '<p><a href="#!angular.directive.ng:foo"><code>ng:foo</code></a></p>'); +      }); + +      it('should increment all headings by two', function() { +        var doc = new Doc('@description # foo\nabc\n## bar \n xyz'); +        doc.parse(); +        expect(doc.description). +          toBe('<h3>foo</h3>\n\n<p>abc</p>\n\n<h4>bar</h4>\n\n<p>xyz</p>'); +      }); +    }); + +    describe('@example', function(){ +      it('should not remove {{}}', function(){ +        var doc = new Doc('@example text {{ abc }}'); +        doc.parse(); +        expect(doc.example).toEqual('text {{ abc }}'); +      }); +    }); + +    describe('@deprecated', function() { +      it('should parse @deprecated', function() { +        var doc = new Doc('@deprecated Replaced with foo.'); +        doc.parse(); +        expect(doc.deprecated).toBe('Replaced with foo.'); +      }); +    }); +  }); + +}); diff --git a/docs/spec/specs.js b/docs/spec/specs.js new file mode 100644 index 00000000..a6ba17a9 --- /dev/null +++ b/docs/spec/specs.js @@ -0,0 +1,39 @@ +if (global.jasmine) return; + +require.paths.push(__dirname + "/../../lib"); +require.paths.push(__dirname + '/../src'); +var jasmine = require('jasmine-1.0.1'); +var sys = require('util'); + +for(var key in jasmine) { +  global[key] = jasmine[key]; +} + +//Patch Jasmine for proper stack traces +jasmine.Spec.prototype.fail = function (e) { +  var expectationResult = new jasmine.ExpectationResult({ +    passed: false, +    message: e ? jasmine.util.formatException(e) : 'Exception' +  }); +  // PATCH +  if (e) { +   expectationResult.trace = e; +  } +  this.results_.addResult(expectationResult); +}; + + + +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, function(runner, log){ +  process.exit(runner.results().failedCount); +}, isVerbose, showColors); diff --git a/docs/spec/writerSpec.js b/docs/spec/writerSpec.js new file mode 100644 index 00000000..1a722ca6 --- /dev/null +++ b/docs/spec/writerSpec.js @@ -0,0 +1,18 @@ +var writer = require('writer.js'); +describe('writer', function(){ +  describe('toString', function(){ +    var toString = writer.toString; +     +    it('should merge string', function(){ +      expect(toString('abc')).toEqual('abc'); +    }); +     +    it('should merge obj', function(){ +      expect(toString({a:1})).toEqual('{"a":1}'); +    }); +     +    it('should merge array', function(){ +      expect(toString(['abc',{}])).toEqual('abc{}'); +    }); +  }); +});
\ No newline at end of file diff --git a/docs/specs.js b/docs/specs.js deleted file mode 100644 index d564b045..00000000 --- a/docs/specs.js +++ /dev/null @@ -1,21 +0,0 @@ -require.paths.push("./lib"); -var jasmine = require('jasmine-1.0.1'); -var sys = require('util'); - -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/callback.js b/docs/src/callback.js index 0d0669d1..aaf69cde 100644 --- a/docs/callback.js +++ b/docs/src/callback.js @@ -2,7 +2,10 @@ function noop(){}  function chain(delegateFn, explicitDone){    var onDoneFn = noop; -  var onErrorFn = noop; +  var onErrorFn = function(e){ +    console.error(e.stack || e); +    process.exit(-1); +  };    var waitForCount = 1;    delegateFn = delegateFn || noop;    var stackError = new Error('capture stack'); diff --git a/docs/src/dom.js b/docs/src/dom.js new file mode 100644 index 00000000..e6dd09e6 --- /dev/null +++ b/docs/src/dom.js @@ -0,0 +1,123 @@ +/** + * DOM generation class + */ + +exports.DOM = DOM; + +////////////////////////////////////////////////////////// + +function DOM(){ +  this.out = []; +  this.headingDepth = 1; +} + +var INLINE_TAGS = { +    i: true, +    b: true +}; + +DOM.prototype = {  +  toString: function() { +    return this.out.join(''); +  }, +   +  text: function(content) { +    if (typeof content == "string") { +      this.out.push(content.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')); +    } else if (typeof content == 'function') { +      content.call(this, this); +    } else if (content instanceof Array) { +      this.ul(content); +    } +  }, + +  html: function(html) { +    if (html) { +      this.out.push(html); +    } +  }, + +  tag: function(name, attr, text) { +    if (!text) { +      text = attr; +      attr = {}; +      if (name == 'code') +        attr['ng:non-bindable'] = ''; +    } +    this.out.push('<' + name); +    for(var key in attr) { +      this.out.push(" " + key + '="' + attr[key] + '"'); +    } +    this.out.push('>'); +    this.text(text); +    this.out.push('</' + name + '>'); +    if (!INLINE_TAGS[name])  +      this.out.push('\n'); +  }, +   +  code: function(text) { +    this.tag('div', {'ng:non-bindable':''}, function(){ +      this.tag('pre', {'class':"brush: js; html-script: true;"}, text); +    }); +  }, +   +  example: function(source, scenario) { +    if (source || scenario) { +      this.h('Example', function(){ +        if (scenario === false) { +          this.code(source); +        } else { +          this.tag('doc:example', function(){ +            if (source) this.tag('doc:source', source); +            if (scenario) this.tag('doc:scenario', scenario); +          }); +        } +      }); +    } +  }, +   +  h: function(heading, content, fn){ +    if (content==undefined || content && content.legth == 0) return; +    this.tag('h' + this.headingDepth, heading); +    this.headingDepth++; +    if (content instanceof Array) { +      this.ul(content, {'class': heading.toLowerCase()}, fn); +    } else if (fn) { +      fn.call(this, content); +    } else { +      this.text(content); +    }  +    this.headingDepth--; +  }, +   +  h1: function(attr, text) { +    this.tag('h1', attr, text); +  }, +   +  h2: function(attr, text) { +    this.tag('h2', attr, text); +  }, +   +  h3: function(attr, text) { +    this.tag('h3', attr, text); +  }, +   +  p: function(attr, text) { +    this.tag('p', attr, text); +  }, +   +  ul: function(list, attr, fn) { +    if (typeof attr == 'function') { +      fn = attr; +      attr = {}; +    } +    this.tag('ul', attr, function(dom){ +      list.forEach(function(item){ +        dom.out.push('<li>'); +        dom.text(fn ? fn(item) : item); +        dom.out.push('</li>\n'); +      }); +    }); +  } +   +};
\ No newline at end of file diff --git a/docs/src/gen-docs.js b/docs/src/gen-docs.js new file mode 100644 index 00000000..b4e30a53 --- /dev/null +++ b/docs/src/gen-docs.js @@ -0,0 +1,42 @@ +require.paths.push(__dirname); +require.paths.push('lib'); +var reader = require('reader.js'), +    ngdoc = require('ngdoc.js'), +    writer = require('writer.js'), +    callback = require('callback.js'); + +var docs = []; +var start; +var work = callback.chain(function(){ +  start = now(); +  console.log('Generating Angular Reference Documentation...'); +  reader.collect(work.waitMany(function(text, file, line){ +    var doc = new ngdoc.Doc(text, file, line); +    docs.push(doc); +    doc.parse(); +  })); +}); +var writes = callback.chain(function(){ +  ngdoc.merge(docs); +  docs.forEach(function(doc){ +    writer.output(doc.name + '.html', doc.html(), writes.waitFor()); +  }); +  var metadata = ngdoc.metadata(docs); +  writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata), ';'], writes.waitFor()); +  writer.copy('index.html', writes.waitFor()); +  writer.copy('docs.js', writes.waitFor()); +  writer.copy('docs.css', writes.waitFor()); +  writer.copy('doc_widgets.js', writes.waitFor()); +  writer.copy('doc_widgets.css', writes.waitFor()); +  writer.copy('docs-scenario.html', writes.waitFor()); +  writer.output('docs-scenario.js', ngdoc.scenarios(docs), writes.waitFor()); +}); +writes.onDone(function(){ +  console.log('DONE. Generated ' + docs.length + ' pages in ' +  +      (now()-start) + 'ms.' ); +}); +work.onDone(writes); +writer.makeDir('build/docs', work); + +/////////////////////////////////// +function now(){ return new Date().getTime(); } diff --git a/docs/src/ignore.words b/docs/src/ignore.words new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/docs/src/ignore.words diff --git a/docs/src/ngdoc.js b/docs/src/ngdoc.js new file mode 100644 index 00000000..ac7d0bb1 --- /dev/null +++ b/docs/src/ngdoc.js @@ -0,0 +1,614 @@ +/** + * All parsing/transformation code goes here. All code here should be sync to ease testing. + */ + +var Showdown = require('showdown').Showdown; +var DOM = require('dom.js').DOM; +var NEW_LINE = /\n\r?/; + +exports.markdown = markdown; +exports.markdownNoP = markdownNoP; +exports.trim = trim; +exports.metadata = metadata; +exports.scenarios = scenarios; +exports.merge = merge; +exports.Doc = Doc; + +////////////////////////////////////////////////////////// +function Doc(text, file, line) { +  if (typeof text == 'object') { +    for ( var key in text) { +      this[key] = text[key]; +    } +  } else { +    this.text = text; +    this.file = file; +    this.line = line; +  } +} +Doc.METADATA_IGNORE = (function(){ +  var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8'); +  return words.toString().split(/[,\s\n\r]+/gm); +})(); + + + +Doc.prototype = { +  keywords: function keywords(){ +    var keywords = {}; +    Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; }); +    var words = []; +    var tokens = this.text.toLowerCase().split(/[,\.\`\'\"\s]+/mg); +    tokens.forEach(function(key){ +      var match = key.match(/^(([a-z]|ng\:)[\w\_\-]{2,})/); +      if (match){ +        key = match[1]; +        if (!keywords[key]) { +          keywords[key] = true; +          words.push(key); +        } +      } +    }); +    words.sort(); +    return words.join(' '); +  }, +   +  parse: function(){ +    var atName; +    var atText; +    var match; +    var self = this; +    self.text.split(NEW_LINE).forEach(function(line){ +      if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) { +        // we found @name ... +        // if we have existing name +        flush(); +        atName = match[1]; +        atText = []; +        if(match[3]) atText.push(match[3]); +      } else { +        if (atName) { +          atText.push(line); +        } +      } +    }); +    flush(); +    this.shortName = (this.name || '').split(/[\.#]/).pop(); +    this.description = markdown(this.description); +     +    function flush(){ +      if (atName) { +        var text = trim(atText.join('\n')); +        if (atName == 'param') { +          var match = text.match(/^{([^}=]+)(=)?}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/); +                                //  1      12 2     34       4   5   5 6      6  3   7  7 +          if (!match) { +            throw new Error("Not a valid 'param' format: " + text); +          } +          var param = { +            name: match[5] || match[4], +            description:markdownNoP(text.replace(match[0], match[7])), +            type: match[1], +            optional: !!match[2], +            'default':match[6] +          }; +          self.param = self.param || []; +          self.param.push(param); +        } else if (atName == 'returns') { +          var match = text.match(/^{([^}=]+)}\s+(.*)/); +          if (!match) { +            throw new Error("Not a valid 'returns' format: " + text); +          } +          self.returns = { +            type: match[1], +            description: markdownNoP(text.replace(match[0], match[2])) +          }; +        } else if(atName == 'requires') { +          self.requires = self.requires || []; +          self.requires.push(text); +        } else if(atName == 'property') { +          var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/); +          if (!match) { +            throw new Error("Not a valid 'property' format: " + text); +          } +          var property = { +              type: match[2], +              name: match[3], +              description: match[5] || '' +            }; +          self.properties = self.properties || []; +          self.properties.push(property); +        } else { +          self[atName] = text; +        } +      } +    } +  }, +   +  html: function(){ +    var dom = new DOM(), +        self = this; +     +    dom.h(this.name, function(){ +      notice('workInProgress', 'Work in Progress',  +          'This page is currently being revised. It might be incomplete or contain inaccuracies.'); +      notice('depricated', 'Depricated API'); +      dom.h('Description', self.description, html); +      dom.h('Dependencies', self.requires); +       +      usage(); +       +      dom.h('Methods', self.methods, function(method){ +        var signature = (method.param || []).map(property('name')); +        dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){ +          dom.html(method.description); +          method.html_usage_parameters(dom); +          dom.example(method.example, false); +        }); +      }); +      dom.h('Properties', self.properties, function(property){ +        dom.h(property.name, function(){ +         dom.text(property.description);  +         dom.example(property.example, false); +        }); +      }); +       +      dom.example(self.example, self.scenario); +    }); +     +    return dom.toString(); +     +    ////////////////////////// +     +    function html(text){ +      this.html(text); +    } +     +    function usage(){ +      (self['html_usage_' + self.ngdoc] || function(){ +        throw new Error("Don't know how to format @ngdoc: " + self.ngdoc); +      }).call(self, dom); +    } +     +    function section(name, property, fn) { +      var value = self[property]; +      if (value) { +        dom.h2(name); +        if (typeof value == 'string') { +          value = markdown(value) + '\n'; +          fn ? fn(value) : dom.html(value); +        } else if (value instanceof Array) { +          dom.ul(value, fn); +        } +      } +    } +     +    function notice(name, legend, msg){ +      if (self[name] == undefined) return; +      dom.tag('fieldset', {'class':name}, function(dom){ +        dom.tag('legend', legend); +        dom.text(msg); +      }); +    } +     +  }, +   +  html_usage_parameters: function(dom) { +    dom.h('Parameters', this.param, function(param){ +      dom.tag('code', function(){ +        dom.text(param.name); +        if (param.optional) { +          dom.tag('i', function(){ +            dom.text('(optional'); +            if(param['default']) { +              dom.text('=' + param['default']); +            } +            dom.text(')'); +          }); +        } +        dom.text(' – {'); +        dom.text(param.type); +        dom.text('} – '); +      }); +      dom.html(param.description); +    }); +  }, +   +  html_usage_returns: function(dom) { +    var self = this; +    if (self.returns) { +      dom.h('Returns', function(){ +        dom.tag('code', self.returns.type); +        dom.text('– '); +        dom.html(self.returns.description); +      }); +    } +  }, +   +  html_usage_function: function(dom){ +    var self = this; +    dom.h('Usage', function(){ +      dom.code(function(){ +        dom.text(self.name); +        dom.text('('); +        var first = true; +        (self.param || []).forEach(function(param){ +          if (first) { +            first = false; +          } else { +            dom.text(', '); +          } +          dom.text(param.name); +        }); +        dom.text(');'); +      }); +       +      self.html_usage_parameters(dom); +      self.html_usage_returns(dom); +    }); +  }, +     +  html_usage_directive: function(dom){ +    var self = this; +    dom.h('Usage', function(){ +      dom.tag('pre', {'class':"brush: js; html-script: true;"}, function(){ +        dom.text('<' + self.element + ' '); +        dom.text(self.shortName); +        if (self.param) { +          dom.text('="' + self.param[0].name + '"'); +        } +        dom.text('>\n   ...\n'); +        dom.text('</' + self.element + '>'); +      }); +      self.html_usage_parameters(dom); +    }); +  }, +     +  html_usage_filter: function(dom){ +    var self = this; +    dom.h('Usage', function(){ +      dom.h('In HTML Template Binding', function(){ +        dom.tag('code', function(){ +          dom.text('{{ '); +          dom.text(self.shortName); +          dom.text('_expression | '); +          dom.text(self.shortName); +          var first = true; +          (self.param||[]).forEach(function(param){ +            if (first) { +              first = false; +            } else { +              if (param.optional) { +                dom.tag('i', function(){ +                  dom.text('[:' + param.name + ']'); +                }); +              } else { +                dom.text(':' + param.name); +              } +            } +          }); +          dom.text(' }}'); +        }); +      }); +       +      dom.h3('In JavaScript', function(){ +        dom.tag('code', function(){ +          dom.text('angular.filter.'); +          dom.text(self.shortName); +          dom.text('('); +          var first = true; +          (self.param||[]).forEach(function(param){ +            if (first) { +              first = false; +              dom.text(param.name); +            } else { +              if (param.optional) { +                dom.tag('i', function(){ +                  dom.text('[, ' + param.name + ']'); +                }); +              } else { +                dom.text(', ' + param.name); +              } +            } +          }); +          dom.text(')'); +        }); +      }); +       +      self.html_usage_parameters(dom); +      self.html_usage_returns(dom); +    }); +  }, +     +  html_usage_formatter: function(dom){ +    var self = this; +    dom.h('Usage', function(){ +      dom.h('In HTML Template Binding', function(){ +        dom.code(function(){ +          dom.text('<input type="text" ng:format="'); +          dom.text(self.shortName); +          dom.text('">'); +        }); +      }); +       +      dom.h3('In JavaScript', function(){ +        dom.code(function(){ +          dom.text('var userInputString = angular.formatter.'); +          dom.text(self.shortName); +          dom.text('.format(modelValue);'); +        }); +        dom.html('<br/>'); +        dom.code(function(){ +          dom.text('var modelValue = angular.formatter.'); +          dom.text(self.shortName); +          dom.text('.parse(userInputString);'); +        }); +      }); +       +      self.html_usage_returns(dom);   +    }); +  }, + +  html_usage_validator: function(dom){ +    var self = this; +    dom.h('Usage', function(){ +      dom.h('In HTML Template Binding', function(){ +        dom.code(function(){ +          dom.text('<input type="text" ng:validate="'); +          dom.text(self.shortName); +          var first = true; +          (self.param||[]).forEach(function(param){ +            if (first) { +              first = false; +            } else { +              if (param.optional) { +                dom.text('[:' + param.name + ']'); +              } else { +                dom.text(':' + param.name); +              } +            } +          }); +          dom.text('"/>'); +        }); +      }); +       +      dom.h('In JavaScript', function(){ +        dom.code(function(){ +          dom.text('angular.validator.'); +          dom.text(self.shortName); +          dom.text('('); +          var first = true; +          (self.param||[]).forEach(function(param){ +            if (first) { +              first = false; +              dom.text(param.name); +            } else { +              if (param.optional) { +                dom.text('[, ' + param.name + ']'); +              } else { +                dom.text(', ' + param.name); +              } +            } +          }); +          dom.text(')'); +        }); +      }); +       +      self.html_usage_parameters(dom); +      self.html_usage_returns(dom); +    }); +  }, +   +  html_usage_widget: function(dom){ +    var self = this; +    dom.h('Usage', function(){ +      dom.h('In HTML Template Binding', function(){ +        dom.code(function(){ +          if (self.shortName.match(/^@/)) { +            dom.text('<'); +            dom.text(self.element); +            dom.text(' '); +            dom.text(self.shortName.substring(1)); +            if (self.param) { +              dom.text('="'); +              dom.text(self.param[0].name); +              dom.text('"'); +            } +            dom.text('>\n   ...\n</'); +            dom.text(self.element); +            dom.text('>'); +          } else { +            dom.text('<'); +            dom.text(self.shortName); +            (self.param||[]).forEach(function(param){ +              if (param.optional) { +                dom.text(' [' + param.name + '="..."]'); +              } else { +                dom.text(' ' + param.name + '="..."'); +              } +            }); +            dom.text('></'); +            dom.text(self.shortName); +            dom.text('>'); +          } +        }); +      }); +       +      self.html_usage_parameters(dom); +      self.html_usage_returns(dom); +    }); +  }, +     +  html_usage_overview: function(dom){ +  }, +     +  html_usage_service: function(dom){ +  } + +}; +////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////// +function markdown (text) { +  if (!text) return text; +  var parts = text.split(/(<pre>[\s\S]*?<\/pre>)/), +      match; + +  parts.forEach(function(text, i){ +    if (text.match(/^<pre>/)) { +      text = text. +        replace(/^<pre>/, '<div ng:non-bindable><pre class="brush: js; html-script: true;">'). +        replace(/<\/pre>/, '</pre></div>'); +    } else { +      text = text.replace(/<angular\/>/gm, '<tt><angular/></tt>'); +      text = new Showdown.converter().makeHtml(text.replace(/^#/gm, '###')); + +      while (match = text.match(R_LINK)) { +        text = text.replace(match[0], '<a href="#!' + match[1] + '"><code>' + +                                        (match[4] || match[1]) + +                                      '</code></a>'); +      } +    } +    parts[i] = text; +  }); +  return parts.join(''); +}; +var R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m; +            //       1       123     3 4     42 +function markdownNoP(text) { +  var lines = markdown(text).split(NEW_LINE); +  var last = lines.length - 1; +  lines[0] = lines[0].replace(/^<p>/, ''); +  lines[last] = lines[last].replace(/<\/p>$/, ''); +  return lines.join('\n'); +} + + +////////////////////////////////////////////////////////// +function scenarios(docs){ +  var specs = []; +  docs.forEach(function(doc){ +    if (doc.scenario) { +      specs.push('describe("'); +      specs.push(doc.name); +      specs.push('", function(){\n'); +      specs.push('  beforeEach(function(){\n'); +      specs.push('    browser().navigateTo("index.html#!' + doc.name + '");'); +      specs.push('  });\n\n'); +      specs.push(doc.scenario); +      specs.push('\n});\n\n'); +    } +  }); +  return specs; +} + + +////////////////////////////////////////////////////////// +function metadata(docs){ +  var words = []; +  docs.forEach(function(doc){ +    words.push({ +      name:doc.name, +      type: doc.ngdoc, +      keywords:doc.keywords() +    }); +  }); +  words.sort(keywordSort); +  return words; +} + +function keywordSort(a,b){ +  // supper ugly comparator that orders all utility methods and objects before all the other stuff +  // like widgets, directives, services, etc. +  // Mother of all beautiful code please forgive me for the sin that this code certainly is. + +  if (a.name === b.name) return 0; +  if (a.name === 'angular') return -1; +  if (b.name === 'angular') return 1; + +  function namespacedName(page) { +    return (page.name.match(/\./g).length === 1 && page.type !== 'overview' ? '0' : '1') + page.name; +  } + +  var namespacedA = namespacedName(a), +      namespacedB = namespacedName(b); + +  return namespacedA < namespacedB ? -1 : 1; +} + + +////////////////////////////////////////////////////////// +function trim(text) { +  var MAX = 9999; +  var empty = RegExp.prototype.test.bind(/^\s*$/); +  var lines = text.split('\n'); +  var minIndent = MAX; +  lines.forEach(function(line){ +    minIndent = Math.min(minIndent, indent(line)); +  }); +  for ( var i = 0; i < lines.length; i++) { +    lines[i] = lines[i].substring(minIndent); +  } +  // remove leading lines +  while (empty(lines[0])) { +    lines.shift(); +  } +  // remove trailing +  while (empty(lines[lines.length - 1])) { +    lines.pop(); +  } +  return lines.join('\n'); +   +  function indent(line) { +    for(var i = 0; i < line.length; i++) { +      if (line.charAt(i) != ' ')  { +        return i; +      } +    } +    return MAX; +  } +} + +////////////////////////////////////////////////////////// +function merge(docs){ +  var byName = {}; +  docs.forEach(function(doc){ +    byName[doc.name] = doc; +  }); +  for(var i=0; i<docs.length;) { +    if (findParent(docs[i], 'method') || +          findParent(docs[i], 'property')) { +      docs.splice(i, 1); +    } else { +      i++; +    } +  } +   +  function findParent(doc, name){ +    var parentName = doc[name+'Of']; +    if (!parentName) return false; +     +    var parent = byName[parentName]; +    if (!parent)  +      throw new Error("No parent named '" + parentName + "' for '" +  +          doc.name + "' in @" + name + "Of."); +     +    var listName = (name + 's').replace(/ys$/, 'ies'); +    var list = parent[listName] = (parent[listName] || []); +    list.push(doc); +    list.sort(orderByName); +    return true; +  } +   +  function orderByName(a, b){ +    return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0); +  } +} +////////////////////////////////////////////////////////// + +function property(name) { +  return function(value){  +    return value[name]; +  }; +} 
\ No newline at end of file diff --git a/docs/src/reader.js b/docs/src/reader.js new file mode 100644 index 00000000..8f9f22c3 --- /dev/null +++ b/docs/src/reader.js @@ -0,0 +1,91 @@ +/** + * All reading related code here. This is so that we can separate the async code from sync code + * for testability + */ +require.paths.push(__dirname); +var fs       = require('fs'), +    callback = require('callback'); + +var NEW_LINE = /\n\r?/; + +function collect(callback){ +  findJsFiles('src', callback.waitMany(function(file) { +    //console.log('reading', file, '...'); +    findNgDocInJsFile(file, callback.waitMany(function(doc, line) { +      callback(doc, file, line); +    })); +  })); +  findNgDocInDir('docs/', callback.waitMany(callback)); +  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(); +  })); +} + +function findNgDocInDir(directory, docNotify) { +  fs.readdir(directory, docNotify.waitFor(function(err, files){ +    if (err) return this.error(err); +    files.forEach(function(file){ +      //console.log('reading', directory + file, '...'); +      if (!file.match(/\.ngdoc$/)) return; +      fs.readFile(directory + file, docNotify.waitFor(function(err, content){ +        if (err) return this.error(err); +        docNotify(content.toString(), directory + file, 1); +      })); +    }); +    docNotify.done(); +  })); +} + +function findNgDocInJsFile(file, callback) { +  fs.readFile(file, callback.waitFor(function(err, content){ +    var lines = content.toString().split(NEW_LINE); +    var text; +    var startingLine ; +    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; +        text = []; +        startingLine = lineNumber; +      } +      // are we done? +      if (inDoc && line.match(/\*\//)) { +        text = text.join('\n'); +        text = text.replace(/^\n/, ''); +        if (text.match(/@ngdoc/)){ +          callback(text, startingLine); +        } +        doc = null; +        inDoc = false; +      } +      // is the comment add text +      if (inDoc){ +        text.push(line.replace(/^\s*\*\s?/, '')); +      } +    }); +    callback.done(); +  })); +} + + + +exports.collect = collect;
\ No newline at end of file diff --git a/docs/doc_widgets.css b/docs/src/templates/doc_widgets.css index 8361f105..8361f105 100644 --- a/docs/doc_widgets.css +++ b/docs/src/templates/doc_widgets.css diff --git a/docs/doc_widgets.js b/docs/src/templates/doc_widgets.js index b119e326..b119e326 100644 --- a/docs/doc_widgets.js +++ b/docs/src/templates/doc_widgets.js diff --git a/docs/docs-scenario.html b/docs/src/templates/docs-scenario.html index c75155c5..c75155c5 100644 --- a/docs/docs-scenario.html +++ b/docs/src/templates/docs-scenario.html diff --git a/docs/docs-scenario.js b/docs/src/templates/docs-scenario.js index 2ee8387b..2ee8387b 100644 --- a/docs/docs-scenario.js +++ b/docs/src/templates/docs-scenario.js diff --git a/docs/docs.css b/docs/src/templates/docs.css index 21e9d6ed..aaef7e58 100644 --- a/docs/docs.css +++ b/docs/src/templates/docs.css @@ -100,6 +100,12 @@ a {    margin-top: 1.5em;  } +#main ul.methods h3, +#main ul.properties h3 { +  margin-top: 1.5em; +  font-family: "Courier New", monospace; +} +  .main-title {    float: right;  } diff --git a/docs/docs.js b/docs/src/templates/docs.js index 6bf86ed3..6bf86ed3 100644 --- a/docs/docs.js +++ b/docs/src/templates/docs.js diff --git a/docs/index.html b/docs/src/templates/index.html index 1722e98a..64b73bfc 100644 --- a/docs/index.html +++ b/docs/src/templates/index.html @@ -20,7 +20,7 @@    <script src="../angular.min.js" ng:autobind></script>    <script src="docs.js"></script>    <script src="doc_widgets.js"></script> -  <script src="docs-data.js"></script> +  <script src="docs-keywords.js"></script>  </head>  <body style="display:none;" ng:show="true">    <div id="header"> diff --git a/docs/src/writer.js b/docs/src/writer.js new file mode 100644 index 00000000..eb1b190f --- /dev/null +++ b/docs/src/writer.js @@ -0,0 +1,61 @@ +/** + * All writing related code here. This is so that we can separate the async code from sync code + * for testability + */ +require.paths.push(__dirname); +var fs         = require('fs'); +var OUTPUT_DIR = "build/docs/"; + +function output(docs, content, callback){ +  callback(); +} + +exports.output = function(file, content, callback){ +  //console.log('writing', OUTPUT_DIR + file, '...'); +  fs.writeFile( +      OUTPUT_DIR + file,  +      exports.toString(content),  +      callback); +}; + + +exports.toString = function toString(obj){ +  switch (typeof obj) { +  case 'string':  +    return obj; +  case 'object':  +    if (obj instanceof Array) { +      obj.forEach(function (value, key){ +        obj[key] = toString(value); +      }); +      return obj.join(''); +    } else { +      return JSON.stringify(obj); +    } +  } +  return obj; +}; + +exports.makeDir = function (path, callback) { +  var parts = path.split(/\//); +  path = '.'; +  (function next(){ +    if (parts.length) { +      path += '/' + parts.shift(); +      fs.mkdir(path, 0777, next); +    } else { +      callback(); +    } +  })(); +}; + +exports.copy = function(filename, callback){ +  //console.log('writing', OUTPUT_DIR + filename, '...'); +  fs.readFile('docs/src/templates/' + filename, function(err, content){ +    if (err) return callback.error(err); +    fs.writeFile( +        OUTPUT_DIR + filename,  +        content,  +        callback); +  }); +}; diff --git a/docs/validator.template b/docs/validator.template deleted file mode 100644 index 64ab37b7..00000000 --- a/docs/validator.template +++ /dev/null @@ -1,59 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -<h2>Description</h2> -{{{description}}} - -<h2>Usage</h2> -<h3>In HTML Template Binding</h3> -<tt> -  <input type="text" ng:validate="{{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}]</i>{{/default}}{{/paramRest}}"/> -</tt> - -<h3>In JavaScript</h3> -<tt ng:non-bindable> -angular.validator.{{shortName}}({{paramFirst.name}}{{#paramRest}}{{^default}}, {{name}}{{/default}}{{#default}}<i>[, {{name}}]</i>{{/default}}{{/paramRest}} ); -</tt> - -<h3>Parameters</h3> -<ul> -  {{#param}} -  <li><tt>{{name}}</tt> – -      <tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt> -      <tt>{{#default}}[{{default}}]{{/default}}</tt> -      – {{{description}}}</li> -  {{/param}} -</ul> -{{{paramDescription}}} - -{{#css}} -<h3>CSS</h3> -{{{css}}} -{{/css}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} diff --git a/docs/widget.template b/docs/widget.template deleted file mode 100644 index 418ad407..00000000 --- a/docs/widget.template +++ /dev/null @@ -1,68 +0,0 @@ -<h1>{{name}}</h1> - -{{#workInProgress}} -<fieldset class="workInProgress"> -  <legend>Work In Progress</legend> -  This page is currently being revised. It might be incomplete or contain inaccuracies. -  {{{workInProgress.description}}} -</fieldset> -{{/workInProgress}} - -{{#deprecated}} -<fieldset class="deprecated"> -  <legend>Deprecated API</legend> -  {{deprecated}} -</fieldset> -{{/deprecated}} - -<h2>Description</h2> -{{{description}}} - -<h2>Usage</h2> -<h3>In HTML Template Binding</h3> -<tt> -{{^element}} -  <pre> -<{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}>{{#usageContent}}  - -  {{usageContent}} -{{/usageContent}}</{{shortName}}> -  </pre> -{{/element}} -{{#element}} -  <pre> -<{{element}} {{shortName}}{{#paramFirst}}="{{paramFirst.name}}{{/paramFirst}}"> -  ... -</{{element}}> -  </pre> -{{/element}} -</tt> - -<h3>Parameters</h3> -<ul> -  {{#param}} -  <li><tt>{{name}}</tt> – -      <tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt> -      <tt>{{#default}}[{{default}}]{{/default}}</tt> -      – {{{description}}}</li> -  {{/param}} -</ul> -{{{paramDescription}}} - -{{#css}} -<h3>CSS</h3> -{{{css}}} -{{/css}} - -{{#example}} -<h2>Example</h2> -{{{exampleDescription}}} -<doc:example> -  <doc:source> -{{/example}} -    {{{example}}} -{{#example}} -  </doc:source> -  <doc:scenario>{{{scenario}}}</doc:scenario> -</doc:example> -{{/example}} | 
