diff options
| author | Misko Hevery | 2011-01-25 21:55:11 -0800 | 
|---|---|---|
| committer | Igor Minar | 2011-01-26 23:31:15 -0800 | 
| commit | bd33f60276a0fa37acffbad7a0cdcff92db594c8 (patch) | |
| tree | 7cd957c19182ddc430a6320b77b2f03cb1bd223d | |
| parent | 8682befc7284a3c0b35cd5d85d4f42b1484ec71a (diff) | |
| download | angular.js-bd33f60276a0fa37acffbad7a0cdcff92db594c8.tar.bz2 | |
Added part of guide documentation and supporting changes to doc generator
| -rw-r--r-- | docs/guide.bootstrap.ngdoc | 97 | ||||
| -rw-r--r-- | docs/guide.compiler.ngdoc | 163 | ||||
| -rw-r--r-- | docs/guide.data-binding.ngdoc | 41 | ||||
| -rw-r--r-- | docs/guide.ngdoc | 36 | ||||
| -rw-r--r-- | docs/guide.overview.ngdoc | 166 | ||||
| -rw-r--r-- | docs/guide.template.ngdoc | 22 | ||||
| -rw-r--r-- | docs/img/One_Way_Data_Binding.png | bin | 0 -> 32339 bytes | |||
| -rw-r--r-- | docs/img/Two_Way_Data_Binding.png | bin | 0 -> 50276 bytes | |||
| -rw-r--r-- | docs/spec/domSpec.js | 32 | ||||
| -rw-r--r-- | docs/spec/ngdocSpec.js | 119 | ||||
| -rw-r--r-- | docs/src/dom.js | 25 | ||||
| -rw-r--r-- | docs/src/gen-docs.js | 5 | ||||
| -rw-r--r-- | docs/src/ngdoc.js | 210 | ||||
| -rw-r--r-- | docs/src/templates/doc_widgets.js | 15 | ||||
| -rw-r--r-- | docs/src/templates/docs.css | 18 | ||||
| -rw-r--r-- | docs/src/templates/docs.js | 10 | ||||
| -rw-r--r-- | docs/src/templates/index.html | 2 | ||||
| -rw-r--r-- | docs/src/writer.js | 27 | 
18 files changed, 861 insertions, 127 deletions
diff --git a/docs/guide.bootstrap.ngdoc b/docs/guide.bootstrap.ngdoc new file mode 100644 index 00000000..835fe494 --- /dev/null +++ b/docs/guide.bootstrap.ngdoc @@ -0,0 +1,97 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Bootstrap +@description + +# Bootstrap +This section explains how to bootstrap your application to the angular environment using either +the `angular.js` or `angular.min.js` script. + +## The bootstrap code + +Note that there are two versions of the bootstrap code that you can use: + +* `angular-0.0.0.js` - this file is unobfuscated, uncompressed, and thus human-readable. +* `angular-0.0.0.min.js` - this is a compressed and obfuscated version of angular-debug.js. + +In this section and throughout the Developer Guide, feel free to use `angular.min.js` instead of +`angular.js` when working through code examples. + +## ng:autobind + +The simplest way to get an angular application up and running is by inserting a script tag in your +HTML file that bootstraps the `angular.js` code and uses the special `ng:autobind` attribute, +like in this snippet of HTML: + +<doc:example> + <doc:source> +  Hello {{'World'}}! + </doc:source> +</doc:example> + +The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The +compilation occurs in the page's onLoad handler. Note that you don't need to explicitly add an +onLoad event; auto bind mode takes care of all the magic for you. + +## Manual bind + +Using autobind mode is a handy way to start using angular, but advanced users who want more +control over the initialization process might prefer to use manual bind mode instead. + +The best way to get started with manual bind mode is to look at the magic behind `ng:autobind` +by writing out each step of the autobind process explicitly. Note that the following code is +equivalent to the code in the previous section. + +<pre> +<!DOCTYPE HTML> +<html xmlns:ng="http://angularjs.org"> + <script type="text/javascript" src="http://code.angularjs.org/angular-0.0.0.min.js"></script> + <script type="text/javascript"> +  (function(window, previousOnLoad){ +   window.onload = function(){ +   try { (previousOnLoad||angular.noop)(); } catch(e) {} +   angular.compile(window.document).$init(); +   }; +  })(window, window.onload); + </script> + <body> +  Hello {{'World'}}! + </body> +</html> +</pre> + +This is the sequence that your code should follow if you're writing your own manual binding code: + + * After the page is loaded, find the root of the HTML template, which is typically the root of +   the document. + * Run the HTML compiler, which converts the templates into an executable, bi-directionally +   bound application. + + +# XML Namespace + +**IMPORTANT:** When using angular you must declare the `ng` namespace using the `xmlns` tag. +  If you don't declare the namespace, Internet Explorer does not render widgets properly. + +<pre> +<html  xmlns:ng="http://angularjs.org"> +</pre> + + +# Create your own namespace + +If you want to define your own widgets, you must create your own namespace and use that namespace +to form the fully qualified widget name. For example, you could map the alias my to your domain +and create a widget called my:widget. To create your own namespace, simply add another xmlsn tag +to your page, create an alias, and set it to your unique domain: + +<pre> +<html  xmlns:my="http://mydomain.com"> +</pre> + + +# Global Object + +The angular script creates a single global variable `angular` in the global namespace. All APIs are +bound to fields of this global object. + diff --git a/docs/guide.compiler.ngdoc b/docs/guide.compiler.ngdoc new file mode 100644 index 00000000..8896db43 --- /dev/null +++ b/docs/guide.compiler.ngdoc @@ -0,0 +1,163 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Compiler +@description + +#Compiler + +While angular might look like just a cool way to build web applications, the core of angular is +actually an HTML compiler. The default HTML transformations that this compiler provides are useful +for building generic apps, but you can also use them to create a domain-specific language for +building specific types of web applications. + +The compiler allows you to add behavior to existing HTML through widgets, directives, and text +markup. + +All of this compilation happens in the web browser, meaning no server is involved. + +# The compilation process +This section describes the steps that angular's HTML compiler goes through. If you use +`ng:autobind` in your application, this compilation process happens automatically when the +application is initialized (e.g. when the user loads the app in a browser). If you're an advanced +user using manual bind mode, you can decide when and how often the compilation happens. + +First, a bit of background of what the compilation step is for. Every type of +{@link angular.widget widget}, {@link angular.markup markup}, and +{@link angular.directive directive} in angular is defined with a compile function, and that +compile function returns an optional link function. Here is the relationship between the two: + + * **compile function** - registers a listener for the widget, markup, or directive's expression. +   This function is called exactly once. + * **link function** - sets up the listener. This function can be called multiple times, once per +   cloned DOM element (e.g. repeating element). + +Note that angular's built-in widgets, markup, and directives have predefined compile and link +functions that you don't need to modify. However, if you're writing your own widgets, markup, or +directives, you write compile and link functions. Refer to the Compiler API for more information. + +When the HTML compiler compiles a page, it goes through 3 phases: Compile, Create Root Scope, and +Link. + +## 1. Compile Phase + + * Recursively traverse the DOM, depth-first. + * Look for a matching compile function of type widget, then markup, then directive. + * If a compile function is found then execute it. + * When the compile function completes, it should return a link function. Aggregate this link +   function with all link functions returned previously by step 1c. + * Repeat steps 1c and 1d for all compile functions found. The result of the compilation step is +   the aggregate link function, which comprises all of the individual link functions. + +## 2. Create Root Scope + + * Inject all of the services into the root scope. + +## 3. Link Phase + + * Execute the aggregate link function with the root scope. The aggregate link function calls all +   the individual link functions that were generated in the compile phase. + * If there are any clones of the DOM caused by repeating elements, call the link function multiple +   times, one for each repeating item. + +Note that while the compile function is executed exactly once, the link function can be executed +multiple times: once for each iteration in a repeater. + +# Example + +The compilation process is best understood through example. Let's say that in your namespace my, +you want to create a new DOM element <my:greeter/>, which should display a greeting. + +If we want this HTML source: + +<pre> +<div ng:init="salutation='Hello'; name='World'"> +  <my:greeter salutation="salutation" name="name"/> +</div> +</pre> + +To produce this DOM: + +<pre> +<div ng:init="salutation='Hello'; name='World'"> +  <my:greeter salutation="salutation" name="name"/> +    <span class="salutation">Hello</span> +    <span class="name">World</span>! +  </my:greeter> +</div> +</pre> + +Write this widget definition (assuming you've already declared the my namespace in the page): + + +<pre> +angular.widget('my:greeter', function(compileElement){ +  var compiler = this; +  compileElement.css('display', 'block'); +  var salutationExp = compileElement.attr('salutation'); +  var nameExp = compileElement.attr('name'); +  return function(linkElement){ +    var salutationSpan = angular.element('<span class="salutation"></span'); +    var nameSpan = angular.element('<span class="name"></span>'); +    linkElement.append(salutationSpan); +    linkElement.append(compiler.text(' ')); +    linkElement.append(nameSpan); +    linkElement.append(compiler.text('!')); +    this.$watch(salutationExp, function(value){ +      salutationSpan.text(value); +    }); +    this.$watch(nameExp, function(value){ +    nameSpan.text(value); +    }); +  }; +}); +</pre> + +Note: For more about widgets, see {@link angular.widget Widget}. + +## Compilation process for this example + +Here are the steps that the compiler goes through for the page that contains this widget definition: + +### Compile Phase + + * Recursively traverse the DOM depth-first. + * Find the angular.widget definition. + *  Find and execute the widget's compileElement function, which includes the following steps: +   * Add a style element with attribute display: block; to the template DOM so that the browser +     knows to treat the element as block element for rendering. (Note: because this style element +     was added on the template compileElement, this style is automatically applied to any clones +     of the template (i.e. any repeating elements)). +   * Extract the salutation and name HTML attributes as angular expressions. + * Return the aggregate link function, which includes just one link function in this example. + +### Link Phase + + * Execute the aggregate link function, which includes the following steps: +   * Create a <span> element set to the salutation class +   * Create a <span> element set to the name class. + * Add the span elements to the linkElement. (Note: be careful not to add them to the +   compileElement, because that's the template.) + * Set up watches on the expressions. When an expression changes, copy the data to the +   corresponding spans. + + +## Compiler API + +If you define your own widgets, markup, or directives, you need to access the compiler API. +This section describes the methods on the compiler that you can call. + +Note: As of 12 August 2010, these methods are subject to change. + +Recall that the compile function's this is a reference to the compiler. + + * `compile(element)` - returns `linker` - Invoke new instance of compiler to compile a DOM element +   and return a linker function. You can apply the linker function to the original element or a +   clone of the original element. The linker function returns a scope. + * `comment(commentText)` - returns `element` - Create a comment element. + * `element(elementName)` - returns `element` - Create an element by name. + * `text(text)` - returns `element` - Create a text element. + * `descend([set])` - returns `descend` - State  Get or set the current descend state. If true the +    compiler will descend to children elements. + * `directives([set])` - returns `directive` - State Get or set the current directives processing +   state. The compiler will process directives only when directives set to true. + diff --git a/docs/guide.data-binding.ngdoc b/docs/guide.data-binding.ngdoc new file mode 100644 index 00000000..12a926bd --- /dev/null +++ b/docs/guide.data-binding.ngdoc @@ -0,0 +1,41 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Data Binding +@description + +# Data Binding + +Data-binding allows you to treat the model as the single-source-of-truth of your application, and +consider the view as only a projection of the model, at all times. The process of copying the model +values to the view, and any changes to the view by the user to the model, is known as data-binding. + +## Classical Template Systems + +<img class="right" src="img/One_Way_Data_Binding.png"/> +At the highest level, angular looks like a just another templating system. But there is one +important reason why angular templating system is different and makes it very good fit for +application development: two-way data binding. + +Most templating systems bind data in only one direction: they merge a template and model together +into a view, as illustrated in the diagram to the right. After the merge occurs, any changes to +the model or in related sections of the view are NOT automatically reflected in the view. Worse, +any changes that the user makes to the view are not reflected in the model. This means that the +developer has to write code that constantly syncs the view with the model and the model with the +view. + + +# angular Template Systems +<img class="right" src="img/Two_Way_Data_Binding.png"/> +The way angular templates works is different, as illustrated in the diagram on the right. They are +different because first the template (which is the uncompiled HTML along with any additional markup +or directives) is compiled on the browser, and second, the compilation step produces a live view. +We say live because any changes to the view are immediately reflected in the model, and any changes +in the model are propagated to the view. This makes the model always the single-source-of-truth for +the application state, greatly simplifying the programing model for the developer. You can think of +the view as simply an instant projection of your model. + +Because the view is just a projection of the model, the controller is completely separated from the +view and unaware of it. This makes testing a snap because it is easy to test your controller in +isolation without the view and the related DOM/browser dependency. + +For details about how data binding works in angular, see {@link angular.scope Scope}. diff --git a/docs/guide.ngdoc b/docs/guide.ngdoc new file mode 100644 index 00000000..b1ac143e --- /dev/null +++ b/docs/guide.ngdoc @@ -0,0 +1,36 @@ +@workInProgress +@ngdoc overview +@name Developer Guide +@description + +* {@link guide.overview Overview} - An overview of angular, including its philosophy and how it +  works. +* {@link guide.bootstrap Bootstrap} - How to bootstrap your application to the angular environment. +* {@link guide.template Template} - How to define your application's view using HTML, CSS, and +  other built-in angular constructs. +* {@link guide.compiler Compiler} - All about the HTML compiler that's at the core of angular. +  * {@link angular.directive Directive} - How to use XML attributes to augment an existing DOM +    element. +  * {@link angular.markup Markup} - How to use markup to create shorthand for a widget or a +    directive. For example, markup is what allows you to use the double curly brace notation +    `{{}}` to bind expressions to elements. +    * {@link guide.data-binding Data Binding} - About the mechanism that keeps the model the single +      source of truth of your application at all times, with the view as a live projection of the +      model. +    * {@link angular.filter Filter} - How to format your data for display to the user. +  * {@link angular.widget Widget} - How to create new DOM elements that the browser doesn't already +    understand. +    * {@link angular.validator Validator} - How to validate user input. +    * {@link angular.formatter Formatter} - How to format stored data to user-readable text and +      parse the text back to the stored form. +  * {@link guide.css CSS} - Built-in CSS classes, when angular assigns them, and how to override +    their styles. +* {@link angular.scope Scope} - The model in the model-view-controller design pattern. You can +  think about scopes as the JavaScript objects that have extra APIs for registering watchers. +  * {@link guide.expression Expression} - The bindings that are embedded in an angular View. +* {@link angular.service Service} - Objects that are wired through dependency injection and then +  injected into the root scope. +* {@link guide.testing Testing} +  * service:$browser(mock) +* {@link guide.downloading Downloading} - How to download, compile, and host the angular +  environment on your own server. diff --git a/docs/guide.overview.ngdoc b/docs/guide.overview.ngdoc new file mode 100644 index 00000000..d4615f88 --- /dev/null +++ b/docs/guide.overview.ngdoc @@ -0,0 +1,166 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Overview +@description + +# What is angular? +Angular teaches your old browser new tricks. It is what HTML would have been if it had been +designed for building web applications. + +Take a simple example of user input as shown below. If you were using just HTML and JavaScript to +implement this form, you would need to define listeners, DOM updates, and complex input validators +in order to update and format the result. In angular you can achieve the effect with zero lines +of JavaScript code using a declarative approach. Click on the source tab of the example below to +view the angular implementation of this form. + +<doc:example> + <doc:source> +  QTY: <input name="qty" value="1" ng:validate="integer:0" ng:required/> +  * +  Cost: <input name="cost" value="19.95" ng:validate="number" ng:required/> +  = +  {{qty * cost | currency}} + </doc:source> + <doc:scenario> +  it('should show of angular binding', function(){ +    expect(binding('qty * cost')).toEqual('$19.95'); +    input('qty').enter('2'); +    input('cost').enter('5.00'); +    expect(binding('qty * cost')).toEqual('$10.00'); +  }); + </doc:scenario> +</doc:example> + +Angular is to AJAX apps as Ruby on Rails is to round trip apps. + +# Angular frees you from: + * **Registering callbacks:** Registering callbacks clutters your code, and it makes it hard to see +   the forest from the trees. Removing common boilerplate code such as callbacks is advantageous +   because it leaves the JavaScript with a more succinct version of your code, better describing +   what your application does. + * **Manipulating HTML DOM programatically:** Manipulating HTML DOM is a cornerstone of AJAX +   applications, but it is very cumbersome and error-prone. By declaratively describing how the UI +   should change as your application state changes, you are freed from low level DOM manipulation +   activities. Most applications written with angular never have to programatically manipulate +   the DOM. + * **Marshaling data to and from the UI:** CRUD operations make up the majority of most AJAX +   applications. The flow of marshaling data from the server to an internal object to a HTML form, +   validating the form, displaying validation errors, returning to an internal model and then back +   to the server creates a lot of boilerplate code. angular eliminates almost all of this +   boilerplate. leaving code that is richer and describes the overall flow of the application +   rather than implementation details. + * **Writing tons of initialization code just to get started:** Typically you need to write a lot +   of plumbing and initialization code just to get a basic "Hello World" AJAX app working. With +   angular you can bootstrap your app easily using services, which are auto-injected into your +   application in a GUICE-like dependency-injection style. This allows you to get started +   developing features quickly. As a bonus, you get full control over the initialization process +   in automated tests. + + +# angular is/has: + * **An HTML Compiler:** angular is an HTML compiler in the browser. It allows you to give meaning +   to any HTML element, attribute, or text and create new primitives as building blocks for your +   application. + * **Declarative:** Declarative means that you describe what the page looks like rather than +   instructing how to draw the page. HTML is great at declaring static documents. Angular extends +   the declarative nature of HTML beyond static documents to define dynamic applications. + * **Declarative Templates:** In angular, you write HTML to declare the view and UI templates for +   your application. You can express many common application constructs without using any +   JavaScript at all. + * **Bidirectional Data Binding:** Allows your application to have a single source of truth +   (your model), where the HTML is a projection of the internal state of your application, which +   you can provide to the user in a declarative way. + * **Built-in Services:** angular provides many standard AJAX operations to get you going quickly, +   and dependency-injection allows you to swap them out as needed. Common services include: +   Dependency Inject, History Management, URL Router, AJAX/XHR requests, and data caching, +   to name a few. + * **Very testable:** Testing difficulty is dramatically affected by the way you structure your +   code. With angular, testability is baked in. + +# angular is NOT a: + * **Library:** You don't call its functions. + * **Framework:** It does not call your functions. + * **DOM Manipulation Library:** It does not provide a way to manipulate DOM, but does provide +   primitives to create UI projections of your data. + * **Widget Library:** There are lots of existing widget libraries that you can integrate with +   angular. + + + +# Not just another templating system + +At the highest level, angular looks like a just another templating system, but there are few +important reasons why angular is different and makes it a very good fit for application +development. + +Angular: + * **Uses HTML/CSS syntax:** This makes it easy to read and can be edited with existing HTML/CSS +   authoring tools. + * **Extends HTML vocabulary:** Angular allows you to create new HTML tags, which expand into +   dynamic UI components. + * **Executes in the browser:** Removes the round trip to the server for many operations and +   creates instant feedback for users. + * **Bidirectional data binding:** The model is the single source of truth. Programmatic changes +   to the model are automatically reflected in the view. Any changes by the user to the view are +   automatically reflected in the model. + * **Services:** These allow for a reusable way of injecting dependencies into an application. + * **MVC:** Clean separation between model-view-controller, which aids in understanding, +   maintenance, and testing of large systems. + + +# The angular philosophy + +Angular is built around the belief that declarative code is preferred over imperative when it +comes to building UIs and connecting the pieces together. + +As an example, if you wanted to add a new label to your application, you could do so by simply +adding text to the HTML template, saving the code, and refreshing your browser: + +<pre> +<span class="label">Hello</span> +</pre> + +In programmatic systems you would have to write and run code like this: + +<pre> +var label = new Label(); +label.setText('Hello'); +label.setClass('label'); +parent.addChild(label); +</pre> + +## Benefits: + + * Compile-free: Change your template and logic code and reload the browser to see it run +   immediately. In contrast, programmatic serverside views often need to be compiled and executed +   to be viewed, which takes time. This dramatically increases the speed of your development cycle. + * Declarative templates are easier to understand and change than programmatic instructions. + * Declarative templates can be edited in existing HTML editors such as DreamWeaver, Eclipse, +   TextMate, Vim, etc. + * Declarative templates can be edited by web designers without the need to work with web +   developers. + +HTML is missing certain features, which angular adds via its compiler, thereby "teaching" the +browser these new tricks: + + * Dynamic behavior + * Componentizing HTML snippets into reusable components + * Dynamically include other HTML templates + * Two-way data binding + * Rich validation in forms + * Model-View-Controller modularization + +# Watch a presentation about angular + +<object width="480" height="385"> +  <param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"></param> +  <param name="allowFullScreen" value="true"></param> +  <param name="allowscriptaccess" value="always"></param> +  <embed src="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1" +         type="application/x-shockwave-flash" allowscriptaccess="always" +         allowfullscreen="true" width="480" height="385"></embed> +</object> + +{@link https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID Presentation} +| +{@link https://docs.google.com/document/edit?id=1ZHVhqC0apbzPRQcgnb1Ye-bAUbNJ-IlFMyPBPCZ2cYU&hl=en&authkey=CInnwLYO Source} diff --git a/docs/guide.template.ngdoc b/docs/guide.template.ngdoc new file mode 100644 index 00000000..ae9bba92 --- /dev/null +++ b/docs/guide.template.ngdoc @@ -0,0 +1,22 @@ +@workInProgress +@ngdoc overview +@name Developer Guide: Template +@description +#Template + +You can think of a template in angular as a domain-specific language that you can use to easily +build the view of your web application. You create a template by writing HTML and CSS, and you can +add any constructs that you want to the HTML. This means that you can attach rendering and behavior +to any HTML element, attribute or markup text. + +In addition to writing HTML and CSS, you can also use the following angular constructs to create +your template: + + * **Directive** - XML attributes that augment an existing DOM element. + * **Markup** - Lets you create shorthand for a widget or a directive. For example, markup is what +                allows you to use the double curly brace notation {{}} to bind expressions to +                elements. + * **Filter** - Lets you format your data for display to the user. + * **Widget** - Lets you create new DOM elements that the browser doesn't already understand. + * **Validator** - Lets you validate user input. + * **Formatter** - Lets you format the input object into a user readable view. diff --git a/docs/img/One_Way_Data_Binding.png b/docs/img/One_Way_Data_Binding.png Binary files differnew file mode 100644 index 00000000..60e7e3bf --- /dev/null +++ b/docs/img/One_Way_Data_Binding.png diff --git a/docs/img/Two_Way_Data_Binding.png b/docs/img/Two_Way_Data_Binding.png Binary files differnew file mode 100644 index 00000000..3a9c6d1e --- /dev/null +++ b/docs/img/Two_Way_Data_Binding.png diff --git a/docs/spec/domSpec.js b/docs/spec/domSpec.js index 58063789..f9308ed6 100644 --- a/docs/spec/domSpec.js +++ b/docs/spec/domSpec.js @@ -1,9 +1,14 @@  var DOM = require('dom.js').DOM;  describe('dom', function(){ +  var dom; + +  beforeEach(function(){ +    dom = new DOM(); +  }); +    describe('example', function(){      it('should render code, live, test', function(){ -      var dom = new DOM();        dom.example('desc', 'src', 'scenario');        expect(dom.toString()).toEqual(            '<h1>Example</h1>\n' + @@ -15,7 +20,6 @@ describe('dom', function(){      });      it('should render non-live, test with description', function(){ -      var dom = new DOM();        dom.example('desc', 'src', false);        expect(dom.toString()).toEqual('<h1>Example</h1>\n' +            '<div class="example">' + @@ -26,10 +30,32 @@ describe('dom', function(){      });      it('should render non-live, test', function(){ -      var dom = new DOM();        dom.example('desc', 'src', false);        expect(dom.toString()).toContain('<pre class="brush: js; html-script: true;">src</pre>');      }); +  }); + +  describe('h', function(){ + +    it('should render using function', function(){ +      var cbThis; +      var cdValue; +      dom.h('heading', 'content', function(value){ +        cbThis = this; +        cbValue = value; +      }); +      expect(cbThis).toEqual(dom); +      expect(cbValue).toEqual('content'); +    }); + +    it('should update heading numbers', function(){ +      dom.h('heading', function(){ +        this.html('<h1>sub-heading</h1>'); +      }); +      expect(dom.toString()).toContain('<h1>heading</h1>'); +      expect(dom.toString()).toContain('<h2>sub-heading</h2>'); +    });    }); +  }); diff --git a/docs/spec/ngdocSpec.js b/docs/spec/ngdocSpec.js index 8f57e329..9d84ee7e 100644 --- a/docs/spec/ngdocSpec.js +++ b/docs/spec/ngdocSpec.js @@ -19,9 +19,28 @@ describe('ngdoc', function(){      describe('metadata', function(){        it('should find keywords', function(){ -        expect(new Doc('\nHello: World! @ignore.').keywords()).toEqual('hello world'); +        expect(new Doc('\nHello: World! @ignore. $abc').keywords()).toEqual('$abc hello world');          expect(new Doc('The `ng:class-odd` and').keywords()).toEqual('and ng:class-odd the');        }); + +      it('should have shortName', function(){ +        var d1 = new Doc('@name a.b.c').parse(); +        var d2 = new Doc('@name a.b.ng:c').parse(); +        var d3 = new Doc('@name some text: more text').parse(); +        expect(ngdoc.metadata([d1])[0].shortName).toEqual('c'); +        expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng:c'); +        expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text'); +      }); + +      it('should have depth information', function(){ +        var d1 = new Doc('@name a.b.c').parse(); +        var d2 = new Doc('@name a.b.ng:c').parse(); +        var d3 = new Doc('@name some text: more text').parse(); +        expect(ngdoc.metadata([d1])[0].depth).toEqual(2); +        expect(ngdoc.metadata([d2])[0].depth).toEqual(2); +        expect(ngdoc.metadata([d3])[0].depth).toEqual(1); +      }); +      });      describe('parse', function(){ @@ -61,9 +80,68 @@ describe('ngdoc', function(){          expect(doc.example).toEqual('A\n\nB');        }); -    }); +      it('should parse filename', function(){ +        var doc = new Doc('@name friendly name', 'docs/a.b.ngdoc', 1); +        doc.parse(0); +        expect(doc.id).toEqual('a.b'); +        expect(doc.name).toEqual('friendly name'); +      }); +      it('should escape <doc:source> element', function(){ +        var doc = new Doc('@description before <doc:example>' + +            '<doc:source>\n<>\n</doc:source></doc:example> after'); +        doc.parse(); +        expect(doc.description).toContain('<p>before </p><doc:example>' + +            '<doc:source>\n<>\n</doc:source></doc:example><p>after</p>'); +      }); + +      it('should escape <doc:scenario> element', function(){ +        var doc = new Doc('@description before <doc:example>' + +            '<doc:scenario>\n<>\n</doc:scenario></doc:example> after'); +        doc.parse(); +        expect(doc.description).toContain('<p>before </p><doc:example>' + +            '<doc:scenario>\n<>\n</doc:scenario></doc:example><p>after</p>'); +      }); + +      describe('sorting', function(){ +        function property(name) { +          return function(obj) {return obj[name];}; +        } +        function noop(){} +        function doc(type, name){ +          return { +              id: name, +              ngdoc: type, +              keywords: noop +          }; +        } + +        var angular_widget = doc('overview', 'angular.widget'); +        var angular_x = doc('function', 'angular.x'); +        var angular_y = doc('property', 'angular.y'); + +        it('should put angular.fn() in front of angular.widget, etc', function(){ +          expect(ngdoc.metadata([angular_widget, angular_y, angular_x]).map(property('id'))) +            .toEqual(['angular.x', 'angular.y', 'angular.widget' ]); +        }); +      }); +    }); +  }); +  describe('scenario', function(){ +    it('should render from @example/@scenario and <doc:example>', function(){ +      var doc = new Doc( +          '@id id\n' + +          '@description <doc:example><doc:scenario>scenario0</doc:scenario></doc:example>' + +          '@example exempleText\n' + +          '@scenario scenario1\n' + +          '@scenario scenario2').parse(); +      expect(ngdoc.scenarios([doc])).toContain('describe("id"'); +      expect(ngdoc.scenarios([doc])).toContain('navigateTo("index.html#!id")'); +      expect(ngdoc.scenarios([doc])).toContain('\n  scenario0\n'); +      expect(ngdoc.scenarios([doc])).toContain('\n  scenario1\n'); +      expect(ngdoc.scenarios([doc])).toContain('\n  scenario2\n'); +    });    });    describe('markdown', function(){ @@ -86,8 +164,9 @@ describe('ngdoc', function(){      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'); +        toMatch('</div><h1>One</h1><div');      }); +    });    describe('trim', function(){ @@ -230,7 +309,7 @@ describe('ngdoc', function(){          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' + +               '<h1>bah</h1>\n\n' +                 '<p>foo </p>' +                 '<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>'); @@ -243,18 +322,15 @@ describe('ngdoc', function(){              '{@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(); +          toContain('foo <a href="#!angular.foo"><code>angular.foo</code></a>'); +        expect(doc.description). +          toContain('da <a href="#!angular.foo"><code>bar foo bar</code></a>');          expect(doc.description). -          toBe('<h3>foo</h3>\n\n<p>abc</p>\n\n<h4>bar</h4>\n\n<p>xyz</p>'); +          toContain('dad<a href="#!angular.foo"><code>angular.foo</code></a>'); +        expect(doc.description). +          toContain('<a href="#!angular.directive.ng:foo"><code>ng:foo</code></a>');        }); +      });      describe('@example', function(){ @@ -291,8 +367,8 @@ describe('ngdoc', function(){      }); -    describe('@deprecated', function() { -      it('should parse @deprecated', function() { +    describe('@depricated', function() { +      it('should parse @depricated', function() {          var doc = new Doc('@deprecated Replaced with foo.');          doc.parse();          expect(doc.deprecated).toBe('Replaced with foo.'); @@ -315,6 +391,17 @@ describe('ngdoc', function(){    });    describe('usage', function(){ +    describe('overview', function(){ +      it('should supress description heading', function(){ +        var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext'); +        doc.parse(); +        expect(doc.html()).toContain('text'); +        expect(doc.html()).toContain('<h2>heading</h2>'); +        expect(doc.html()).not.toContain('Description'); +      }); +    }); + +      describe('filter', function(){        it('should format', function(){          var doc = new Doc({ diff --git a/docs/src/dom.js b/docs/src/dom.js index fedd4e19..7708cbc9 100644 --- a/docs/src/dom.js +++ b/docs/src/dom.js @@ -3,12 +3,18 @@   */  exports.DOM = DOM; +exports.htmlEscape = htmlEscape;  ////////////////////////////////////////////////////////// +function htmlEscape(text){ +  return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); +} + +  function DOM(){    this.out = []; -  this.headingDepth = 1; +  this.headingDepth = 0;  }  var INLINE_TAGS = { @@ -23,7 +29,7 @@ DOM.prototype = {    text: function(content) {      if (typeof content == "string") { -      this.out.push(content.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')); +      this.out.push(htmlEscape(content));      } else if (typeof content == 'function') {        content.call(this, this);      } else if (content instanceof Array) { @@ -33,6 +39,13 @@ DOM.prototype = {    html: function(html) {      if (html) { +      var headingDepth = this.headingDepth; +      for ( var i = 10; i > 0; --i) { +        html = html +          .replace(new RegExp('(<\/?h)' + i + '(>)', 'gm'), function(all, start, end){ +            return start + (i + headingDepth) + end; +          }); +      }        this.out.push(html);      }    }, @@ -80,15 +93,17 @@ DOM.prototype = {    h: function(heading, content, fn){      if (content==undefined || content && content.legth == 0) return; -    this.tag('h' + this.headingDepth, heading);      this.headingDepth++; +    this.tag('h' + this.headingDepth, heading);      var className = typeof heading == 'string' -      ? {'class': heading.toLowerCase().replace(/[^\d\w_]/, '-')} +      ? {'class': heading.toLowerCase().replace(/[^\d\w_]/mg, '-').replace(/-+/gm, '-')}        : null;      if (content instanceof Array) {        this.ul(content, className, fn);      } else if (fn) { -      this.tag('div', className, fn); +      this.tag('div', className, function(){ +        fn.call(this, content); +      });      } else {        this.tag('div', className, content);      } diff --git a/docs/src/gen-docs.js b/docs/src/gen-docs.js index a287a2be..7e17ea8f 100644 --- a/docs/src/gen-docs.js +++ b/docs/src/gen-docs.js @@ -20,10 +20,11 @@ var work = callback.chain(function(){  var writes = callback.chain(function(){    ngdoc.merge(docs);    docs.forEach(function(doc){ -    writer.output(doc.name + '.html', doc.html(), writes.waitFor()); +    writer.output(doc.id + '.html', doc.html(), writes.waitFor());    });    var metadata = ngdoc.metadata(docs); -  writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata), ';'], writes.waitFor()); +  writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';'], writes.waitFor()); +  writer.copyImages(writes.waitFor());    writer.copy('index.html', writes.waitFor());    writer.copy('docs.js', writes.waitFor());    writer.copy('docs.css', writes.waitFor()); diff --git a/docs/src/ngdoc.js b/docs/src/ngdoc.js index 7fb9af73..e01d9ccf 100644 --- a/docs/src/ngdoc.js +++ b/docs/src/ngdoc.js @@ -4,6 +4,7 @@  var Showdown = require('showdown').Showdown;  var DOM = require('dom.js').DOM; +var htmlEscape = require('dom.js').htmlEscape;  var NEW_LINE = /\n\r?/;  exports.markdown = markdown; @@ -39,7 +40,7 @@ Doc.prototype = {      var words = [];      var tokens = this.text.toLowerCase().split(/[,\.\`\'\"\s]+/mg);      tokens.forEach(function(key){ -      var match = key.match(/^(([a-z]|ng\:)[\w\_\-]{2,})/); +      var match = key.match(/^(([\$\_a-z]|ng\:)[\w\_\-]{2,})/);        if (match){          key = match[1];          if (!keywords[key]) { @@ -57,6 +58,11 @@ Doc.prototype = {      var atText;      var match;      var self = this; +    this.scenarios = []; +    this.requires = []; +    this.param = []; +    this.properties = []; +    this.methods = [];      self.text.split(NEW_LINE).forEach(function(line){        if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {          // we found @name ... @@ -73,6 +79,9 @@ Doc.prototype = {      });      flush();      this.shortName = (this.name || '').split(/[\.#]/).pop(); +    this.id = this.id // if we have an id just use it +      || (((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) // try to extract it from file name +      || this.name; // default to name      this.description = markdown(this.description);      this['this'] = markdown(this['this']);      this.exampleDescription = markdown(this.exampleDescription || this.exampleDesc); @@ -94,7 +103,6 @@ Doc.prototype = {              optional: !!match[2],              'default':match[6]            }; -          self.param = self.param || [];            self.param.push(param);          } else if (atName == 'returns') {            var match = text.match(/^{([^}=]+)}\s+(.*)/); @@ -105,9 +113,16 @@ Doc.prototype = {              type: match[1],              description: markdown(text.replace(match[0], match[2]))            }; +        } else if(atName == 'description') { +          text.replace(/<doc:scenario>([\s\S]*)<\/doc:scenario>/mi, +              function(_, scenario){ +                self.scenarios.push(scenario); +              }); +          self.description = text;          } else if(atName == 'requires') { -          self.requires = self.requires || [];            self.requires.push(text); +        } else if(atName == 'scenario') { +          self.scenarios.push(text);          } else if(atName == 'property') {            var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/);            if (!match) { @@ -118,7 +133,6 @@ Doc.prototype = {                name: match[3],                description: match[5] || ''              }; -          self.properties = self.properties || [];            self.properties.push(property);          } else {            self[atName] = text; @@ -135,25 +149,10 @@ Doc.prototype = {        notice('workInProgress', 'Work in Progress',            'This page is currently being revised. It might be incomplete or contain inaccuracies.');        notice('deprecated', 'Deprecated API', self.deprecated); -      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.exampleDescription, method.example, false); -        }); -      }); -      dom.h('Properties', self.properties, function(property){ -        dom.h(property.name, function(){ -         dom.text(property.description); -         dom.example(property.exampleDescription, property.example, false); -        }); -      }); +      (self['html_usage_' + self.ngdoc] || function(){ +        throw new Error("Don't know how to format @ngdoc: " + self.ngdoc); +      }).call(self, dom);        dom.example(self.exampleDescription, self.example, self.scenario);      }); @@ -162,29 +161,6 @@ Doc.prototype = {      ////////////////////////// -    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){ @@ -240,6 +216,8 @@ Doc.prototype = {    html_usage_function: function(dom){      var self = this; +    dom.h('Description', self.description, dom.html); +    dom.h('Dependencies', self.requires);      dom.h('Usage', function(){        dom.code(function(){          dom.text(self.name); @@ -256,11 +234,13 @@ Doc.prototype = {    html_usage_directive: function(dom){      var self = this; +    dom.h('Description', self.description, dom.html); +    dom.h('Dependencies', self.requires);      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) { +        if (self.param.length) {            dom.text('="' + self.param[0].name + '"');          }          dom.text('>\n   ...\n'); @@ -272,6 +252,8 @@ Doc.prototype = {    html_usage_filter: function(dom){      var self = this; +    dom.h('Description', self.description, dom.html); +    dom.h('Dependencies', self.requires);      dom.h('Usage', function(){        dom.h('In HTML Template Binding', function(){          dom.tag('code', function(){ @@ -302,6 +284,8 @@ Doc.prototype = {    html_usage_formatter: function(dom){      var self = this; +    dom.h('Description', self.description, dom.html); +    dom.h('Dependencies', self.requires);      dom.h('Usage', function(){        dom.h('In HTML Template Binding', function(){          dom.code(function(){ @@ -340,6 +324,8 @@ Doc.prototype = {    html_usage_validator: function(dom){      var self = this; +    dom.h('Description', self.description, dom.html); +    dom.h('Dependencies', self.requires);      dom.h('Usage', function(){        dom.h('In HTML Template Binding', function(){          dom.code(function(){ @@ -368,6 +354,8 @@ Doc.prototype = {    html_usage_widget: function(dom){      var self = this; +    dom.h('Description', self.description, dom.html); +    dom.h('Dependencies', self.requires);      dom.h('Usage', function(){        dom.h('In HTML Template Binding', function(){          dom.code(function(){ @@ -376,7 +364,7 @@ Doc.prototype = {              dom.text(self.element);              dom.text(' ');              dom.text(self.shortName.substring(1)); -            if (self.param) { +            if (self.param.length) {                dom.text('="');                dom.text(self.param[0].name);                dom.text('"'); @@ -407,9 +395,27 @@ Doc.prototype = {    },    html_usage_overview: function(dom){ +    dom.html(this.description);    },    html_usage_service: function(dom){ +    dom.h('Description', this.description, dom.html); +    dom.h('Dependencies', this.requires); + +    dom.h('Methods', this.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.exampleDescription, method.example, false); +      }); +    }); +    dom.h('Properties', this.properties, function(property){ +      dom.h(property.name, function(){ +       dom.text(property.description); +       dom.example(property.exampleDescription, property.example, false); +      }); +    });    },    parameters: function(dom, separator, skipFirst, prefix) { @@ -433,7 +439,7 @@ Doc.prototype = {  //////////////////////////////////////////////////////////  function markdown (text) {    if (!text) return text; -  var parts = text.split(/(<pre>[\s\S]*?<\/pre>)/), +  var parts = text.split(/(<pre>[\s\S]*?<\/pre>|<doc:example>[\s\S]*?<\/doc:example>)/),        match;    parts.forEach(function(text, i){ @@ -443,39 +449,51 @@ function markdown (text) {                  content.replace(/</g, '<').replace(/>/g, '>') +                 '</pre></div>';        }); +    } else if (text.match(/^<doc:example>/)) { +      text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi, +        function(_, before, content, after){ +          return before + htmlEscape(content) + after; +        }); +      text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi, +        function(_, before, content, after){ +          return before + htmlEscape(content) + after; +        });      } 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>'); -      } +      text = text.replace(/{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/gm, +        function(_all, url, _2, _3, title){ +          return '<a href="#!' + url + '">' +            + (url.match(/^angular\./) ? '<code>' : '') +            + (title || url) +            + (url.match(/^angular\./) ? '</code>' : '') +            + '</a>'; +        }); +      text = new Showdown.converter().makeHtml(text);      }      parts[i] = text;    });    return parts.join('');  }; -var R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m; -            //       1       123     3 4     42  //////////////////////////////////////////////////////////  function scenarios(docs){    var specs = [];    docs.forEach(function(doc){ +    specs.push('describe("' + doc.id + '", function(){'); +    specs.push('  beforeEach(function(){'); +    specs.push('    browser().navigateTo("index.html#!' + doc.id + '");'); +    specs.push('  });'); +    specs.push(''); +    doc.scenarios.forEach(function(scenario){ +      specs.push(trim(scenario, '  ')); +      specs.push(''); +    }); +    specs.push('});'); +    specs.push('');      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; +  return specs.join('\n');  } @@ -483,8 +501,17 @@ function scenarios(docs){  function metadata(docs){    var words = [];    docs.forEach(function(doc){ +    var path = (doc.name || '').split(/(\.|\:\s+)/); +    for ( var i = 1; i < path.length; i++) { +      path.splice(i, 1); +    } +    var depth = path.length - 1; +    var shortName = path.pop();      words.push({ -      name:doc.name, +      id: doc.id, +      name: doc.name, +      depth: depth, +      shortName: shortName,        type: doc.ngdoc,        keywords:doc.keywords()      }); @@ -493,37 +520,50 @@ function metadata(docs){    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 KEYWORD_PRIORITY = { +  '.guide': 1, +  '.guide.overview': 1, +  '.angular': 7, +  '.angular.Array': 7, +  '.angular.Object': 7, +  '.angular.directive': 7, +  '.angular.filter': 7, +  '.angular.formatter': 7, +  '.angular.scope': 7, +  '.angular.service': 7, +  '.angular.validator': 7, +  '.angular.widget': 7 +}; +function keywordSort(a, b){ +  function mangleName(doc) { +    var path = doc.id.split(/\./); +    var mangled = []; +    var partialName = ''; +    path.forEach(function(name){ +      partialName += '.' + name; +      mangled.push(KEYWORD_PRIORITY[partialName] || 5); +      mangled.push(name); +    }); +    return mangled.join('.');    } - -  var namespacedA = namespacedName(a), -      namespacedB = namespacedName(b); - -  return namespacedA < namespacedB ? -1 : 1; +  var nameA = mangleName(a); +  var nameB = mangleName(b); +  return nameA < nameB ? -1 : (nameA > nameB ? 1 : 0);  }  ////////////////////////////////////////////////////////// -function trim(text) { +function trim(text, prefix) {    var MAX = 9999;    var empty = RegExp.prototype.test.bind(/^\s*$/);    var lines = text.split('\n');    var minIndent = MAX; +  prefix = prefix || '';    lines.forEach(function(line){      minIndent = Math.min(minIndent, indent(line));    });    for ( var i = 0; i < lines.length; i++) { -    lines[i] = lines[i].substring(minIndent); +    lines[i] = prefix + lines[i].substring(minIndent);    }    // remove leading lines    while (empty(lines[0])) { diff --git a/docs/src/templates/doc_widgets.js b/docs/src/templates/doc_widgets.js index 2d1ab8c6..39b9ff48 100644 --- a/docs/src/templates/doc_widgets.js +++ b/docs/src/templates/doc_widgets.js @@ -58,11 +58,22 @@    function indent(text) {      var lines = text.split(/\n/);      var lineNo = []; +    // remove any leading blank lines      while (lines[0].match(/^\s*$/)) lines.shift(); +    // remove any trailing blank lines      while (lines[lines.length - 1].match(/^\s*$/)) lines.pop(); +    var minIndent = 999;      for ( var i = 0; i < lines.length; i++) { -      lines[i] = '  ' + lines[i]; -      lineNo.push(6 + i); +      var line = lines[0]; +      var indent = line.match(/^\s*/)[0]; +      if (indent !== line && indent.length < minIndent) { +        minIndent = indent.length; +      } +    } + +    for ( var i = 0; i < lines.length; i++) { +      lines[i] = '  ' + lines[i].substring(minIndent); +      lineNo.push(5 + i);      }      return {html: lines.join('\n'), hilite: lineNo.join(',') };    }; diff --git a/docs/src/templates/docs.css b/docs/src/templates/docs.css index a687ce87..a7566e79 100644 --- a/docs/src/templates/docs.css +++ b/docs/src/templates/docs.css @@ -181,11 +181,16 @@ a {  }  #sidebar ul li.level-0 { +  margin-top: 0.5em;    margin-left: 0em;    font-weight: bold;    font-size: 1.2em;  } +#sidebar ul li.level-0:first-child { +  margin-top: 0; +} +  #sidebar ul li.level-1.level-angular {    font-family: monospace;    font-weight: normal; @@ -211,6 +216,11 @@ a {    font-family: monospace;  } +#sidebar ul li.level-4 { +  margin-left: 4em; +  font-family: monospace; +} +  /* Warning and Info Banners */ @@ -282,3 +292,11 @@ a {  #main::-webkit-scrollbar {    background-color:#fff;  } + +/* Content */ +img.right { +  float: right; +} +h1, h2, h3, h4, h5 { +  clear: both; +} diff --git a/docs/src/templates/docs.js b/docs/src/templates/docs.js index f1cfc3e7..aaea8c9d 100644 --- a/docs/src/templates/docs.js +++ b/docs/src/templates/docs.js @@ -4,7 +4,7 @@ function DocsController($location, $browser, $window) {    window.$root = this.$root;    this.getUrl = function(page){ -    return '#!' + page.name; +    return '#!' + page.id;    };    this.getCurrentPartial = function(){ @@ -13,14 +13,14 @@ function DocsController($location, $browser, $window) {    this.getTitle = function(){      var hashPath = $location.hashPath || '!angular'; -    if (hashPath.match(/^!angular/)) { +    if (hashPath.match(/^!/)) {        this.partialTitle = hashPath.substring(1);      }      return this.partialTitle;    };    this.getClass = function(page) { -    var depth = page.name.split(/\./).length - 1, +    var depth = page.depth,          cssClass = 'level-' + depth + (page.name == this.getTitle() ? ' selected' : '');      if (depth == 1 && page.type !== 'overview') cssClass += ' level-angular'; @@ -40,8 +40,4 @@ function DocsController($location, $browser, $window) {  } -angular.filter('short', function(name){ -  return (name||'').split(/\./).pop(); -}); -  SyntaxHighlighter['defaults'].toolbar = false; diff --git a/docs/src/templates/index.html b/docs/src/templates/index.html index bb80a4ba..538be297 100644 --- a/docs/src/templates/index.html +++ b/docs/src/templates/index.html @@ -34,7 +34,7 @@             tabindex="1" accesskey="s"/>      <ul id="api-list">        <li ng:repeat="page in pages.$filter(search)" ng:class="getClass(page)"> -        <a href="{{getUrl(page)}}" ng:click="" tabindex="2">{{page.name | short}}</a> +        <a href="{{getUrl(page)}}" ng:click="" tabindex="2">{{page.shortName}}</a>        </li>      </ul>    </div> diff --git a/docs/src/writer.js b/docs/src/writer.js index c72a54a4..953302d4 100644 --- a/docs/src/writer.js +++ b/docs/src/writer.js @@ -50,12 +50,27 @@ exports.makeDir = function (path, callback) {  };  exports.copy = function(filename, callback){ -  //console.log('writing', OUTPUT_DIR + filename, '...'); -  fs.readFile('docs/src/templates/' + filename, function(err, content){ +  copy('docs/src/templates/' + filename, OUTPUT_DIR + filename, callback); +}; + +function copy(from, to, callback) { +  //console.log('writing', to, '...'); +  fs.readFile(from, function(err, content){      if (err) return callback.error(err); -    fs.writeFile( -        OUTPUT_DIR + filename, -        content, -        callback); +    fs.writeFile(to, content, callback);    }); +} + +exports.copyImages = function(callback) { +  exports.makeDir(OUTPUT_DIR + '/img', callback.waitFor(function(){ +    fs.readdir('docs/img', callback.waitFor(function(err, files){ +      if (err) return this.error(err); +      files.forEach(function(file){ +        if (file.match(/\.(png|gif|jpg|jpeg)$/)) { +          copy('docs/img/' + file, OUTPUT_DIR  + '/img/' + file, callback.waitFor()); +        } +      }); +      callback(); +    })); +  }));  };  | 
