mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-04-28 18:34:44 +00:00
allow documentation to be in external file
* Load templates once instead of per request * show timing information * load files ending in .ngdoc and process them
This commit is contained in:
parent
2bbced212e
commit
58d0e8945d
11 changed files with 646 additions and 615 deletions
53
docs/angular.directive.ngdoc
Normal file
53
docs/angular.directive.ngdoc
Normal file
|
|
@ -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.
|
||||||
43
docs/angular.element.ngdoc
Normal file
43
docs/angular.element.ngdoc
Normal file
|
|
@ -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.
|
||||||
76
docs/angular.filter.ngdoc
Normal file
76
docs/angular.filter.ngdoc
Normal file
|
|
@ -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"}}
|
||||||
|
|
||||||
78
docs/angular.formatter.ngdoc
Normal file
78
docs/angular.formatter.ngdoc
Normal file
|
|
@ -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');
|
||||||
|
});
|
||||||
4
docs/angular.ngdoc
Normal file
4
docs/angular.ngdoc
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
@workInProgress
|
||||||
|
@ngdoc overview
|
||||||
|
@name angular
|
||||||
|
@namespace The exported angular namespace.
|
||||||
159
docs/angular.service.ngdoc
Normal file
159
docs/angular.service.ngdoc
Normal file
|
|
@ -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>
|
||||||
73
docs/angular.validator.ngdoc
Normal file
73
docs/angular.validator.ngdoc
Normal file
|
|
@ -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: <input type="text" name="number" ng:validate="integer" value="123">
|
||||||
|
</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/);
|
||||||
|
});
|
||||||
73
docs/angular.widget.ngdoc
Normal file
73
docs/angular.widget.ngdoc
Normal file
|
|
@ -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>
|
||||||
|
<my:watch exp="name"/>
|
||||||
|
</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>
|
||||||
|
<div my-watch="name">text</div>
|
||||||
|
</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>
|
||||||
|
|
||||||
112
docs/collect.js
112
docs/collect.js
|
|
@ -16,39 +16,29 @@ var keywordPages = [];
|
||||||
var SRC_DIR = "docs/";
|
var SRC_DIR = "docs/";
|
||||||
var OUTPUT_DIR = "build/docs/";
|
var OUTPUT_DIR = "build/docs/";
|
||||||
var NEW_LINE = /\n\r?/;
|
var NEW_LINE = /\n\r?/;
|
||||||
|
var TEMPLATES = {};
|
||||||
|
var start = now();
|
||||||
|
|
||||||
|
function now(){ return new Date().getTime(); }
|
||||||
var work = callback.chain(function () {
|
var work = callback.chain(function () {
|
||||||
console.log('Parsing Angular Reference Documentation');
|
console.log('Parsing Angular Reference Documentation');
|
||||||
mkdirPath(OUTPUT_DIR, work.waitFor(function(){
|
findJsFiles('src', work.waitMany(function(file) {
|
||||||
findJsFiles('src', work.waitMany(function(file) {
|
//console.log('reading', file, '...');
|
||||||
//console.log('reading', file, '...');
|
findNgDocInJsFile(file, work.waitMany(function(doc) {
|
||||||
findNgDoc(file, work.waitMany(function(doc) {
|
parseNgDoc(doc);
|
||||||
parseNgDoc(doc);
|
processNgDoc(documentation, 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){
|
}).onError(function(err){
|
||||||
console.log('ERROR:', err.stack || err);
|
console.log('ERROR:', err.stack || err);
|
||||||
}).onDone(function(){
|
}).onDone(function(){
|
||||||
keywordPages.sort(function(a,b){
|
keywordPages.sort(keywordSort);
|
||||||
// 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;
|
|
||||||
});
|
|
||||||
writeDoc(documentation.pages);
|
writeDoc(documentation.pages);
|
||||||
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(keywordPages)}, callback.chain());
|
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(keywordPages)}, callback.chain());
|
||||||
mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, 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('docs.js', 'docs.js', documentation, callback.chain());
|
||||||
mergeTemplate('doc_widgets.css', 'doc_widgets.css', documentation, callback.chain());
|
mergeTemplate('doc_widgets.css', 'doc_widgets.css', documentation, callback.chain());
|
||||||
mergeTemplate('doc_widgets.js', 'doc_widgets.js', 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();
|
if (!this.testmode) work();
|
||||||
////////////////////
|
////////////////////
|
||||||
|
|
@ -163,7 +153,7 @@ function markdownTag(doc, name, value) {
|
||||||
replace(/\<\/pre\>/gmi, '</pre></div>');
|
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
|
// 1 123 3 4 42
|
||||||
|
|
||||||
function markdown(text) {
|
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){
|
fs.readFile(file, callback.waitFor(function(err, content){
|
||||||
var lines = content.toString().split(NEW_LINE);
|
var lines = content.toString().split(NEW_LINE);
|
||||||
var doc;
|
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){
|
function findJsFiles(dir, callback){
|
||||||
fs.readdir(dir, callback.waitFor(function(err, files){
|
fs.readdir(dir, callback.waitFor(function(err, files){
|
||||||
if (err) return this.error(err);
|
if (err) return this.error(err);
|
||||||
|
|
@ -365,7 +371,7 @@ function findJsFiles(dir, callback){
|
||||||
|
|
||||||
function processNgDoc(documentation, doc) {
|
function processNgDoc(documentation, doc) {
|
||||||
if (!doc.ngdoc) return;
|
if (!doc.ngdoc) return;
|
||||||
console.log('Found:', doc.ngdoc + ':' + doc.name);
|
//console.log('Found:', doc.ngdoc + ':' + doc.name);
|
||||||
|
|
||||||
documentation.byName[doc.name] = doc;
|
documentation.byName[doc.name] = doc;
|
||||||
|
|
||||||
|
|
@ -385,10 +391,50 @@ function processNgDoc(documentation, doc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeDoc(pages) {
|
function writeDoc(pages, callback) {
|
||||||
pages.forEach(function(doc) {
|
pages.forEach(function(doc) {
|
||||||
mergeTemplate(
|
var template = TEMPLATES[doc.ngdoc];
|
||||||
doc.ngdoc + '.template',
|
if (!template) throw new Error("No template for:" + doc.ngdoc);
|
||||||
doc.name + '.html', doc, callback.chain());
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<script src="doc_widgets.js"></script>
|
<script src="doc_widgets.js"></script>
|
||||||
<script src="docs-data.js"></script>
|
<script src="docs-data.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body style="display:none;" ng:show="true">
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<h1>
|
<h1>
|
||||||
<span class="main-title">{{getTitle()}}</span>
|
<span class="main-title">{{getTitle()}}</span>
|
||||||
|
|
|
||||||
588
src/Angular.js
588
src/Angular.js
|
|
@ -87,599 +87,25 @@ var _undefined = undefined,
|
||||||
_ = window['_'],
|
_ = window['_'],
|
||||||
/** holds major version number for IE or NaN for real browsers */
|
/** holds major version number for IE or NaN for real browsers */
|
||||||
msie = parseInt((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1], 10),
|
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,
|
jqLite = jQuery || jqLiteWrap,
|
||||||
slice = Array.prototype.slice,
|
slice = Array.prototype.slice,
|
||||||
push = Array.prototype.push,
|
push = Array.prototype.push,
|
||||||
error = window[$console] ? bind(window[$console], window[$console]['error'] || noop) : noop,
|
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] = {}),
|
angular = window[$angular] || (window[$angular] = {}),
|
||||||
angularTextMarkup = extensionMap(angular, 'markup'),
|
angularTextMarkup = extensionMap(angular, 'markup'),
|
||||||
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
|
angularAttrMarkup = extensionMap(angular, 'attrMarkup'),
|
||||||
/**
|
/** @name angular.directive */
|
||||||
* @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.
|
|
||||||
*/
|
|
||||||
angularDirective = extensionMap(angular, 'directive'),
|
angularDirective = extensionMap(angular, 'directive'),
|
||||||
|
/** @name angular.widget */
|
||||||
/**
|
|
||||||
* @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>
|
|
||||||
* <my:watch exp="name"/>
|
|
||||||
* </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>
|
|
||||||
* <div my-watch="name">text</div>
|
|
||||||
* </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>
|
|
||||||
*/
|
|
||||||
angularWidget = extensionMap(angular, 'widget', lowercase),
|
angularWidget = extensionMap(angular, 'widget', lowercase),
|
||||||
|
/** @name angular.validator */
|
||||||
/**
|
|
||||||
* @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: <input type="text" name="number" ng:validate="integer" value="123">
|
|
||||||
* </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/);
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
angularValidator = extensionMap(angular, 'validator'),
|
angularValidator = extensionMap(angular, 'validator'),
|
||||||
|
/** @name angular.fileter */
|
||||||
/**
|
|
||||||
* @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"}}
|
|
||||||
|
|
||||||
*/
|
|
||||||
angularFilter = extensionMap(angular, 'filter'),
|
angularFilter = extensionMap(angular, 'filter'),
|
||||||
/**
|
/** @name angular.formatter */
|
||||||
* @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');
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
angularFormatter = extensionMap(angular, 'formatter'),
|
angularFormatter = extensionMap(angular, 'formatter'),
|
||||||
|
/** @name angular.service */
|
||||||
/**
|
|
||||||
* @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>
|
|
||||||
*/
|
|
||||||
angularService = extensionMap(angular, 'service'),
|
angularService = extensionMap(angular, 'service'),
|
||||||
angularCallbacks = extensionMap(angular, 'callbacks'),
|
angularCallbacks = extensionMap(angular, 'callbacks'),
|
||||||
nodeName,
|
nodeName,
|
||||||
|
|
@ -942,7 +368,7 @@ function isNumber(value){ return typeof value == $number;}
|
||||||
* @function
|
* @function
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Checks if a reference is defined.
|
* Checks if value is a date.
|
||||||
*
|
*
|
||||||
* @param {*} value Reference to check.
|
* @param {*} value Reference to check.
|
||||||
* @returns {boolean} True if `value` is a `Date`.
|
* @returns {boolean} True if `value` is a `Date`.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue