diff options
| -rw-r--r-- | docs/angular.directive.ngdoc | 53 | ||||
| -rw-r--r-- | docs/angular.element.ngdoc | 43 | ||||
| -rw-r--r-- | docs/angular.filter.ngdoc | 76 | ||||
| -rw-r--r-- | docs/angular.formatter.ngdoc | 78 | ||||
| -rw-r--r-- | docs/angular.ngdoc | 4 | ||||
| -rw-r--r-- | docs/angular.service.ngdoc | 159 | ||||
| -rw-r--r-- | docs/angular.validator.ngdoc | 73 | ||||
| -rw-r--r-- | docs/angular.widget.ngdoc | 73 | ||||
| -rw-r--r-- | docs/collect.js | 112 | ||||
| -rw-r--r-- | docs/index.html | 2 | ||||
| -rw-r--r-- | src/Angular.js | 588 |
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: <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/); +}); 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> +<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> +
\ 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> - * <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> - */ + /** @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: <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/); - * }); - * - */ + /** @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`. |
