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(); + })); + })); }; |
