aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/angular.directive.ngdoc53
-rw-r--r--docs/angular.element.ngdoc43
-rw-r--r--docs/angular.filter.ngdoc76
-rw-r--r--docs/angular.formatter.ngdoc78
-rw-r--r--docs/angular.ngdoc4
-rw-r--r--docs/angular.service.ngdoc159
-rw-r--r--docs/angular.validator.ngdoc73
-rw-r--r--docs/angular.widget.ngdoc73
-rw-r--r--docs/collect.js112
-rw-r--r--docs/index.html2
-rw-r--r--src/Angular.js588
11 files changed, 646 insertions, 615 deletions
diff --git a/docs/angular.directive.ngdoc b/docs/angular.directive.ngdoc
new file mode 100644
index 00000000..9a08e4c7
--- /dev/null
+++ b/docs/angular.directive.ngdoc
@@ -0,0 +1,53 @@
+@workInProgress
+@ngdoc overview
+@name angular.directive
+@namespace Namespace for all directives.
+
+@description
+A directive is an HTML attribute that you can use in an existing HTML element type or in a
+DOM element type that you create as {@link angular.widget}, to modify that element's
+properties. You can use any number of directives per element.
+
+For example, you can add the ng:bind directive as an attribute of an HTML span element, as in
+`<span ng:bind="1+2"></span>`. How does this work? The compiler passes the attribute value
+`1+2` to the ng:bind extension, which in turn tells the {@link angular.scope} to watch that
+expression and report changes. On any change it sets the span text to the expression value.
+
+Here's how to define {@link angular.directive.ng:bind ng:bind}:
+<pre>
+ angular.directive('ng:bind', function(expression, compiledElement) {
+ var compiler = this;
+ return function(linkElement) {
+ var currentScope = this;
+ currentScope.$watch(expression, function(value) {
+ linkElement.text(value);
+ });
+ };
+ });
+</pre>
+
+# Directive vs. Attribute Widget
+Both [attribute widgets](#!angular.widget) and directives can compile a DOM element
+attribute. So why have two different ways to do the same thing? The answer is that order
+matters, but we have no control over the order in which attributes are read. To solve this
+we apply attribute widget before the directive.
+
+For example, consider this piece of HTML, which uses the directives `ng:repeat`, `ng:init`,
+and `ng:bind`:
+<pre>
+ <ul ng:init="people=['mike', 'mary']">
+ <li ng:repeat="person in people" ng:init="a=a+1" ng:bind="person"></li>
+ </ul>
+</pre>
+
+Notice that the order of execution matters here. We need to execute
+{@link angular.directive.ng:repeat ng:repeat} before we run the
+{@link angular.directive.ng:init ng:init} and `ng:bind` on the `<li/>;`. This is because we
+want to run the `ng:init="a=a+1` and `ng:bind="person"` once for each person in people. We
+could not have used directive to create this template because attributes are read in an
+unspecified order and there is no way of guaranteeing that the repeater attribute would
+execute first. Using the `ng:repeat` attribute directive ensures that we can transform the
+DOM element into a template.
+
+Widgets run before directives. Widgets may manipulate the DOM whereas directives are not
+expected to do so, and so they run last.
diff --git a/docs/angular.element.ngdoc b/docs/angular.element.ngdoc
new file mode 100644
index 00000000..a636cc25
--- /dev/null
+++ b/docs/angular.element.ngdoc
@@ -0,0 +1,43 @@
+@workInProgress
+@ngdoc function
+@name angular.element
+@function
+
+@description
+Wraps a raw DOM element or HTML string as [jQuery](http://jquery.com) element.
+`angular.element` is either an alias for [jQuery](http://api.jquery.com/jQuery/) function if
+jQuery is loaded or a function that wraps the element or string in angular's jQuery lite
+implementation.
+
+Real jQuery always takes precedence if it was loaded before angular.
+
+Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows
+angular to manipulate DOM. The functions implemented are usually just the basic versions of
+them and might not support arguments and invocation styles.
+
+NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
+raw DOM references.
+
+Angular's jQuery lite implements these functions:
+
+- [addClass()](http://api.jquery.com/addClass/)
+- [after()](http://api.jquery.com/after/)
+- [append()](http://api.jquery.com/append/)
+- [attr()](http://api.jquery.com/attr/)
+- [bind()](http://api.jquery.com/bind/)
+- [children()](http://api.jquery.com/children/)
+- [clone()](http://api.jquery.com/clone/)
+- [css()](http://api.jquery.com/css/)
+- [data()](http://api.jquery.com/data/)
+- [hasClass()](http://api.jquery.com/hasClass/)
+- [parent()](http://api.jquery.com/parent/)
+- [remove()](http://api.jquery.com/remove/)
+- [removeAttr()](http://api.jquery.com/removeAttr/)
+- [removeClass()](http://api.jquery.com/removeClass/)
+- [removeData()](http://api.jquery.com/removeData/)
+- [replaceWith()](http://api.jquery.com/replaceWith/)
+- [text()](http://api.jquery.com/text/)
+- [trigger()](http://api.jquery.com/trigger/)
+
+@param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
+@returns {Object} jQuery object.
diff --git a/docs/angular.filter.ngdoc b/docs/angular.filter.ngdoc
new file mode 100644
index 00000000..9d1191c5
--- /dev/null
+++ b/docs/angular.filter.ngdoc
@@ -0,0 +1,76 @@
+@workInProgress
+@ngdoc overview
+@name angular.filter
+@namespace Namespace for all filters.
+@description
+# Overview
+Filters are a standard way to format your data for display to the user. For example, you
+might have the number 1234.5678 and would like to display it as US currency: $1,234.57.
+Filters allow you to do just that. In addition to transforming the data, filters also modify
+the DOM. This allows the filters to for example apply css styles to the filtered output if
+certain conditions were met.
+
+
+# Standard Filters
+
+The Angular framework provides a standard set of filters for common operations, including:
+{@link angular.filter.currency currency}, {@link angular.filter.json json},
+{@link angular.filter.number number}, and {@link angular.filter.html html}. You can also add
+your own filters.
+
+
+# Syntax
+
+Filters can be part of any {@link angular.scope} evaluation but are typically used with
+{{bindings}}. Filters typically transform the data to a new data type, formating the data in
+the process. Filters can be chained and take optional arguments. Here are few examples:
+
+* No filter: {{1234.5678}} => 1234.5678
+* Number filter: {{1234.5678|number}} => 1,234.57. Notice the “,” and rounding to two
+ significant digits.
+* Filter with arguments: {{1234.5678|number:5}} => 1,234.56780. Filters can take optional
+ arguments, separated by colons in a binding. To number, the argument “5” requests 5 digits
+ to the right of the decimal point.
+
+
+# Writing your own Filters
+
+Writing your own filter is very easy: just define a JavaScript function on `angular.filter`.
+The framework passes in the input value as the first argument to your function. Any filter
+arguments are passed in as additional function arguments.
+
+You can use these variables in the function:
+
+* `this` — The current scope.
+* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
+ the DOM in addition to transforming the input.
+
+
+@exampleDescription
+ The following example filter reverses a text string. In addition, it conditionally makes the
+ text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
+ modification).
+
+@example
+ <script type="text/javascript">
+ angular.filter('reverse', function(input, uppercase, color) {
+ var out = "";
+ for (var i = 0; i < input.length; i++) {
+ out = input.charAt(i) + out;
+ }
+ if (uppercase) {
+ out = out.toUpperCase();
+ }
+ if (color) {
+ this.$element.css('color', color);
+ }
+ return out;
+ });
+ </script>
+
+ <input name="text" type="text" value="hello" /><br>
+ No filter: {{text}}<br>
+ Reverse: {{text|reverse}}<br>
+ Reverse + uppercase: {{text|reverse:true}}<br>
+ Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
+
diff --git a/docs/angular.formatter.ngdoc b/docs/angular.formatter.ngdoc
new file mode 100644
index 00000000..ba28471f
--- /dev/null
+++ b/docs/angular.formatter.ngdoc
@@ -0,0 +1,78 @@
+@workInProgress
+@ngdoc overview
+@name angular.formatter
+@namespace Namespace for all formats.
+@description
+# Overview
+The formatters are responsible for translating user readable text in an input widget to a
+data model stored in an application.
+
+# Writting your own Formatter
+Writing your own formatter is easy. Just register a pair of JavaScript functions with
+`angular.formatter`. One function for parsing user input text to the stored form,
+and one for formatting the stored data to user-visible text.
+
+Here is an example of a "reverse" formatter: The data is stored in uppercase and in
+reverse, while it is displayed in lower case and non-reversed. User edits are
+automatically parsed into the internal form and data changes are automatically
+formatted to the viewed form.
+
+<pre>
+function reverse(text) {
+ var reversed = [];
+ for (var i = 0; i < text.length; i++) {
+ reversed.unshift(text.charAt(i));
+ }
+ return reversed.join('');
+}
+
+angular.formatter('reverse', {
+ parse: function(value){
+ return reverse(value||'').toUpperCase();
+ },
+ format: function(value){
+ return reverse(value||'').toLowerCase();
+ }
+});
+</pre>
+
+@example
+<script type="text/javascript">
+function reverse(text) {
+ var reversed = [];
+ for (var i = 0; i < text.length; i++) {
+ reversed.unshift(text.charAt(i));
+ }
+ return reversed.join('');
+}
+
+angular.formatter('reverse', {
+ parse: function(value){
+ return reverse(value||'').toUpperCase();
+ },
+ format: function(value){
+ return reverse(value||'').toLowerCase();
+ }
+});
+</script>
+
+Formatted:
+<input type="text" name="data" value="angular" ng:format="reverse"/>
+<br/>
+
+Stored:
+<input type="text" name="data"/><br/>
+<pre>{{data}}</pre>
+
+
+@scenario
+it('should store reverse', function(){
+ expect(element('.doc-example input:first').val()).toEqual('angular');
+ expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
+
+ this.addFutureAction('change to XYZ', function($window, $document, done){
+ $document.elements('.doc-example input:last').val('XYZ').trigger('change');
+ done();
+ });
+ expect(element('.doc-example input:first').val()).toEqual('zyx');
+});
diff --git a/docs/angular.ngdoc b/docs/angular.ngdoc
new file mode 100644
index 00000000..3f342d1b
--- /dev/null
+++ b/docs/angular.ngdoc
@@ -0,0 +1,4 @@
+@workInProgress
+@ngdoc overview
+@name angular
+@namespace The exported angular namespace.
diff --git a/docs/angular.service.ngdoc b/docs/angular.service.ngdoc
new file mode 100644
index 00000000..30fb8512
--- /dev/null
+++ b/docs/angular.service.ngdoc
@@ -0,0 +1,159 @@
+@workInProgress
+@ngdoc overview
+@name angular.service
+
+@description
+# Overview
+Services are substituable objects, which are wired together using dependency injection.
+Each service could have dependencies (other services), which are passed in constructor.
+Because JS is dynamicaly typed language, dependency injection can not use static types
+to satisfy these dependencies, so each service must explicitely define its dependencies.
+This is done by `$inject` property.
+
+For now, life time of all services is the same as the life time of page.
+
+
+# Built-in services
+The Angular framework provides a standard set of services for common operations.
+You can write your own services and rewrite these standard services as well.
+Like other core angular variables, the built-in services always start with $.
+
+ * `angular.service.$browser`
+ * `angular.service.$window`
+ * `angular.service.$document`
+ * `angular.service.$location`
+ * `angular.service.$log`
+ * `angular.service.$exceptionHandler`
+ * `angular.service.$hover`
+ * `angular.service.$invalidWidgets`
+ * `angular.service.$route`
+ * `angular.service.$xhr`
+ * `angular.service.$xhr.error`
+ * `angular.service.$xhr.bulk`
+ * `angular.service.$xhr.cache`
+ * `angular.service.$resource`
+ * `angular.service.$cookies`
+ * `angular.service.$cookieStore`
+
+# Writing your own custom services
+Angular provides only set of basic services, so you will probably need to write your custom
+service very soon. To do so, you need to write a factory function and register this function
+to angular's dependency injector. This factory function must return an object - your service
+(it is not called with new operator).
+
+**angular.service** has three parameters:
+
+ - `{string} name` - Name of the service
+ - `{function()} factory` - Factory function (called just once by DI)
+ - `{Object} config` - Hash of configuration (`$inject`, `$creation`)
+
+If your service requires - depends on other services, you need to specify them
+in config hash - property $inject. This property is an array of strings (service names).
+These dependencies will be passed as parameters to the factory function by DI.
+This approach is very useful when testing, as you can inject mocks/stubs/dummies.
+
+Here is an example of very simple service. This service requires $window service (it's
+passed as a parameter to factory function) and it's just a function.
+
+This service simple stores all notifications and after third one, it displays all of them by
+window alert.
+<pre>
+ angular.service('notify', function(win) {
+ var msgs = [];
+ return function(msg) {
+ msgs.push(msg);
+ if (msgs.length == 3) {
+ win.alert(msgs.join("\n"));
+ msgs = [];
+ }
+ };
+ }, {$inject: ['$window']});
+</pre>
+
+And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert.
+<pre>
+var mock, notify;
+
+beforeEach(function() {
+ mock = {alert: jasmine.createSpy()};
+ notify = angular.service('notify')(mock);
+});
+
+it('should not alert first two notifications', function() {
+ notify('one');
+ notify('two');
+ expect(mock.alert).not.toHaveBeenCalled();
+});
+
+it('should alert all after third notification', function() {
+ notify('one');
+ notify('two');
+ notify('three');
+ expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
+});
+
+it('should clear messages after alert', function() {
+ notify('one');
+ notify('two');
+ notify('third');
+ notify('more');
+ notify('two');
+ notify('third');
+ expect(mock.alert.callCount).toEqual(2);
+ expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
+});
+</pre>
+
+# Injecting services into controllers
+Using services in a controllers is very similar to using service in other service.
+Again, we will use dependency injection.
+
+JavaScript is dynamic language, so DI is not able to figure out which services to inject by
+static types (like in static typed languages). Therefore you must specify the service name
+by the `$inject` property - it's an array that contains strings with names of services to be
+injected. The name must match the id that service has been registered as with angular.
+The order of the services in the array matters, because this order will be used when calling
+the factory function with injected parameters. The names of parameters in factory function
+don't matter, but by convention they match the service ids.
+<pre>
+function myController($loc, $log) {
+ this.firstMethod = function() {
+ // use $location service
+ $loc.setHash();
+ };
+ this.secondMethod = function() {
+ // use $log service
+ $log.info('...');
+ };
+}
+// which services to inject ?
+myController.$inject = ['$location', '$log'];
+</pre>
+
+@example
+<script type="text/javascript">
+ angular.service('notify', function(win) {
+ var msgs = [];
+ return function(msg) {
+ msgs.push(msg);
+ if (msgs.length == 3) {
+ win.alert(msgs.join("\n"));
+ msgs = [];
+ }
+ };
+ }, {$inject: ['$window']});
+
+ function myController(notifyService) {
+ this.callNotify = function(msg) {
+ notifyService(msg);
+ };
+ }
+
+ myController.$inject = ['notify'];
+</script>
+
+<div ng:controller="myController">
+<p>Let's try this simple notify service, injected into the controller...</p>
+<input ng:init="message='test'" type="text" name="message" />
+<button ng:click="callNotify(message);">NOTIFY</button>
+</div>
diff --git a/docs/angular.validator.ngdoc b/docs/angular.validator.ngdoc
new file mode 100644
index 00000000..acd3caf2
--- /dev/null
+++ b/docs/angular.validator.ngdoc
@@ -0,0 +1,73 @@
+@workInProgress
+@ngdoc overview
+@name angular.validator
+@namespace Namespace for all filters.
+@description
+# Overview
+Validators are a standard way to check the user input against a specific criteria. For
+example, you might need to check that an input field contains a well-formed phone number.
+
+# Syntax
+Attach a validator on user input widgets using the `ng:validate` attribute.
+
+<doc:example>
+ <doc:source>
+ Change me: &lt;input type="text" name="number" ng:validate="integer" value="123"&gt;
+ </doc:source>
+ <doc:scenario>
+ it('should validate the default number string', function() {
+ expect(element('input[name=number]').attr('class')).
+ not().toMatch(/ng-validation-error/);
+ });
+ it('should not validate "foo"', function() {
+ input('number').enter('foo');
+ expect(element('input[name=number]').attr('class')).
+ toMatch(/ng-validation-error/);
+ });
+ </doc:scenario>
+</doc:example>
+
+
+# Writing your own Validators
+Writing your own validator is easy. To make a function available as a
+validator, just define the JavaScript function on the `angular.validator`
+object. <angular/> passes in the input to validate as the first argument
+to your function. Any additional validator arguments are passed in as
+additional arguments to your function.
+
+You can use these variables in the function:
+
+* `this` — The current scope.
+* `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
+ the DOM in addition to transforming the input.
+
+In this example we have written a upsTrackingNo validator.
+It marks the input text "valid" only when the user enters a well-formed
+UPS tracking number.
+
+@css ng-validation-error
+ When validation fails, this css class is applied to the binding, making its borders red by
+ default.
+
+@example
+<script>
+ angular.validator('upsTrackingNo', function(input, format) {
+ var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
+ return input.match(regexp)?"":"The format must match " + format;
+ });
+</script>
+<input type="text" name="trackNo" size="40"
+ ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
+ value="1Z 123 456 78 9012 345 6"/>
+
+@scenario
+it('should validate correct UPS tracking number', function() {
+ expect(element('input[name=trackNo]').attr('class')).
+ not().toMatch(/ng-validation-error/);
+});
+
+it('should not validate in correct UPS tracking number', function() {
+ input('trackNo').enter('foo');
+ expect(element('input[name=trackNo]').attr('class')).
+ toMatch(/ng-validation-error/);
+});
diff --git a/docs/angular.widget.ngdoc b/docs/angular.widget.ngdoc
new file mode 100644
index 00000000..5f15398f
--- /dev/null
+++ b/docs/angular.widget.ngdoc
@@ -0,0 +1,73 @@
+@workInProgress
+@ngdoc overview
+@name angular.widget
+@namespace Namespace for all widgets.
+@description
+# Overview
+Widgets allow you to create DOM elements that the browser doesn't
+already understand. You create the widget in your namespace and
+assign it behavior. You can only bind one widget per DOM element
+(unlike directives, in which you can use any number per DOM
+element). Widgets are expected to manipulate the DOM tree by
+adding new elements whereas directives are expected to only modify
+element properties.
+
+Widgets come in two flavors: element and attribute.
+
+# Element Widget
+Let's say we would like to create a new element type in the
+namespace `my` that can watch an expression and alert() the user
+with each new value.
+
+<pre>
+&lt;my:watch exp="name"/&gt;
+</pre>
+
+You can implement `my:watch` like this:
+<pre>
+angular.widget('my:watch', function(compileElement) {
+ var compiler = this;
+ var exp = compileElement.attr('exp');
+ return function(linkElement) {
+ var currentScope = this;
+ currentScope.$watch(exp, function(value){
+ alert(value);
+ }};
+ };
+});
+</pre>
+
+# Attribute Widget
+Let's implement the same widget, but this time as an attribute
+that can be added to any existing DOM element.
+<pre>
+&lt;div my-watch="name"&gt;text&lt;/div&gt;
+</pre>
+You can implement `my:watch` attribute like this:
+<pre>
+angular.widget('@my:watch', function(expression, compileElement) {
+ var compiler = this;
+ return function(linkElement) {
+ var currentScope = this;
+ currentScope.$watch(expression, function(value){
+ alert(value);
+ });
+ };
+});
+</pre>
+
+@example
+<script>
+ angular.widget('my:time', function(compileElement){
+ compileElement.css('display', 'block');
+ return function(linkElement){
+ function update(){
+ linkElement.text('Current time is: ' + new Date());
+ setTimeout(update, 1000);
+ }
+ update();
+ };
+ });
+</script>
+<my:time></my:time>
+ \ No newline at end of file
diff --git a/docs/collect.js b/docs/collect.js
index 666c92bf..e2f3b940 100644
--- a/docs/collect.js
+++ b/docs/collect.js
@@ -16,39 +16,29 @@ var keywordPages = [];
var SRC_DIR = "docs/";
var OUTPUT_DIR = "build/docs/";
var NEW_LINE = /\n\r?/;
+var TEMPLATES = {};
+var start = now();
+function now(){ return new Date().getTime(); }
var work = callback.chain(function () {
console.log('Parsing Angular Reference Documentation');
- mkdirPath(OUTPUT_DIR, work.waitFor(function(){
- findJsFiles('src', work.waitMany(function(file) {
- //console.log('reading', file, '...');
- findNgDoc(file, work.waitMany(function(doc) {
- parseNgDoc(doc);
- processNgDoc(documentation, doc);
- }));
+ findJsFiles('src', work.waitMany(function(file) {
+ //console.log('reading', file, '...');
+ findNgDocInJsFile(file, work.waitMany(function(doc) {
+ parseNgDoc(doc);
+ processNgDoc(documentation, doc);
}));
}));
+ findNgDocInDir(SRC_DIR, work.waitMany(function(doc){
+ parseNgDoc(doc);
+ processNgDoc(documentation, doc);
+ }));
+ loadTemplates(TEMPLATES, work.waitFor());
+ mkdirPath(OUTPUT_DIR, work.waitFor());
}).onError(function(err){
console.log('ERROR:', err.stack || err);
}).onDone(function(){
- keywordPages.sort(function(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 beatiful code please forgive me for the sin that this code certainly is.
-
- if (a.name === b.name) return 0;
- if (a.name === 'angular') return -1;
- if (b.name === 'angular') return 1;
-
- function namespacedName(page) {
- return (page.name.match(/\./g).length === 1 && page.type !== 'overview' ? '0' : '1') + page.name;
- }
-
- var namespacedA = namespacedName(a),
- namespacedB = namespacedName(b);
-
- return namespacedA < namespacedB ? -1 : 1;
- });
+ keywordPages.sort(keywordSort);
writeDoc(documentation.pages);
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(keywordPages)}, callback.chain());
mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain());
@@ -58,7 +48,7 @@ var work = callback.chain(function () {
mergeTemplate('docs.js', 'docs.js', documentation, callback.chain());
mergeTemplate('doc_widgets.css', 'doc_widgets.css', documentation, callback.chain());
mergeTemplate('doc_widgets.js', 'doc_widgets.js', documentation, callback.chain());
- console.log('DONE');
+ console.log('DONE', now() - start, 'ms.');
});
if (!this.testmode) work();
////////////////////
@@ -163,7 +153,7 @@ function markdownTag(doc, name, value) {
replace(/\<\/pre\>/gmi, '</pre></div>');
}
-R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m
+var R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m;
// 1 123 3 4 42
function markdown(text) {
@@ -313,7 +303,7 @@ function parseNgDoc(doc){
}
}
-function findNgDoc(file, callback) {
+function findNgDocInJsFile(file, callback) {
fs.readFile(file, callback.waitFor(function(err, content){
var lines = content.toString().split(NEW_LINE);
var doc;
@@ -346,6 +336,22 @@ function findNgDoc(file, callback) {
}));
}
+function loadTemplates(cache, callback){
+ fs.readdir('docs', callback.waitFor(function(err, files){
+ if (err) return this.error(err);
+ files.forEach(function(file){
+ var match = file.match(/^(.*)\.template$/);
+ if (match) {
+ fs.readFile(SRC_DIR + file, callback.waitFor(function(err, content){
+ if (err) return this.error(err);
+ cache[match[1]] = content.toString();
+ }));
+ }
+ });
+ callback();
+ }));
+};
+
function findJsFiles(dir, callback){
fs.readdir(dir, callback.waitFor(function(err, files){
if (err) return this.error(err);
@@ -365,7 +371,7 @@ function findJsFiles(dir, callback){
function processNgDoc(documentation, doc) {
if (!doc.ngdoc) return;
- console.log('Found:', doc.ngdoc + ':' + doc.name);
+ //console.log('Found:', doc.ngdoc + ':' + doc.name);
documentation.byName[doc.name] = doc;
@@ -385,10 +391,50 @@ function processNgDoc(documentation, doc) {
}
}
-function writeDoc(pages) {
+function writeDoc(pages, callback) {
pages.forEach(function(doc) {
- mergeTemplate(
- doc.ngdoc + '.template',
- doc.name + '.html', doc, callback.chain());
+ var template = TEMPLATES[doc.ngdoc];
+ if (!template) throw new Error("No template for:" + doc.ngdoc);
+ var content = mustache.to_html(template, doc);
+ fs.writeFile(OUTPUT_DIR + doc.name + '.html', content, callback);
});
}
+
+function findNgDocInDir(directory, docNotify) {
+ fs.readdir(directory, docNotify.waitFor(function(err, files){
+ if (err) return this.error(err);
+ files.forEach(function(file){
+ console.log(file);
+ if (!file.match(/\.ngdoc$/)) return;
+ fs.readFile(directory + file, docNotify.waitFor(function(err, content){
+ if (err) return this.error(err);
+ docNotify({
+ raw:{
+ text:content.toString(),
+ file: directory + file,
+ line: 1}
+ });
+ }));
+ });
+ docNotify.done();
+ }));
+}
+
+function keywordSort(a,b){
+ // supper ugly comparator that orders all utility methods and objects before all the other stuff
+ // like widgets, directives, services, etc.
+ // Mother of all beautiful code please forgive me for the sin that this code certainly is.
+
+ if (a.name === b.name) return 0;
+ if (a.name === 'angular') return -1;
+ if (b.name === 'angular') return 1;
+
+ function namespacedName(page) {
+ return (page.name.match(/\./g).length === 1 && page.type !== 'overview' ? '0' : '1') + page.name;
+ }
+
+ var namespacedA = namespacedName(a),
+ namespacedB = namespacedName(b);
+
+ return namespacedA < namespacedB ? -1 : 1;
+}
diff --git a/docs/index.html b/docs/index.html
index f5502664..fcc36a2d 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -22,7 +22,7 @@
<script src="doc_widgets.js"></script>
<script src="docs-data.js"></script>
</head>
-<body>
+<body style="display:none;" ng:show="true">
<div id="header">
<h1>
<span class="main-title">{{getTitle()}}</span>
diff --git a/src/Angular.js b/src/Angular.js
index 97792868..d5b5144d 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -87,599 +87,25 @@ var _undefined = undefined,
_ = window['_'],
/** holds major version number for IE or NaN for real browsers */
msie = parseInt((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1], 10),
-
- /**
- * @workInProgress
- * @ngdoc function
- * @name angular.element
- * @function
- *
- * @description
- * Wraps a raw DOM element or HTML string as [jQuery](http://jquery.com) element.
- * `angular.element` is either an alias for [jQuery](http://api.jquery.com/jQuery/) function if
- * jQuery is loaded or a function that wraps the element or string in angular's jQuery lite
- * implementation.
- *
- * Real jQuery always takes precedence if it was loaded before angular.
- *
- * Angular's jQuery lite implementation is a tiny API-compatible subset of jQuery which allows
- * angular to manipulate DOM. The functions implemented are usually just the basic versions of
- * them and might not support arguments and invocation styles.
- *
- * NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
- * raw DOM references.
- *
- * Angular's jQuery lite implements these functions:
- *
- * - [addClass()](http://api.jquery.com/addClass/)
- * - [after()](http://api.jquery.com/after/)
- * - [append()](http://api.jquery.com/append/)
- * - [attr()](http://api.jquery.com/attr/)
- * - [bind()](http://api.jquery.com/bind/)
- * - [children()](http://api.jquery.com/children/)
- * - [clone()](http://api.jquery.com/clone/)
- * - [css()](http://api.jquery.com/css/)
- * - [data()](http://api.jquery.com/data/)
- * - [hasClass()](http://api.jquery.com/hasClass/)
- * - [parent()](http://api.jquery.com/parent/)
- * - [remove()](http://api.jquery.com/remove/)
- * - [removeAttr()](http://api.jquery.com/removeAttr/)
- * - [removeClass()](http://api.jquery.com/removeClass/)
- * - [removeData()](http://api.jquery.com/removeData/)
- * - [replaceWith()](http://api.jquery.com/replaceWith/)
- * - [text()](http://api.jquery.com/text/)
- * - [trigger()](http://api.jquery.com/trigger/)
- *
- * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
- * @returns {Object} jQuery object.
- */
jqLite = jQuery || jqLiteWrap,
slice = Array.prototype.slice,
push = Array.prototype.push,
error = window[$console] ? bind(window[$console], window[$console]['error'] || noop) : noop,
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular
- * @namespace The exported angular namespace.
- */
angular = window[$angular] || (window[$angular] = {}),
angularTextMarkup = extensionMap(angular, 'markup'),
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular.directive
- * @namespace Namespace for all directives.
- *
- * @description
- * A directive is an HTML attribute that you can use in an existing HTML element type or in a
- * DOM element type that you create as {@link angular.widget}, to modify that element's
- * properties. You can use any number of directives per element.
- *
- * For example, you can add the ng:bind directive as an attribute of an HTML span element, as in
- * `<span ng:bind="1+2"></span>`. How does this work? The compiler passes the attribute value
- * `1+2` to the ng:bind extension, which in turn tells the {@link angular.scope} to watch that
- * expression and report changes. On any change it sets the span text to the expression value.
- *
- * Here's how to define {@link angular.directive.ng:bind ng:bind}:
- * <pre>
- angular.directive('ng:bind', function(expression, compiledElement) {
- var compiler = this;
- return function(linkElement) {
- var currentScope = this;
- currentScope.$watch(expression, function(value) {
- linkElement.text(value);
- });
- };
- });
- * </pre>
- *
- * # Directive vs. Attribute Widget
- * Both [attribute widgets](#!angular.widget) and directives can compile a DOM element
- * attribute. So why have two different ways to do the same thing? The answer is that order
- * matters, but we have no control over the order in which attributes are read. To solve this
- * we apply attribute widget before the directive.
- *
- * For example, consider this piece of HTML, which uses the directives `ng:repeat`, `ng:init`,
- * and `ng:bind`:
- * <pre>
- <ul ng:init="people=['mike', 'mary']">
- <li ng:repeat="person in people" ng:init="a=a+1" ng:bind="person"></li>
- </ul>
- * </pre>
- *
- * Notice that the order of execution matters here. We need to execute
- * {@link angular.directive.ng:repeat ng:repeat} before we run the
- * {@link angular.directive.ng:init ng:init} and `ng:bind` on the `<li/>;`. This is because we
- * want to run the `ng:init="a=a+1` and `ng:bind="person"` once for each person in people. We
- * could not have used directive to create this template because attributes are read in an
- * unspecified order and there is no way of guaranteeing that the repeater attribute would
- * execute first. Using the `ng:repeat` attribute directive ensures that we can transform the
- * DOM element into a template.
- *
- * Widgets run before directives. Widgets may manipulate the DOM whereas directives are not
- * expected to do so, and so they run last.
- */
+ /** @name angular.directive */
angularDirective = extensionMap(angular, 'directive'),
-
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular.widget
- * @namespace Namespace for all widgets.
- * @description
- * # Overview
- * Widgets allow you to create DOM elements that the browser doesn't
- * already understand. You create the widget in your namespace and
- * assign it behavior. You can only bind one widget per DOM element
- * (unlike directives, in which you can use any number per DOM
- * element). Widgets are expected to manipulate the DOM tree by
- * adding new elements whereas directives are expected to only modify
- * element properties.
- *
- * Widgets come in two flavors: element and attribute.
- *
- * # Element Widget
- * Let's say we would like to create a new element type in the
- * namespace `my` that can watch an expression and alert() the user
- * with each new value.
- *
- * <pre>
- * &lt;my:watch exp="name"/&gt;
- * </pre>
- *
- * You can implement `my:watch` like this:
- * <pre>
- * angular.widget('my:watch', function(compileElement) {
- * var compiler = this;
- * var exp = compileElement.attr('exp');
- * return function(linkElement) {
- * var currentScope = this;
- * currentScope.$watch(exp, function(value){
- * alert(value);
- * }};
- * };
- * });
- * </pre>
- *
- * # Attribute Widget
- * Let's implement the same widget, but this time as an attribute
- * that can be added to any existing DOM element.
- * <pre>
- * &lt;div my-watch="name"&gt;text&lt;/div&gt;
- * </pre>
- * You can implement `my:watch` attribute like this:
- * <pre>
- * angular.widget('@my:watch', function(expression, compileElement) {
- * var compiler = this;
- * return function(linkElement) {
- * var currentScope = this;
- * currentScope.$watch(expression, function(value){
- * alert(value);
- * });
- * };
- * });
- * </pre>
- *
- * @example
- * <script>
- * angular.widget('my:time', function(compileElement){
- * compileElement.css('display', 'block');
- * return function(linkElement){
- * function update(){
- * linkElement.text('Current time is: ' + new Date());
- * setTimeout(update, 1000);
- * }
- * update();
- * };
- * });
- * </script>
- * <my:time></my:time>
- */
+ /** @name angular.widget */
angularWidget = extensionMap(angular, 'widget', lowercase),
-
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular.validator
- * @namespace Namespace for all filters.
- * @description
- * # Overview
- * Validators are a standard way to check the user input against a specific criteria. For
- * example, you might need to check that an input field contains a well-formed phone number.
- *
- * # Syntax
- * Attach a validator on user input widgets using the `ng:validate` attribute.
- *
- * <doc:example>
- * <doc:source>
- * Change me: &lt;input type="text" name="number" ng:validate="integer" value="123"&gt;
- * </doc:source>
- * <doc:scenario>
- * it('should validate the default number string', function() {
- * expect(element('input[name=number]').attr('class')).
- * not().toMatch(/ng-validation-error/);
- * });
- * it('should not validate "foo"', function() {
- * input('number').enter('foo');
- * expect(element('input[name=number]').attr('class')).
- * toMatch(/ng-validation-error/);
- * });
- * </doc:scenario>
- * </doc:example>
- *
- *
- * # Writing your own Validators
- * Writing your own validator is easy. To make a function available as a
- * validator, just define the JavaScript function on the `angular.validator`
- * object. <angular/> passes in the input to validate as the first argument
- * to your function. Any additional validator arguments are passed in as
- * additional arguments to your function.
- *
- * You can use these variables in the function:
- *
- * * `this` — The current scope.
- * * `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
- * the DOM in addition to transforming the input.
- *
- * In this example we have written a upsTrackingNo validator.
- * It marks the input text "valid" only when the user enters a well-formed
- * UPS tracking number.
- *
- * @css ng-validation-error
- * When validation fails, this css class is applied to the binding, making its borders red by
- * default.
- *
- * @example
- * <script>
- * angular.validator('upsTrackingNo', function(input, format) {
- * var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
- * return input.match(regexp)?"":"The format must match " + format;
- * });
- * </script>
- * <input type="text" name="trackNo" size="40"
- * ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
- * value="1Z 123 456 78 9012 345 6"/>
- *
- * @scenario
- * it('should validate correct UPS tracking number', function() {
- * expect(element('input[name=trackNo]').attr('class')).
- * not().toMatch(/ng-validation-error/);
- * });
- *
- * it('should not validate in correct UPS tracking number', function() {
- * input('trackNo').enter('foo');
- * expect(element('input[name=trackNo]').attr('class')).
- * toMatch(/ng-validation-error/);
- * });
- *
- */
+ /** @name angular.validator */
angularValidator = extensionMap(angular, 'validator'),
-
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular.filter
- * @namespace Namespace for all filters.
- * @description
- * # Overview
- * Filters are a standard way to format your data for display to the user. For example, you
- * might have the number 1234.5678 and would like to display it as US currency: $1,234.57.
- * Filters allow you to do just that. In addition to transforming the data, filters also modify
- * the DOM. This allows the filters to for example apply css styles to the filtered output if
- * certain conditions were met.
- *
- *
- * # Standard Filters
- *
- * The Angular framework provides a standard set of filters for common operations, including:
- * {@link angular.filter.currency currency}, {@link angular.filter.json json},
- * {@link angular.filter.number number}, and {@link angular.filter.html html}. You can also add
- * your own filters.
- *
- *
- * # Syntax
- *
- * Filters can be part of any {@link angular.scope} evaluation but are typically used with
- * {{bindings}}. Filters typically transform the data to a new data type, formating the data in
- * the process. Filters can be chained and take optional arguments. Here are few examples:
- *
- * * No filter: {{1234.5678}} => 1234.5678
- * * Number filter: {{1234.5678|number}} => 1,234.57. Notice the “,” and rounding to two
- * significant digits.
- * * Filter with arguments: {{1234.5678|number:5}} => 1,234.56780. Filters can take optional
- * arguments, separated by colons in a binding. To number, the argument “5” requests 5 digits
- * to the right of the decimal point.
- *
- *
- * # Writing your own Filters
- *
- * Writing your own filter is very easy: just define a JavaScript function on `angular.filter`.
- * The framework passes in the input value as the first argument to your function. Any filter
- * arguments are passed in as additional function arguments.
- *
- * You can use these variables in the function:
- *
- * * `this` — The current scope.
- * * `this.$element` — The DOM element containing the binding. This allows the filter to manipulate
- * the DOM in addition to transforming the input.
- *
- *
- * @exampleDescription
- * The following example filter reverses a text string. In addition, it conditionally makes the
- * text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
- * modification).
- *
- * @example
- <script type="text/javascript">
- angular.filter('reverse', function(input, uppercase, color) {
- var out = "";
- for (var i = 0; i < input.length; i++) {
- out = input.charAt(i) + out;
- }
- if (uppercase) {
- out = out.toUpperCase();
- }
- if (color) {
- this.$element.css('color', color);
- }
- return out;
- });
- </script>
-
- <input name="text" type="text" value="hello" /><br>
- No filter: {{text}}<br>
- Reverse: {{text|reverse}}<br>
- Reverse + uppercase: {{text|reverse:true}}<br>
- Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
-
- */
+ /** @name angular.fileter */
angularFilter = extensionMap(angular, 'filter'),
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular.formatter
- * @namespace Namespace for all formats.
- * @description
- * # Overview
- * The formatters are responsible for translating user readable text in an input widget to a
- * data model stored in an application.
- *
- * # Writting your own Formatter
- * Writing your own formatter is easy. Just register a pair of JavaScript functions with
- * `angular.formatter`. One function for parsing user input text to the stored form,
- * and one for formatting the stored data to user-visible text.
- *
- * Here is an example of a "reverse" formatter: The data is stored in uppercase and in
- * reverse, while it is displayed in lower case and non-reversed. User edits are
- * automatically parsed into the internal form and data changes are automatically
- * formatted to the viewed form.
- *
- * <pre>
- * function reverse(text) {
- * var reversed = [];
- * for (var i = 0; i < text.length; i++) {
- * reversed.unshift(text.charAt(i));
- * }
- * return reversed.join('');
- * }
- *
- * angular.formatter('reverse', {
- * parse: function(value){
- * return reverse(value||'').toUpperCase();
- * },
- * format: function(value){
- * return reverse(value||'').toLowerCase();
- * }
- * });
- * </pre>
- *
- * @example
- * <script type="text/javascript">
- * function reverse(text) {
- * var reversed = [];
- * for (var i = 0; i < text.length; i++) {
- * reversed.unshift(text.charAt(i));
- * }
- * return reversed.join('');
- * }
- *
- * angular.formatter('reverse', {
- * parse: function(value){
- * return reverse(value||'').toUpperCase();
- * },
- * format: function(value){
- * return reverse(value||'').toLowerCase();
- * }
- * });
- * </script>
- *
- * Formatted:
- * <input type="text" name="data" value="angular" ng:format="reverse"/>
- * <br/>
- *
- * Stored:
- * <input type="text" name="data"/><br/>
- * <pre>{{data}}</pre>
- *
- *
- * @scenario
- * it('should store reverse', function(){
- * expect(element('.doc-example input:first').val()).toEqual('angular');
- * expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
- *
- * this.addFutureAction('change to XYZ', function($window, $document, done){
- * $document.elements('.doc-example input:last').val('XYZ').trigger('change');
- * done();
- * });
- * expect(element('.doc-example input:first').val()).toEqual('zyx');
- * });
- */
+ /** @name angular.formatter */
angularFormatter = extensionMap(angular, 'formatter'),
-
- /**
- * @workInProgress
- * @ngdoc overview
- * @name angular.service
- *
- * @description
- * # Overview
- * Services are substituable objects, which are wired together using dependency injection.
- * Each service could have dependencies (other services), which are passed in constructor.
- * Because JS is dynamicaly typed language, dependency injection can not use static types
- * to satisfy these dependencies, so each service must explicitely define its dependencies.
- * This is done by `$inject` property.
- *
- * For now, life time of all services is the same as the life time of page.
- *
- *
- * # Built-in services
- * The Angular framework provides a standard set of services for common operations.
- * You can write your own services and rewrite these standard services as well.
- * Like other core angular variables, the built-in services always start with $.
- *
- * * `angular.service.$browser`
- * * `angular.service.$window`
- * * `angular.service.$document`
- * * `angular.service.$location`
- * * `angular.service.$log`
- * * `angular.service.$exceptionHandler`
- * * `angular.service.$hover`
- * * `angular.service.$invalidWidgets`
- * * `angular.service.$route`
- * * `angular.service.$xhr`
- * * `angular.service.$xhr.error`
- * * `angular.service.$xhr.bulk`
- * * `angular.service.$xhr.cache`
- * * `angular.service.$resource`
- * * `angular.service.$cookies`
- * * `angular.service.$cookieStore`
- *
- * # Writing your own custom services
- * Angular provides only set of basic services, so you will probably need to write your custom
- * service very soon. To do so, you need to write a factory function and register this function
- * to angular's dependency injector. This factory function must return an object - your service
- * (it is not called with new operator).
- *
- * **angular.service** has three parameters:
- *
- * - `{string} name` - Name of the service
- * - `{function()} factory` - Factory function (called just once by DI)
- * - `{Object} config` - Hash of configuration (`$inject`, `$creation`)
- *
- * If your service requires - depends on other services, you need to specify them
- * in config hash - property $inject. This property is an array of strings (service names).
- * These dependencies will be passed as parameters to the factory function by DI.
- * This approach is very useful when testing, as you can inject mocks/stubs/dummies.
- *
- * Here is an example of very simple service. This service requires $window service (it's
- * passed as a parameter to factory function) and it's just a function.
- *
- * This service simple stores all notifications and after third one, it displays all of them by
- * window alert.
- * <pre>
- angular.service('notify', function(win) {
- var msgs = [];
- return function(msg) {
- msgs.push(msg);
- if (msgs.length == 3) {
- win.alert(msgs.join("\n"));
- msgs = [];
- }
- };
- }, {$inject: ['$window']});
- * </pre>
- *
- * And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert.
- * <pre>
- * var mock, notify;
- *
- * beforeEach(function() {
- * mock = {alert: jasmine.createSpy()};
- * notify = angular.service('notify')(mock);
- * });
- *
- * it('should not alert first two notifications', function() {
- * notify('one');
- * notify('two');
- * expect(mock.alert).not.toHaveBeenCalled();
- * });
- *
- * it('should alert all after third notification', function() {
- * notify('one');
- * notify('two');
- * notify('three');
- * expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
- * });
- *
- * it('should clear messages after alert', function() {
- * notify('one');
- * notify('two');
- * notify('third');
- * notify('more');
- * notify('two');
- * notify('third');
- * expect(mock.alert.callCount).toEqual(2);
- * expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
- * });
- * </pre>
- *
- * # Injecting services into controllers
- * Using services in a controllers is very similar to using service in other service.
- * Again, we will use dependency injection.
- *
- * JavaScript is dynamic language, so DI is not able to figure out which services to inject by
- * static types (like in static typed languages). Therefore you must specify the service name
- * by the `$inject` property - it's an array that contains strings with names of services to be
- * injected. The name must match the id that service has been registered as with angular.
- * The order of the services in the array matters, because this order will be used when calling
- * the factory function with injected parameters. The names of parameters in factory function
- * don't matter, but by convention they match the service ids.
- * <pre>
- * function myController($loc, $log) {
- * this.firstMethod = function() {
- * // use $location service
- * $loc.setHash();
- * };
- * this.secondMethod = function() {
- * // use $log service
- * $log.info('...');
- * };
- * }
- * // which services to inject ?
- * myController.$inject = ['$location', '$log'];
- * </pre>
- *
- * @example
- * <script type="text/javascript">
- * angular.service('notify', function(win) {
- * var msgs = [];
- * return function(msg) {
- * msgs.push(msg);
- * if (msgs.length == 3) {
- * win.alert(msgs.join("\n"));
- * msgs = [];
- * }
- * };
- * }, {$inject: ['$window']});
- *
- * function myController(notifyService) {
- * this.callNotify = function(msg) {
- * notifyService(msg);
- * };
- * }
- *
- * myController.$inject = ['notify'];
- * </script>
- *
- * <div ng:controller="myController">
- * <p>Let's try this simple notify service, injected into the controller...</p>
- * <input ng:init="message='test'" type="text" name="message" />
- * <button ng:click="callNotify(message);">NOTIFY</button>
- * </div>
- */
+ /** @name angular.service */
angularService = extensionMap(angular, 'service'),
angularCallbacks = extensionMap(angular, 'callbacks'),
nodeName,
@@ -942,7 +368,7 @@ function isNumber(value){ return typeof value == $number;}
* @function
*
* @description
- * Checks if a reference is defined.
+ * Checks if value is a date.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Date`.