Compare commits
203 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f947e5b8b | |||
| 75de7395bb | |||
| 9f73b1f290 | |||
| bd33f60276 | |||
| 8682befc72 | |||
| 3f0a37f380 | |||
| f5d08963b0 | |||
| 7a48ee6aa9 | |||
| 6512a736ac | |||
| 9e0fa5b7c8 | |||
| 9368ea3814 | |||
| a6a4c18ecd | |||
| d8acfe7389 | |||
| b036c0bc58 | |||
| bf03eb007c | |||
| c2f2587a79 | |||
| 5d0d34ae72 | |||
| 70c74a9c4e | |||
| 22c5b7059b | |||
| 190498efd7 | |||
| a5990050d4 | |||
| 8d507df8c9 | |||
| 567f33823b | |||
| 5340d1e0b1 | |||
| 8d91ec4173 | |||
| 746e7a9768 | |||
| 94737cd017 | |||
| c8bb044be1 | |||
| 4a569560d8 | |||
| 17ee0f031a | |||
| 10646c9f6f | |||
| 964e394555 | |||
| 2c0f7ffe3a | |||
| a5eb3ed107 | |||
| 63690d1892 | |||
| 050e5d773f | |||
| da4b8a74c3 | |||
| 5be92f7b9c | |||
| 459a01e582 | |||
| 72e554ffeb | |||
| 3bd96609e9 | |||
| d73635d573 | |||
| ad3b811b8b | |||
| d3f7bd699e | |||
| 1c55123f9c | |||
| 1d7b9d5626 | |||
| 347be5ae9a | |||
| 934f44f69e | |||
| d3d2a3a374 | |||
| 527d0a1600 | |||
| 23875cb330 | |||
| b0be87f663 | |||
| 9ccd2f0412 | |||
| 99004b0aed | |||
| ab040254f0 | |||
| 4f5d5029c2 | |||
| f534def0c6 | |||
| c3e32f1a51 | |||
| 4f22d6866c | |||
| aab3df7aea | |||
| 0a6cf70deb | |||
| c79aba92f6 | |||
| 84dedb81e7 | |||
| e999740044 | |||
| 0ad39dde4f | |||
| 4c71824a69 | |||
| 47c454a315 | |||
| 16086aa37c | |||
| c0a26b1853 | |||
| 9db2170dcf | |||
| b28dee7fd5 | |||
| 142a985f33 | |||
| bd5ec7c32a | |||
| bdc251c5a5 | |||
| ad9537cdf6 | |||
| 807d8c92b3 | |||
| 454626ad39 | |||
| 247c99a8a4 | |||
| da1d50fbe9 | |||
| 67d064820c | |||
| b2631f6170 | |||
| 1430c6d6b1 | |||
| 3ea5941f0e | |||
| d0270d9256 | |||
| 5f080193cb | |||
| cf891428bf | |||
| 5b9967518e | |||
| 38f462d572 | |||
| 56eeba0f3c | |||
| 5a534235b6 | |||
| e7a0fb250f | |||
| e3ddc2bcc4 | |||
| d11088eb43 | |||
| a5df1fc41f | |||
| ec4d446f89 | |||
| b225083a21 | |||
| e84d3334b0 | |||
| 7989c7d24a | |||
| 5c36f466e1 | |||
| f8151afd90 | |||
| 74120eaa0f | |||
| b370fac4fc | |||
| 23fc73081f | |||
| e5e69d9b90 | |||
| fa722447f8 | |||
| 81d10e819e | |||
| 809ca94e1c | |||
| 824eab9029 | |||
| d503dfe99b | |||
| e4d33917e3 | |||
| 6326e2028b | |||
| 8aed2047f0 | |||
| f60b6b0938 | |||
| eea7113abe | |||
| c8f34e7f6b | |||
| 011fa39c2a | |||
| 58d0e8945d | |||
| 2bbced212e | |||
| 5a8ad8fe32 | |||
| 41d5938883 | |||
| 5480d2a80b | |||
| 95adf3a4d8 | |||
| cc315ef4cc | |||
| 41c233ada1 | |||
| 46091f811b | |||
| fde2ccb3f5 | |||
| 1cc2ad2443 | |||
| 1aa46fac62 | |||
| 5bde02a8ca | |||
| d07e9f77f1 | |||
| aa21c521eb | |||
| bd14a90610 | |||
| 9f1b9849d8 | |||
| 47f159cdf3 | |||
| 99eb123d79 | |||
| 6515adc118 | |||
| b7aff92354 | |||
| 6b3b00e095 | |||
| 921f7ce49e | |||
| 17eee57c52 | |||
| 4fc3ee8040 | |||
| 39d3ae80d9 | |||
| 480f2f33c1 | |||
| 9c9a89f7ff | |||
| 73194009a9 | |||
| 162f41a1ab | |||
| 7c82c4f837 | |||
| 97b1371199 | |||
| 95d1768c77 | |||
| c3d99d68da | |||
| 303a683081 | |||
| a0e8c45880 | |||
| 870547d185 | |||
| 0d1f8a0532 | |||
| b94600d71e | |||
| 3e5a4ef86c | |||
| efec0c358d | |||
| 1f59de35c9 | |||
| 9b53b25f15 | |||
| 3fbfa357ca | |||
| 50ef1f8e35 | |||
| 66c0bfaa8e | |||
| 1719b0aca5 | |||
| 7ee102eecf | |||
| fc7f11d03b | |||
| 3c7874b07b | |||
| 7f339a1782 | |||
| 72a5f007d8 | |||
| 63380bbbda | |||
| c635b69f5c | |||
| 522ec1a9ec | |||
| 9cb57772a4 | |||
| d54f09ef29 | |||
| 65989c6f0d | |||
| 4491bbdede | |||
| a6978b201b | |||
| 28e72cbe6b | |||
| 916dadd8ec | |||
| e509ec37f5 | |||
| ee0e9a4452 | |||
| 9d36368ff9 | |||
| d4bcee0799 | |||
| dd687e2bf5 | |||
| 4c69d694d7 | |||
| ff7c738c21 | |||
| 51a22cf435 | |||
| c2c60ab49a | |||
| 71c2f24fc6 | |||
| fc78738cc6 | |||
| c7052f098d | |||
| 7d6f5f986e | |||
| beeb5ff908 | |||
| b2d63ac48b | |||
| 4af32de84a | |||
| a130bb899d | |||
| cc749760fd | |||
| b467a50bc7 | |||
| a1652057a5 | |||
| 7e6f999221 | |||
| 625cc7609c | |||
| c51273b1fb | |||
| 0a8b3161b1 | |||
| ba554eeb1b |
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="true"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE" value="${working_set:<?xml version="1.0" encoding="UTF-8"?> <launchConfigurationWorkingSet editPageId="org.eclipse.ui.resourceWorkingSetPage" factoryID="org.eclipse.ui.internal.WorkingSetFactory" id="1262905463390_2" label="workingSet" name="workingSet"> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/angular.js/test" type="2"/> <item factoryID="org.eclipse.ui.internal.model.ResourceFactory" path="/angular.js/src" type="2"/> </launchConfigurationWorkingSet>}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/angular.js}/test.sh"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,"/>
|
||||
|
||||
+2
-1
@@ -2,5 +2,6 @@ build/
|
||||
angularjs.netrc
|
||||
jstd.log
|
||||
.DS_Store
|
||||
regression/temp.html
|
||||
regression/temp*.html
|
||||
performance/temp*.html
|
||||
.idea/workspace.xml
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#Mon Jan 24 10:31:47 PST 2011
|
||||
activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF
|
||||
addNewLine=true
|
||||
convertActionOnSaave=AnyEdit.CnvrtTabToSpaces
|
||||
eclipse.preferences.version=1
|
||||
inActiveContentFilterList=
|
||||
javaTabWidthForJava=true
|
||||
org.eclipse.jdt.ui.editor.tab.width=2
|
||||
projectPropsEnabled=false
|
||||
removeTrailingSpaces=true
|
||||
replaceAllSpaces=false
|
||||
replaceAllTabs=false
|
||||
saveAndAddLine=true
|
||||
saveAndConvert=true
|
||||
saveAndTrim=true
|
||||
useModulo4Tabs=false
|
||||
+180
@@ -1,3 +1,183 @@
|
||||
# <angular/> 0.9.10 flea-whisperer (2011-01-26) #
|
||||
|
||||
### Features
|
||||
- new [`ng:view`](http://docs.angularjs.org/#!angular.widget.ng:view) widget to simplify integration
|
||||
with the `$route` service
|
||||
- the content of all standard HTML widgets is now being processed
|
||||
(e.g. `<button>{{foo}}</button>` works now) (commit 1d7b9d56)
|
||||
- new [`$log`](http://docs.angularjs.org/#!angular.mock.service.$log) and
|
||||
[`$exceptionHandler`](http://docs.angularjs.org/#!angular.mock.service.$exceptionHandler) service
|
||||
mocks now part of `angular-mocks.js` (commit f5d08963)
|
||||
|
||||
### Bug Fixes
|
||||
- <select> (one/multiple) could not chose from a list of objects (commit 347be5ae)
|
||||
- null and other falsy values should not be rendered in the view (issue #242)
|
||||
|
||||
### Docs
|
||||
- rewrite of several major portions of angular.service.*, angular.Array.*, angular.Object.* docs
|
||||
- added support for [sitemap]((http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
|
||||
search crawlers
|
||||
- transition of Developer Guide docs from the wiki into docs.angularjs.org
|
||||
- lots of improvements related to formatting of the content of docs.anguarjs.org
|
||||
|
||||
|
||||
# <angular/> 0.9.9 time-shift (2011-01-13) #
|
||||
|
||||
### Security
|
||||
- Added a just in case security check for JSON parsing. (commit 5f080193)
|
||||
- Completed security review with the Google Security Team.
|
||||
|
||||
### Performance
|
||||
- $location and $cookies services are now lazily initialized to avoid the polling overhead when
|
||||
not needed.
|
||||
- $location service now listens for `onhashchange` events (if supported by browser) instead of
|
||||
constant polling. (commit 16086aa3)
|
||||
- input widgets known listens on keydown events instead of keyup which improves perceived
|
||||
performance (commit 47c454a3)
|
||||
- angular boots significantly sooner by listening for DOMContentLoaded event instead of
|
||||
window.load when supported by browser (commit c79aba92)
|
||||
- new service $updateView which may be used in favor of $root.$eval() to run a complete eval on
|
||||
the entire document. This service bulks and throttles DOM updates to improve performance.
|
||||
(commit 47c454a3)
|
||||
|
||||
### Docs
|
||||
- Major improvements to the doc parser (commit 4f22d686)
|
||||
- Docs now offline enabled (all dependencies are bundled in the tarball) (commit 4f5d5029)
|
||||
- Added support for navigating the docs app with keyboard shortcuts (tab and ctrl+alt+s)
|
||||
|
||||
### Bugfixes
|
||||
- `angular.Object.equals` now properly handless comparing an object with a null (commit b0be87f6)
|
||||
- Several issues were addressed in the `$location` service (commit 23875cb3)
|
||||
- angular.filter.date now properly handles some corner-cases (issue #159 - fix contributed by Vojta)
|
||||
|
||||
### Breaking changes
|
||||
- API for accessing registered services — `scope.$inject` — was renamed to
|
||||
[`scope.$service`](http://docs.angularjs.org/#!angular.scope.$service). (commit b2631f61)
|
||||
|
||||
- Support for `eager-published` services was removed. This change was done to make explicit
|
||||
dependency declaration always required in order to allow making relatively expensive services
|
||||
lazily initialized (e.g. $cookie, $location), as well as remove 'magic' and reduce unnecessary
|
||||
scope namespace pollution. (commit 3ea5941f)
|
||||
|
||||
Complete list of affected services:
|
||||
|
||||
- $location
|
||||
- $route
|
||||
- $cookies
|
||||
- $window
|
||||
- $document
|
||||
- $exceptionHandler
|
||||
- $invalidWidgets
|
||||
|
||||
To temporarily preserve the 'eager-published' status for these services, you may use `ng:init`
|
||||
(e.g. `ng:init="$location = $service('$location'), ...`) in the view or more correctly create
|
||||
a service like this:
|
||||
|
||||
angular.service('published-svc-shim', function() {
|
||||
this.$location = this.$service('$location');
|
||||
this.$route = this.$service('$route');
|
||||
this.$cookies = this.$service('$cookies');
|
||||
this.$window = this.$service('$window');
|
||||
this.$document = this.$service('$document');
|
||||
this.$exceptionHandler = this.$service('$exceptionHandler');
|
||||
this.$invalidWidgets = this.$service('$invalidWidgets');
|
||||
}, {$eager: true});
|
||||
|
||||
- In the light of the `eager-published` change, to complete the cleanup we renamed `$creation`
|
||||
property of services to `eager` with its value being a boolean.
|
||||
To transition, please rename all `$creation: 'eager'` declarations to `$eager: true`.
|
||||
(commit 1430c6d6)
|
||||
|
||||
- `angular.foreach` was renamed to `angular.forEach` to make the api consistent. (commit 0a6cf70d)
|
||||
|
||||
- The `toString` method of the `angular.service.$location` service was removed. (commit 23875cb3)
|
||||
|
||||
|
||||
# <angular/> 0.9.8 astral-projection (2010-12-23) #
|
||||
|
||||
### Docs/Getting started
|
||||
- angular-seed project to get you hacking on an angular apps quickly
|
||||
https://github.com/angular/angular-seed
|
||||
|
||||
### Performance
|
||||
- Delegate JSON parsing to native parser (JSON.parse) if available
|
||||
|
||||
### Bug Fixes
|
||||
- Ignore input widgets which have no name (issue #153)
|
||||
|
||||
|
||||
# <angular/> 0.9.7 sonic-scream (2010-12-10) #
|
||||
|
||||
### Bug Fixes
|
||||
- $defer service should always call $eval on the root scope after a callback runs (issue #189)
|
||||
- fix for failed assignments of form obj[0].name=value (issue #169)
|
||||
- significant parser improvements that resulted in lower memory usage
|
||||
(commit 23fc73081feb640164615930b36ef185c23a3526)
|
||||
|
||||
### Docs
|
||||
- small docs improvements (mainly docs for the $resource service)
|
||||
|
||||
### Breaking changes
|
||||
- Angular expressions in the view used to support regular expressions. This feature was rarely
|
||||
used and added unnecessary complexity. It not a good idea to have regexps in the view anyway,
|
||||
so we removed this support. If you had any regexp in your views, you will have to move them to
|
||||
your controllers. (commit e5e69d9b90850eb653883f52c76e28dd870ee067)
|
||||
|
||||
|
||||
# <angular/> 0.9.6 night-vision (2010-12-06) #
|
||||
|
||||
### Security
|
||||
- several improvements in the HTML sanitizer code to prevent code execution via `href`s and other
|
||||
attributes.
|
||||
Commits:
|
||||
- 41d5938883a3d06ffe8a88a51efd8d1896f7d747
|
||||
- 2bbced212e2ee93948c45360fee00b2e3f960392
|
||||
|
||||
### Docs
|
||||
- set up http://docs.angularjs.org domain, the docs for the latest release will from now on be
|
||||
deployed here.
|
||||
- docs app UI polishing with dual scrolling and other improvements
|
||||
|
||||
### Bug Fixes
|
||||
- `select` widget now behaves correctly when it's `option` items are created via `ng:repeat`
|
||||
(issue #170)
|
||||
- fix for async xhr cache issue #152 by adding `$browser.defer` and `$defer` service
|
||||
|
||||
### Breaking Changes
|
||||
- Fix for issue #152 might break some tests that were relying on the incorrect behavior. The
|
||||
breakage will usually affect code that tests resources, xhr or services/widgets build on top of
|
||||
these. All that is typically needed to resolve the issue is adding a call to
|
||||
`$browser.defer.flush()` in your test just before the point where you expect all cached
|
||||
resource/xhr requests to return any results. Please see 011fa39c2a0b5da843395b538fc4e52e5ade8287
|
||||
for more info.
|
||||
- The HTML sanitizer is slightly more strinct now. Please see info in the "Security" section above.
|
||||
|
||||
|
||||
# <angular/> 0.9.5 turkey-blast (2010-11-25) #
|
||||
|
||||
### Docs
|
||||
- 99% of the content from the angular wiki is now in the docs
|
||||
|
||||
### Api
|
||||
- added `angular.Array.limitTo` to make it easy to select first or last few items of an array
|
||||
|
||||
|
||||
# <angular/> 0.9.4 total-recall (2010-11-18) #
|
||||
|
||||
### Docs
|
||||
- searchable docs
|
||||
- UI improvements
|
||||
- we now have ~85% of the wiki docs migrated to ng docs
|
||||
- some but not all docs were updated along the way
|
||||
|
||||
|
||||
### Api
|
||||
- ng:include now supports `onload` attribute (commit cc749760)
|
||||
|
||||
### Misc
|
||||
- Better error handling - compilation exception now contain stack trace (commit b2d63ac4)
|
||||
|
||||
|
||||
# <angular/> 0.9.3 cold-resistance (2010-11-10) #
|
||||
|
||||
### Docs
|
||||
|
||||
@@ -158,7 +158,7 @@ end
|
||||
|
||||
desc 'Generate docs'
|
||||
task :docs do
|
||||
`node docs/collect.js`
|
||||
`node docs/src/gen-docs.js`
|
||||
end
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ task :package => [:clean, :compile, :docs] do
|
||||
FileUtils.rm_r(path_to('pkg'), :force => true)
|
||||
FileUtils.mkdir_p(pkg_dir)
|
||||
|
||||
['test/angular-mocks.js',
|
||||
['src/angular-mocks.js',
|
||||
path_to('angular.js'),
|
||||
path_to('angular.min.js'),
|
||||
path_to('angular-ie-compat.js'),
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
border: 2px solid #FF0000;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.ng-validation-error {
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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"}}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular
|
||||
@namespace The exported angular namespace.
|
||||
@@ -0,0 +1,166 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name angular.service
|
||||
|
||||
@description
|
||||
# Overview
|
||||
Services are substituable objects, which are wired together using dependency injection (DI).
|
||||
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 identify these dependencies, so each service must explicitely define its dependencies.
|
||||
This is done by `$inject` property.
|
||||
|
||||
|
||||
# Built-in services
|
||||
angular provides a set of services for common operations. These services can be overriden by custom
|
||||
services if needed.
|
||||
|
||||
Like other core angular variables and identifiers, the built-in services always start with `$`.
|
||||
|
||||
* `{@link angular.service.$browser $browser}`
|
||||
* `{@link angular.service.$window $window}`
|
||||
* `{@link angular.service.$document $document}`
|
||||
* `{@link angular.service.$location $location}`
|
||||
* `{@link angular.service.$log $log}`
|
||||
* `{@link angular.service.$exceptionHandler $exceptionHandler}`
|
||||
* `{@link angular.service.$hover $hover}`
|
||||
* `{@link angular.service.$invalidWidgets $invalidWidgets}`
|
||||
* `{@link angular.service.$route $route}`
|
||||
* `{@link angular.service.$xhr $xhr}`
|
||||
* `{@link angular.service.$xhr.error $xhr.error}`
|
||||
* `{@link angular.service.$xhr.bulk $xhr.bulk}`
|
||||
* `{@link angular.service.$xhr.cache $xhr.cache}`
|
||||
* `{@link angular.service.$resource $resource}`
|
||||
* `{@link angular.service.$cookies $cookies}`
|
||||
* `{@link angular.service.$cookieStore $cookieStore}`
|
||||
|
||||
# Writing your own custom services
|
||||
angular provides only set of basic services, so for any nontrivial application it will be necessary
|
||||
to write one or more custom services. To do so, a factory function that creates a services needs to
|
||||
be registered with angular's dependency injector. This factory function must return an object - the
|
||||
service (it is not called with the `new` operator).
|
||||
|
||||
**angular.service** accepts three parameters:
|
||||
|
||||
- `{string} name` - Name of the service.
|
||||
- `{function()} factory` - Factory function (called just once by DI).
|
||||
- `{Object} config` - Configuration object with following properties:
|
||||
- `$inject` - {Array.<string>} - Array of service ids that this service depends on. These
|
||||
services will be passed as arguments into the factory function in the same order as specified
|
||||
in the `$inject` array. Defaults to `[]`.
|
||||
- `$eager` - {boolean} - If true, the service factory will be called and thus, the service will
|
||||
be instantiated when angular boots. If false, service will be lazily instantiated when it is
|
||||
first requested during instantiation of a dependant. Defaults to `false`.
|
||||
|
||||
The `this` of the factory function is bound to the root scope of the angular application.
|
||||
|
||||
angular enables services to participate in dependency injection (DI) by registering themselves with
|
||||
angular's DI system (injector) under a `name` (id) as well as by declaring dependencies which need
|
||||
to be provided for the factory function of the registered service. The ability to swap dependencies
|
||||
for mocks/stubs/dummies in tests allows for services to be highly testable.
|
||||
|
||||
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 as dependencies for controllers is very similar to using them as dependencies for
|
||||
another service.
|
||||
|
||||
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>
|
||||
@@ -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/);
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
require.paths.push("./lib");
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs'),
|
||||
spawn = require('child_process').spawn,
|
||||
mustache = require('mustache'),
|
||||
callback = require('callback'),
|
||||
Showdown = require('showdown').Showdown;
|
||||
|
||||
var documentation = {
|
||||
section:{},
|
||||
all:[]
|
||||
};
|
||||
|
||||
var SRC_DIR = "docs/";
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
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);
|
||||
if (doc.ngdoc) {
|
||||
delete doc.raw.text;
|
||||
var section = documentation.section;
|
||||
(section[doc.ngdoc] = section[doc.ngdoc] || []).push(doc);
|
||||
documentation.all.push(doc);
|
||||
console.log('Found:', doc.ngdoc + ':' + doc.shortName);
|
||||
mergeTemplate(
|
||||
doc.ngdoc + '.template',
|
||||
doc.name + '.html', doc, work.waitFor());
|
||||
}
|
||||
}));
|
||||
}));
|
||||
}));
|
||||
}).onError(function(err){
|
||||
console.log('ERROR:', err.stack || err);
|
||||
}).onDone(function(){
|
||||
mergeTemplate('docs-data.js', 'docs-data.js', {JSON:JSON.stringify(documentation)}, callback.chain());
|
||||
mergeTemplate('docs-scenario.js', 'docs-scenario.js', documentation, callback.chain());
|
||||
copy('docs-scenario.html', callback.chain());
|
||||
copy('index.html', callback.chain());
|
||||
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');
|
||||
});
|
||||
if (!this.testmode) work();
|
||||
////////////////////
|
||||
|
||||
function noop(){}
|
||||
function mkdirPath(path, callback) {
|
||||
var parts = path.split(/\//);
|
||||
path = '.';
|
||||
(function next(){
|
||||
if (parts.length) {
|
||||
path += '/' + parts.shift();
|
||||
fs.mkdir(path, 0777, next);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function copy(name, callback){
|
||||
fs.readFile(SRC_DIR + name, callback.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
fs.writeFile(OUTPUT_DIR + name, content, callback);
|
||||
}));
|
||||
}
|
||||
|
||||
function mergeTemplate(template, output, doc, callback){
|
||||
fs.readFile(SRC_DIR + template,
|
||||
callback.waitFor(function(err, template){
|
||||
if (err) return this.error(err);
|
||||
var content = mustache.to_html(template.toString(), doc);
|
||||
fs.writeFile(OUTPUT_DIR + output, content, callback);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function trim(text) {
|
||||
var MAX = 9999;
|
||||
var empty = RegExp.prototype.test.bind(/^\s*$/);
|
||||
var lines = text.split('\n');
|
||||
var minIndent = MAX;
|
||||
lines.forEach(function(line){
|
||||
minIndent = Math.min(minIndent, indent(line));
|
||||
});
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = lines[i].substring(minIndent);
|
||||
}
|
||||
// remove leading lines
|
||||
while (empty(lines[0])) {
|
||||
lines.shift();
|
||||
}
|
||||
// remove trailing
|
||||
while (empty(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
return lines.join('\n');
|
||||
|
||||
function indent(line) {
|
||||
for(var i = 0; i < line.length; i++) {
|
||||
if (line.charAt(i) != ' ') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MAX;
|
||||
}
|
||||
}
|
||||
|
||||
function unknownTag(doc, name) {
|
||||
var error = "[" + doc.raw.file + ":" + doc.raw.line + "]: unknown tag: " + name;
|
||||
console.log(error);
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
function valueTag(doc, name, value) {
|
||||
doc[name] = value;
|
||||
}
|
||||
|
||||
function escapedHtmlTag(doc, name, value) {
|
||||
doc[name] = value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function markdownTag(doc, name, value) {
|
||||
doc[name] = markdown(value.replace(/^#/gm, '##')).
|
||||
replace(/\<pre\>/gmi, '<pre class="brush: xml; brush: js;" ng:non-bindable>');
|
||||
}
|
||||
|
||||
function markdown(text) {
|
||||
text = text.replace(/<angular\/>/gm, '<tt><angular/></tt>');
|
||||
return new Showdown.converter().makeHtml(text);
|
||||
}
|
||||
|
||||
function markdownNoP(text) {
|
||||
var lines = markdown(text).split(NEW_LINE);
|
||||
var last = lines.length - 1;
|
||||
lines[0] = lines[0].replace(/^<p>/, '');
|
||||
lines[last] = lines[last].replace(/<\/p>$/, '');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
var TAG = {
|
||||
ngdoc: valueTag,
|
||||
example: escapedHtmlTag,
|
||||
scenario: valueTag,
|
||||
namespace: valueTag,
|
||||
css: valueTag,
|
||||
see: valueTag,
|
||||
usageContent: valueTag,
|
||||
'function': valueTag,
|
||||
description: markdownTag,
|
||||
TODO: markdownTag,
|
||||
returns: markdownTag,
|
||||
paramDescription: markdownTag,
|
||||
exampleDescription: markdownTag,
|
||||
element: valueTag,
|
||||
name: function(doc, name, value) {
|
||||
doc.name = value;
|
||||
var match = value.match(/^angular[\.\#](([^\.]+)\.(.*)|(.*))/);
|
||||
doc.shortName = match[3] || match[4];
|
||||
},
|
||||
param: function(doc, name, value){
|
||||
doc.param = doc.param || [];
|
||||
doc.paramRest = doc.paramRest || [];
|
||||
var match = value.match(/^({([^\s=]+)(=)?}\s*)?(([^\s=]+)|\[(\S+)+=([^\]]+)\])\s+(.*)/);
|
||||
if (match) {
|
||||
var param = {
|
||||
type: match[2],
|
||||
name: match[6] || match[5],
|
||||
'default':match[7],
|
||||
description:markdownNoP(value.replace(match[0], match[8]))
|
||||
};
|
||||
doc.param.push(param);
|
||||
if (!doc.paramFirst) {
|
||||
doc.paramFirst = param;
|
||||
} else {
|
||||
doc.paramRest.push(param);
|
||||
}
|
||||
} else {
|
||||
throw "[" + doc.raw.file + ":" + doc.raw.line +
|
||||
"]: @param must be in format '{type} name=value description' got: " + value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function parseNgDoc(doc){
|
||||
var atName;
|
||||
var atText;
|
||||
var match;
|
||||
doc.raw.text.split(NEW_LINE).forEach(function(line, lineNumber){
|
||||
if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {
|
||||
// we found @name ...
|
||||
// if we have existing name
|
||||
if (atName) {
|
||||
(TAG[atName] || unknownTag)(doc, atName, trim(atText.join('\n')));
|
||||
}
|
||||
atName = match[1];
|
||||
atText = [];
|
||||
if(match[3]) atText.push(match[3]);
|
||||
} else {
|
||||
if (atName) {
|
||||
atText.push(line);
|
||||
} else {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
});
|
||||
if (atName) {
|
||||
(TAG[atName] || unknownTag)(doc, atName, atText.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
function findNgDoc(file, callback) {
|
||||
fs.readFile(file, callback.waitFor(function(err, content){
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var doc;
|
||||
var match;
|
||||
var inDoc = false;
|
||||
lines.forEach(function(line, lineNumber){
|
||||
lineNumber++;
|
||||
// is the comment starting?
|
||||
if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) {
|
||||
line = match[1];
|
||||
inDoc = true;
|
||||
doc = {raw:{file:file, line:lineNumber, text:[]}};
|
||||
}
|
||||
// are we done?
|
||||
if (inDoc && line.match(/\*\//)) {
|
||||
doc.raw.text = doc.raw.text.join('\n');
|
||||
doc.raw.text = doc.raw.text.replace(/^\n/, '');
|
||||
if (doc.raw.text.match(/@ngdoc/)){
|
||||
callback(doc);
|
||||
}
|
||||
doc = null;
|
||||
inDoc = false;
|
||||
}
|
||||
// is the comment add text
|
||||
if (inDoc){
|
||||
doc.raw.text.push(line.replace(/^\s*\*\s?/, ''));
|
||||
}
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findJsFiles(dir, callback){
|
||||
fs.readdir(dir, callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
var path = dir + '/' + file;
|
||||
fs.lstat(path, callback.waitFor(function(err, stat){
|
||||
if (err) return this.error(err);
|
||||
if (stat.isDirectory())
|
||||
findJsFiles(path, callback.waitMany(callback));
|
||||
else if (/\.js$/.test(path))
|
||||
callback(path);
|
||||
}));
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<pre>
|
||||
<{{element}} {{shortName}}="{{paramFirst.name}}">
|
||||
...
|
||||
</{{element}}>
|
||||
</pre>
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}{{^type}}:*{{/type}}{{#default}}={{default}}{{/default}}</tt>: {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
{{{paramDescription}}}
|
||||
|
||||
{{#css}}<h3>CSS</h3>{{/css}}
|
||||
{{{css}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1 +0,0 @@
|
||||
NG_DOC={{{JSON}}};
|
||||
@@ -1,9 +0,0 @@
|
||||
{{#all}}
|
||||
describe('{{name}}', function(){
|
||||
beforeEach(function(){
|
||||
browser().navigateTo('index.html#{{name}}');
|
||||
});
|
||||
// {{raw.file}}:{{raw.line}}
|
||||
{{{scenario}}}
|
||||
});
|
||||
{{/all}}
|
||||
@@ -1,7 +0,0 @@
|
||||
function DocController($resource, $location){
|
||||
this.docs = $resource('documentation.json').get();
|
||||
this.getPartialDoc = function(){
|
||||
return encodeURIComponent($location.hashPath) + '.html';
|
||||
};
|
||||
}
|
||||
DocController.$inject=['$resource', '$location'];
|
||||
@@ -1,42 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<span>{{</span>
|
||||
{{paramFirst.name}}_expression
|
||||
| {{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}={{default}}]</i>{{/default}}{{/paramRest}}
|
||||
<span> }}</span>
|
||||
</tt>
|
||||
<h3>In JavaScript</h3>
|
||||
<tt ng:non-bindable>
|
||||
angular.filter.{{shortName}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/paramRest}} );
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}{{^type}}:*{{/type}}{{#default}}={{default}}{{/default}}</tt>: {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
|
||||
<h3>Returns</h3>
|
||||
{{{returns}}}
|
||||
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,33 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<input type="text" ng:format="{{shortName}}">
|
||||
</tt>
|
||||
<h3>In JavaScript</h3>
|
||||
<tt ng:non-bindable>
|
||||
var userInputString = angular.formatter.{{shortName}}.format(modelValue);<br/>
|
||||
var modelValue = angular.formatter.{{shortName}}.parse(userInputString);
|
||||
</tt>
|
||||
|
||||
<h3>Returns</h3>
|
||||
{{{returns}}}
|
||||
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -0,0 +1,97 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Bootstrap
|
||||
@description
|
||||
|
||||
# Bootstrap
|
||||
This section explains how to bootstrap your application to the angular environment using either
|
||||
the `angular.js` or `angular.min.js` script.
|
||||
|
||||
## The bootstrap code
|
||||
|
||||
Note that there are two versions of the bootstrap code that you can use:
|
||||
|
||||
* `angular-0.0.0.js` - this file is unobfuscated, uncompressed, and thus human-readable.
|
||||
* `angular-0.0.0.min.js` - this is a compressed and obfuscated version of angular-debug.js.
|
||||
|
||||
In this section and throughout the Developer Guide, feel free to use `angular.min.js` instead of
|
||||
`angular.js` when working through code examples.
|
||||
|
||||
## ng:autobind
|
||||
|
||||
The simplest way to get an angular application up and running is by inserting a script tag in your
|
||||
HTML file that bootstraps the `angular.js` code and uses the special `ng:autobind` attribute,
|
||||
like in this snippet of HTML:
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
Hello {{'World'}}!
|
||||
</doc:source>
|
||||
</doc:example>
|
||||
|
||||
The `ng:autobind` attribute tells angular to compile and manage the whole HTML document. The
|
||||
compilation occurs in the page's onLoad handler. Note that you don't need to explicitly add an
|
||||
onLoad event; auto bind mode takes care of all the magic for you.
|
||||
|
||||
## Manual bind
|
||||
|
||||
Using autobind mode is a handy way to start using angular, but advanced users who want more
|
||||
control over the initialization process might prefer to use manual bind mode instead.
|
||||
|
||||
The best way to get started with manual bind mode is to look at the magic behind `ng:autobind`
|
||||
by writing out each step of the autobind process explicitly. Note that the following code is
|
||||
equivalent to the code in the previous section.
|
||||
|
||||
<pre>
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<script type="text/javascript" src="http://code.angularjs.org/angular-0.0.0.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
(function(window, previousOnLoad){
|
||||
window.onload = function(){
|
||||
try { (previousOnLoad||angular.noop)(); } catch(e) {}
|
||||
angular.compile(window.document).$init();
|
||||
};
|
||||
})(window, window.onload);
|
||||
</script>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
</body>
|
||||
</html>
|
||||
</pre>
|
||||
|
||||
This is the sequence that your code should follow if you're writing your own manual binding code:
|
||||
|
||||
* After the page is loaded, find the root of the HTML template, which is typically the root of
|
||||
the document.
|
||||
* Run the HTML compiler, which converts the templates into an executable, bi-directionally
|
||||
bound application.
|
||||
|
||||
|
||||
# XML Namespace
|
||||
|
||||
**IMPORTANT:** When using angular you must declare the `ng` namespace using the `xmlns` tag.
|
||||
If you don't declare the namespace, Internet Explorer does not render widgets properly.
|
||||
|
||||
<pre>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
</pre>
|
||||
|
||||
|
||||
# Create your own namespace
|
||||
|
||||
If you want to define your own widgets, you must create your own namespace and use that namespace
|
||||
to form the fully qualified widget name. For example, you could map the alias my to your domain
|
||||
and create a widget called my:widget. To create your own namespace, simply add another xmlsn tag
|
||||
to your page, create an alias, and set it to your unique domain:
|
||||
|
||||
<pre>
|
||||
<html xmlns:my="http://mydomain.com">
|
||||
</pre>
|
||||
|
||||
|
||||
# Global Object
|
||||
|
||||
The angular script creates a single global variable `angular` in the global namespace. All APIs are
|
||||
bound to fields of this global object.
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Compiler
|
||||
@description
|
||||
|
||||
#Compiler
|
||||
|
||||
While angular might look like just a cool way to build web applications, the core of angular is
|
||||
actually an HTML compiler. The default HTML transformations that this compiler provides are useful
|
||||
for building generic apps, but you can also use them to create a domain-specific language for
|
||||
building specific types of web applications.
|
||||
|
||||
The compiler allows you to add behavior to existing HTML through widgets, directives, and text
|
||||
markup.
|
||||
|
||||
All of this compilation happens in the web browser, meaning no server is involved.
|
||||
|
||||
# The compilation process
|
||||
This section describes the steps that angular's HTML compiler goes through. If you use
|
||||
`ng:autobind` in your application, this compilation process happens automatically when the
|
||||
application is initialized (e.g. when the user loads the app in a browser). If you're an advanced
|
||||
user using manual bind mode, you can decide when and how often the compilation happens.
|
||||
|
||||
First, a bit of background of what the compilation step is for. Every type of
|
||||
{@link angular.widget widget}, {@link angular.markup markup}, and
|
||||
{@link angular.directive directive} in angular is defined with a compile function, and that
|
||||
compile function returns an optional link function. Here is the relationship between the two:
|
||||
|
||||
* **compile function** - registers a listener for the widget, markup, or directive's expression.
|
||||
This function is called exactly once.
|
||||
* **link function** - sets up the listener. This function can be called multiple times, once per
|
||||
cloned DOM element (e.g. repeating element).
|
||||
|
||||
Note that angular's built-in widgets, markup, and directives have predefined compile and link
|
||||
functions that you don't need to modify. However, if you're writing your own widgets, markup, or
|
||||
directives, you write compile and link functions. Refer to the Compiler API for more information.
|
||||
|
||||
When the HTML compiler compiles a page, it goes through 3 phases: Compile, Create Root Scope, and
|
||||
Link.
|
||||
|
||||
## 1. Compile Phase
|
||||
|
||||
* Recursively traverse the DOM, depth-first.
|
||||
* Look for a matching compile function of type widget, then markup, then directive.
|
||||
* If a compile function is found then execute it.
|
||||
* When the compile function completes, it should return a link function. Aggregate this link
|
||||
function with all link functions returned previously by step 1c.
|
||||
* Repeat steps 1c and 1d for all compile functions found. The result of the compilation step is
|
||||
the aggregate link function, which comprises all of the individual link functions.
|
||||
|
||||
## 2. Create Root Scope
|
||||
|
||||
* Inject all of the services into the root scope.
|
||||
|
||||
## 3. Link Phase
|
||||
|
||||
* Execute the aggregate link function with the root scope. The aggregate link function calls all
|
||||
the individual link functions that were generated in the compile phase.
|
||||
* If there are any clones of the DOM caused by repeating elements, call the link function multiple
|
||||
times, one for each repeating item.
|
||||
|
||||
Note that while the compile function is executed exactly once, the link function can be executed
|
||||
multiple times: once for each iteration in a repeater.
|
||||
|
||||
# Example
|
||||
|
||||
The compilation process is best understood through example. Let's say that in your namespace my,
|
||||
you want to create a new DOM element <my:greeter/>, which should display a greeting.
|
||||
|
||||
If we want this HTML source:
|
||||
|
||||
<pre>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
To produce this DOM:
|
||||
|
||||
<pre>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
<span class="salutation">Hello</span>
|
||||
<span class="name">World</span>!
|
||||
</my:greeter>
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
Write this widget definition (assuming you've already declared the my namespace in the page):
|
||||
|
||||
|
||||
<pre>
|
||||
angular.widget('my:greeter', function(compileElement){
|
||||
var compiler = this;
|
||||
compileElement.css('display', 'block');
|
||||
var salutationExp = compileElement.attr('salutation');
|
||||
var nameExp = compileElement.attr('name');
|
||||
return function(linkElement){
|
||||
var salutationSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutationSpan);
|
||||
linkElement.append(compiler.text(' '));
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append(compiler.text('!'));
|
||||
this.$watch(salutationExp, function(value){
|
||||
salutationSpan.text(value);
|
||||
});
|
||||
this.$watch(nameExp, function(value){
|
||||
nameSpan.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</pre>
|
||||
|
||||
Note: For more about widgets, see {@link angular.widget Widget}.
|
||||
|
||||
## Compilation process for this example
|
||||
|
||||
Here are the steps that the compiler goes through for the page that contains this widget definition:
|
||||
|
||||
### Compile Phase
|
||||
|
||||
* Recursively traverse the DOM depth-first.
|
||||
* Find the angular.widget definition.
|
||||
* Find and execute the widget's compileElement function, which includes the following steps:
|
||||
* Add a style element with attribute display: block; to the template DOM so that the browser
|
||||
knows to treat the element as block element for rendering. (Note: because this style element
|
||||
was added on the template compileElement, this style is automatically applied to any clones
|
||||
of the template (i.e. any repeating elements)).
|
||||
* Extract the salutation and name HTML attributes as angular expressions.
|
||||
* Return the aggregate link function, which includes just one link function in this example.
|
||||
|
||||
### Link Phase
|
||||
|
||||
* Execute the aggregate link function, which includes the following steps:
|
||||
* Create a <span> element set to the salutation class
|
||||
* Create a <span> element set to the name class.
|
||||
* Add the span elements to the linkElement. (Note: be careful not to add them to the
|
||||
compileElement, because that's the template.)
|
||||
* Set up watches on the expressions. When an expression changes, copy the data to the
|
||||
corresponding spans.
|
||||
|
||||
|
||||
## Compiler API
|
||||
|
||||
If you define your own widgets, markup, or directives, you need to access the compiler API.
|
||||
This section describes the methods on the compiler that you can call.
|
||||
|
||||
Note: As of 12 August 2010, these methods are subject to change.
|
||||
|
||||
Recall that the compile function's this is a reference to the compiler.
|
||||
|
||||
* `compile(element)` - returns `linker` - Invoke new instance of compiler to compile a DOM element
|
||||
and return a linker function. You can apply the linker function to the original element or a
|
||||
clone of the original element. The linker function returns a scope.
|
||||
* `comment(commentText)` - returns `element` - Create a comment element.
|
||||
* `element(elementName)` - returns `element` - Create an element by name.
|
||||
* `text(text)` - returns `element` - Create a text element.
|
||||
* `descend([set])` - returns `descend` - State Get or set the current descend state. If true the
|
||||
compiler will descend to children elements.
|
||||
* `directives([set])` - returns `directive` - State Get or set the current directives processing
|
||||
state. The compiler will process directives only when directives set to true.
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Data Binding
|
||||
@description
|
||||
|
||||
# Data Binding
|
||||
|
||||
Data-binding allows you to treat the model as the single-source-of-truth of your application, and
|
||||
consider the view as only a projection of the model, at all times. The process of copying the model
|
||||
values to the view, and any changes to the view by the user to the model, is known as data-binding.
|
||||
|
||||
## Classical Template Systems
|
||||
|
||||
<img class="right" src="img/One_Way_Data_Binding.png"/>
|
||||
At the highest level, angular looks like a just another templating system. But there is one
|
||||
important reason why angular templating system is different and makes it very good fit for
|
||||
application development: two-way data binding.
|
||||
|
||||
Most templating systems bind data in only one direction: they merge a template and model together
|
||||
into a view, as illustrated in the diagram to the right. After the merge occurs, any changes to
|
||||
the model or in related sections of the view are NOT automatically reflected in the view. Worse,
|
||||
any changes that the user makes to the view are not reflected in the model. This means that the
|
||||
developer has to write code that constantly syncs the view with the model and the model with the
|
||||
view.
|
||||
|
||||
|
||||
# angular Template Systems
|
||||
<img class="right" src="img/Two_Way_Data_Binding.png"/>
|
||||
The way angular templates works is different, as illustrated in the diagram on the right. They are
|
||||
different because first the template (which is the uncompiled HTML along with any additional markup
|
||||
or directives) is compiled on the browser, and second, the compilation step produces a live view.
|
||||
We say live because any changes to the view are immediately reflected in the model, and any changes
|
||||
in the model are propagated to the view. This makes the model always the single-source-of-truth for
|
||||
the application state, greatly simplifying the programing model for the developer. You can think of
|
||||
the view as simply an instant projection of your model.
|
||||
|
||||
Because the view is just a projection of the model, the controller is completely separated from the
|
||||
view and unaware of it. This makes testing a snap because it is easy to test your controller in
|
||||
isolation without the view and the related DOM/browser dependency.
|
||||
|
||||
For details about how data binding works in angular, see {@link angular.scope Scope}.
|
||||
@@ -0,0 +1,36 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide
|
||||
@description
|
||||
|
||||
* {@link guide.overview Overview} - An overview of angular, including its philosophy and how it
|
||||
works.
|
||||
* {@link guide.bootstrap Bootstrap} - How to bootstrap your application to the angular environment.
|
||||
* {@link guide.template Template} - How to define your application's view using HTML, CSS, and
|
||||
other built-in angular constructs.
|
||||
* {@link guide.compiler Compiler} - All about the HTML compiler that's at the core of angular.
|
||||
* {@link angular.directive Directive} - How to use XML attributes to augment an existing DOM
|
||||
element.
|
||||
* {@link angular.markup Markup} - How to use markup to create shorthand for a widget or a
|
||||
directive. For example, markup is what allows you to use the double curly brace notation
|
||||
`{{}}` to bind expressions to elements.
|
||||
* {@link guide.data-binding Data Binding} - About the mechanism that keeps the model the single
|
||||
source of truth of your application at all times, with the view as a live projection of the
|
||||
model.
|
||||
* {@link angular.filter Filter} - How to format your data for display to the user.
|
||||
* {@link angular.widget Widget} - How to create new DOM elements that the browser doesn't already
|
||||
understand.
|
||||
* {@link angular.validator Validator} - How to validate user input.
|
||||
* {@link angular.formatter Formatter} - How to format stored data to user-readable text and
|
||||
parse the text back to the stored form.
|
||||
* {@link guide.css CSS} - Built-in CSS classes, when angular assigns them, and how to override
|
||||
their styles.
|
||||
* {@link angular.scope Scope} - The model in the model-view-controller design pattern. You can
|
||||
think about scopes as the JavaScript objects that have extra APIs for registering watchers.
|
||||
* {@link guide.expression Expression} - The bindings that are embedded in an angular View.
|
||||
* {@link angular.service Service} - Objects that are wired through dependency injection and then
|
||||
injected into the root scope.
|
||||
* {@link guide.testing Testing}
|
||||
* service:$browser(mock)
|
||||
* {@link guide.downloading Downloading} - How to download, compile, and host the angular
|
||||
environment on your own server.
|
||||
@@ -0,0 +1,166 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Overview
|
||||
@description
|
||||
|
||||
# What is angular?
|
||||
Angular teaches your old browser new tricks. It is what HTML would have been if it had been
|
||||
designed for building web applications.
|
||||
|
||||
Take a simple example of user input as shown below. If you were using just HTML and JavaScript to
|
||||
implement this form, you would need to define listeners, DOM updates, and complex input validators
|
||||
in order to update and format the result. In angular you can achieve the effect with zero lines
|
||||
of JavaScript code using a declarative approach. Click on the source tab of the example below to
|
||||
view the angular implementation of this form.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
QTY: <input name="qty" value="1" ng:validate="integer:0" ng:required/>
|
||||
*
|
||||
Cost: <input name="cost" value="19.95" ng:validate="number" ng:required/>
|
||||
=
|
||||
{{qty * cost | currency}}
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should show of angular binding', function(){
|
||||
expect(binding('qty * cost')).toEqual('$19.95');
|
||||
input('qty').enter('2');
|
||||
input('cost').enter('5.00');
|
||||
expect(binding('qty * cost')).toEqual('$10.00');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
|
||||
Angular is to AJAX apps as Ruby on Rails is to round trip apps.
|
||||
|
||||
# Angular frees you from:
|
||||
* **Registering callbacks:** Registering callbacks clutters your code, and it makes it hard to see
|
||||
the forest from the trees. Removing common boilerplate code such as callbacks is advantageous
|
||||
because it leaves the JavaScript with a more succinct version of your code, better describing
|
||||
what your application does.
|
||||
* **Manipulating HTML DOM programatically:** Manipulating HTML DOM is a cornerstone of AJAX
|
||||
applications, but it is very cumbersome and error-prone. By declaratively describing how the UI
|
||||
should change as your application state changes, you are freed from low level DOM manipulation
|
||||
activities. Most applications written with angular never have to programatically manipulate
|
||||
the DOM.
|
||||
* **Marshaling data to and from the UI:** CRUD operations make up the majority of most AJAX
|
||||
applications. The flow of marshaling data from the server to an internal object to a HTML form,
|
||||
validating the form, displaying validation errors, returning to an internal model and then back
|
||||
to the server creates a lot of boilerplate code. angular eliminates almost all of this
|
||||
boilerplate. leaving code that is richer and describes the overall flow of the application
|
||||
rather than implementation details.
|
||||
* **Writing tons of initialization code just to get started:** Typically you need to write a lot
|
||||
of plumbing and initialization code just to get a basic "Hello World" AJAX app working. With
|
||||
angular you can bootstrap your app easily using services, which are auto-injected into your
|
||||
application in a GUICE-like dependency-injection style. This allows you to get started
|
||||
developing features quickly. As a bonus, you get full control over the initialization process
|
||||
in automated tests.
|
||||
|
||||
|
||||
# angular is/has:
|
||||
* **An HTML Compiler:** angular is an HTML compiler in the browser. It allows you to give meaning
|
||||
to any HTML element, attribute, or text and create new primitives as building blocks for your
|
||||
application.
|
||||
* **Declarative:** Declarative means that you describe what the page looks like rather than
|
||||
instructing how to draw the page. HTML is great at declaring static documents. Angular extends
|
||||
the declarative nature of HTML beyond static documents to define dynamic applications.
|
||||
* **Declarative Templates:** In angular, you write HTML to declare the view and UI templates for
|
||||
your application. You can express many common application constructs without using any
|
||||
JavaScript at all.
|
||||
* **Bidirectional Data Binding:** Allows your application to have a single source of truth
|
||||
(your model), where the HTML is a projection of the internal state of your application, which
|
||||
you can provide to the user in a declarative way.
|
||||
* **Built-in Services:** angular provides many standard AJAX operations to get you going quickly,
|
||||
and dependency-injection allows you to swap them out as needed. Common services include:
|
||||
Dependency Inject, History Management, URL Router, AJAX/XHR requests, and data caching,
|
||||
to name a few.
|
||||
* **Very testable:** Testing difficulty is dramatically affected by the way you structure your
|
||||
code. With angular, testability is baked in.
|
||||
|
||||
# angular is NOT a:
|
||||
* **Library:** You don't call its functions.
|
||||
* **Framework:** It does not call your functions.
|
||||
* **DOM Manipulation Library:** It does not provide a way to manipulate DOM, but does provide
|
||||
primitives to create UI projections of your data.
|
||||
* **Widget Library:** There are lots of existing widget libraries that you can integrate with
|
||||
angular.
|
||||
|
||||
|
||||
|
||||
# Not just another templating system
|
||||
|
||||
At the highest level, angular looks like a just another templating system, but there are few
|
||||
important reasons why angular is different and makes it a very good fit for application
|
||||
development.
|
||||
|
||||
Angular:
|
||||
* **Uses HTML/CSS syntax:** This makes it easy to read and can be edited with existing HTML/CSS
|
||||
authoring tools.
|
||||
* **Extends HTML vocabulary:** Angular allows you to create new HTML tags, which expand into
|
||||
dynamic UI components.
|
||||
* **Executes in the browser:** Removes the round trip to the server for many operations and
|
||||
creates instant feedback for users.
|
||||
* **Bidirectional data binding:** The model is the single source of truth. Programmatic changes
|
||||
to the model are automatically reflected in the view. Any changes by the user to the view are
|
||||
automatically reflected in the model.
|
||||
* **Services:** These allow for a reusable way of injecting dependencies into an application.
|
||||
* **MVC:** Clean separation between model-view-controller, which aids in understanding,
|
||||
maintenance, and testing of large systems.
|
||||
|
||||
|
||||
# The angular philosophy
|
||||
|
||||
Angular is built around the belief that declarative code is preferred over imperative when it
|
||||
comes to building UIs and connecting the pieces together.
|
||||
|
||||
As an example, if you wanted to add a new label to your application, you could do so by simply
|
||||
adding text to the HTML template, saving the code, and refreshing your browser:
|
||||
|
||||
<pre>
|
||||
<span class="label">Hello</span>
|
||||
</pre>
|
||||
|
||||
In programmatic systems you would have to write and run code like this:
|
||||
|
||||
<pre>
|
||||
var label = new Label();
|
||||
label.setText('Hello');
|
||||
label.setClass('label');
|
||||
parent.addChild(label);
|
||||
</pre>
|
||||
|
||||
## Benefits:
|
||||
|
||||
* Compile-free: Change your template and logic code and reload the browser to see it run
|
||||
immediately. In contrast, programmatic serverside views often need to be compiled and executed
|
||||
to be viewed, which takes time. This dramatically increases the speed of your development cycle.
|
||||
* Declarative templates are easier to understand and change than programmatic instructions.
|
||||
* Declarative templates can be edited in existing HTML editors such as DreamWeaver, Eclipse,
|
||||
TextMate, Vim, etc.
|
||||
* Declarative templates can be edited by web designers without the need to work with web
|
||||
developers.
|
||||
|
||||
HTML is missing certain features, which angular adds via its compiler, thereby "teaching" the
|
||||
browser these new tricks:
|
||||
|
||||
* Dynamic behavior
|
||||
* Componentizing HTML snippets into reusable components
|
||||
* Dynamically include other HTML templates
|
||||
* Two-way data binding
|
||||
* Rich validation in forms
|
||||
* Model-View-Controller modularization
|
||||
|
||||
# Watch a presentation about angular
|
||||
|
||||
<object width="480" height="385">
|
||||
<param name="movie" value="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"></param>
|
||||
<param name="allowFullScreen" value="true"></param>
|
||||
<param name="allowscriptaccess" value="always"></param>
|
||||
<embed src="http://www.youtube.com/v/elvcgVSynRg&hl=en_US&fs=1"
|
||||
type="application/x-shockwave-flash" allowscriptaccess="always"
|
||||
allowfullscreen="true" width="480" height="385"></embed>
|
||||
</object>
|
||||
|
||||
{@link https://docs.google.com/present/edit?id=0Abz6S2TvsDWSZDQ0OWdjaF8yNTRnODczazdmZg&hl=en&authkey=CO-b7oID Presentation}
|
||||
|
|
||||
{@link https://docs.google.com/document/edit?id=1ZHVhqC0apbzPRQcgnb1Ye-bAUbNJ-IlFMyPBPCZ2cYU&hl=en&authkey=CInnwLYO Source}
|
||||
@@ -0,0 +1,22 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name Developer Guide: Template
|
||||
@description
|
||||
#Template
|
||||
|
||||
You can think of a template in angular as a domain-specific language that you can use to easily
|
||||
build the view of your web application. You create a template by writing HTML and CSS, and you can
|
||||
add any constructs that you want to the HTML. This means that you can attach rendering and behavior
|
||||
to any HTML element, attribute or markup text.
|
||||
|
||||
In addition to writing HTML and CSS, you can also use the following angular constructs to create
|
||||
your template:
|
||||
|
||||
* **Directive** - XML attributes that augment an existing DOM element.
|
||||
* **Markup** - Lets you create shorthand for a widget or a directive. For example, markup is what
|
||||
allows you to use the double curly brace notation {{}} to bind expressions to
|
||||
elements.
|
||||
* **Filter** - Lets you format your data for display to the user.
|
||||
* **Widget** - Lets you create new DOM elements that the browser doesn't already understand.
|
||||
* **Validator** - Lets you validate user input.
|
||||
* **Formatter** - Lets you format the input object into a user readable view.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
-149
@@ -1,149 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org/" xmlns:doc="http://docs.angularjs.org/" ng:controller="DocsController">
|
||||
<head>
|
||||
<title><Angular/> Docs</title>
|
||||
<link rel="stylesheet" href="wiki_widgets.css" type="text/css" media="screen">
|
||||
<link rel="stylesheet" href="doc_widgets.css" type="text/css" media="screen" />
|
||||
<link rel="stylesheet" href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" type="text/css" media="screen" />
|
||||
<link rel="stylesheet" href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" type="text/css" media="screen" />
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../angular.min.js" ng:autobind></script>
|
||||
<script type="text/javascript" src="doc_widgets.js"></script>
|
||||
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js"></script>
|
||||
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js"></script>
|
||||
<script type="text/javascript" src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js"></script>
|
||||
<script type="text/javascript" src="docs-data.js"></script>
|
||||
<script type="text/javascript">
|
||||
SyntaxHighlighter['defaults'].toolbar = false;
|
||||
|
||||
DocsController.$inject = ['$location', '$browser']
|
||||
function DocsController($location, $browser) {
|
||||
this.docs = NG_DOC;
|
||||
window.$root = this.$root;
|
||||
|
||||
this.getUrl = function(page){
|
||||
return '#' + encodeURIComponent(page.name);
|
||||
};
|
||||
|
||||
this.getCurrentPartial = function(){
|
||||
if ($location.hashPath.match(/^angular\./)) {
|
||||
this.partialUrl = './' + $location.hashPath + '.html';
|
||||
}
|
||||
return this.partialUrl;
|
||||
}
|
||||
|
||||
this.getTitle = function(){
|
||||
if ($location.hashPath.match(/^angular\./)) {
|
||||
this.partialTitle = $location.hashPath;
|
||||
}
|
||||
return this.partialTitle;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style type="text/css" media="screen">
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
width: 15em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: #F2C200;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
font-weight: normal;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
margin: 0;
|
||||
padding: 10px 10px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#header .angular {
|
||||
font-family: Courier New, monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#header h1 a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#section {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#section ng\:include {
|
||||
margin: 0 1em 1em 15em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#section h1 {
|
||||
font-family: monospace;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#sidebar h2 {
|
||||
font-size: 1.2em;
|
||||
margin: 5px 5px 5px 0.8em;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
list-style-type: none;
|
||||
/*TODO(esprehn): Can we just reset globally and not break examples?*/
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li {
|
||||
margin: 0;
|
||||
padding: 1px 1px 1px 1.5em;
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
margin-left: 1em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
<title><angular/>: {{getTitle()}}</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
<h1>
|
||||
<span class="section-title">{{getTitle()}}</span>
|
||||
<a href="index.html"><span class="angular"><angular/></span> Docs</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="sidebar" class="nav">
|
||||
<div ng:repeat="(name, type) in docs.section" class="nav-section">
|
||||
<h2>{{name}}</h2>
|
||||
<ul>
|
||||
<li ng:repeat="page in type.$orderBy('shortName')">
|
||||
<a href="{{getUrl(page)}}" ng:click="">{{page.shortName}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="section">
|
||||
<ng:include src="getCurrentPartial()"></ng:include>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
{{{description}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -0,0 +1,57 @@
|
||||
<h1><tt>{{name}}</tt></h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Dependencies</h2>
|
||||
<ul>
|
||||
{{#requires}}
|
||||
<li><tt>{{name}}</tt></li>
|
||||
{{/requires}}
|
||||
</ul>
|
||||
|
||||
{{#method.length}}
|
||||
<h2>Methods</h2>
|
||||
<ul>
|
||||
{{#method}}
|
||||
<li><tt>{{shortName}}()</tt>: {{{description}}}</li>
|
||||
{{/method}}
|
||||
</ul>
|
||||
{{/method.length}}
|
||||
|
||||
{{#property.length}}
|
||||
<h2>Properties</h2>
|
||||
<ul>
|
||||
{{#property}}
|
||||
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}</tt>{{#description}}: {{{description}}}{{/description}}</li>
|
||||
{{/property}}
|
||||
</ul>
|
||||
{{/property.length}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
{{#scenario}}<doc:scenario>{{{scenario}}}</doc:scenario>{{/scenario}}
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,83 +0,0 @@
|
||||
console.log(__dirname);
|
||||
require.paths.push(__dirname + "/../");
|
||||
require.paths.push(__dirname + "/../../");
|
||||
var fs = require('fs');
|
||||
var Script = process.binding('evals').Script;
|
||||
var collect = load('docs/collect.js');
|
||||
|
||||
describe('collect', function(){
|
||||
describe('markdown', function(){
|
||||
it('should replace angular in markdown', function(){
|
||||
expect(collect.markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TAG', function(){
|
||||
var TAG = collect.TAG;
|
||||
var doc;
|
||||
beforeEach(function(){
|
||||
doc = {};
|
||||
});
|
||||
|
||||
describe('@param', function(){
|
||||
it('should parse with no default', function(){
|
||||
TAG.param(doc, 'param',
|
||||
'{(number|string)} number Number \n to format.');
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'number',
|
||||
'default' : undefined,
|
||||
description : 'Number \n to format.' }]);
|
||||
});
|
||||
it('should parse with default', function(){
|
||||
TAG.param(doc, 'param',
|
||||
'{(number|string)=} [fractionSize=2] desc');
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'fractionSize',
|
||||
'default' : '2',
|
||||
description : 'desc' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@describe', function(){
|
||||
it('should support pre blocks', function(){
|
||||
TAG.description(doc, 'description', '<pre class="brush: xml;" ng:non-bindable>abc</pre>');
|
||||
expect(doc.description).toEqual('<pre class="brush: xml;" ng:non-bindable>abc</pre>');
|
||||
});
|
||||
|
||||
describe('@example', function(){
|
||||
it('should not remove {{}}', function(){
|
||||
TAG.example(doc, 'example', 'text {{ abc }}');
|
||||
expect(doc.example).toEqual('text {{ abc }}');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('trim', function(){
|
||||
var trim = collect.trim;
|
||||
it('should remove leading/trailing space', function(){
|
||||
expect(trim(' \nabc\n ')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should remove leading space on every line', function(){
|
||||
expect(trim('\n 1\n 2\n 3\n')).toEqual('1\n 2\n 3');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function load(path){
|
||||
var sandbox = {
|
||||
require: require,
|
||||
console: console,
|
||||
__dirname: __dirname,
|
||||
testmode: true
|
||||
};
|
||||
Script.runInNewContext(fs.readFileSync(path), sandbox, path);
|
||||
return sandbox;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
var DOM = require('dom.js').DOM;
|
||||
|
||||
describe('dom', function(){
|
||||
var dom;
|
||||
|
||||
beforeEach(function(){
|
||||
dom = new DOM();
|
||||
});
|
||||
|
||||
describe('example', function(){
|
||||
it('should render code, live, test', function(){
|
||||
dom.example('desc', 'src', 'scenario');
|
||||
expect(dom.toString()).toEqual(
|
||||
'<h1>Example</h1>\n' +
|
||||
'<div class="example">' +
|
||||
'desc<doc:example><doc:source>src</doc:source>\n' +
|
||||
'<doc:scenario>scenario</doc:scenario>\n'+
|
||||
'</doc:example>\n' +
|
||||
'</div>\n');
|
||||
});
|
||||
|
||||
it('should render non-live, test with description', function(){
|
||||
dom.example('desc', 'src', false);
|
||||
expect(dom.toString()).toEqual('<h1>Example</h1>\n' +
|
||||
'<div class="example">' +
|
||||
'desc<div ng:non-bindable="">' +
|
||||
'<pre class="brush: js; html-script: true;">src</pre>\n' +
|
||||
'</div>\n' +
|
||||
'</div>\n');
|
||||
});
|
||||
|
||||
it('should render non-live, test', function(){
|
||||
dom.example('desc', 'src', false);
|
||||
expect(dom.toString()).toContain('<pre class="brush: js; html-script: true;">src</pre>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('h', function(){
|
||||
|
||||
it('should render using function', function(){
|
||||
var cbThis;
|
||||
var cdValue;
|
||||
dom.h('heading', 'content', function(value){
|
||||
cbThis = this;
|
||||
cbValue = value;
|
||||
});
|
||||
expect(cbThis).toEqual(dom);
|
||||
expect(cbValue).toEqual('content');
|
||||
});
|
||||
|
||||
it('should update heading numbers', function(){
|
||||
dom.h('heading', function(){
|
||||
this.html('<h1>sub-heading</h1>');
|
||||
});
|
||||
expect(dom.toString()).toContain('<h1>heading</h1>');
|
||||
expect(dom.toString()).toContain('<h2>sub-heading</h2>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,454 @@
|
||||
var ngdoc = require('ngdoc.js');
|
||||
var DOM = require('dom.js').DOM;
|
||||
|
||||
describe('ngdoc', function(){
|
||||
var Doc = ngdoc.Doc;
|
||||
var dom;
|
||||
|
||||
beforeEach(function(){
|
||||
dom = new DOM();
|
||||
this.addMatchers({
|
||||
toContain: function(text) {
|
||||
this.actual = this.actual.toString();
|
||||
return this.actual.indexOf(text) > -1;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Doc', function(){
|
||||
describe('metadata', function(){
|
||||
|
||||
it('should find keywords', function(){
|
||||
expect(new Doc('\nHello: World! @ignore. $abc').keywords()).toEqual('$abc hello world');
|
||||
expect(new Doc('The `ng:class-odd` and').keywords()).toEqual('and ng:class-odd the');
|
||||
});
|
||||
|
||||
it('should have shortName', function(){
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng:c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].shortName).toEqual('c');
|
||||
expect(ngdoc.metadata([d2])[0].shortName).toEqual('ng:c');
|
||||
expect(ngdoc.metadata([d3])[0].shortName).toEqual('more text');
|
||||
});
|
||||
|
||||
it('should have depth information', function(){
|
||||
var d1 = new Doc('@name a.b.c').parse();
|
||||
var d2 = new Doc('@name a.b.ng:c').parse();
|
||||
var d3 = new Doc('@name some text: more text').parse();
|
||||
expect(ngdoc.metadata([d1])[0].depth).toEqual(2);
|
||||
expect(ngdoc.metadata([d2])[0].depth).toEqual(2);
|
||||
expect(ngdoc.metadata([d3])[0].depth).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('parse', function(){
|
||||
it('should convert @names into properties', function(){
|
||||
var doc = new Doc('\n@name name\n@desc\ndesc\ndesc2\n@dep\n');
|
||||
doc.parse();
|
||||
expect(doc.name).toEqual('name');
|
||||
expect(doc.desc).toEqual('desc\ndesc2');
|
||||
expect(doc.dep).toEqual('');
|
||||
});
|
||||
|
||||
it('should parse parameters', function(){
|
||||
var doc = new Doc(
|
||||
'@param {*} a short\n' +
|
||||
'@param {Type} b med\n' +
|
||||
'@param {Class=} [c=2] long\nline');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([
|
||||
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse return', function(){
|
||||
var doc = new Doc('@returns {Type} text *bold*.');
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({
|
||||
type: 'Type',
|
||||
description: '<p>text <em>bold</em>.</p>'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not remove extra line breaks', function(){
|
||||
var doc = new Doc('@example\nA\n\nB');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('A\n\nB');
|
||||
});
|
||||
|
||||
it('should parse filename', function(){
|
||||
var doc = new Doc('@name friendly name', 'docs/a.b.ngdoc', 1);
|
||||
doc.parse(0);
|
||||
expect(doc.id).toEqual('a.b');
|
||||
expect(doc.name).toEqual('friendly name');
|
||||
});
|
||||
|
||||
it('should escape <doc:source> element', function(){
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:source>\n<>\n</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<doc:source>\n<>\n</doc:source></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should escape <doc:scenario> element', function(){
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
describe('sorting', function(){
|
||||
function property(name) {
|
||||
return function(obj) {return obj[name];};
|
||||
}
|
||||
function noop(){}
|
||||
function doc(type, name){
|
||||
return {
|
||||
id: name,
|
||||
ngdoc: type,
|
||||
keywords: noop
|
||||
};
|
||||
}
|
||||
|
||||
var angular_widget = doc('overview', 'angular.widget');
|
||||
var angular_x = doc('function', 'angular.x');
|
||||
var angular_y = doc('property', 'angular.y');
|
||||
|
||||
it('should put angular.fn() in front of angular.widget, etc', function(){
|
||||
expect(ngdoc.metadata([angular_widget, angular_y, angular_x]).map(property('id')))
|
||||
.toEqual(['angular.x', 'angular.y', 'angular.widget' ]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('scenario', function(){
|
||||
it('should render from @example/@scenario and <doc:example>', function(){
|
||||
var doc = new Doc(
|
||||
'@id id\n' +
|
||||
'@description <doc:example><doc:scenario>scenario0</doc:scenario></doc:example>' +
|
||||
'@example exempleText\n' +
|
||||
'@scenario scenario1\n' +
|
||||
'@scenario scenario2').parse();
|
||||
expect(ngdoc.scenarios([doc])).toContain('describe("id"');
|
||||
expect(ngdoc.scenarios([doc])).toContain('navigateTo("index.html#!id")');
|
||||
expect(ngdoc.scenarios([doc])).toContain('\n scenario0\n');
|
||||
expect(ngdoc.scenarios([doc])).toContain('\n scenario1\n');
|
||||
expect(ngdoc.scenarios([doc])).toContain('\n scenario2\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('markdown', function(){
|
||||
var markdown = ngdoc.markdown;
|
||||
|
||||
it('should replace angular in markdown', function(){
|
||||
expect(markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
|
||||
it('should not replace anything in <pre>, but escape the html escape the content', function(){
|
||||
expect(markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x</p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' +
|
||||
'<b>angular</b>.k\n' +
|
||||
'</pre></div>' +
|
||||
'<p>asdf x</p>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(markdown('<pre>x</pre># One<pre>b</pre>')).
|
||||
toMatch('</div><h1>One</h1><div');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('trim', function(){
|
||||
var trim = ngdoc.trim;
|
||||
it('should remove leading/trailing space', function(){
|
||||
expect(trim(' \nabc\n ')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should remove leading space on every line', function(){
|
||||
expect(trim('\n 1\n 2\n 3\n')).toEqual('1\n 2\n 3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('merge', function(){
|
||||
it('should merge child with parent', function(){
|
||||
var parent = new Doc({name:'angular.service.abc'});
|
||||
var methodA = new Doc({name:'methodA', methodOf:'angular.service.abc'});
|
||||
var methodB = new Doc({name:'methodB', methodOf:'angular.service.abc'});
|
||||
var propA = new Doc({name:'propA', propertyOf:'angular.service.abc'});
|
||||
var propB = new Doc({name:'propB', propertyOf:'angular.service.abc'});
|
||||
;var docs = [methodB, methodA, propB, propA, parent]; // keep wrong order;
|
||||
ngdoc.merge(docs);
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].name).toEqual('angular.service.abc');
|
||||
expect(docs[0].methods).toEqual([methodA, methodB]);
|
||||
expect(docs[0].properties).toEqual([propA, propB]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
describe('TAG', function(){
|
||||
describe('@param', function(){
|
||||
it('should parse with no default', function(){
|
||||
var doc = new Doc('@param {(number|string)} number Number \n to format.');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'number',
|
||||
optional: false,
|
||||
'default' : undefined,
|
||||
description : '<p>Number \n to format.</p>' }]);
|
||||
});
|
||||
|
||||
it('should parse with default and optional', function(){
|
||||
var doc = new Doc('@param {(number|string)=} [fractionSize=2] desc');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'fractionSize',
|
||||
optional: true,
|
||||
'default' : '2',
|
||||
description : '<p>desc</p>' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@requires', function() {
|
||||
it('should parse more @requires tag into array', function() {
|
||||
var doc = new Doc('@requires $service\n@requires $another');
|
||||
doc.parse();
|
||||
expect(doc.requires).toEqual(['$service', '$another']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@property', function() {
|
||||
it('should parse @property tags into array', function() {
|
||||
var doc = new Doc("@property {type} name1 desc\n@property {type} name2 desc");
|
||||
doc.parse();
|
||||
expect(doc.properties.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should parse @property with only name', function() {
|
||||
var doc = new Doc("@property fake");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('fake');
|
||||
});
|
||||
|
||||
it('should parse @property with optional type', function() {
|
||||
var doc = new Doc("@property {string} name");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('string');
|
||||
});
|
||||
|
||||
it('should parse @property with optional description', function() {
|
||||
var doc = new Doc("@property name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
it('should parse @property with type and description both', function() {
|
||||
var doc = new Doc("@property {bool} name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('bool');
|
||||
expect(doc.properties[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@returns', function() {
|
||||
it('should not parse @returns without type', function() {
|
||||
var doc = new Doc("@returns lala");
|
||||
expect(doc.parse).toThrow();
|
||||
});
|
||||
|
||||
it('should parse @returns with type and description', function() {
|
||||
var doc = new Doc("@returns {string} descrip tion");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip <em>tion</em></p>'});
|
||||
});
|
||||
|
||||
it('should support multiline content', function() {
|
||||
var doc = new Doc("@returns {string} description\n new line\n another line");
|
||||
doc.parse();
|
||||
expect(doc.returns).
|
||||
toEqual({type: 'string', description: '<p>description\n new line\n another line</p>'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('@description', function(){
|
||||
it('should support pre blocks', function(){
|
||||
var doc = new Doc("@description <pre>abc</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
var doc = new Doc("@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>' +
|
||||
'<h1>bah</h1>\n\n' +
|
||||
'<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>');
|
||||
|
||||
});
|
||||
|
||||
it('should support nested @link annotations with or without description', function() {
|
||||
var doc = new Doc("@description " +
|
||||
'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' +
|
||||
'dad{@link angular.foo}\n\n' +
|
||||
'{@link angular.directive.ng:foo ng:foo}');
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toContain('foo <a href="#!angular.foo"><code>angular.foo</code></a>');
|
||||
expect(doc.description).
|
||||
toContain('da <a href="#!angular.foo"><code>bar foo bar</code></a>');
|
||||
expect(doc.description).
|
||||
toContain('dad<a href="#!angular.foo"><code>angular.foo</code></a>');
|
||||
expect(doc.description).
|
||||
toContain('<a href="#!angular.directive.ng:foo"><code>ng:foo</code></a>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@example', function(){
|
||||
it('should not remove {{}}', function(){
|
||||
var doc = new Doc('@example text {{ abc }}');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('text {{ abc }}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@exampleDescription', function(){
|
||||
it('should render example description', function(){
|
||||
var doc = new Doc('@exampleDescription some\n text');
|
||||
doc.ngdoc = "filter";
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<p>some\n text');
|
||||
});
|
||||
|
||||
it('should alias @exampleDescription to @exampleDesc', function(){
|
||||
var doc = new Doc('@exampleDesc some\n text');
|
||||
doc.ngdoc = "filter";
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<p>some\n text');
|
||||
});
|
||||
|
||||
it('should render description in related method', function(){
|
||||
var doc = new Doc();
|
||||
doc.ngdoc = 'service';
|
||||
doc.methods = [new Doc('@ngdoc method\n@exampleDescription MDesc\n@example MExmp').parse()];
|
||||
doc.properties = [new Doc('@ngdoc property\n@exampleDescription PDesc\n@example PExmp').parse()];
|
||||
expect(doc.html()).toContain('<p>MDesc</p><div ng:non-bindable=""><pre class="brush: js; html-script: true;">MExmp</pre>');
|
||||
expect(doc.html()).toContain('<p>PDesc</p><div ng:non-bindable=""><pre class="brush: js; html-script: true;">PExmp</pre>');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@depricated', function() {
|
||||
it('should parse @depricated', function() {
|
||||
var doc = new Doc('@deprecated Replaced with foo.');
|
||||
doc.parse();
|
||||
expect(doc.deprecated).toBe('Replaced with foo.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@this', function(){
|
||||
it('should render @this', function() {
|
||||
var doc = new Doc('@this I am self.');
|
||||
doc.ngdoc = 'filter';
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div>' +
|
||||
'<p>I am self.</p>' +
|
||||
'</div>\n');
|
||||
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
|
||||
'<div><p>I am self.</p></div>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('usage', function(){
|
||||
describe('overview', function(){
|
||||
it('should supress description heading', function(){
|
||||
var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext');
|
||||
doc.parse();
|
||||
expect(doc.html()).toContain('text');
|
||||
expect(doc.html()).toContain('<h2>heading</h2>');
|
||||
expect(doc.html()).not.toContain('Description');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('filter', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'formatter',
|
||||
shortName:'myFilter',
|
||||
param: [
|
||||
{name:'a'},
|
||||
{name:'b'}
|
||||
]
|
||||
});
|
||||
doc.html_usage_filter(dom);
|
||||
expect(dom).toContain('myFilter_expression | myFilter:b');
|
||||
expect(dom).toContain('angular.filter.myFilter(a, b)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validator', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'validator',
|
||||
shortName:'myValidator',
|
||||
param: [
|
||||
{name:'a'},
|
||||
{name:'b'}
|
||||
]
|
||||
});
|
||||
doc.html_usage_validator(dom);
|
||||
expect(dom).toContain('ng:validate="myValidator:b"');
|
||||
expect(dom).toContain('angular.validator.myValidator(a, b)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatter', function(){
|
||||
it('should format', function(){
|
||||
var doc = new Doc({
|
||||
ngdoc:'formatter',
|
||||
shortName:'myFormatter',
|
||||
param: [
|
||||
{name:'a'},
|
||||
]
|
||||
});
|
||||
doc.html_usage_formatter(dom);
|
||||
expect(dom).toContain('ng:format="myFormatter:a"');
|
||||
expect(dom).toContain('var userInputString = angular.formatter.myFormatter.format(modelValue, a);');
|
||||
expect(dom).toContain('var modelValue = angular.formatter.myFormatter.parse(userInputString, a);');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
var SiteMap = require('sitemap.js').SiteMap;
|
||||
var Doc = require('ngdoc.js').Doc;
|
||||
|
||||
|
||||
describe('sitemap', function(){
|
||||
it('should render empty sitemap', function(){
|
||||
var map = new SiteMap([]);
|
||||
expect(map.render()).toEqual([
|
||||
'<?xml version="1.0" encoding="UTF-8"?>',
|
||||
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
|
||||
'</sitemapindex>', ''].join('\n'));
|
||||
});
|
||||
|
||||
it('should render ngdoc url', function(){
|
||||
var map = new SiteMap([new Doc({name: 'a.b.c<>\'"&'})]);
|
||||
expect(map.render()).toContain([
|
||||
' <url>',
|
||||
'<loc>http://docs.angularjs.org/#!a.b.c<>'"&</loc>',
|
||||
'<changefreq>weekly</changefreq>',
|
||||
'</url>'].join(''));
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
if (global.jasmine) return;
|
||||
|
||||
require.paths.push(__dirname + "/../../lib");
|
||||
require.paths.push(__dirname + '/../src');
|
||||
var jasmine = require('jasmine-1.0.1');
|
||||
var sys = require('util');
|
||||
|
||||
for(var key in jasmine) {
|
||||
global[key] = jasmine[key];
|
||||
}
|
||||
|
||||
//Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
});
|
||||
// PATCH
|
||||
if (e) {
|
||||
expectationResult.trace = e;
|
||||
}
|
||||
this.results_.addResult(expectationResult);
|
||||
};
|
||||
|
||||
|
||||
|
||||
var isVerbose = false;
|
||||
var showColors = true;
|
||||
process.argv.forEach(function(arg){
|
||||
switch(arg) {
|
||||
case '--color': showColors = true; break;
|
||||
case '--noColor': showColors = false; break;
|
||||
case '--verbose': isVerbose = true; break;
|
||||
}
|
||||
});
|
||||
|
||||
jasmine.executeSpecsInFolder(__dirname, function(runner, log){
|
||||
process.exit(runner.results().failedCount);
|
||||
}, isVerbose, showColors);
|
||||
@@ -0,0 +1,18 @@
|
||||
var writer = require('writer.js');
|
||||
describe('writer', function(){
|
||||
describe('toString', function(){
|
||||
var toString = writer.toString;
|
||||
|
||||
it('should merge string', function(){
|
||||
expect(toString('abc')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should merge obj', function(){
|
||||
expect(toString({a:1})).toEqual('{"a":1}');
|
||||
});
|
||||
|
||||
it('should merge array', function(){
|
||||
expect(toString(['abc',{}])).toEqual('abc{}');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
require.paths.push("./lib");
|
||||
var jasmine = require('jasmine-1.0.1');
|
||||
var sys = require('sys');
|
||||
|
||||
for(var key in jasmine) {
|
||||
global[key] = jasmine[key];
|
||||
}
|
||||
|
||||
var isVerbose = false;
|
||||
var showColors = true;
|
||||
process.argv.forEach(function(arg){
|
||||
switch(arg) {
|
||||
case '--color': showColors = true; break;
|
||||
case '--noColor': showColors = false; break;
|
||||
case '--verbose': isVerbose = true; break;
|
||||
}
|
||||
});
|
||||
|
||||
jasmine.executeSpecsInFolder(__dirname + '/spec', function(runner, log){
|
||||
process.exit(runner.results().failedCount);
|
||||
}, isVerbose, showColors);
|
||||
@@ -0,0 +1,31 @@
|
||||
exports.SiteMap = SiteMap;
|
||||
|
||||
/**
|
||||
* @see http://www.sitemaps.org/protocol.php
|
||||
*
|
||||
* @param docs
|
||||
* @returns {SiteMap}
|
||||
*/
|
||||
function SiteMap(docs){
|
||||
this.render = function(){
|
||||
var map = [];
|
||||
map.push('<?xml version="1.0" encoding="UTF-8"?>');
|
||||
map.push('<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">');
|
||||
docs.forEach(function(doc){
|
||||
map.push(' <url><loc>http://docs.angularjs.org/#!' +
|
||||
encode(doc.name) + '</loc><changefreq>weekly</changefreq></url>');
|
||||
});
|
||||
map.push('</sitemapindex>');
|
||||
map.push('');
|
||||
return map.join('\n');
|
||||
};
|
||||
|
||||
function encode(text){
|
||||
return text
|
||||
.replace(/&/mg, '&')
|
||||
.replace(/</mg, '<')
|
||||
.replace(/>/mg, '>')
|
||||
.replace(/'/mg, ''')
|
||||
.replace(/"/mg, '"');
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@ function noop(){}
|
||||
|
||||
function chain(delegateFn, explicitDone){
|
||||
var onDoneFn = noop;
|
||||
var onErrorFn = noop;
|
||||
var onErrorFn = function(e){
|
||||
console.error(e.stack || e);
|
||||
process.exit(-1);
|
||||
};
|
||||
var waitForCount = 1;
|
||||
delegateFn = delegateFn || noop;
|
||||
var stackError = new Error('capture stack');
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* DOM generation class
|
||||
*/
|
||||
|
||||
exports.DOM = DOM;
|
||||
exports.htmlEscape = htmlEscape;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
function htmlEscape(text){
|
||||
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
|
||||
function DOM(){
|
||||
this.out = [];
|
||||
this.headingDepth = 0;
|
||||
}
|
||||
|
||||
var INLINE_TAGS = {
|
||||
i: true,
|
||||
b: true
|
||||
};
|
||||
|
||||
DOM.prototype = {
|
||||
toString: function() {
|
||||
return this.out.join('');
|
||||
},
|
||||
|
||||
text: function(content) {
|
||||
if (typeof content == "string") {
|
||||
this.out.push(htmlEscape(content));
|
||||
} else if (typeof content == 'function') {
|
||||
content.call(this, this);
|
||||
} else if (content instanceof Array) {
|
||||
this.ul(content);
|
||||
}
|
||||
},
|
||||
|
||||
html: function(html) {
|
||||
if (html) {
|
||||
var headingDepth = this.headingDepth;
|
||||
for ( var i = 10; i > 0; --i) {
|
||||
html = html
|
||||
.replace(new RegExp('(<\/?h)' + i + '(>)', 'gm'), function(all, start, end){
|
||||
return start + (i + headingDepth) + end;
|
||||
});
|
||||
}
|
||||
this.out.push(html);
|
||||
}
|
||||
},
|
||||
|
||||
tag: function(name, attr, text) {
|
||||
if (!text) {
|
||||
text = attr;
|
||||
attr = {};
|
||||
if (name == 'code')
|
||||
attr['ng:non-bindable'] = '';
|
||||
}
|
||||
this.out.push('<' + name);
|
||||
for(var key in attr) {
|
||||
this.out.push(" " + key + '="' + attr[key] + '"');
|
||||
}
|
||||
this.out.push('>');
|
||||
this.text(text);
|
||||
this.out.push('</' + name + '>');
|
||||
if (!INLINE_TAGS[name])
|
||||
this.out.push('\n');
|
||||
},
|
||||
|
||||
code: function(text) {
|
||||
this.tag('div', {'ng:non-bindable':''}, function(){
|
||||
this.tag('pre', {'class':"brush: js; html-script: true;"}, text);
|
||||
});
|
||||
},
|
||||
|
||||
example: function(description, source, scenario) {
|
||||
if (description || source || scenario) {
|
||||
this.h('Example', function(){
|
||||
if (description)
|
||||
this.html(description);
|
||||
if (scenario === false) {
|
||||
this.code(source);
|
||||
} else {
|
||||
this.tag('doc:example', function(){
|
||||
if (source) this.tag('doc:source', source);
|
||||
if (scenario) this.tag('doc:scenario', scenario);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
h: function(heading, content, fn){
|
||||
if (content==undefined || content && content.legth == 0) return;
|
||||
this.headingDepth++;
|
||||
this.tag('h' + this.headingDepth, heading);
|
||||
var className = typeof heading == 'string'
|
||||
? {'class': heading.toLowerCase().replace(/[^\d\w_]/mg, '-').replace(/-+/gm, '-')}
|
||||
: null;
|
||||
if (content instanceof Array) {
|
||||
this.ul(content, className, fn);
|
||||
} else if (fn) {
|
||||
this.tag('div', className, function(){
|
||||
fn.call(this, content);
|
||||
});
|
||||
} else {
|
||||
this.tag('div', className, content);
|
||||
}
|
||||
this.headingDepth--;
|
||||
},
|
||||
|
||||
h1: function(attr, text) {
|
||||
this.tag('h1', attr, text);
|
||||
},
|
||||
|
||||
h2: function(attr, text) {
|
||||
this.tag('h2', attr, text);
|
||||
},
|
||||
|
||||
h3: function(attr, text) {
|
||||
this.tag('h3', attr, text);
|
||||
},
|
||||
|
||||
p: function(attr, text) {
|
||||
this.tag('p', attr, text);
|
||||
},
|
||||
|
||||
ul: function(list, attr, fn) {
|
||||
if (typeof attr == 'function') {
|
||||
fn = attr;
|
||||
attr = {};
|
||||
}
|
||||
this.tag('ul', attr, function(dom){
|
||||
list.forEach(function(item){
|
||||
dom.out.push('<li>');
|
||||
dom.text(fn ? fn(item) : item);
|
||||
dom.out.push('</li>\n');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
require.paths.push(__dirname);
|
||||
require.paths.push('lib');
|
||||
var reader = require('reader.js'),
|
||||
ngdoc = require('ngdoc.js'),
|
||||
writer = require('writer.js'),
|
||||
callback = require('callback.js'),
|
||||
SiteMap = require('SiteMap.js').SiteMap;
|
||||
|
||||
var docs = [];
|
||||
var start;
|
||||
var work = callback.chain(function(){
|
||||
start = now();
|
||||
console.log('Generating Angular Reference Documentation...');
|
||||
reader.collect(work.waitMany(function(text, file, line){
|
||||
var doc = new ngdoc.Doc(text, file, line);
|
||||
docs.push(doc);
|
||||
doc.parse();
|
||||
}));
|
||||
});
|
||||
var writes = callback.chain(function(){
|
||||
ngdoc.merge(docs);
|
||||
docs.forEach(function(doc){
|
||||
writer.output(doc.id + '.html', doc.html(), writes.waitFor());
|
||||
});
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';'], writes.waitFor());
|
||||
writer.copyImages(writes.waitFor());
|
||||
writer.copy('index.html', writes.waitFor());
|
||||
writer.copy('docs.js', writes.waitFor());
|
||||
writer.copy('docs.css', writes.waitFor());
|
||||
writer.copy('doc_widgets.js', writes.waitFor());
|
||||
writer.copy('doc_widgets.css', writes.waitFor());
|
||||
writer.copy('docs-scenario.html', writes.waitFor());
|
||||
writer.output('docs-scenario.js', ngdoc.scenarios(docs), writes.waitFor());
|
||||
writer.output('sitemap.xml', new SiteMap(docs).render(), writes.waitFor());
|
||||
writer.output('robots.txt', 'Sitemap: http://docs.angularjs.org/sitemap.xml\n', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shBrushJScript.js', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shBrushXml.js', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shCore.css', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shCore.js', writes.waitFor());
|
||||
writer.copy('syntaxhighlighter/shThemeDefault.css', writes.waitFor());
|
||||
writer.copy('jquery.min.js', writes.waitFor());
|
||||
});
|
||||
writes.onDone(function(){
|
||||
console.log('DONE. Generated ' + docs.length + ' pages in ' +
|
||||
(now()-start) + 'ms.' );
|
||||
});
|
||||
work.onDone(writes);
|
||||
writer.makeDir('build/docs/syntaxhighlighter', work);
|
||||
|
||||
///////////////////////////////////
|
||||
function now(){ return new Date().getTime(); }
|
||||
@@ -0,0 +1,629 @@
|
||||
/**
|
||||
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
||||
*/
|
||||
|
||||
var Showdown = require('showdown').Showdown;
|
||||
var DOM = require('dom.js').DOM;
|
||||
var htmlEscape = require('dom.js').htmlEscape;
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
exports.markdown = markdown;
|
||||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
exports.scenarios = scenarios;
|
||||
exports.merge = merge;
|
||||
exports.Doc = Doc;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function Doc(text, file, line) {
|
||||
if (typeof text == 'object') {
|
||||
for ( var key in text) {
|
||||
this[key] = text[key];
|
||||
}
|
||||
} else {
|
||||
this.text = text;
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
}
|
||||
}
|
||||
Doc.METADATA_IGNORE = (function(){
|
||||
var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8');
|
||||
return words.toString().split(/[,\s\n\r]+/gm);
|
||||
})();
|
||||
|
||||
|
||||
|
||||
Doc.prototype = {
|
||||
keywords: function keywords(){
|
||||
var keywords = {};
|
||||
Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; });
|
||||
var words = [];
|
||||
var tokens = this.text.toLowerCase().split(/[,\.\`\'\"\s]+/mg);
|
||||
tokens.forEach(function(key){
|
||||
var match = key.match(/^(([\$\_a-z]|ng\:)[\w\_\-]{2,})/);
|
||||
if (match){
|
||||
key = match[1];
|
||||
if (!keywords[key]) {
|
||||
keywords[key] = true;
|
||||
words.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
words.sort();
|
||||
return words.join(' ');
|
||||
},
|
||||
|
||||
parse: function(){
|
||||
var atName;
|
||||
var atText;
|
||||
var match;
|
||||
var self = this;
|
||||
this.scenarios = [];
|
||||
this.requires = [];
|
||||
this.param = [];
|
||||
this.properties = [];
|
||||
this.methods = [];
|
||||
self.text.split(NEW_LINE).forEach(function(line){
|
||||
if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {
|
||||
// we found @name ...
|
||||
// if we have existing name
|
||||
flush();
|
||||
atName = match[1];
|
||||
atText = [];
|
||||
if(match[3]) atText.push(match[3]);
|
||||
} else {
|
||||
if (atName) {
|
||||
atText.push(line);
|
||||
}
|
||||
}
|
||||
});
|
||||
flush();
|
||||
this.shortName = (this.name || '').split(/[\.#]/).pop();
|
||||
this.id = this.id // if we have an id just use it
|
||||
|| (((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) // try to extract it from file name
|
||||
|| this.name; // default to name
|
||||
this.description = markdown(this.description);
|
||||
this['this'] = markdown(this['this']);
|
||||
this.exampleDescription = markdown(this.exampleDescription || this.exampleDesc);
|
||||
return this;
|
||||
|
||||
function flush(){
|
||||
if (atName) {
|
||||
var text = trim(atText.join('\n'));
|
||||
if (atName == 'param') {
|
||||
var match = text.match(/^{([^}=]+)(=)?}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 12 2 34 4 5 5 6 6 3 7 7
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'param' format: " + text);
|
||||
}
|
||||
var param = {
|
||||
name: match[5] || match[4],
|
||||
description:markdown(text.replace(match[0], match[7])),
|
||||
type: match[1],
|
||||
optional: !!match[2],
|
||||
'default':match[6]
|
||||
};
|
||||
self.param.push(param);
|
||||
} else if (atName == 'returns') {
|
||||
var match = text.match(/^{([^}=]+)}\s+(.*)/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'returns' format: " + text);
|
||||
}
|
||||
self.returns = {
|
||||
type: match[1],
|
||||
description: markdown(text.replace(match[0], match[2]))
|
||||
};
|
||||
} else if(atName == 'description') {
|
||||
text.replace(/<doc:scenario>([\s\S]*)<\/doc:scenario>/mi,
|
||||
function(_, scenario){
|
||||
self.scenarios.push(scenario);
|
||||
});
|
||||
self.description = text;
|
||||
} else if(atName == 'requires') {
|
||||
self.requires.push(text);
|
||||
} else if(atName == 'scenario') {
|
||||
self.scenarios.push(text);
|
||||
} else if(atName == 'property') {
|
||||
var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'property' format: " + text);
|
||||
}
|
||||
var property = {
|
||||
type: match[2],
|
||||
name: match[3],
|
||||
description: match[5] || ''
|
||||
};
|
||||
self.properties.push(property);
|
||||
} else {
|
||||
self[atName] = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var dom = new DOM(),
|
||||
self = this;
|
||||
|
||||
dom.h(this.name, function(){
|
||||
notice('workInProgress', 'Work in Progress',
|
||||
'This page is currently being revised. It might be incomplete or contain inaccuracies.');
|
||||
notice('deprecated', 'Deprecated API', self.deprecated);
|
||||
|
||||
(self['html_usage_' + self.ngdoc] || function(){
|
||||
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
|
||||
}).call(self, dom);
|
||||
|
||||
dom.example(self.exampleDescription, self.example, self.scenario);
|
||||
});
|
||||
|
||||
return dom.toString();
|
||||
|
||||
//////////////////////////
|
||||
|
||||
function notice(name, legend, msg){
|
||||
if (self[name] == undefined) return;
|
||||
dom.tag('fieldset', {'class':name}, function(dom){
|
||||
dom.tag('legend', legend);
|
||||
dom.text(msg);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
html_usage_parameters: function(dom) {
|
||||
dom.h('Parameters', this.param, function(param){
|
||||
dom.tag('code', function(){
|
||||
dom.text(param.name);
|
||||
if (param.optional) {
|
||||
dom.tag('i', function(){
|
||||
dom.text('(optional');
|
||||
if(param['default']) {
|
||||
dom.text('=' + param['default']);
|
||||
}
|
||||
dom.text(')');
|
||||
});
|
||||
}
|
||||
dom.text(' – {');
|
||||
dom.text(param.type);
|
||||
dom.text('} – ');
|
||||
});
|
||||
dom.html(param.description);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_returns: function(dom) {
|
||||
var self = this;
|
||||
if (self.returns) {
|
||||
dom.h('Returns', function(){
|
||||
dom.tag('code', '{' + self.returns.type + '}');
|
||||
dom.text('– ');
|
||||
dom.html(self.returns.description);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
html_usage_this: function(dom) {
|
||||
var self = this;
|
||||
if (self['this']) {
|
||||
dom.h(function(dom){
|
||||
dom.html("Method's <code>this</code>");
|
||||
}, function(dom){
|
||||
dom.html(self['this']);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
html_usage_function: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.text(self.name);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(');');
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_directive: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function(){
|
||||
dom.text('<' + self.element + ' ');
|
||||
dom.text(self.shortName);
|
||||
if (self.param.length) {
|
||||
dom.text('="' + self.param[0].name + '"');
|
||||
}
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + self.element + '>');
|
||||
});
|
||||
self.html_usage_parameters(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_filter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.text('{{ ');
|
||||
dom.text(self.shortName);
|
||||
dom.text('_expression | ');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', true);
|
||||
dom.text(' }}');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.text('angular.filter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_formatter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
if (self.inputType=='select')
|
||||
dom.text('<select name="bindExpression"');
|
||||
else
|
||||
dom.text('<input type="text" name="bindExpression"');
|
||||
dom.text(' ng:format="');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', false, true);
|
||||
dom.text('">');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('var userInputString = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.format(modelValue');
|
||||
self.parameters(dom, ', ', false, true);
|
||||
dom.text(');');
|
||||
dom.text('\n');
|
||||
dom.text('var modelValue = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.parse(userInputString');
|
||||
self.parameters(dom, ', ', false, true);
|
||||
dom.text(');');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_validator: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
dom.text('<input type="text" ng:validate="');
|
||||
dom.text(self.shortName);
|
||||
self.parameters(dom, ':', true);
|
||||
dom.text('"/>');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('angular.validator.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
self.parameters(dom, ', ');
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_this(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_widget: function(dom){
|
||||
var self = this;
|
||||
dom.h('Description', self.description, dom.html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
if (self.shortName.match(/^@/)) {
|
||||
dom.text('<');
|
||||
dom.text(self.element);
|
||||
dom.text(' ');
|
||||
dom.text(self.shortName.substring(1));
|
||||
if (self.param.length) {
|
||||
dom.text('="');
|
||||
dom.text(self.param[0].name);
|
||||
dom.text('"');
|
||||
}
|
||||
dom.text('>\n ...\n</');
|
||||
dom.text(self.element);
|
||||
dom.text('>');
|
||||
} else {
|
||||
dom.text('<');
|
||||
dom.text(self.shortName);
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (param.optional) {
|
||||
dom.text(' [' + param.name + '="..."]');
|
||||
} else {
|
||||
dom.text(' ' + param.name + '="..."');
|
||||
}
|
||||
});
|
||||
dom.text('></');
|
||||
dom.text(self.shortName);
|
||||
dom.text('>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_overview: function(dom){
|
||||
dom.html(this.description);
|
||||
},
|
||||
|
||||
html_usage_service: function(dom){
|
||||
dom.h('Description', this.description, dom.html);
|
||||
dom.h('Dependencies', this.requires);
|
||||
|
||||
dom.h('Methods', this.methods, function(method){
|
||||
var signature = (method.param || []).map(property('name'));
|
||||
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){
|
||||
dom.html(method.description);
|
||||
method.html_usage_parameters(dom);
|
||||
dom.example(method.exampleDescription, method.example, false);
|
||||
});
|
||||
});
|
||||
dom.h('Properties', this.properties, function(property){
|
||||
dom.h(property.name, function(){
|
||||
dom.text(property.description);
|
||||
dom.example(property.exampleDescription, property.example, false);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
parameters: function(dom, separator, skipFirst, prefix) {
|
||||
var sep = prefix ? separator : '';
|
||||
(this.param||[]).forEach(function(param, i){
|
||||
if (!(skipFirst && i==0)) {
|
||||
if (param.optional) {
|
||||
dom.text('[' + sep + param.name + ']');
|
||||
} else {
|
||||
dom.text(sep + param.name);
|
||||
}
|
||||
}
|
||||
sep = separator;
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function markdown (text) {
|
||||
if (!text) return text;
|
||||
var parts = text.split(/(<pre>[\s\S]*?<\/pre>|<doc:example>[\s\S]*?<\/doc:example>)/),
|
||||
match;
|
||||
|
||||
parts.forEach(function(text, i){
|
||||
if (text.match(/^<pre>/)) {
|
||||
text = text.replace(/^<pre>([\s\S]*)<\/pre>/mi, function(_, content){
|
||||
return '<div ng:non-bindable><pre class="brush: js; html-script: true;">' +
|
||||
content.replace(/</g, '<').replace(/>/g, '>') +
|
||||
'</pre></div>';
|
||||
});
|
||||
} else if (text.match(/^<doc:example>/)) {
|
||||
text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi,
|
||||
function(_, before, content, after){
|
||||
return before + htmlEscape(content) + after;
|
||||
});
|
||||
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
|
||||
function(_, before, content, after){
|
||||
return before + htmlEscape(content) + after;
|
||||
});
|
||||
} else {
|
||||
text = text.replace(/<angular\/>/gm, '<tt><angular/></tt>');
|
||||
text = text.replace(/{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/gm,
|
||||
function(_all, url, _2, _3, title){
|
||||
return '<a href="#!' + url + '">'
|
||||
+ (url.match(/^angular\./) ? '<code>' : '')
|
||||
+ (title || url)
|
||||
+ (url.match(/^angular\./) ? '</code>' : '')
|
||||
+ '</a>';
|
||||
});
|
||||
text = new Showdown.converter().makeHtml(text);
|
||||
}
|
||||
parts[i] = text;
|
||||
});
|
||||
return parts.join('');
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function scenarios(docs){
|
||||
var specs = [];
|
||||
docs.forEach(function(doc){
|
||||
specs.push('describe("' + doc.id + '", function(){');
|
||||
specs.push(' beforeEach(function(){');
|
||||
specs.push(' browser().navigateTo("index.html#!' + doc.id + '");');
|
||||
specs.push(' });');
|
||||
specs.push('');
|
||||
doc.scenarios.forEach(function(scenario){
|
||||
specs.push(trim(scenario, ' '));
|
||||
specs.push('');
|
||||
});
|
||||
specs.push('});');
|
||||
specs.push('');
|
||||
if (doc.scenario) {
|
||||
}
|
||||
});
|
||||
return specs.join('\n');
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function metadata(docs){
|
||||
var words = [];
|
||||
docs.forEach(function(doc){
|
||||
var path = (doc.name || '').split(/(\.|\:\s+)/);
|
||||
for ( var i = 1; i < path.length; i++) {
|
||||
path.splice(i, 1);
|
||||
}
|
||||
var depth = path.length - 1;
|
||||
var shortName = path.pop();
|
||||
words.push({
|
||||
id: doc.id,
|
||||
name: doc.name,
|
||||
depth: depth,
|
||||
shortName: shortName,
|
||||
type: doc.ngdoc,
|
||||
keywords:doc.keywords()
|
||||
});
|
||||
});
|
||||
words.sort(keywordSort);
|
||||
return words;
|
||||
}
|
||||
|
||||
var KEYWORD_PRIORITY = {
|
||||
'.guide': 1,
|
||||
'.guide.overview': 1,
|
||||
'.angular': 7,
|
||||
'.angular.Array': 7,
|
||||
'.angular.Object': 7,
|
||||
'.angular.directive': 7,
|
||||
'.angular.filter': 7,
|
||||
'.angular.formatter': 7,
|
||||
'.angular.scope': 7,
|
||||
'.angular.service': 7,
|
||||
'.angular.validator': 7,
|
||||
'.angular.widget': 7
|
||||
};
|
||||
function keywordSort(a, b){
|
||||
function mangleName(doc) {
|
||||
var path = doc.id.split(/\./);
|
||||
var mangled = [];
|
||||
var partialName = '';
|
||||
path.forEach(function(name){
|
||||
partialName += '.' + name;
|
||||
mangled.push(KEYWORD_PRIORITY[partialName] || 5);
|
||||
mangled.push(name);
|
||||
});
|
||||
return mangled.join('.');
|
||||
}
|
||||
var nameA = mangleName(a);
|
||||
var nameB = mangleName(b);
|
||||
return nameA < nameB ? -1 : (nameA > nameB ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function trim(text, prefix) {
|
||||
var MAX = 9999;
|
||||
var empty = RegExp.prototype.test.bind(/^\s*$/);
|
||||
var lines = text.split('\n');
|
||||
var minIndent = MAX;
|
||||
prefix = prefix || '';
|
||||
lines.forEach(function(line){
|
||||
minIndent = Math.min(minIndent, indent(line));
|
||||
});
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = prefix + lines[i].substring(minIndent);
|
||||
}
|
||||
// remove leading lines
|
||||
while (empty(lines[0])) {
|
||||
lines.shift();
|
||||
}
|
||||
// remove trailing
|
||||
while (empty(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
return lines.join('\n');
|
||||
|
||||
function indent(line) {
|
||||
for(var i = 0; i < line.length; i++) {
|
||||
if (line.charAt(i) != ' ') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MAX;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function merge(docs){
|
||||
var byName = {};
|
||||
docs.forEach(function(doc){
|
||||
byName[doc.name] = doc;
|
||||
});
|
||||
for(var i=0; i<docs.length;) {
|
||||
if (findParent(docs[i], 'method') ||
|
||||
findParent(docs[i], 'property')) {
|
||||
docs.splice(i, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
function findParent(doc, name){
|
||||
var parentName = doc[name+'Of'];
|
||||
if (!parentName) return false;
|
||||
|
||||
var parent = byName[parentName];
|
||||
if (!parent)
|
||||
throw new Error("No parent named '" + parentName + "' for '" +
|
||||
doc.name + "' in @" + name + "Of.");
|
||||
|
||||
var listName = (name + 's').replace(/ys$/, 'ies');
|
||||
var list = parent[listName] = (parent[listName] || []);
|
||||
list.push(doc);
|
||||
list.sort(orderByName);
|
||||
return true;
|
||||
}
|
||||
|
||||
function orderByName(a, b){
|
||||
return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
function property(name) {
|
||||
return function(value){
|
||||
return value[name];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* All reading related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs'),
|
||||
callback = require('callback');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
function collect(callback){
|
||||
findJsFiles('src', callback.waitMany(function(file) {
|
||||
//console.log('reading', file, '...');
|
||||
findNgDocInJsFile(file, callback.waitMany(function(doc, line) {
|
||||
callback(doc, file, line);
|
||||
}));
|
||||
}));
|
||||
findNgDocInDir('docs/', callback.waitMany(callback));
|
||||
callback.done();
|
||||
}
|
||||
|
||||
function findJsFiles(dir, callback){
|
||||
fs.readdir(dir, callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
var path = dir + '/' + file;
|
||||
fs.lstat(path, callback.waitFor(function(err, stat){
|
||||
if (err) return this.error(err);
|
||||
if (stat.isDirectory())
|
||||
findJsFiles(path, callback.waitMany(callback));
|
||||
else if (/\.js$/.test(path))
|
||||
callback(path);
|
||||
}));
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInDir(directory, docNotify) {
|
||||
fs.readdir(directory, docNotify.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
//console.log('reading', directory + file, '...');
|
||||
if (!file.match(/\.ngdoc$/)) return;
|
||||
fs.readFile(directory + file, docNotify.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
docNotify(content.toString(), directory + file, 1);
|
||||
}));
|
||||
});
|
||||
docNotify.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInJsFile(file, callback) {
|
||||
fs.readFile(file, callback.waitFor(function(err, content){
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var text;
|
||||
var startingLine ;
|
||||
var match;
|
||||
var inDoc = false;
|
||||
lines.forEach(function(line, lineNumber){
|
||||
lineNumber++;
|
||||
// is the comment starting?
|
||||
if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) {
|
||||
line = match[1];
|
||||
inDoc = true;
|
||||
text = [];
|
||||
startingLine = lineNumber;
|
||||
}
|
||||
// are we done?
|
||||
if (inDoc && line.match(/\*\//)) {
|
||||
text = text.join('\n');
|
||||
text = text.replace(/^\n/, '');
|
||||
if (text.match(/@ngdoc/)){
|
||||
callback(text, startingLine);
|
||||
}
|
||||
doc = null;
|
||||
inDoc = false;
|
||||
}
|
||||
// is the comment add text
|
||||
if (inDoc){
|
||||
text.push(line.replace(/^\s*\*\s?/, ''));
|
||||
}
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports.collect = collect;
|
||||
@@ -32,4 +32,4 @@ li.doc-example-live {
|
||||
|
||||
div.syntaxhighlighter {
|
||||
padding-bottom: 1px !important; /* fix to remove unnecessary scrollbars http://is.gd/gSMgC */
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
(function(){
|
||||
|
||||
|
||||
var angularJsUrl;
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
var filename = /(.*\/)angular([^\/]*)/;
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
var src = scripts[j].src;
|
||||
if (src && src.match(filename)) {
|
||||
angularJsUrl = src;
|
||||
angularJsUrl = src.replace('docs.angularjs.org', 'code.angularjs.org');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var HTML_TEMPLATE =
|
||||
'<!doctype html>\n' +
|
||||
'<html xmlns:ng="http://angularjs.org">\n' +
|
||||
' <script type="text/javascript" ng:autobind\n' +
|
||||
' src="' + angularJsUrl + '"></script>\n' +
|
||||
' <script src="' + angularJsUrl + '" ng:autobind></script>\n' +
|
||||
' <body>\n' +
|
||||
'_HTML_SOURCE_\n' +
|
||||
' </body>\n' +
|
||||
@@ -33,15 +32,15 @@
|
||||
var tabs = angular.element(
|
||||
'<ul class="doc-example">' +
|
||||
'<li class="doc-example-heading"><h3>Source</h3></li>' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
code.hilite + ']; toolbar: false;"></pre></li>' +
|
||||
'<li class="doc-example-heading"><h3>Live Preview</h3></li>' +
|
||||
'<li class="doc-example-live">' + exampleSrc +'</li>' +
|
||||
'<li class="doc-example-heading"><h3>Scenario Test</h3></li>' +
|
||||
'<li class="doc-example-scenario"><pre class="brush: js">' + scenario.text() + '</pre></li>' +
|
||||
'</ul>');
|
||||
|
||||
|
||||
tabs.find('li.doc-example-source > pre').text(HTML_TEMPLATE.replace('_HTML_SOURCE_', code.html));
|
||||
|
||||
element.html('');
|
||||
@@ -54,22 +53,29 @@
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
|
||||
return function() {
|
||||
SyntaxHighlighter.highlight();
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
function indent(text) {
|
||||
var lines = text.split(/\n/);
|
||||
var lineNo = [];
|
||||
// remove any leading blank lines
|
||||
while (lines[0].match(/^\s*$/)) lines.shift();
|
||||
// remove any trailing blank lines
|
||||
while (lines[lines.length - 1].match(/^\s*$/)) lines.pop();
|
||||
var minIndent = 999;
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = ' ' + lines[i];
|
||||
lineNo.push(6 + i);
|
||||
var line = lines[0];
|
||||
var indent = line.match(/^\s*/)[0];
|
||||
if (indent !== line && indent.length < minIndent) {
|
||||
minIndent = indent.length;
|
||||
}
|
||||
}
|
||||
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = ' ' + lines[i].substring(minIndent);
|
||||
lineNo.push(5 + i);
|
||||
}
|
||||
return {html: lines.join('\n'), hilite: lineNo.join(',') };
|
||||
};
|
||||
|
||||
})();
|
||||
|
||||
})();
|
||||
@@ -7,4 +7,4 @@
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -0,0 +1,302 @@
|
||||
/* Common Style */
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
|
||||
/* Main Layout */
|
||||
|
||||
#header {
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
#sidebar,
|
||||
#main {
|
||||
position: absolute;
|
||||
top: 3.5em;
|
||||
bottom: 0;
|
||||
margin-top: 1px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
width: 13.8em;
|
||||
padding: 0.8em 0em 1.5em 0.8em;
|
||||
}
|
||||
|
||||
#main {
|
||||
left: 14.6em;
|
||||
right: 0;
|
||||
padding: 1em;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#api-list {
|
||||
position: absolute;
|
||||
top: 3em;
|
||||
bottom: 1em;
|
||||
overflow-y: scroll;
|
||||
padding-right: 0.8em;
|
||||
}
|
||||
|
||||
|
||||
/* App Header */
|
||||
|
||||
#header {
|
||||
background-color: #F2C200;
|
||||
border-bottom: 1px solid #957800;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
font-weight: normal;
|
||||
font-size: 30px;
|
||||
line-height: 30px;
|
||||
margin: 0;
|
||||
padding: 10px 10px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#header .angular {
|
||||
font-family: "Courier New", monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#header h1 a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
/* Main main Style */
|
||||
|
||||
#main h1 {
|
||||
font-family: monospace;
|
||||
margin-top: 0;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #CCC;
|
||||
}
|
||||
|
||||
#main h2 {
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
|
||||
#main h1 + h2 {
|
||||
margin-top: 1.3em;
|
||||
}
|
||||
|
||||
#main h3 {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
#main ul.methods,
|
||||
#main ul.properties {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
#main ul.methods > li,
|
||||
#main ul.properties > li {
|
||||
list-style: none;
|
||||
padding: 0 10px 10px 10px;
|
||||
margin-top: 1.5em;
|
||||
border: 1px solid #000;
|
||||
}
|
||||
|
||||
#main ul.methods > li > h3,
|
||||
#main ul.properties > li > h3 {
|
||||
font-family: "Courier New", monospace;
|
||||
background-color: #E0E0E0;
|
||||
margin: 0 -10px 5px -10px;
|
||||
padding: .5em .5em .25em .5em;
|
||||
border-bottom: 1px solid #888;
|
||||
}
|
||||
|
||||
#main li > p,
|
||||
#main div.returns > p {
|
||||
display: inline; /* the top most paragraph should not cause new lines inside lists. */
|
||||
}
|
||||
|
||||
.main-title {
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
/* Searchbox & Sidebar Style */
|
||||
|
||||
#search-box, #sidebar {
|
||||
border-right: 1px solid #DDD;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
background-color: #EEE;
|
||||
}
|
||||
|
||||
#search-box {
|
||||
width: 16em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#sidebar a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#sidebar a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#sidebar ul {
|
||||
list-style-type: none;
|
||||
/*TODO(esprehn): Can we just reset globally and not break examples?*/
|
||||
margin: 0;
|
||||
padding: 0 0.8em 0 0;
|
||||
width: 13em;
|
||||
}
|
||||
|
||||
#sidebar ul li {
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
display: block;
|
||||
padding: 2px 2px 2px 4px;
|
||||
}
|
||||
|
||||
#sidebar ul li.selected a {
|
||||
background-color: #DDD;
|
||||
border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border: 1px solid #CCC;
|
||||
padding: 1px 1px 1px 3px;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-0 {
|
||||
margin-top: 0.5em;
|
||||
margin-left: 0em;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-0:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-1.level-angular {
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-1 {
|
||||
margin-left: 1em;
|
||||
margin-top: 5px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-2 {
|
||||
margin-left: 2em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-3 {
|
||||
margin-left: 3em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#sidebar ul li.level-4 {
|
||||
margin-left: 4em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
|
||||
/* Warning and Info Banners */
|
||||
|
||||
.deprecated {
|
||||
border: 2px solid red;
|
||||
}
|
||||
|
||||
.deprecated legend {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.workInProgress {
|
||||
border: 2px solid orange;
|
||||
}
|
||||
|
||||
.workInProgress legend {
|
||||
font-weight: bold;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
|
||||
/* Feedback Link */
|
||||
|
||||
#feedback {
|
||||
float: right;
|
||||
width: 10em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
/* Live Example Style */
|
||||
|
||||
.doc-example-live table td {
|
||||
padding: 0 1.5em;
|
||||
}
|
||||
|
||||
|
||||
/* Scrollbars */
|
||||
|
||||
::-webkit-scrollbar{
|
||||
width:0.8em;
|
||||
margin: 0.2em 0em;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:hover{
|
||||
background-color:#eee;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb{
|
||||
min-height:0.8em;
|
||||
min-width:0.8em;
|
||||
-webkit-border-radius:0.5em;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover{
|
||||
background-color: #bbb;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active{
|
||||
background-color:#888;
|
||||
}
|
||||
|
||||
#sidebar::-webkit-scrollbar {
|
||||
background-color:#eee;
|
||||
}
|
||||
|
||||
#main::-webkit-scrollbar {
|
||||
background-color:#fff;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
img.right {
|
||||
float: right;
|
||||
}
|
||||
h1, h2, h3, h4, h5 {
|
||||
clear: both;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
DocsController.$inject = ['$location', '$browser', '$window'];
|
||||
function DocsController($location, $browser, $window) {
|
||||
this.pages = NG_PAGES;
|
||||
window.$root = this.$root;
|
||||
|
||||
this.getUrl = function(page){
|
||||
return '#!' + page.id;
|
||||
};
|
||||
|
||||
this.getCurrentPartial = function(){
|
||||
return './' + this.getTitle() + '.html';
|
||||
};
|
||||
|
||||
this.getTitle = function(){
|
||||
var hashPath = $location.hashPath || '!angular';
|
||||
if (hashPath.match(/^!/)) {
|
||||
this.partialTitle = hashPath.substring(1);
|
||||
}
|
||||
return this.partialTitle;
|
||||
};
|
||||
|
||||
this.getClass = function(page) {
|
||||
var depth = page.depth,
|
||||
cssClass = 'level-' + depth + (page.name == this.getTitle() ? ' selected' : '');
|
||||
|
||||
if (depth == 1 && page.type !== 'overview') cssClass += ' level-angular';
|
||||
|
||||
return cssClass;
|
||||
};
|
||||
|
||||
this.afterPartialLoaded = function() {
|
||||
SyntaxHighlighter.highlight();
|
||||
};
|
||||
|
||||
this.getFeedbackUrl = function() {
|
||||
return "mailto:angular@googlegroups.com?" +
|
||||
"subject=" + escape("Feedback on " + $location.href) + "&" +
|
||||
"body=" + escape("Hi there,\n\nI read " + $location.href + " and wanted to ask ....");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
SyntaxHighlighter['defaults'].toolbar = false;
|
||||
@@ -0,0 +1,46 @@
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org/"
|
||||
xmlns:doc="http://docs.angularjs.org/"
|
||||
ng:controller="DocsController">
|
||||
<head>
|
||||
<title ng:bind-template="<angular/>: {{getTitle()}}"><angular/></title>
|
||||
|
||||
<meta name="fragment" content="!">
|
||||
|
||||
<link rel="stylesheet" href="doc_widgets.css" type="text/css" />
|
||||
<link rel="stylesheet" href="docs.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="syntaxhighlighter/shCore.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="syntaxhighlighter/shThemeDefault.css" type="text/css"/>
|
||||
|
||||
<script src="jquery.min.js"></script>
|
||||
<script src="syntaxhighlighter/shCore.js"></script>
|
||||
<script src="syntaxhighlighter/shBrushJScript.js"></script>
|
||||
<script src="syntaxhighlighter/shBrushXml.js"></script>
|
||||
|
||||
<script src="../angular.min.js" ng:autobind></script>
|
||||
<script src="docs.js"></script>
|
||||
<script src="doc_widgets.js"></script>
|
||||
<script src="docs-keywords.js"></script>
|
||||
</head>
|
||||
<body style="display:none;" ng:show="true">
|
||||
<div id="header">
|
||||
<h1>
|
||||
<span class="main-title">{{getTitle()}}</span>
|
||||
<a href="#" tabindex="0"><span class="angular"><angular/></span> Docs</a>
|
||||
</h1>
|
||||
</div>
|
||||
<div id="sidebar">
|
||||
<input type="text" name="search" id="search-box" placeholder="search the docs"
|
||||
tabindex="1" accesskey="s"/>
|
||||
<ul id="api-list">
|
||||
<li ng:repeat="page in pages.$filter(search)" ng:class="getClass(page)">
|
||||
<a href="{{getUrl(page)}}" ng:click="" tabindex="2">{{page.shortName}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<a id="feedback" ng:href="{{getFeedbackUrl()}}">Report an Issue or Ask a Question</a>
|
||||
<ng:include src="getCurrentPartial()" onload="afterPartialLoaded()"></ng:include>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../lib/jquery/jquery-1.4.2.min.js
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
var keywords = 'break case catch continue ' +
|
||||
'default delete do else false ' +
|
||||
'for function if in instanceof ' +
|
||||
'new null return super switch ' +
|
||||
'this throw true try typeof var while with'
|
||||
;
|
||||
|
||||
var r = SyntaxHighlighter.regexLib;
|
||||
|
||||
this.regexList = [
|
||||
{ regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
|
||||
{ regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
|
||||
{ regex: r.singleLineCComments, css: 'comments' }, // one line comments
|
||||
{ regex: r.multiLineCComments, css: 'comments' }, // multiline comments
|
||||
{ regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
|
||||
{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' } // keywords
|
||||
];
|
||||
|
||||
this.forHtmlScript(r.scriptScriptTags);
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['js', 'jscript', 'javascript'];
|
||||
|
||||
SyntaxHighlighter.brushes.JScript = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
;(function()
|
||||
{
|
||||
// CommonJS
|
||||
typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
|
||||
|
||||
function Brush()
|
||||
{
|
||||
function process(match, regexInfo)
|
||||
{
|
||||
var constructor = SyntaxHighlighter.Match,
|
||||
code = match[0],
|
||||
tag = new XRegExp('(<|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
|
||||
result = []
|
||||
;
|
||||
|
||||
if (match.attributes != null)
|
||||
{
|
||||
var attributes,
|
||||
regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
|
||||
'\\s*=\\s*' +
|
||||
'(?<value> ".*?"|\'.*?\'|\\w+)',
|
||||
'xg');
|
||||
|
||||
while ((attributes = regex.exec(code)) != null)
|
||||
{
|
||||
result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
|
||||
result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
|
||||
}
|
||||
}
|
||||
|
||||
if (tag != null)
|
||||
result.push(
|
||||
new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
this.regexList = [
|
||||
{ regex: new XRegExp('(\\<|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\>|>)', 'gm'), css: 'color2' }, // <![ ... [ ... ]]>
|
||||
{ regex: SyntaxHighlighter.regexLib.xmlComments, css: 'comments' }, // <!-- ... -->
|
||||
{ regex: new XRegExp('(<|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(>|>)', 'sg'), func: process }
|
||||
];
|
||||
};
|
||||
|
||||
Brush.prototype = new SyntaxHighlighter.Highlighter();
|
||||
Brush.aliases = ['xml', 'xhtml', 'xslt', 'html'];
|
||||
|
||||
SyntaxHighlighter.brushes.Xml = Brush;
|
||||
|
||||
// CommonJS
|
||||
typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
|
||||
})();
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter a,
|
||||
.syntaxhighlighter div,
|
||||
.syntaxhighlighter code,
|
||||
.syntaxhighlighter table,
|
||||
.syntaxhighlighter table td,
|
||||
.syntaxhighlighter table tr,
|
||||
.syntaxhighlighter table tbody,
|
||||
.syntaxhighlighter table thead,
|
||||
.syntaxhighlighter table caption,
|
||||
.syntaxhighlighter textarea {
|
||||
-moz-border-radius: 0 0 0 0 !important;
|
||||
-webkit-border-radius: 0 0 0 0 !important;
|
||||
background: none !important;
|
||||
border: 0 !important;
|
||||
bottom: auto !important;
|
||||
float: none !important;
|
||||
height: auto !important;
|
||||
left: auto !important;
|
||||
line-height: 1.1em !important;
|
||||
margin: 0 !important;
|
||||
outline: 0 !important;
|
||||
overflow: visible !important;
|
||||
padding: 0 !important;
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
text-align: left !important;
|
||||
top: auto !important;
|
||||
vertical-align: baseline !important;
|
||||
width: auto !important;
|
||||
box-sizing: content-box !important;
|
||||
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
|
||||
font-weight: normal !important;
|
||||
font-style: normal !important;
|
||||
font-size: 1em !important;
|
||||
min-height: inherit !important;
|
||||
min-height: auto !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter {
|
||||
width: 100% !important;
|
||||
margin: 1em 0 1em 0 !important;
|
||||
position: relative !important;
|
||||
overflow: auto !important;
|
||||
font-size: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.source {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.syntaxhighlighter .bold {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter .italic {
|
||||
font-style: italic !important;
|
||||
}
|
||||
.syntaxhighlighter .line {
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
text-align: left !important;
|
||||
padding: .5em 0 0.5em 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code {
|
||||
width: 100% !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container {
|
||||
position: relative !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .container textarea {
|
||||
box-sizing: border-box !important;
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
border: none !important;
|
||||
background: white !important;
|
||||
padding-left: 1em !important;
|
||||
overflow: hidden !important;
|
||||
white-space: pre !important;
|
||||
}
|
||||
.syntaxhighlighter table td.gutter .line {
|
||||
text-align: right !important;
|
||||
padding: 0 0.5em 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter table td.code .line {
|
||||
padding: 0 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
|
||||
padding-left: 0em !important;
|
||||
}
|
||||
.syntaxhighlighter.show {
|
||||
display: block !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed table {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
padding: 0.1em 0.8em 0em 0.8em !important;
|
||||
font-size: 1em !important;
|
||||
position: static !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span {
|
||||
display: inline !important;
|
||||
margin-right: 1em !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a {
|
||||
padding: 0 !important;
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar span a.expandSource {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
position: absolute !important;
|
||||
right: 1px !important;
|
||||
top: 1px !important;
|
||||
width: 11px !important;
|
||||
height: 11px !important;
|
||||
font-size: 10px !important;
|
||||
z-index: 10 !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar span.title {
|
||||
display: inline !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
display: block !important;
|
||||
text-align: center !important;
|
||||
text-decoration: none !important;
|
||||
padding-top: 1px !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a.expandSource {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.ie {
|
||||
font-size: .9em !important;
|
||||
padding: 1px 0 1px 0 !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar {
|
||||
line-height: 8px !important;
|
||||
}
|
||||
.syntaxhighlighter.ie .toolbar a {
|
||||
padding-top: 0px !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.alt2 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted .number,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt1 .content,
|
||||
.syntaxhighlighter.printing .line.highlighted.alt2 .content {
|
||||
background: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .number {
|
||||
color: #bbbbbb !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .toolbar {
|
||||
display: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .keyword {
|
||||
color: #006699 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .script {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
|
||||
color: black !important;
|
||||
}
|
||||
+17
File diff suppressed because one or more lines are too long
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* SyntaxHighlighter
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter
|
||||
*
|
||||
* SyntaxHighlighter is donationware. If you are using it, please donate.
|
||||
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
|
||||
*
|
||||
* @version
|
||||
* 3.0.83 (July 02 2010)
|
||||
*
|
||||
* @copyright
|
||||
* Copyright (C) 2004-2010 Alex Gorbatchev.
|
||||
*
|
||||
* @license
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
*/
|
||||
.syntaxhighlighter {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt1 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.alt2 {
|
||||
background-color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
|
||||
background-color: #e0e0e0 !important;
|
||||
}
|
||||
.syntaxhighlighter .line.highlighted.number {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter table caption {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter {
|
||||
color: #afafaf !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line {
|
||||
border-right: 3px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter .gutter .line.highlighted {
|
||||
background-color: #6ce26c !important;
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter.printing .line .content {
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar {
|
||||
color: blue !important;
|
||||
background: white !important;
|
||||
border: 1px solid #6ce26c !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter.collapsed .toolbar a:hover {
|
||||
color: red !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar {
|
||||
color: white !important;
|
||||
background: #6ce26c !important;
|
||||
border: none !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a {
|
||||
color: white !important;
|
||||
}
|
||||
.syntaxhighlighter .toolbar a:hover {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
|
||||
color: black !important;
|
||||
}
|
||||
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
|
||||
color: #008200 !important;
|
||||
}
|
||||
.syntaxhighlighter .string, .syntaxhighlighter .string a {
|
||||
color: blue !important;
|
||||
}
|
||||
.syntaxhighlighter .keyword {
|
||||
color: #006699 !important;
|
||||
}
|
||||
.syntaxhighlighter .preprocessor {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .variable {
|
||||
color: #aa7700 !important;
|
||||
}
|
||||
.syntaxhighlighter .value {
|
||||
color: #009900 !important;
|
||||
}
|
||||
.syntaxhighlighter .functions {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .constants {
|
||||
color: #0066cc !important;
|
||||
}
|
||||
.syntaxhighlighter .script {
|
||||
font-weight: bold !important;
|
||||
color: #006699 !important;
|
||||
background-color: none !important;
|
||||
}
|
||||
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
|
||||
color: gray !important;
|
||||
}
|
||||
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
|
||||
color: #ff1493 !important;
|
||||
}
|
||||
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.syntaxhighlighter .keyword {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
3.0.83
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* All writing related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs');
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
|
||||
function output(docs, content, callback){
|
||||
callback();
|
||||
}
|
||||
|
||||
exports.output = function(file, content, callback){
|
||||
//console.log('writing', OUTPUT_DIR + file, '...');
|
||||
fs.writeFile(
|
||||
OUTPUT_DIR + file,
|
||||
exports.toString(content),
|
||||
callback);
|
||||
};
|
||||
|
||||
|
||||
exports.toString = function toString(obj){
|
||||
switch (typeof obj) {
|
||||
case 'string':
|
||||
return obj;
|
||||
case 'object':
|
||||
if (obj instanceof Array) {
|
||||
obj.forEach(function (value, key){
|
||||
obj[key] = toString(value);
|
||||
});
|
||||
return obj.join('');
|
||||
} else {
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
exports.makeDir = function (path, callback) {
|
||||
var parts = path.split(/\//);
|
||||
path = '.';
|
||||
(function next(){
|
||||
if (parts.length) {
|
||||
path += '/' + parts.shift();
|
||||
fs.mkdir(path, 0777, next);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
exports.copy = function(filename, callback){
|
||||
copy('docs/src/templates/' + filename, OUTPUT_DIR + filename, callback);
|
||||
};
|
||||
|
||||
function copy(from, to, callback) {
|
||||
//console.log('writing', to, '...');
|
||||
fs.readFile(from, function(err, content){
|
||||
if (err) return callback.error(err);
|
||||
fs.writeFile(to, content, callback);
|
||||
});
|
||||
}
|
||||
|
||||
exports.copyImages = function(callback) {
|
||||
exports.makeDir(OUTPUT_DIR + '/img', callback.waitFor(function(){
|
||||
fs.readdir('docs/img', callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
if (file.match(/\.(png|gif|jpg|jpeg)$/)) {
|
||||
copy('docs/img/' + file, OUTPUT_DIR + '/img/' + file, callback.waitFor());
|
||||
}
|
||||
});
|
||||
callback();
|
||||
}));
|
||||
}));
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<input type="text" ng:validate="{{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}]</i>{{/default}}{{/paramRest}}"/>
|
||||
</tt>
|
||||
|
||||
<h3>In JavaScript</h3>
|
||||
<tt ng:non-bindable>
|
||||
angular.validator.{{shortName}}({{paramFirst.name}}{{#paramRest}}{{^default}}, {{name}}{{/default}}{{#default}}<i>[, {{name}}]</i>{{/default}}{{/paramRest}} );
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}{{^type}}:*{{/type}}{{#default}}={{default}}{{/default}}</tt>: {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
{{{paramDescription}}}
|
||||
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,39 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<pre>
|
||||
<{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}>{{#usageContent}}
|
||||
|
||||
{{usageContent}}
|
||||
{{/usageContent}}</{{shortName}}>
|
||||
</pre>
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}:{{#type}}{{type}}{{/type}}{{^type}}:*{{/type}}{{#default}}={{default}}{{/default}}</tt>: {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
{{{paramDescription}}}
|
||||
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<!-- TODO: we need to expose $root so that we can delete cookies in the scenario runner, there
|
||||
must be a better way to do this -->
|
||||
<body ng:controller="example.personalLog.LogCtrl" ng:init="$window.$root = $root">
|
||||
<body ng:controller="example.personalLog.LogCtrl">
|
||||
|
||||
<form action="" ng:submit="addLog(newMsg)">
|
||||
<input type="text" name="newMsg" />
|
||||
|
||||
@@ -81,15 +81,14 @@ angular.scenario.dsl('clearCookies', function() {
|
||||
*/
|
||||
return function() {
|
||||
this.addFutureAction('clear all cookies', function($window, $document, done) {
|
||||
//TODO: accessing angular services is pretty nasty, we need a better way to reach them
|
||||
var $cookies = $window.$root.$cookies,
|
||||
var rootScope = $window.angular.element($document[0]).data('$scope'),
|
||||
$cookies = rootScope.$service('$cookies'),
|
||||
cookieName;
|
||||
|
||||
for (cookieName in $cookies) {
|
||||
console.log('deleting cookie: ' + cookieName);
|
||||
delete $cookies[cookieName];
|
||||
}
|
||||
$window.$root.$eval();
|
||||
rootScope.$eval();
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ describe('example.personalLog.LogCtrl', function() {
|
||||
|
||||
function createNotesCtrl() {
|
||||
var scope = angular.scope();
|
||||
scope.$cookies = scope.$service('$cookies');
|
||||
return scope.$new(example.personalLog.LogCtrl);
|
||||
}
|
||||
|
||||
|
||||
+17
-31
@@ -1,36 +1,22 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<!doctype html>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript"
|
||||
src="../angular-debug.js" ng:autobind ng:css="css/angular.css"></script>
|
||||
<title>angular dev sandbox</title>
|
||||
<script src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<script>
|
||||
angular.service('routeConfig', function($route) {
|
||||
$route.when('/view1', {controller: MyCtrl, template: 'view1.html'});
|
||||
$route.when('/view2', {controller: MyCtrl, template: 'view2.html'});
|
||||
|
||||
function MyCtrl() {};
|
||||
}, {$inject: ['$route'], $eager: true});
|
||||
</script>
|
||||
</head>
|
||||
<body ng:init="$window.$root = this">
|
||||
|
||||
<script>
|
||||
angular.widget('my:greeter', function(compileElement){
|
||||
var compiler = this;
|
||||
compileElement.css('style', 'block');
|
||||
var salutaitonExp = compileElement.attr('salutation');
|
||||
var nameExp = compileElement.attr('name');
|
||||
return function(linkElement){
|
||||
var salutaitonSpan = angular.element('<span class="salutation"></span');
|
||||
var nameSpan = angular.element('<span class="name"></span>');
|
||||
linkElement.append(salutaitonSpan);
|
||||
linkElement.append(compiler.text(' '));
|
||||
linkElement.append(nameSpan);
|
||||
linkElement.append(compiler.text('!'));
|
||||
this.$watch(salutaitonExp, function(value){
|
||||
salutaitonSpan.text(value);
|
||||
});
|
||||
this.$watch(nameExp, function(value){
|
||||
nameSpan.text(value);
|
||||
});
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<div ng:init="salutation='Hello'; name='World'">
|
||||
<my:greeter salutation="salutation" name="name"/>
|
||||
</div>
|
||||
<body ng:init="$service('$window').$root = this">
|
||||
<p>
|
||||
<a href="#/view1">view1</a> | <a href="#/view2">view2</a> | <a href="#">blank</a>
|
||||
</p>
|
||||
|
||||
view: <ng:view></ng:view>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
node docs/specs.js --noColor && node docs/collect.js
|
||||
#!/bin/bash
|
||||
. ~/.bashrc
|
||||
node docs/spec/specs.js --noColor | grep -v '/lib/jasmine' && node docs/src/gen-docs.js
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
server: http://localhost:9876
|
||||
|
||||
load:
|
||||
- lib/jasmine-1.0.1/jasmine.js
|
||||
- lib/jasmine-jstd-adapter/JasmineAdapter.js
|
||||
- lib/jquery/jquery-1.4.2.js
|
||||
- test/jquery_remove.js
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/scenario/*.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/*.js
|
||||
- example/personalLog/test/*.js
|
||||
|
||||
exclude:
|
||||
- test/jquery_alias.js
|
||||
- src/angular.prefix
|
||||
- src/angular.suffix
|
||||
- src/angular-bootstrap.js
|
||||
- src/scenario/angular-bootstrap.js
|
||||
- src/AngularPublic.js
|
||||
|
||||
plugin:
|
||||
- name: "coverage"
|
||||
jar: "lib/jstestdriver/coverage.jar"
|
||||
module: "com.google.jstestdriver.coverage.CoverageModule"
|
||||
|
||||
@@ -8,14 +8,17 @@ load:
|
||||
- src/Angular.js
|
||||
- src/JSON.js
|
||||
- src/*.js
|
||||
- example/personalLog/*.js
|
||||
- test/testabilityPatch.js
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/scenario/*.js
|
||||
- test/angular-mocks.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/*.js
|
||||
- example/personalLog/test/*.js
|
||||
|
||||
exclude:
|
||||
- src/angular.prefix
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
server: http://localhost:9876
|
||||
|
||||
load:
|
||||
- lib/jasmine-1.0.1/jasmine.js
|
||||
- lib/jasmine-jstd-adapter/JasmineAdapter.js
|
||||
- lib/jquery/jquery-1.4.2.js
|
||||
- test/jquery_remove.js
|
||||
- build/angular.min.js
|
||||
- perf/data/*.js
|
||||
- perf/testUtils.js
|
||||
- perf/*.js
|
||||
|
||||
exclude:
|
||||
+2
-1
@@ -13,7 +13,8 @@ load:
|
||||
- src/scenario/Scenario.js
|
||||
- src/scenario/output/*.js
|
||||
- src/scenario/*.js
|
||||
- test/angular-mocks.js
|
||||
- src/angular-mocks.js
|
||||
- test/mocks.js
|
||||
- test/scenario/*.js
|
||||
- test/scenario/output/*.js
|
||||
- test/*.js
|
||||
|
||||
@@ -1,111 +1,176 @@
|
||||
/**
|
||||
* @fileoverview Jasmine JsTestDriver Adapter.
|
||||
* @author ibolmo@gmail.com (Olmo Maldonado)
|
||||
* @author misko@hevery.com (Misko Hevery)
|
||||
*/
|
||||
(function(window) {
|
||||
var rootDescribes = new Describes(window);
|
||||
var describePath = [];
|
||||
rootDescribes.collectMode();
|
||||
|
||||
var jasmineTest = TestCase('Jasmine Adapter Tests');
|
||||
|
||||
var jasminePlugin = {
|
||||
name:'jasmine',
|
||||
runTestConfiguration: function(testRunConfiguration, onTestDone, onTestRunConfigurationComplete){
|
||||
if (testRunConfiguration.testCaseInfo_.template_ !== jasmineTest) return;
|
||||
|
||||
var jasmineEnv = jasmine.currentEnv_ = new jasmine.Env();
|
||||
rootDescribes.playback();
|
||||
var specLog = jstestdriver.console.log_ = [];
|
||||
var start;
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return rootDescribes.isExclusive(spec);
|
||||
};
|
||||
jasmineEnv.reporter = {
|
||||
log: function(str){
|
||||
specLog.push(str);
|
||||
},
|
||||
|
||||
(function() {
|
||||
reportRunnerStarting: function(runner) { },
|
||||
|
||||
function bind(_this, _function){
|
||||
return function(){
|
||||
return _function.call(_this);
|
||||
};
|
||||
reportSpecStarting: function(spec) {
|
||||
specLog = jstestdriver.console.log_ = [];
|
||||
start = new Date().getTime();
|
||||
},
|
||||
|
||||
reportSpecResults: function(spec) {
|
||||
var suite = spec.suite;
|
||||
var results = spec.results();
|
||||
if (results.skipped) return;
|
||||
var end = new Date().getTime();
|
||||
var messages = [];
|
||||
var resultItems = results.getItems();
|
||||
var state = 'passed';
|
||||
for ( var i = 0; i < resultItems.length; i++) {
|
||||
if (!resultItems[i].passed()) {
|
||||
state = resultItems[i].message.match(/AssertionError:/) ? 'error' : 'failed';
|
||||
messages.push(resultItems[i].toString());
|
||||
messages.push(formatStack(resultItems[i].trace.stack));
|
||||
}
|
||||
}
|
||||
onTestDone(
|
||||
new jstestdriver.TestResult(
|
||||
suite.getFullName(),
|
||||
spec.description,
|
||||
state,
|
||||
messages.join('\n'),
|
||||
specLog.join('\n'),
|
||||
end - start));
|
||||
},
|
||||
|
||||
reportSuiteResults: function(suite) {},
|
||||
|
||||
reportRunnerResults: function(runner) {
|
||||
onTestRunConfigurationComplete();
|
||||
}
|
||||
};
|
||||
jasmineEnv.execute();
|
||||
return true;
|
||||
},
|
||||
onTestsFinish: function(){
|
||||
jasmine.currentEnv_ = null;
|
||||
rootDescribes.collectMode();
|
||||
}
|
||||
};
|
||||
jstestdriver.pluginRegistrar.register(jasminePlugin);
|
||||
|
||||
function formatStack(stack) {
|
||||
var lines = (stack||'').split(/\r?\n/);
|
||||
var frames = [];
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
if (!lines[i].match(/\/jasmine[\.-]/)) {
|
||||
frames.push(lines[i].replace(/https?:\/\/\w+(:\d+)?\/test\//, '').replace(/^\s*/, ' '));
|
||||
}
|
||||
}
|
||||
return frames.join('\n');
|
||||
}
|
||||
|
||||
var currentFrame = frame(null, null);
|
||||
|
||||
function frame(parent, name){
|
||||
var caseName = (parent && parent.caseName ? parent.caseName + " " : '') + (name ? name : '');
|
||||
var frame = {
|
||||
name: name,
|
||||
caseName: caseName,
|
||||
parent: parent,
|
||||
testCase: TestCase(caseName),
|
||||
before: [],
|
||||
after: [],
|
||||
runBefore: function(){
|
||||
if (parent) parent.runBefore.apply(this);
|
||||
for ( var i = 0; i < frame.before.length; i++) {
|
||||
frame.before[i].apply(this);
|
||||
}
|
||||
},
|
||||
runAfter: function(){
|
||||
for ( var i = 0; i < frame.after.length; i++) {
|
||||
frame.after[i].apply(this);
|
||||
}
|
||||
if (parent) parent.runAfter.apply(this);
|
||||
}
|
||||
};
|
||||
return frame;
|
||||
};
|
||||
|
||||
jasmine.Env.prototype.describe = (function(describe){
|
||||
return function(description){
|
||||
currentFrame = frame(currentFrame, description);
|
||||
var val = describe.apply(this, arguments);
|
||||
currentFrame = currentFrame.parent;
|
||||
return val;
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.describe);
|
||||
|
||||
var id = 0;
|
||||
|
||||
jasmine.Env.prototype.it = (function(it){
|
||||
return function(desc, itFn){
|
||||
var self = this;
|
||||
var spec = it.apply(this, arguments);
|
||||
var currentSpec = this.currentSpec;
|
||||
if (!currentSpec.$id) {
|
||||
currentSpec.$id = id++;
|
||||
}
|
||||
var frame = this.jstdFrame = currentFrame;
|
||||
var name = 'test that it ' + desc;
|
||||
if (this.jstdFrame.testCase.prototype[name])
|
||||
throw "Spec with name '" + desc + "' already exists.";
|
||||
this.jstdFrame.testCase.prototype[name] = function(){
|
||||
jasmine.getEnv().currentSpec = currentSpec;
|
||||
frame.runBefore.apply(currentSpec);
|
||||
try {
|
||||
itFn.apply(currentSpec);
|
||||
} finally {
|
||||
frame.runAfter.apply(currentSpec);
|
||||
function noop(){}
|
||||
function Describes(window){
|
||||
var describes = {};
|
||||
var beforeEachs = {};
|
||||
var afterEachs = {};
|
||||
var exclusive;
|
||||
var collectMode = true;
|
||||
intercept('describe', describes);
|
||||
intercept('xdescribe', describes);
|
||||
intercept('beforeEach', beforeEachs);
|
||||
intercept('afterEach', afterEachs);
|
||||
|
||||
function intercept(functionName, collection){
|
||||
window[functionName] = function(desc, fn){
|
||||
if (collectMode) {
|
||||
collection[desc] = function(){
|
||||
jasmine.getEnv()[functionName](desc, fn);
|
||||
};
|
||||
} else {
|
||||
jasmine.getEnv()[functionName](desc, fn);
|
||||
}
|
||||
};
|
||||
return spec;
|
||||
}
|
||||
window.ddescribe = function(name, fn){
|
||||
exclusive = true;
|
||||
console.log('ddescribe', name);
|
||||
window.describe(name, function(){
|
||||
var oldIt = window.it;
|
||||
window.it = window.iit;
|
||||
try {
|
||||
fn.call(this);
|
||||
} finally {
|
||||
window.it = oldIt;
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.it);
|
||||
|
||||
|
||||
jasmine.Env.prototype.beforeEach = (function(beforeEach){
|
||||
return function(beforeEachFunction) {
|
||||
beforeEach.apply(this, arguments);
|
||||
currentFrame.before.push(beforeEachFunction);
|
||||
window.iit = function(name, fn){
|
||||
exclusive = fn.exclusive = true;
|
||||
console.log(fn);
|
||||
jasmine.getEnv().it(name, fn);
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.beforeEach);
|
||||
|
||||
|
||||
jasmine.Env.prototype.afterEach = (function(afterEach){
|
||||
return function(afterEachFunction) {
|
||||
afterEach.apply(this, arguments);
|
||||
currentFrame.after.push(afterEachFunction);
|
||||
|
||||
|
||||
this.collectMode = function() {
|
||||
collectMode = true;
|
||||
exclusive = false;
|
||||
};
|
||||
|
||||
})(jasmine.Env.prototype.afterEach);
|
||||
|
||||
|
||||
jasmine.NestedResults.prototype.addResult = (function(addResult){
|
||||
return function(result) {
|
||||
addResult.call(this, result);
|
||||
if (result.type != 'MessageResult' && !result.passed()) fail(result.message);
|
||||
this.playback = function(){
|
||||
collectMode = false;
|
||||
playback(beforeEachs);
|
||||
playback(afterEachs);
|
||||
playback(describes);
|
||||
|
||||
function playback(set) {
|
||||
for ( var name in set) {
|
||||
set[name]();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.isExclusive = function(spec) {
|
||||
if (exclusive) {
|
||||
var blocks = spec.queue.blocks;
|
||||
for ( var i = 0; i < blocks.length; i++) {
|
||||
if (blocks[i].func.exclusive) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
})(window);
|
||||
|
||||
})(jasmine.NestedResults.prototype.addResult);
|
||||
// Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
});
|
||||
// PATCH
|
||||
if (e) {
|
||||
expectationResult.trace = e;
|
||||
}
|
||||
this.results_.addResult(expectationResult);
|
||||
};
|
||||
|
||||
// Reset environment with overriden methods.
|
||||
jasmine.currentEnv_ = null;
|
||||
jasmine.getEnv();
|
||||
|
||||
})();
|
||||
|
||||
-77
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
Copyright (c) 2009 Chris Wanstrath (Ruby)
|
||||
Copyright (c) 2010 Jan Lehnardt (JavaScript)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* CommonJS-compatible mustache.js module
|
||||
*
|
||||
* See http://github.com/janl/mustache.js for more info.
|
||||
*/
|
||||
/*
|
||||
mustache.js Ñ Logic-less templates in JavaScript
|
||||
|
||||
See http://mustache.github.com/ for more info.
|
||||
*/
|
||||
|
||||
var Mustache = function() {
|
||||
var Renderer = function() {};
|
||||
|
||||
Renderer.prototype = {
|
||||
otag: "{{",
|
||||
ctag: "}}",
|
||||
pragmas: {},
|
||||
buffer: [],
|
||||
pragmas_implemented: {
|
||||
"IMPLICIT-ITERATOR": true
|
||||
},
|
||||
context: {},
|
||||
|
||||
render: function(template, context, partials, in_recursion) {
|
||||
// reset buffer & set context
|
||||
if(!in_recursion) {
|
||||
this.context = context;
|
||||
this.buffer = []; // TODO: make this non-lazy
|
||||
}
|
||||
|
||||
// fail fast
|
||||
if(!this.includes("", template)) {
|
||||
if(in_recursion) {
|
||||
return template;
|
||||
} else {
|
||||
this.send(template);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template = this.render_pragmas(template);
|
||||
var html = this.render_section(template, context, partials);
|
||||
if(in_recursion) {
|
||||
return this.render_tags(html, context, partials, in_recursion);
|
||||
}
|
||||
|
||||
this.render_tags(html, context, partials, in_recursion);
|
||||
},
|
||||
|
||||
/*
|
||||
Sends parsed lines
|
||||
*/
|
||||
send: function(line) {
|
||||
if(line != "") {
|
||||
this.buffer.push(line);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
Looks for %PRAGMAS
|
||||
*/
|
||||
render_pragmas: function(template) {
|
||||
// no pragmas
|
||||
if(!this.includes("%", template)) {
|
||||
return template;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
|
||||
this.ctag);
|
||||
return template.replace(regex, function(match, pragma, options) {
|
||||
if(!that.pragmas_implemented[pragma]) {
|
||||
throw({message:
|
||||
"This implementation of mustache doesn't understand the '" +
|
||||
pragma + "' pragma"});
|
||||
}
|
||||
that.pragmas[pragma] = {};
|
||||
if(options) {
|
||||
var opts = options.split("=");
|
||||
that.pragmas[pragma][opts[0]] = opts[1];
|
||||
}
|
||||
return "";
|
||||
// ignore unknown pragmas silently
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
Tries to find a partial in the curent scope and render it
|
||||
*/
|
||||
render_partial: function(name, context, partials) {
|
||||
name = this.trim(name);
|
||||
if(!partials || partials[name] === undefined) {
|
||||
throw({message: "unknown_partial '" + name + "'"});
|
||||
}
|
||||
if(typeof(context[name]) != "object") {
|
||||
return this.render(partials[name], context, partials, true);
|
||||
}
|
||||
return this.render(partials[name], context[name], partials, true);
|
||||
},
|
||||
|
||||
/*
|
||||
Renders inverted (^) and normal (#) sections
|
||||
*/
|
||||
render_section: function(template, context, partials) {
|
||||
if(!this.includes("#", template) && !this.includes("^", template)) {
|
||||
return template;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
// CSW - Added "+?" so it finds the tighest bound, not the widest
|
||||
var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
|
||||
"\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
|
||||
"\\s*", "mg");
|
||||
|
||||
// for each {{#foo}}{{/foo}} section do...
|
||||
return template.replace(regex, function(match, type, name, content) {
|
||||
var value = that.find(name, context);
|
||||
if(type == "^") { // inverted section
|
||||
if(!value || that.is_array(value) && value.length === 0) {
|
||||
// false or empty list, render it
|
||||
return that.render(content, context, partials, true);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else if(type == "#") { // normal section
|
||||
if(that.is_array(value)) { // Enumerable, Let's loop!
|
||||
return that.map(value, function(row) {
|
||||
return that.render(content, that.create_context(row),
|
||||
partials, true);
|
||||
}).join("");
|
||||
} else if(that.is_object(value)) { // Object, Use it as subcontext!
|
||||
return that.render(content, that.create_context(value),
|
||||
partials, true);
|
||||
} else if(typeof value === "function") {
|
||||
// higher order section
|
||||
return value.call(context, content, function(text) {
|
||||
return that.render(text, context, partials, true);
|
||||
});
|
||||
} else if(value) { // boolean section
|
||||
return that.render(content, context, partials, true);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
Replace {{foo}} and friends with values from our view
|
||||
*/
|
||||
render_tags: function(template, context, partials, in_recursion) {
|
||||
// tit for tat
|
||||
var that = this;
|
||||
|
||||
var new_regex = function() {
|
||||
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
|
||||
that.ctag + "+", "g");
|
||||
};
|
||||
|
||||
var regex = new_regex();
|
||||
var tag_replace_callback = function(match, operator, name) {
|
||||
switch(operator) {
|
||||
case "!": // ignore comments
|
||||
return "";
|
||||
case "=": // set new delimiters, rebuild the replace regexp
|
||||
that.set_delimiters(name);
|
||||
regex = new_regex();
|
||||
return "";
|
||||
case ">": // render partial
|
||||
return that.render_partial(name, context, partials);
|
||||
case "{": // the triple mustache is unescaped
|
||||
return that.find(name, context);
|
||||
default: // escape the value
|
||||
return that.escape(that.find(name, context));
|
||||
}
|
||||
};
|
||||
var lines = template.split("\n");
|
||||
for(var i = 0; i < lines.length; i++) {
|
||||
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
|
||||
if(!in_recursion) {
|
||||
this.send(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(in_recursion) {
|
||||
return lines.join("\n");
|
||||
}
|
||||
},
|
||||
|
||||
set_delimiters: function(delimiters) {
|
||||
var dels = delimiters.split(" ");
|
||||
this.otag = this.escape_regex(dels[0]);
|
||||
this.ctag = this.escape_regex(dels[1]);
|
||||
},
|
||||
|
||||
escape_regex: function(text) {
|
||||
// thank you Simon Willison
|
||||
if(!arguments.callee.sRE) {
|
||||
var specials = [
|
||||
'/', '.', '*', '+', '?', '|',
|
||||
'(', ')', '[', ']', '{', '}', '\\'
|
||||
];
|
||||
arguments.callee.sRE = new RegExp(
|
||||
'(\\' + specials.join('|\\') + ')', 'g'
|
||||
);
|
||||
}
|
||||
return text.replace(arguments.callee.sRE, '\\$1');
|
||||
},
|
||||
|
||||
/*
|
||||
find `name` in current `context`. That is find me a value
|
||||
from the view object
|
||||
*/
|
||||
find: function(name, context) {
|
||||
name = this.trim(name);
|
||||
|
||||
// Checks whether a value is thruthy or false or 0
|
||||
function is_kinda_truthy(bool) {
|
||||
return bool === false || bool === 0 || bool;
|
||||
}
|
||||
|
||||
var value = context;
|
||||
var path = name.split(/\./);
|
||||
for(var i = 0; i < path.length; i++) {
|
||||
name = path[i];
|
||||
if(value && is_kinda_truthy(value[name])) {
|
||||
value = value[name];
|
||||
} else if(i == 0 && is_kinda_truthy(this.context[name])) {
|
||||
value = this.context[name];
|
||||
} else {
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof value === "function") {
|
||||
return value.apply(context);
|
||||
}
|
||||
if(value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
// silently ignore unkown variables
|
||||
return "";
|
||||
},
|
||||
|
||||
// Utility methods
|
||||
|
||||
/* includes tag */
|
||||
includes: function(needle, haystack) {
|
||||
return haystack.indexOf(this.otag + needle) != -1;
|
||||
},
|
||||
|
||||
/*
|
||||
Does away with nasty characters
|
||||
*/
|
||||
escape: function(s) {
|
||||
s = String(s === null ? "" : s);
|
||||
return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
|
||||
switch(s) {
|
||||
case "&": return "&";
|
||||
case "\\": return "\\\\";
|
||||
case '"': return '"';
|
||||
case "'": return ''';
|
||||
case "<": return "<";
|
||||
case ">": return ">";
|
||||
default: return s;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// by @langalex, support for arrays of strings
|
||||
create_context: function(_context) {
|
||||
if(this.is_object(_context)) {
|
||||
return _context;
|
||||
} else {
|
||||
var iterator = ".";
|
||||
if(this.pragmas["IMPLICIT-ITERATOR"]) {
|
||||
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
|
||||
}
|
||||
var ctx = {};
|
||||
ctx[iterator] = _context;
|
||||
return ctx;
|
||||
}
|
||||
},
|
||||
|
||||
is_object: function(a) {
|
||||
return a && typeof a == "object";
|
||||
},
|
||||
|
||||
is_array: function(a) {
|
||||
return Object.prototype.toString.call(a) === '[object Array]';
|
||||
},
|
||||
|
||||
/*
|
||||
Gets rid of leading and trailing whitespace
|
||||
*/
|
||||
trim: function(s) {
|
||||
return s.replace(/^\s*|\s*$/g, "");
|
||||
},
|
||||
|
||||
/*
|
||||
Why, why, why? Because IE. Cry, cry cry.
|
||||
*/
|
||||
map: function(array, fn) {
|
||||
if (typeof array.map == "function") {
|
||||
return array.map(fn);
|
||||
} else {
|
||||
var r = [];
|
||||
var l = array.length;
|
||||
for(var i = 0; i < l; i++) {
|
||||
r.push(fn(array[i]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return({
|
||||
name: "mustache.js",
|
||||
version: "0.3.1-dev",
|
||||
|
||||
/*
|
||||
Turns a template and view into HTML
|
||||
*/
|
||||
to_html: function(template, view, partials, send_fun) {
|
||||
var renderer = new Renderer();
|
||||
if(send_fun) {
|
||||
renderer.send = send_fun;
|
||||
}
|
||||
renderer.render(template, view, partials);
|
||||
if(!send_fun) {
|
||||
return renderer.buffer.join("\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
}();
|
||||
|
||||
|
||||
exports.name = Mustache.name;
|
||||
exports.version = Mustache.version;
|
||||
|
||||
exports.to_html = function() {
|
||||
return Mustache.to_html.apply(this, arguments);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
|
||||
This test demonstrates the time difference between document's DOMContentLoaded and window's load events.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
startTS = new Date().getTime();
|
||||
onDOMContentLoadedTS = 0; // default for browsers where DOMCL is not supported
|
||||
</script>
|
||||
<title>DOMContentLoaded test</title>
|
||||
<script src="../build/angular.min.js" ng:autobind></script>
|
||||
<script>
|
||||
angular.element(document).bind('DOMContentLoaded', function(e) {onDOMContentLoadedTS = new Date().getTime()});
|
||||
angular.element(window).bind('load', function(e) {
|
||||
onloadTS = new Date().getTime();
|
||||
log.innerHTML = 'start: ' + new Date(startTS) + '<br/>DOMContentLoaded: +' + (onDOMContentLoadedTS - startTS) + 'ms<br/> load: +' + (onloadTS - startTS) + 'ms';
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>DOMContentLoaded test</h1>
|
||||
<p>{{ 'yay!' || 'angular starting...' }}</p>
|
||||
|
||||
<img width="100px" src="http://lh5.ggpht.com/_BLyMhylclm0/TST_bbGH0zI/AAAAAAAAATY/oNUn9kivKN8/s912/1020047.jpg" />
|
||||
<img width="100px" src="http://lh5.ggpht.com/_MqEybfAuUFk/TSOOiegUlPI/AAAAAAAADHY/AEwEWc64_-M/s800/IMG_7294.JPG" />
|
||||
<img width="100px" src="http://lh3.ggpht.com/_LdjD3ua8rpE/TSOW99rwjZI/AAAAAAAAFC0/0qJRhhN45RM/s912/Saison%2010%20%2834%29.JPG" />
|
||||
<img width="100px" src="http://lh6.ggpht.com/_oy_-am3CVUw/TSOQBddZpwI/AAAAAAAACaw/ogFgoD79bVE/s912/P1100886.JPG" />
|
||||
<img width="100px" src="http://lh4.ggpht.com/_srSaA7ZN7oc/TDdxXbA_i1I/AAAAAAAAQ2w/ii3vgrnfCrM/s800/Urlaub10%20157.jpg" />
|
||||
<img width="100px" src="http://lh5.ggpht.com/_y6vXu6iRrfM/SIaYhRQBYNI/AAAAAAAAAmE/lV2NYwxtsQM/s912/North%20Dakota%20Trip%20014.JPG" />
|
||||
<img width="100px" src="http://lh5.ggpht.com/_Jjv9cIn9cS8/RuwZCgfOl6I/AAAAAAAAAOc/QrrMe8vpawg/s800/Shark%20Trip%20-%20day%202%20513.JPG" />
|
||||
|
||||
<p id="log"></p>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,49 @@
|
||||
def generate_object(f, objName, iterations)
|
||||
f.write("var #{objName}='[");
|
||||
|
||||
iterations.times do |i|
|
||||
f.write('{')
|
||||
|
||||
f.write('"simpleStringProperty":') #23
|
||||
f.write('"some string value ' + ('%07d' % i) + '"') #27
|
||||
f.write(',')
|
||||
|
||||
f.write('"stringWithQuotes":') #19
|
||||
f.write('"some string with \\\\"quotes\\\\" ' + ('%07d' % i) + '"') #36
|
||||
f.write(',')
|
||||
|
||||
f.write('"stringWithUnicode":')
|
||||
f.write('"short string with \\u1234 unicode \\u2345 chars ' + ('%07d' % i) + '"')
|
||||
f.write(',')
|
||||
|
||||
f.write('"aNumber":') #10
|
||||
f.write(i) #?
|
||||
f.write(',')
|
||||
|
||||
f.write('"smallArray":')
|
||||
f.write('["a",23,"b",42,' + i.to_s + ']')
|
||||
f.write(',')
|
||||
|
||||
f.write('"smallObj":')
|
||||
f.write('{"foo":"bar","baz":543,"num":' + i.to_s + ',"fuz":"fuz buz huz duz ' + i.to_s + '"}')
|
||||
f.write(',')
|
||||
|
||||
f.write('"timeStamp":')
|
||||
f.write('"2010-12-22T04:58:01.' + ("%03d" % (i%1000)) + '"')
|
||||
|
||||
f.write('},')
|
||||
end
|
||||
|
||||
f.write('"just a padding string"]\';' + "\n\n");
|
||||
end
|
||||
|
||||
file_path = File.join(File.dirname(__FILE__), 'jsonParserPayload.js')
|
||||
|
||||
File.open(file_path, 'w') do |f|
|
||||
generate_object(f, 'superTinyJsonString', 1) #~300b
|
||||
generate_object(f, 'tinyJsonString', 3) #~1kb
|
||||
generate_object(f, 'smallJsonString', 30) #~10kb
|
||||
generate_object(f, 'mediumJsonString', 600) #~200kb
|
||||
generate_object(f, 'largeJsonString', 2000) #~650kb
|
||||
end
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
describe('json', function() {
|
||||
|
||||
it('angular parser', function() {
|
||||
var duration = time(function() {
|
||||
expect(angular.fromJson(largeJsonString)).toBeTruthy();
|
||||
}, 1);
|
||||
|
||||
dump(duration/1 + ' ms per iteration');
|
||||
});
|
||||
|
||||
|
||||
it('angular delegating to native parser', function() {
|
||||
var duration = time(function() {
|
||||
expect(angular.fromJson(largeJsonString, true)).toBeTruthy();
|
||||
}, 100);
|
||||
|
||||
dump(duration/100 + ' ms per iteration');
|
||||
});
|
||||
|
||||
|
||||
it('native json', function() {
|
||||
var duration = time(function() {
|
||||
expect(JSON.parse(largeJsonString)).toBeTruthy();
|
||||
}, 100);
|
||||
|
||||
dump(duration/100 + ' ms per iteration');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script>
|
||||
function el(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
function update() {
|
||||
el("output").innerHTML = el("input").value;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Your name: <input id="input" type="text" value="World"
|
||||
onkeydown="setTimeout(update,0)"/>
|
||||
<hr/>
|
||||
Hello <span id="output">{{yourname}}</span>!
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
if (window.jstestdriver) {
|
||||
jstd = jstestdriver;
|
||||
dump = angular.bind(jstd.console, jstd.console.log);
|
||||
}
|
||||
|
||||
function time(fn, times) {
|
||||
times = times || 1;
|
||||
|
||||
var i,
|
||||
start,
|
||||
duration = 0;
|
||||
|
||||
for (i=0; i<times; i++) {
|
||||
start = Date.now();
|
||||
fn();
|
||||
duration += Date.now() - start;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<body>
|
||||
<span ng:init='x = {d:3}; x1 = {bar:[x,5]}; x1.bar[0].d = 4'>
|
||||
<input name="x1.bar[0].d" type="text"></input>
|
||||
<input name="x.d" type="text"></input>
|
||||
<span> {{x1}} -- {{x1.bar[0].d}}</span>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<head>
|
||||
<script type="text/javascript" src="../lib/jquery/jquery-1.4.2.js"></script>
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<head>
|
||||
<body>
|
||||
<select name='selection0' style="display:block;">
|
||||
<option ng:repeat='value in ["FOO","BAR"]'">{{value}}</option>
|
||||
</select>
|
||||
{{selection0}} <-- FOO should be shown here
|
||||
|
||||
<hr/>
|
||||
|
||||
<select ng:init="selection1='ignore'" name='selection1' style="display:block;">
|
||||
<option ng:repeat='value in ["FOO","BAR"]' ng:bind-attr="{selected:'{{value==\'BAR\'}}'}">{{value}}</option>
|
||||
</select>
|
||||
{{selection1}} <-- BAR should be shown here
|
||||
|
||||
<hr/>
|
||||
|
||||
<select ng:init="selection2=1" name="selection2" style="display:block;">
|
||||
<option value="{{$index}}" ng:repeat="opt in ['zero', 'one']">{{opt}}</option>
|
||||
</select>
|
||||
{{selection2}} <-- 1 should be shown here
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:ng="http://angularjs.org">
|
||||
<script type="text/javascript" src="../src/angular-bootstrap.js" ng:autobind></script>
|
||||
<body>
|
||||
<textarea name="html" rows="10" cols="100"></textarea>
|
||||
<div>{{html|html}}</div>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --browserTimeout 20000 --config jsTestDriver-coverage.conf
|
||||
@@ -1 +1,3 @@
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --browserTimeout 20000
|
||||
#!/bin/bash
|
||||
|
||||
java -jar lib/jstestdriver/JsTestDriver.jar --port 9876 --browserTimeout 90000
|
||||
|
||||
+563
-354
File diff suppressed because it is too large
Load Diff
+19
-9
@@ -1,17 +1,27 @@
|
||||
var browserSingleton;
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.service.$browser
|
||||
* @requires $log
|
||||
*
|
||||
* @description
|
||||
* Represents the browser.
|
||||
*/
|
||||
angularService('$browser', function($log){
|
||||
if (!browserSingleton) {
|
||||
browserSingleton = new Browser(
|
||||
window.location,
|
||||
jqLite(window.document),
|
||||
jqLite(window.document.getElementsByTagName('head')[0]),
|
||||
XHR,
|
||||
$log);
|
||||
browserSingleton.startPoller(50, function(delay, fn){setTimeout(delay,fn);});
|
||||
browserSingleton = new Browser(window, jqLite(window.document), jqLite(window.document.body),
|
||||
XHR, $log);
|
||||
var addPollFn = browserSingleton.addPollFn;
|
||||
browserSingleton.addPollFn = function(){
|
||||
browserSingleton.addPollFn = addPollFn;
|
||||
browserSingleton.startPoller(100, function(delay, fn){setTimeout(delay,fn);});
|
||||
return addPollFn.apply(browserSingleton, arguments);
|
||||
};
|
||||
browserSingleton.bind();
|
||||
}
|
||||
return browserSingleton;
|
||||
}, {inject:['$log']});
|
||||
}, {$inject:['$log']});
|
||||
|
||||
extend(angular, {
|
||||
'element': jqLite,
|
||||
@@ -20,7 +30,7 @@ extend(angular, {
|
||||
'copy': copy,
|
||||
'extend': extend,
|
||||
'equals': equals,
|
||||
'foreach': foreach,
|
||||
'forEach': forEach,
|
||||
'injector': createInjector,
|
||||
'noop':noop,
|
||||
'bind':bind,
|
||||
|
||||
+251
-45
@@ -8,8 +8,29 @@ var XHR = window.XMLHttpRequest || function () {
|
||||
throw new Error("This browser does not support XMLHttpRequest.");
|
||||
};
|
||||
|
||||
function Browser(location, document, head, XHR, $log) {
|
||||
var self = this;
|
||||
/**
|
||||
* @private
|
||||
* @name Browser
|
||||
*
|
||||
* @description
|
||||
* Constructor for the object exposed as $browser service.
|
||||
*
|
||||
* This object has two goals:
|
||||
*
|
||||
* - hide all the global state in the browser caused by the window object
|
||||
* - abstract away all the browser specific features and inconsistencies
|
||||
*
|
||||
* @param {object} window The global window object.
|
||||
* @param {object} document jQuery wrapped document.
|
||||
* @param {object} body jQuery wrapped document.body.
|
||||
* @param {function()} XHR XMLHttpRequest constructor.
|
||||
* @param {object} $log console.log or an object with the same interface.
|
||||
*/
|
||||
function Browser(window, document, body, XHR, $log) {
|
||||
var self = this,
|
||||
location = window.location,
|
||||
setTimeout = window.setTimeout;
|
||||
|
||||
self.isMock = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
@@ -19,7 +40,43 @@ function Browser(location, document, head, XHR, $log) {
|
||||
var outstandingRequestCount = 0;
|
||||
var outstandingRequestCallbacks = [];
|
||||
|
||||
self.xhr = function(method, url, post, callback){
|
||||
|
||||
/**
|
||||
* Executes the `fn` function (supports currying) and decrements the `outstandingRequestCallbacks`
|
||||
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
|
||||
*/
|
||||
function completeOutstandingRequest(fn) {
|
||||
try {
|
||||
fn.apply(null, slice.call(arguments, 1));
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (outstandingRequestCount === 0) {
|
||||
while(outstandingRequestCallbacks.length) {
|
||||
try {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
} catch (e) {
|
||||
$log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#xhr
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} method Requested method (get|post|put|delete|head|json)
|
||||
* @param {string} url Requested url
|
||||
* @param {string=} post Post data to send
|
||||
* @param {function(number, string)} callback Function that will be called on response
|
||||
*
|
||||
* @description
|
||||
* Send ajax request
|
||||
*/
|
||||
self.xhr = function(method, url, post, callback) {
|
||||
if (isFunction(post)) {
|
||||
callback = post;
|
||||
post = _null;
|
||||
@@ -34,7 +91,7 @@ function Browser(location, document, head, XHR, $log) {
|
||||
window[callbackId] = _undefined;
|
||||
callback(200, data);
|
||||
};
|
||||
head.append(script);
|
||||
body.append(script);
|
||||
} else {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
@@ -44,26 +101,22 @@ function Browser(location, document, head, XHR, $log) {
|
||||
outstandingRequestCount ++;
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
try {
|
||||
callback(xhr.status || 200, xhr.responseText);
|
||||
} finally {
|
||||
outstandingRequestCount--;
|
||||
if (outstandingRequestCount === 0) {
|
||||
while(outstandingRequestCallbacks.length) {
|
||||
try {
|
||||
outstandingRequestCallbacks.pop()();
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
completeOutstandingRequest(callback, xhr.status || 200, xhr.responseText);
|
||||
}
|
||||
};
|
||||
xhr.send(post || '');
|
||||
}
|
||||
};
|
||||
|
||||
self.notifyWhenNoOutstandingRequests = function(callback){
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#notifyWhenNoOutstandingRequests
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {function()} callback Function that will be called when no outstanding request
|
||||
*/
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
if (outstandingRequestCount === 0) {
|
||||
callback();
|
||||
} else {
|
||||
@@ -75,27 +128,51 @@ function Browser(location, document, head, XHR, $log) {
|
||||
// Poll Watcher API
|
||||
//////////////////////////////////////////////////////////////
|
||||
var pollFns = [];
|
||||
function poll(){
|
||||
foreach(pollFns, function(pollFn){ pollFn(); });
|
||||
}
|
||||
self.poll = poll;
|
||||
|
||||
/**
|
||||
* Adds a function to the list of functions that poller periodically executes
|
||||
* @return {Function} the added function
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#poll
|
||||
* @methodOf angular.service.$browser
|
||||
*/
|
||||
self.addPollFn = function(/**Function*/fn){
|
||||
self.poll = function() {
|
||||
forEach(pollFns, function(pollFn){ pollFn(); });
|
||||
};
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#addPollFn
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {function()} fn Poll function to add
|
||||
*
|
||||
* @description
|
||||
* Adds a function to the list of functions that poller periodically executes
|
||||
*
|
||||
* @returns {function()} the added function
|
||||
*/
|
||||
self.addPollFn = function(fn) {
|
||||
pollFns.push(fn);
|
||||
return fn;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures the poller to run in the specified intervals, using the specified setTimeout fn and
|
||||
* kicks it off.
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#startPoller
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {number} interval How often should browser call poll functions (ms)
|
||||
* @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
|
||||
*
|
||||
* @description
|
||||
* Configures the poller to run in the specified intervals, using the specified
|
||||
* setTimeout fn and kicks it off.
|
||||
*/
|
||||
self.startPoller = function(/**number*/interval, /**Function*/setTimeout){
|
||||
self.startPoller = function(interval, setTimeout) {
|
||||
(function check(){
|
||||
poll();
|
||||
self.poll();
|
||||
setTimeout(check, interval);
|
||||
})();
|
||||
};
|
||||
@@ -103,15 +180,74 @@ function Browser(location, document, head, XHR, $log) {
|
||||
//////////////////////////////////////////////////////////////
|
||||
// URL API
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#setUrl
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url New url
|
||||
*
|
||||
* @description
|
||||
* Sets browser's url
|
||||
*/
|
||||
self.setUrl = function(url) {
|
||||
var existingURL = location.href;
|
||||
if (!existingURL.match(/#/)) existingURL += '#';
|
||||
if (!url.match(/#/)) url += '#';
|
||||
location.href = url;
|
||||
};
|
||||
self.getUrl = function() {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#getUrl
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Get current browser's url
|
||||
*
|
||||
* @returns {string} Browser's url
|
||||
*/
|
||||
self.getUrl = function() {
|
||||
return location.href;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#onHashChange
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Detects if browser support onhashchange events and register a listener otherwise registers
|
||||
* $browser poller. The `listener` will then get called when the hash changes.
|
||||
*
|
||||
* The listener gets called with either HashChangeEvent object or simple object that also contains
|
||||
* `oldURL` and `newURL` properties.
|
||||
*
|
||||
* NOTE: this is a api is intended for sole use by $location service. Please use
|
||||
* {@link angular.service.$location $location service} to monitor hash changes in angular apps.
|
||||
*
|
||||
* @param {function(event)} listener Listener function to be called when url hash changes.
|
||||
* @return {function()} Returns the registered listener fn - handy if the fn is anonymous.
|
||||
*/
|
||||
self.onHashChange = function(listener) {
|
||||
if ('onhashchange' in window) {
|
||||
jqLite(window).bind('hashchange', listener);
|
||||
} else {
|
||||
var lastBrowserUrl = self.getUrl();
|
||||
|
||||
self.addPollFn(function() {
|
||||
if (lastBrowserUrl != self.getUrl()) {
|
||||
listener();
|
||||
}
|
||||
});
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Cookies API
|
||||
@@ -121,19 +257,28 @@ function Browser(location, document, head, XHR, $log) {
|
||||
var lastCookieString = '';
|
||||
|
||||
/**
|
||||
* The cookies method provides a 'private' low level access to browser cookies. It is not meant to
|
||||
* be used directly, use the $cookie service instead.
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#cookies
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string=} name Cookie name
|
||||
* @param {string=} value Cokkie value
|
||||
*
|
||||
* @description
|
||||
* The cookies method provides a 'private' low level access to browser cookies.
|
||||
* It is not meant to be used directly, use the $cookie service instead.
|
||||
*
|
||||
* The return values vary depending on the arguments that the method was called with as follows:
|
||||
* <ul><li>
|
||||
* cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it
|
||||
* </li><li>
|
||||
* cookies(name, value) -> set name to value, if value is undefined delete the cookie
|
||||
* </li><li>
|
||||
* cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)
|
||||
* </li></ul>
|
||||
* <ul>
|
||||
* <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li>
|
||||
* <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li>
|
||||
* <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @returns {Object} Hash of all cookies (if called without any parameter)
|
||||
*/
|
||||
self.cookies = function (/**string*/name, /**string*/value){
|
||||
self.cookies = function (name, value) {
|
||||
var cookieLength, cookieArray, i, keyValue;
|
||||
|
||||
if (name) {
|
||||
@@ -171,11 +316,56 @@ function Browser(location, document, head, XHR, $log) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#defer
|
||||
* @methodOf angular.service.$browser
|
||||
* @param {function()} fn A function, who's execution should be defered.
|
||||
* @param {int=} [delay=0] of milliseconds to defer the function execution.
|
||||
*
|
||||
* @description
|
||||
* Executes a fn asynchroniously via `setTimeout(fn, delay)`.
|
||||
*
|
||||
* Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
|
||||
* `setTimeout` in tests, the fns are queued in an array, which can be programaticaly flushed via
|
||||
* `$browser.defer.flush()`.
|
||||
*
|
||||
*/
|
||||
self.defer = function(fn, delay) {
|
||||
outstandingRequestCount++;
|
||||
setTimeout(function() { completeOutstandingRequest(fn); }, delay || 0);
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Misc API
|
||||
//////////////////////////////////////////////////////////////
|
||||
var hoverListener = noop;
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#hover
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Set hover listener.
|
||||
*
|
||||
* @param {function(Object, boolean)} listener Function that will be called when hover event
|
||||
* occurs.
|
||||
*/
|
||||
self.hover = function(listener) { hoverListener = listener; };
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#bind
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @description
|
||||
* Register hover function to real browser
|
||||
*/
|
||||
self.bind = function() {
|
||||
document.bind("mouseover", function(event){
|
||||
hoverListener(jqLite(msie ? event.srcElement : event.target), true);
|
||||
@@ -189,25 +379,41 @@ function Browser(location, document, head, XHR, $log) {
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#addCss
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url Url to css file
|
||||
* @description
|
||||
* Adds a stylesheet tag to the head.
|
||||
*/
|
||||
self.addCss = function(/**string*/url) {
|
||||
self.addCss = function(url) {
|
||||
var link = jqLite(rawDocument.createElement('link'));
|
||||
link.attr('rel', 'stylesheet');
|
||||
link.attr('type', 'text/css');
|
||||
link.attr('href', url);
|
||||
head.append(link);
|
||||
body.append(link);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#addJs
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @param {string} url Url to js file
|
||||
* @param {string=} dom_id Optional id for the script tag
|
||||
*
|
||||
* @description
|
||||
* Adds a script tag to the head.
|
||||
*/
|
||||
self.addJs = function(/**string*/url, /**string*/dom_id) {
|
||||
self.addJs = function(url, dom_id) {
|
||||
var script = jqLite(rawDocument.createElement('script'));
|
||||
script.attr('type', 'text/javascript');
|
||||
script.attr('src', url);
|
||||
if (dom_id) script.attr('id', dom_id);
|
||||
head.append(script);
|
||||
body.append(script);
|
||||
};
|
||||
}
|
||||
|
||||
+79
-13
@@ -16,8 +16,8 @@ Template.prototype = {
|
||||
init: function(element, scope) {
|
||||
var inits = {};
|
||||
this.collectInits(element, inits, scope);
|
||||
foreachSorted(inits, function(queue){
|
||||
foreach(queue, function(fn) {fn();});
|
||||
forEachSorted(inits, function(queue){
|
||||
forEach(queue, function(fn) {fn();});
|
||||
});
|
||||
},
|
||||
|
||||
@@ -30,11 +30,12 @@ Template.prototype = {
|
||||
if (this.newScope) {
|
||||
childScope = createScope(scope);
|
||||
scope.$onEval(childScope.$eval);
|
||||
element.data($$scope, childScope);
|
||||
}
|
||||
foreach(this.inits, function(fn) {
|
||||
forEach(this.inits, function(fn) {
|
||||
queue.push(function() {
|
||||
childScope.$tryEval(function(){
|
||||
return childScope.$inject(fn, childScope, element);
|
||||
return childScope.$service(fn, childScope, element);
|
||||
}, element);
|
||||
});
|
||||
});
|
||||
@@ -68,6 +69,18 @@ Template.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Function walks up the element chain looking for the scope associated with the give element.
|
||||
*/
|
||||
function retrieveScope(element) {
|
||||
var scope;
|
||||
element = jqLite(element);
|
||||
while (element && element.length && !(scope = element.data($$scope))) {
|
||||
element = element.parent();
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
///////////////////////////////////
|
||||
//Compiler
|
||||
//////////////////////////////////
|
||||
@@ -79,24 +92,25 @@ function Compiler(markup, attrMarkup, directives, widgets){
|
||||
}
|
||||
|
||||
Compiler.prototype = {
|
||||
compile: function(rawElement) {
|
||||
rawElement = jqLite(rawElement);
|
||||
compile: function(element) {
|
||||
element = jqLite(element);
|
||||
var index = 0,
|
||||
template,
|
||||
parent = rawElement.parent();
|
||||
parent = element.parent();
|
||||
if (parent && parent[0]) {
|
||||
parent = parent[0];
|
||||
for(var i = 0; i < parent.childNodes.length; i++) {
|
||||
if (parent.childNodes[i] == rawElement[0]) {
|
||||
if (parent.childNodes[i] == element[0]) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
template = this.templatize(rawElement, index, 0) || new Template();
|
||||
template = this.templatize(element, index, 0) || new Template();
|
||||
return function(element, parentScope){
|
||||
element = jqLite(element);
|
||||
var scope = parentScope && parentScope.$eval ?
|
||||
parentScope : createScope(parentScope);
|
||||
element.data($$scope, scope);
|
||||
return extend(scope, {
|
||||
$element:element,
|
||||
$init: function() {
|
||||
@@ -109,6 +123,58 @@ Compiler.prototype = {
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc directive
|
||||
* @name angular.directive.ng:eval-order
|
||||
*
|
||||
* @description
|
||||
* Normally the view is updated from top to bottom. This usually is
|
||||
* not a problem, but under some circumstances the values for data
|
||||
* is not available until after the full view is computed. If such
|
||||
* values are needed before they are computed the order of
|
||||
* evaluation can be change using ng:eval-order
|
||||
*
|
||||
* @element ANY
|
||||
* @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant
|
||||
*
|
||||
* @exampleDescription
|
||||
* try changing the invoice and see that the Total will lag in evaluation
|
||||
* @example
|
||||
<div>TOTAL: without ng:eval-order {{ items.$sum('total') | currency }}</div>
|
||||
<div ng:eval-order='LAST'>TOTAL: with ng:eval-order {{ items.$sum('total') | currency }}</div>
|
||||
<table ng:init="items=[{qty:1, cost:9.99, desc:'gadget'}]">
|
||||
<tr>
|
||||
<td>QTY</td>
|
||||
<td>Description</td>
|
||||
<td>Cost</td>
|
||||
<td>Total</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ng:repeat="item in items">
|
||||
<td><input name="item.qty"/></td>
|
||||
<td><input name="item.desc"/></td>
|
||||
<td><input name="item.cost"/></td>
|
||||
<td>{{item.total = item.qty * item.cost | currency}}</td>
|
||||
<td><a href="" ng:click="items.$remove(item)">X</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3"><a href="" ng:click="items.$add()">add</a></td>
|
||||
<td>{{ items.$sum('total') | currency }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
*
|
||||
* @scenario
|
||||
it('should check ng:format', function(){
|
||||
expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
|
||||
expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$9.99');
|
||||
input('item.qty').enter('2');
|
||||
expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
|
||||
expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$19.98');
|
||||
});
|
||||
*/
|
||||
|
||||
templatize: function(element, elementIndex, priority){
|
||||
var self = this,
|
||||
widget,
|
||||
@@ -116,7 +182,7 @@ Compiler.prototype = {
|
||||
directiveFns = self.directives,
|
||||
descend = true,
|
||||
directives = true,
|
||||
elementName = nodeName(element),
|
||||
elementName = nodeName_(element),
|
||||
template,
|
||||
selfApi = {
|
||||
compile: bind(self, self.compile),
|
||||
@@ -166,7 +232,7 @@ Compiler.prototype = {
|
||||
for(var i=0, child=element[0].childNodes;
|
||||
i<child.length; i++) {
|
||||
if (isTextNode(child[i])) {
|
||||
foreach(self.markup, function(markup){
|
||||
forEach(self.markup, function(markup){
|
||||
if (i<child.length) {
|
||||
var textNode = jqLite(child[i]);
|
||||
markup.call(selfApi, textNode.text(), textNode, element);
|
||||
@@ -179,7 +245,7 @@ Compiler.prototype = {
|
||||
if (directives) {
|
||||
// Process attributes/directives
|
||||
eachAttribute(element, function(value, name){
|
||||
foreach(self.attrMarkup, function(markup){
|
||||
forEach(self.attrMarkup, function(markup){
|
||||
markup.call(selfApi, value, name, element);
|
||||
});
|
||||
});
|
||||
@@ -221,6 +287,6 @@ function eachAttribute(element, fn){
|
||||
}
|
||||
attrValue[name] = value;
|
||||
}
|
||||
foreachSorted(attrValue, fn);
|
||||
forEachSorted(attrValue, fn);
|
||||
}
|
||||
|
||||
|
||||
+36
-17
@@ -1,9 +1,20 @@
|
||||
/**
|
||||
* Create an inject method
|
||||
* @param providerScope provider's "this"
|
||||
* @param providers a function(name) which returns provider function
|
||||
* @param cache place where instances are saved for reuse
|
||||
* @returns {Function}
|
||||
* @ngdoc function
|
||||
* @name angular.injector
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Creates an inject function that can be used for dependency injection.
|
||||
*
|
||||
* @param {Object=} [providerScope={}] provider's `this`
|
||||
* @param {Object.<string, function()>=} [providers=angular.service] Map of provider (factory)
|
||||
* function.
|
||||
* @param {Object.<string, function()>=} [cache={}] Place where instances are saved for reuse. Can
|
||||
* also be used to override services speciafied by `providers` (useful in tests).
|
||||
* @returns {function()} Injector function.
|
||||
*
|
||||
* @TODO These docs need a lot of work. Specifically the returned function should be described in
|
||||
* great detail + we need to provide some examples.
|
||||
*/
|
||||
function createInjector(providerScope, providers, cache) {
|
||||
providers = providers || angularService;
|
||||
@@ -19,13 +30,14 @@ function createInjector(providerScope, providers, cache) {
|
||||
* string: return an instance for the injection key.
|
||||
* array of keys: returns an array of instances.
|
||||
* function: look at $inject property of function to determine instances
|
||||
* and then call the function with instances and scope. Any
|
||||
* additional arguments are passed on to function.
|
||||
* and then call the function with instances and `scope`. Any
|
||||
* additional arguments (`args`) are appended to the function
|
||||
* arguments.
|
||||
* object: initialize eager providers and publish them the ones with publish here.
|
||||
* none: same as object but use providerScope as place to publish.
|
||||
*/
|
||||
return function inject(value, scope, args){
|
||||
var returnValue, provider, creation;
|
||||
var returnValue, provider;
|
||||
if (isString(value)) {
|
||||
if (!cache.hasOwnProperty(value)) {
|
||||
provider = providers[value];
|
||||
@@ -35,25 +47,32 @@ function createInjector(providerScope, providers, cache) {
|
||||
returnValue = cache[value];
|
||||
} else if (isArray(value)) {
|
||||
returnValue = [];
|
||||
foreach(value, function(name) {
|
||||
forEach(value, function(name) {
|
||||
returnValue.push(inject(name));
|
||||
});
|
||||
} else if (isFunction(value)) {
|
||||
returnValue = inject(value.$inject || []);
|
||||
returnValue = value.apply(scope, concat(returnValue, arguments, 2));
|
||||
} else if (isObject(value)) {
|
||||
foreach(providers, function(provider, name){
|
||||
creation = provider.$creation;
|
||||
if (creation == 'eager') {
|
||||
forEach(providers, function(provider, name){
|
||||
if (provider.$eager)
|
||||
inject(name);
|
||||
}
|
||||
if (creation == 'eager-published') {
|
||||
setter(value, name, inject(name));
|
||||
}
|
||||
|
||||
if (provider.$creation)
|
||||
throw new Error("Failed to register service '" + name +
|
||||
"': $creation property is unsupported. Use $eager:true or see release notes.");
|
||||
});
|
||||
} else {
|
||||
returnValue = inject(providerScope);
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function injectService(services, fn) {
|
||||
return extend(fn, {$inject:services});;
|
||||
}
|
||||
|
||||
function injectUpdateView(fn) {
|
||||
return injectService(['$updateView'], fn);
|
||||
}
|
||||
|
||||
+51
-4
@@ -1,22 +1,69 @@
|
||||
var array = [].constructor;
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.toJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Serializes the input into a JSON formated string.
|
||||
*
|
||||
* @param {Object|Array|Date|string|number} obj Input to jsonify.
|
||||
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
|
||||
* @returns {string} Jsonified string representing `obj`.
|
||||
*/
|
||||
function toJson(obj, pretty) {
|
||||
var buf = [];
|
||||
toJsonArray(buf, obj, pretty ? "\n " : _null, []);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
function fromJson(json) {
|
||||
if (!json) return json;
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.fromJson
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Deserializes a string in the JSON format.
|
||||
*
|
||||
* @param {string} json JSON string to deserialize.
|
||||
* @param {boolean} [useNative=false] Use native JSON parser if available
|
||||
* @returns {Object|Array|Date|string|number} Deserialized thingy.
|
||||
*/
|
||||
function fromJson(json, useNative) {
|
||||
if (!isString(json)) return json;
|
||||
|
||||
var obj, p, expression;
|
||||
|
||||
try {
|
||||
var p = parser(json, true);
|
||||
var expression = p.primary();
|
||||
if (useNative && JSON && JSON.parse) {
|
||||
obj = JSON.parse(json);
|
||||
return transformDates(obj);
|
||||
}
|
||||
|
||||
p = parser(json, true);
|
||||
expression = p.primary();
|
||||
p.assertAllConsumed();
|
||||
return expression();
|
||||
|
||||
} catch (e) {
|
||||
error("fromJson error: ", json, e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
// TODO make forEach optionally recursive and remove this function
|
||||
function transformDates(obj) {
|
||||
if (isString(obj) && obj.length === DATE_ISOSTRING_LN) {
|
||||
return angularString.toDate(obj);
|
||||
} else if (isArray(obj) || isObject(obj)) {
|
||||
forEach(obj, function(val, name) {
|
||||
obj[name] = transformDates(val);
|
||||
});
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
angular['toJson'] = toJson;
|
||||
|
||||
+7
-6
@@ -4,7 +4,7 @@ function Route(template, defaults) {
|
||||
this.template = template = template + '#';
|
||||
this.defaults = defaults || {};
|
||||
var urlParams = this.urlParams = {};
|
||||
foreach(template.split(/\W/), function(param){
|
||||
forEach(template.split(/\W/), function(param){
|
||||
if (param && template.match(new RegExp(":" + param + "\\W"))) {
|
||||
urlParams[param] = true;
|
||||
}
|
||||
@@ -17,13 +17,13 @@ Route.prototype = {
|
||||
var self = this;
|
||||
var url = this.template;
|
||||
params = params || {};
|
||||
foreach(this.urlParams, function(_, urlParam){
|
||||
forEach(this.urlParams, function(_, urlParam){
|
||||
var value = params[urlParam] || self.defaults[urlParam] || "";
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W)"), value + "$1");
|
||||
});
|
||||
url = url.replace(/\/?#$/, '');
|
||||
var query = [];
|
||||
foreachSorted(params, function(value, key){
|
||||
forEachSorted(params, function(value, key){
|
||||
if (!self.urlParams[key]) {
|
||||
query.push(encodeURI(key) + '=' + encodeURI(value));
|
||||
}
|
||||
@@ -52,7 +52,7 @@ ResourceFactory.prototype = {
|
||||
actions = extend({}, ResourceFactory.DEFAULT_ACTIONS, actions);
|
||||
function extractParams(data){
|
||||
var ids = {};
|
||||
foreach(paramDefaults || {}, function(value, key){
|
||||
forEach(paramDefaults || {}, function(value, key){
|
||||
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
@@ -62,7 +62,7 @@ ResourceFactory.prototype = {
|
||||
copy(value || {}, this);
|
||||
}
|
||||
|
||||
foreach(actions, function(action, name){
|
||||
forEach(actions, function(action, name){
|
||||
var isPostOrPut = action.method == 'POST' || action.method == 'PUT';
|
||||
Resource[name] = function (a1, a2, a3) {
|
||||
var params = {};
|
||||
@@ -73,6 +73,7 @@ ResourceFactory.prototype = {
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
callback = a2;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
@@ -97,7 +98,7 @@ ResourceFactory.prototype = {
|
||||
if (status == 200) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
foreach(response, function(item){
|
||||
forEach(response, function(item){
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
|
||||
+431
-38
@@ -48,7 +48,7 @@ var scopeId = 0,
|
||||
getterFnCache = {},
|
||||
compileCache = {},
|
||||
JS_KEYWORDS = {};
|
||||
foreach(
|
||||
forEach(
|
||||
("abstract,boolean,break,byte,case,catch,char,class,const,continue,debugger,default," +
|
||||
"delete,do,double,else,enum,export,extends,false,final,finally,float,for,function,goto," +
|
||||
"if,implements,import,ininstanceof,intinterface,long,native,new,null,package,private," +
|
||||
@@ -61,12 +61,12 @@ function getterFn(path){
|
||||
if (fn) return fn;
|
||||
|
||||
var code = 'var l, fn, t;\n';
|
||||
foreach(path.split('.'), function(key) {
|
||||
forEach(path.split('.'), function(key) {
|
||||
key = (JS_KEYWORDS[key]) ? '["' + key + '"]' : '.' + key;
|
||||
code += 'if(!s) return s;\n' +
|
||||
'l=s;\n' +
|
||||
's=s' + key + ';\n' +
|
||||
'if(typeof s=="function") s = function(){ return l'+key+'.apply(l, arguments); };\n';
|
||||
'if(typeof s=="function" && !(s instanceof RegExp)) s = function(){ return l'+key+'.apply(l, arguments); };\n';
|
||||
if (key.charAt(1) == '$') {
|
||||
// special code for super-imposed functions
|
||||
var name = key.substr(2);
|
||||
@@ -101,25 +101,264 @@ function expressionCompile(exp){
|
||||
}
|
||||
|
||||
function errorHandlerFor(element, error) {
|
||||
elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error);
|
||||
elementError(element, NG_EXCEPTION, isDefined(error) ? formatError(error) : error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc overview
|
||||
* @name angular.scope
|
||||
*
|
||||
* @description
|
||||
* Scope is a JavaScript object and the execution context for expressions. You can think about
|
||||
* scopes as JavaScript objects that have extra APIs for registering watchers. A scope is the model
|
||||
* in the model-view-controller design pattern.
|
||||
*
|
||||
* A few other characteristics of scopes:
|
||||
*
|
||||
* - Scopes can be nested. A scope (prototypically) inherits properties from its parent scope.
|
||||
* - Scopes can be attached (bound) to the HTML DOM tree (the view).
|
||||
* - A scope {@link angular.scope.$become becomes} `this` for a controller.
|
||||
* - Scope's {@link angular.scope.$eval $eval} is used to update its view.
|
||||
* - Scopes can {@link angular.scope.$watch watch} properties and fire events.
|
||||
*
|
||||
* # Basic Operations
|
||||
* Scopes can be created by calling {@link angular.scope() angular.scope()} or by compiling HTML.
|
||||
*
|
||||
* {@link angular.widget Widgets} and data bindings register listeners on the current scope to get
|
||||
* notified of changes to the scope state. When notified, these listeners push the updated state
|
||||
* through to the DOM.
|
||||
*
|
||||
* Here is a simple scope snippet to show how you can interact with the scope.
|
||||
* <pre>
|
||||
var scope = angular.scope();
|
||||
scope.salutation = 'Hello';
|
||||
scope.name = 'World';
|
||||
|
||||
expect(scope.greeting).toEqual(undefined);
|
||||
|
||||
scope.$watch('name', function(){
|
||||
this.greeting = this.salutation + ' ' + this.name + '!';
|
||||
});
|
||||
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
scope.name = 'Misko';
|
||||
// scope.$eval() will propagate the change to listeners
|
||||
expect(scope.greeting).toEqual('Hello World!');
|
||||
|
||||
scope.$eval();
|
||||
expect(scope.greeting).toEqual('Hello Misko!');
|
||||
* </pre>
|
||||
*
|
||||
* # Inheritance
|
||||
* A scope can inherit from a parent scope, as in this example:
|
||||
* <pre>
|
||||
var parent = angular.scope();
|
||||
var child = angular.scope(parent);
|
||||
|
||||
parent.salutation = "Hello";
|
||||
child.name = "World";
|
||||
expect(child.salutation).toEqual('Hello');
|
||||
|
||||
child.salutation = "Welcome";
|
||||
expect(child.salutation).toEqual('Welcome');
|
||||
expect(parent.salutation).toEqual('Hello');
|
||||
* </pre>
|
||||
*
|
||||
* # Dependency Injection
|
||||
* Scope also acts as a simple dependency injection framework.
|
||||
*
|
||||
* **TODO**: more info needed
|
||||
*
|
||||
* # When scopes are evaluated
|
||||
* Anyone can update a scope by calling its {@link angular.scope.$eval $eval()} method. By default
|
||||
* angular widgets listen to user change events (e.g. the user enters text into text field), copy
|
||||
* the data from the widget to the scope (the MVC model), and then call the `$eval()` method on the
|
||||
* root scope to update dependents. This creates a spreadsheet-like behavior: the bound views update
|
||||
* immediately as the user types into the text field.
|
||||
*
|
||||
* Similarly, when a request to fetch data from a server is made and the response comes back, the
|
||||
* data is written into the model and then $eval() is called to push updates through to the view and
|
||||
* any other dependents.
|
||||
*
|
||||
* Because a change in the model that's triggered either by user input or by server response calls
|
||||
* `$eval()`, it is unnecessary to call `$eval()` from within your controller. The only time when
|
||||
* calling `$eval()` is needed, is when implementing a custom widget or service.
|
||||
*
|
||||
* Because scopes are inherited, the child scope `$eval()` overrides the parent `$eval()` method.
|
||||
* So to update the whole page you need to call `$eval()` on the root scope as `$root.$eval()`.
|
||||
*
|
||||
* Note: A widget that creates scopes (i.e. {@link angular.widget.@ng:repeat ng:repeat}) is
|
||||
* responsible for forwarding `$eval()` calls from the parent to those child scopes. That way,
|
||||
* calling $eval() on the root scope will update the whole page.
|
||||
*
|
||||
*
|
||||
* @TODO THESE PARAMS AND RETURNS ARE NOT RENDERED IN THE TEMPLATE!! FIX THAT!
|
||||
* @param {Object} parent The scope that should become the parent for the newly created scope.
|
||||
* @param {Object.<string, function()>=} providers Map of service factory which need to be provided
|
||||
* for the current scope. Usually {@link angular.service}.
|
||||
* @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
|
||||
* append/override services provided by `providers`.
|
||||
* @returns {Object} Newly created scope.
|
||||
*
|
||||
*
|
||||
* @exampleDescription
|
||||
* This example demonstrates scope inheritance and property overriding.
|
||||
*
|
||||
* In this example, the root scope encompasses the whole HTML DOM tree. This scope has `salutation`,
|
||||
* `name`, and `names` properties. The {@link angular.widget@ng:repeat ng:repeat} creates a child
|
||||
* scope, one for each element in the names array. The repeater also assigns $index and name into
|
||||
* the child scope.
|
||||
*
|
||||
* Notice that:
|
||||
*
|
||||
* - While the name is set in the child scope it does not change the name defined in the root scope.
|
||||
* - The child scope inherits the salutation property from the root scope.
|
||||
* - The $index property does not leak from the child scope to the root scope.
|
||||
*
|
||||
* @example
|
||||
<ul ng:init="salutation='Hello'; name='Misko'; names=['World', 'Earth']">
|
||||
<li ng:repeat="name in names">
|
||||
{{$index}}: {{salutation}} {{name}}!
|
||||
</li>
|
||||
</ul>
|
||||
<pre>
|
||||
$index={{$index}}
|
||||
salutation={{salutation}}
|
||||
name={{name}}</pre>
|
||||
|
||||
@scenario
|
||||
it('should inherit the salutation property and override the name property', function() {
|
||||
expect(using('.doc-example-live').repeater('li').row(0)).
|
||||
toEqual(['0', 'Hello', 'World']);
|
||||
expect(using('.doc-example-live').repeater('li').row(1)).
|
||||
toEqual(['1', 'Hello', 'Earth']);
|
||||
expect(using('.doc-example-live').element('pre').text()).
|
||||
toBe('$index=\nsalutation=Hello\nname=Misko');
|
||||
});
|
||||
|
||||
*/
|
||||
function createScope(parent, providers, instanceCache) {
|
||||
function Parent(){}
|
||||
parent = Parent.prototype = (parent || {});
|
||||
var instance = new Parent();
|
||||
var evalLists = {sorted:[]};
|
||||
var postList = [], postHash = {}, postId = 0;
|
||||
var $log, $exceptionHandler;
|
||||
|
||||
extend(instance, {
|
||||
'this': instance,
|
||||
$id: (scopeId++),
|
||||
$parent: parent,
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$bind
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Binds a function `fn` to the current scope. See: {@link angular.bind}.
|
||||
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
var fn = scope.$bind(function(){
|
||||
return this;
|
||||
});
|
||||
expect(fn()).toEqual(scope);
|
||||
</pre>
|
||||
*
|
||||
* @param {function()} fn Function to be bound.
|
||||
*/
|
||||
$bind: bind(instance, bind, instance),
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$get
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Returns the value for `property_chain` on the current scope. Unlike in JavaScript, if there
|
||||
* are any `undefined` intermediary properties, `undefined` is returned instead of throwing an
|
||||
* exception.
|
||||
*
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
expect(scope.$get('person.name')).toEqual(undefined);
|
||||
scope.person = {};
|
||||
expect(scope.$get('person.name')).toEqual(undefined);
|
||||
scope.person.name = 'misko';
|
||||
expect(scope.$get('person.name')).toEqual('misko');
|
||||
</pre>
|
||||
*
|
||||
* @param {string} property_chain String representing name of a scope property. Optionally
|
||||
* properties can be chained with `.` (dot), e.g. `'person.name.first'`
|
||||
* @returns {*} Value for the (nested) property.
|
||||
*/
|
||||
$get: bind(instance, getter, instance),
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$set
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Assigns a value to a property of the current scope specified via `property_chain`. Unlike in
|
||||
* JavaScript, if there are any `undefined` intermediary properties, empty objects are created
|
||||
* and assigned in to them instead of throwing an exception.
|
||||
*
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
expect(scope.person).toEqual(undefined);
|
||||
scope.$set('person.name', 'misko');
|
||||
expect(scope.person).toEqual({name:'misko'});
|
||||
expect(scope.person.name).toEqual('misko');
|
||||
</pre>
|
||||
*
|
||||
* @param {string} property_chain String representing name of a scope property. Optionally
|
||||
* properties can be chained with `.` (dot), e.g. `'person.name.first'`
|
||||
* @param {*} value Value to assign to the scope property.
|
||||
*/
|
||||
$set: bind(instance, setter, instance),
|
||||
|
||||
$eval: function $eval(exp) {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$eval
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Without the `exp` parameter triggers an eval cycle for this scope and its child scopes.
|
||||
*
|
||||
* With the `exp` parameter, compiles the expression to a function and calls it with `this` set
|
||||
* to the current scope and returns the result. In other words, evaluates `exp` as angular
|
||||
* expression in the context of the current scope.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.a = 1;
|
||||
scope.b = 2;
|
||||
|
||||
expect(scope.$eval('a+b')).toEqual(3);
|
||||
expect(scope.$eval(function(){ return this.a + this.b; })).toEqual(3);
|
||||
|
||||
scope.$onEval('sum = a+b');
|
||||
expect(scope.sum).toEqual(undefined);
|
||||
scope.$eval();
|
||||
expect(scope.sum).toEqual(3);
|
||||
</pre>
|
||||
*
|
||||
* @param {(string|function())=} exp An angular expression to be compiled to a function or a js
|
||||
* function.
|
||||
*
|
||||
* @returns {*} The result of calling compiled `exp` with `this` set to the current scope.
|
||||
*/
|
||||
$eval: function(exp) {
|
||||
var type = typeof exp;
|
||||
var i, iSize;
|
||||
var j, jSize;
|
||||
@@ -133,11 +372,6 @@ function createScope(parent, providers, instanceCache) {
|
||||
instance.$tryEval(queue[j].fn, queue[j].handler);
|
||||
}
|
||||
}
|
||||
while(postList.length) {
|
||||
fn = postList.shift();
|
||||
delete postHash[fn.$postEvalId];
|
||||
instance.$tryEval(fn);
|
||||
}
|
||||
} else if (type === $function) {
|
||||
return exp.call(instance);
|
||||
} else if (type === 'string') {
|
||||
@@ -145,6 +379,44 @@ function createScope(parent, providers, instanceCache) {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$tryEval
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Evaluates the expression in the context of the current scope just like
|
||||
* {@link angular.scope.$eval()} with expression parameter, but also wraps it in a try/catch
|
||||
* block.
|
||||
*
|
||||
* If exception is thrown then `exceptionHandler` is used to handle the exception.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.error = function(){ throw 'myerror'; };
|
||||
scope.$exceptionHandler = function(e) {this.lastException = e; };
|
||||
|
||||
expect(scope.$eval('error()'));
|
||||
expect(scope.lastException).toEqual('myerror');
|
||||
this.lastException = null;
|
||||
|
||||
expect(scope.$eval('error()'), function(e) {this.lastException = e; });
|
||||
expect(scope.lastException).toEqual('myerror');
|
||||
|
||||
var body = angular.element(window.document.body);
|
||||
expect(scope.$eval('error()'), body);
|
||||
expect(body.attr('ng-exception')).toEqual('"myerror"');
|
||||
expect(body.hasClass('ng-exception')).toEqual(true);
|
||||
</pre>
|
||||
*
|
||||
* @param {string|function()} expression Angular expression to evaluate.
|
||||
* @param {function()|DOMElement} exceptionHandler Function to be called or DOMElement to be
|
||||
* decorated.
|
||||
* @returns {*} The result of `expression` evaluation.
|
||||
*/
|
||||
$tryEval: function (expression, exceptionHandler) {
|
||||
var type = typeof expression;
|
||||
try {
|
||||
@@ -154,25 +426,69 @@ function createScope(parent, providers, instanceCache) {
|
||||
return expressionCompile(expression).call(instance);
|
||||
}
|
||||
} catch (e) {
|
||||
(instance.$log || {error:error}).error(e);
|
||||
if ($log) $log.error(e);
|
||||
if (isFunction(exceptionHandler)) {
|
||||
exceptionHandler(e);
|
||||
} else if (exceptionHandler) {
|
||||
errorHandlerFor(exceptionHandler, e);
|
||||
} else if (isFunction(instance.$exceptionHandler)) {
|
||||
instance.$exceptionHandler(e);
|
||||
} else if (isFunction($exceptionHandler)) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
$watch: function(watchExp, listener, exceptionHandler) {
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$watch
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Registers `listener` as a callback to be executed every time the `watchExp` changes. Be aware
|
||||
* that callback gets, by default, called upon registration, this can be prevented via the
|
||||
* `initRun` parameter.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.name = 'misko';
|
||||
scope.counter = 0;
|
||||
|
||||
expect(scope.counter).toEqual(0);
|
||||
scope.$watch('name', 'counter = counter + 1');
|
||||
expect(scope.counter).toEqual(1);
|
||||
|
||||
scope.$eval();
|
||||
expect(scope.counter).toEqual(1);
|
||||
|
||||
scope.name = 'adam';
|
||||
scope.$eval();
|
||||
expect(scope.counter).toEqual(2);
|
||||
</pre>
|
||||
*
|
||||
* @param {function()|string} watchExp Expression that should be evaluated and checked for
|
||||
* change during each eval cycle. Can be an angular string expression or a function.
|
||||
* @param {function()|string} listener Function (or angular string expression) that gets called
|
||||
* every time the value of the `watchExp` changes. The function will be called with two
|
||||
* parameters, `newValue` and `oldValue`.
|
||||
* @param {(function()|DOMElement)=} [exceptionHanlder=angular.service.$exceptionHandler] Handler
|
||||
* that gets called when `watchExp` or `listener` throws an exception. If a DOMElement is
|
||||
* specified as handler, the element gets decorated by angular with the information about the
|
||||
* exception.
|
||||
* @param {boolean=} [initRun=true] Flag that prevents the first execution of the listener upon
|
||||
* registration.
|
||||
*
|
||||
*/
|
||||
$watch: function(watchExp, listener, exceptionHandler, initRun) {
|
||||
var watch = expressionCompile(watchExp),
|
||||
last = {};
|
||||
last = watch.call(instance);
|
||||
listener = expressionCompile(listener);
|
||||
function watcher(){
|
||||
function watcher(firstRun){
|
||||
var value = watch.call(instance),
|
||||
// we have to save the value because listener can call ourselves => inf loop
|
||||
lastValue = last;
|
||||
if (last !== value) {
|
||||
if (firstRun || lastValue !== value) {
|
||||
last = value;
|
||||
instance.$tryEval(function(){
|
||||
return listener.call(instance, value, lastValue);
|
||||
@@ -180,9 +496,36 @@ function createScope(parent, providers, instanceCache) {
|
||||
}
|
||||
}
|
||||
instance.$onEval(PRIORITY_WATCH, watcher);
|
||||
watcher();
|
||||
if (isUndefined(initRun)) initRun = true;
|
||||
if (initRun) watcher(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$onEval
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Evaluates the `expr` expression in the context of the current scope during each
|
||||
* {@link angular.scope.$eval eval cycle}.
|
||||
*
|
||||
* # Example
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
scope.counter = 0;
|
||||
scope.$onEval('counter = counter + 1');
|
||||
expect(scope.counter).toEqual(0);
|
||||
scope.$eval();
|
||||
expect(scope.counter).toEqual(1);
|
||||
</pre>
|
||||
*
|
||||
* @param {number} [priority=0] Execution priority. Lower priority numbers get executed first.
|
||||
* @param {string|function()} expr Angular expression or function to be executed.
|
||||
* @param {(function()|DOMElement)=} [exceptionHandler=angular.service.$exceptionHandler] Handler
|
||||
* function to call or DOM element to decorate when an exception occurs.
|
||||
*
|
||||
*/
|
||||
$onEval: function(priority, expr, exceptionHandler){
|
||||
if (!isNumber(priority)) {
|
||||
exceptionHandler = expr;
|
||||
@@ -202,27 +545,42 @@ function createScope(parent, providers, instanceCache) {
|
||||
});
|
||||
},
|
||||
|
||||
$postEval: function(expr) {
|
||||
if (expr) {
|
||||
var fn = expressionCompile(expr);
|
||||
var id = fn.$postEvalId;
|
||||
if (!id) {
|
||||
id = '$' + instance.$id + "_" + (postId++);
|
||||
fn.$postEvalId = id;
|
||||
}
|
||||
if (!postHash[id]) {
|
||||
postList.push(postHash[id] = fn);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$become
|
||||
* @function
|
||||
* @deprecated This method will be removed before 1.0
|
||||
*
|
||||
* @description
|
||||
* Modifies the scope to act like an instance of the given class by:
|
||||
*
|
||||
* - copying the class's prototype methods
|
||||
* - applying the class's initialization function to the scope instance (without using the new
|
||||
* operator)
|
||||
*
|
||||
* That makes the scope be a `this` for the given class's methods — effectively an instance of
|
||||
* the given class with additional (scope) stuff. A scope can later `$become` another class.
|
||||
*
|
||||
* `$become` gets used to make the current scope act like an instance of a controller class.
|
||||
* This allows for use of a controller class in two ways.
|
||||
*
|
||||
* - as an ordinary JavaScript class for standalone testing, instantiated using the new
|
||||
* operator, with no attached view.
|
||||
* - as a controller for an angular model stored in a scope, "instantiated" by
|
||||
* `scope.$become(ControllerClass)`.
|
||||
*
|
||||
* Either way, the controller's methods refer to the model variables like `this.name`. When
|
||||
* stored in a scope, the model supports data binding. When bound to a view, {{name}} in the
|
||||
* HTML template refers to the same variable.
|
||||
*/
|
||||
$become: function(Class) {
|
||||
if (isFunction(Class)) {
|
||||
instance.constructor = Class;
|
||||
foreach(Class.prototype, function(fn, name){
|
||||
forEach(Class.prototype, function(fn, name){
|
||||
instance[name] = bind(instance, fn);
|
||||
});
|
||||
instance.$inject.apply(instance, concat([Class, instance], arguments, 1));
|
||||
instance.$service.apply(instance, concat([Class, instance], arguments, 1));
|
||||
|
||||
//TODO: backwards compatibility hack, remove when we don't depend on init methods
|
||||
if (isFunction(Class.prototype.init)) {
|
||||
@@ -231,9 +589,25 @@ function createScope(parent, providers, instanceCache) {
|
||||
}
|
||||
},
|
||||
|
||||
$new: function(Class) {
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$new
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Creates a new {@link angular.scope scope}, that:
|
||||
*
|
||||
* - is a child of the current scope
|
||||
* - will {@link angular.scope.$become $become} of type specified via `constructor`
|
||||
*
|
||||
* @param {function()} constructor Constructor function of the type the new scope should assume.
|
||||
* @returns {Object} The newly created child scope.
|
||||
*
|
||||
*/
|
||||
$new: function(constructor) {
|
||||
var child = createScope(instance);
|
||||
child.$become.apply(instance, concat([Class], arguments, 1));
|
||||
child.$become.apply(instance, concat([constructor], arguments, 1));
|
||||
instance.$onEval(child.$eval);
|
||||
return child;
|
||||
}
|
||||
@@ -243,8 +617,27 @@ function createScope(parent, providers, instanceCache) {
|
||||
if (!parent.$root) {
|
||||
instance.$root = instance;
|
||||
instance.$parent = instance;
|
||||
(instance.$inject = createInjector(instance, providers, instanceCache))();
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc function
|
||||
* @name angular.scope.$service
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Provides access to angular's dependency injector and
|
||||
* {@link angular.service registered services}. In general the use of this api is discouraged,
|
||||
* except for tests and components that currently don't support dependency injection (widgets,
|
||||
* filters, etc).
|
||||
*
|
||||
* @param {string} serviceId String ID of the service to return.
|
||||
* @returns {*} Value, object or function returned by the service factory function if any.
|
||||
*/
|
||||
(instance.$service = createInjector(instance, providers, instanceCache))();
|
||||
}
|
||||
|
||||
$log = instance.$service('$log');
|
||||
$exceptionHandler = instance.$service('$exceptionHandler');
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
Vendored
+99
-33
@@ -21,11 +21,14 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
(function(previousOnLoad){
|
||||
var filename = /^(.*)\/angular-bootstrap.js(#.*)?$/,
|
||||
(function(window) {
|
||||
|
||||
var filename = /^(.*\/)angular-bootstrap.js(#.*)?$/,
|
||||
scripts = document.getElementsByTagName("SCRIPT"),
|
||||
serverPath,
|
||||
match;
|
||||
match,
|
||||
globalVars = {};
|
||||
|
||||
for(var j = 0; j < scripts.length; j++) {
|
||||
match = (scripts[j].src || "").match(filename);
|
||||
if (match) {
|
||||
@@ -33,50 +36,113 @@
|
||||
}
|
||||
}
|
||||
|
||||
function addScript(file){
|
||||
document.write('<script type="text/javascript" src="' + serverPath + file +'"></script>');
|
||||
function key(prop) {
|
||||
return "ng-clobber_" + prop;
|
||||
}
|
||||
|
||||
window.angularClobberTest = function(file) {
|
||||
var varKey, prop,
|
||||
clobbered = [];
|
||||
|
||||
for (prop in window) {
|
||||
varKey = key(prop);
|
||||
|
||||
if (prop === 'event') { //skip special variables which keep on changing
|
||||
continue;
|
||||
}
|
||||
else if (!globalVars.hasOwnProperty(varKey)) {
|
||||
//console.log('new global variable found: ', prop);
|
||||
try {
|
||||
globalVars[varKey] = window[prop];
|
||||
} catch(e) {} //ignore properties that throw exception when accessed (common in FF)
|
||||
} else if (globalVars[varKey] !== window[prop] && !isActuallyNaN(window[prop])) {
|
||||
clobbered.push(prop);
|
||||
console.error("Global variable clobbered by script " + file + "! Variable name: " + prop);
|
||||
globalVars[varKey] = window[prop];
|
||||
}
|
||||
}
|
||||
|
||||
for (varKey in globalVars) {
|
||||
prop = varKey.substr(11);
|
||||
if (clobbered.indexOf(prop) == -1 &&
|
||||
prop != 'event' &&
|
||||
!isActuallyNaN(globalVars[varKey]) &&
|
||||
globalVars[varKey] !== window[prop]) {
|
||||
|
||||
delete globalVars[varKey];
|
||||
console.warn("Global variable unexpectedly deleted in script " + file + "! " +
|
||||
"Variable name: " + prop);
|
||||
}
|
||||
}
|
||||
|
||||
function isActuallyNaN(val) {
|
||||
return isNaN(val) && (typeof val === 'number');
|
||||
}
|
||||
}
|
||||
|
||||
function addScripts(){
|
||||
var prop, i;
|
||||
|
||||
// initialize the window property cache
|
||||
for (prop in window) {
|
||||
try {
|
||||
globalVars[key(prop)] = window[prop];
|
||||
} catch(e) {} //ignore properties that throw exception when accessed (common in FF)
|
||||
}
|
||||
|
||||
// load the js scripts
|
||||
for (i in Array.prototype.slice.call(arguments, 0)) {
|
||||
file = arguments[i];
|
||||
document.write('<script type="text/javascript" src="' + serverPath + file + '" ' +
|
||||
'onload="angularClobberTest(\'' + file + '\')"></script>');
|
||||
}
|
||||
}
|
||||
|
||||
function addCss(file) {
|
||||
document.write('<link rel="stylesheet" type="text/css" href="' +
|
||||
serverPath + '/../css' + file + '"/>');
|
||||
serverPath + '../css/' + file + '"/>');
|
||||
}
|
||||
|
||||
addCss("/angular.css");
|
||||
addCss('angular.css');
|
||||
|
||||
addScript("/Angular.js");
|
||||
addScript("/JSON.js");
|
||||
addScript("/Compiler.js");
|
||||
addScript("/Scope.js");
|
||||
addScript("/Injector.js");
|
||||
addScript("/jqLite.js");
|
||||
addScript("/parser.js");
|
||||
addScript("/Resource.js");
|
||||
addScript("/Browser.js");
|
||||
addScript("/sanitizer.js");
|
||||
addScript("/AngularPublic.js");
|
||||
addScripts('Angular.js',
|
||||
'JSON.js',
|
||||
'Compiler.js',
|
||||
'Scope.js',
|
||||
'Injector.js',
|
||||
'jqLite.js',
|
||||
'parser.js',
|
||||
'Resource.js',
|
||||
'Browser.js',
|
||||
'sanitizer.js',
|
||||
'AngularPublic.js',
|
||||
|
||||
// Extension points
|
||||
addScript("/services.js");
|
||||
addScript("/apis.js");
|
||||
addScript("/filters.js");
|
||||
addScript("/formatters.js");
|
||||
addScript("/validators.js");
|
||||
addScript("/directives.js");
|
||||
addScript("/markups.js");
|
||||
addScript("/widgets.js");
|
||||
// Extension points
|
||||
'services.js',
|
||||
'apis.js',
|
||||
'filters.js',
|
||||
'formatters.js',
|
||||
'validators.js',
|
||||
'directives.js',
|
||||
'markups.js',
|
||||
'widgets.js');
|
||||
|
||||
|
||||
window.onload = function(){
|
||||
try {
|
||||
if (previousOnLoad) previousOnLoad();
|
||||
} catch(e) {}
|
||||
function onLoadListener(){
|
||||
// empty the cache to prevent mem leaks
|
||||
globalVars = {};
|
||||
|
||||
//angular-ie-compat.js needs to be pregenerated for development with IE<8
|
||||
if (msie<8) addScript('../angular-ie-compat.js');
|
||||
|
||||
angularInit(angularJsConfig(document));
|
||||
};
|
||||
}
|
||||
|
||||
})(window.onload);
|
||||
if (window.addEventListener){
|
||||
window.addEventListener('load', onLoadListener, false);
|
||||
} else if (window.attachEvent){
|
||||
window.attachEvent('onload', onLoadListener);
|
||||
}
|
||||
|
||||
})(window);
|
||||
|
||||
|
||||
+92
-3
@@ -56,6 +56,24 @@
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.mock
|
||||
* @namespace Namespace for all built-in angular mocks.
|
||||
*
|
||||
* @description
|
||||
* `angular.mock` is a namespace for all built-in mocks that ship with angular and automatically
|
||||
* replace real services if `angular-mocks.js` file is loaded after `angular.js` and before any
|
||||
* tests.
|
||||
*/
|
||||
angular.mock = {};
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.mock.service.$browser
|
||||
*/
|
||||
function MockBrowser() {
|
||||
var self = this,
|
||||
expectations = {},
|
||||
@@ -63,8 +81,25 @@ function MockBrowser() {
|
||||
|
||||
this.isMock = true;
|
||||
self.url = "http://server";
|
||||
self.lastUrl = self.url; // used by url polling fn
|
||||
self.pollFns = [];
|
||||
|
||||
|
||||
// register url polling fn
|
||||
|
||||
self.onHashChange = function(listener) {
|
||||
self.pollFns.push(
|
||||
function() {
|
||||
if (self.lastUrl != self.url) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return listener;
|
||||
};
|
||||
|
||||
|
||||
self.xhr = function(method, url, data, callback) {
|
||||
if (angular.isFunction(data)) {
|
||||
callback = data;
|
||||
@@ -113,11 +148,20 @@ function MockBrowser() {
|
||||
|
||||
self.cookieHash = {};
|
||||
self.lastCookieHash = {};
|
||||
self.deferredFns = [];
|
||||
|
||||
self.defer = function(fn) {
|
||||
self.deferredFns.push(fn);
|
||||
};
|
||||
|
||||
self.defer.flush = function() {
|
||||
while (self.deferredFns.length) self.deferredFns.shift()();
|
||||
};
|
||||
}
|
||||
MockBrowser.prototype = {
|
||||
|
||||
poll: function poll(){
|
||||
angular.foreach(this.pollFns, function(pollFn){
|
||||
angular.forEach(this.pollFns, function(pollFn){
|
||||
pollFn();
|
||||
});
|
||||
},
|
||||
@@ -156,7 +200,6 @@ MockBrowser.prototype = {
|
||||
return this.cookieHash;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
angular.service('$browser', function(){
|
||||
@@ -164,6 +207,52 @@ angular.service('$browser', function(){
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.mock.service.$exceptionHandler
|
||||
*
|
||||
* @description
|
||||
* Mock implementation of {@link angular.service.$exceptionHandler} that rethrows any error passed
|
||||
* into `$exceptionHandler`. If any errors are are passed into the handler in tests, it typically
|
||||
* means that there is a bug in the application or test, so this mock will make these tests fail.
|
||||
*
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
angular.service('$exceptionHandler', function(e) {
|
||||
return function(e) {throw e;};
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.mock.service.$log
|
||||
*
|
||||
* @description
|
||||
* Mock implementation of {@link angular.service.$log} that gathers all logged messages in arrays
|
||||
* (one array per logging level). These arrays are exposed as `logs` property of each of the
|
||||
* level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`.
|
||||
*
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
angular.service('$log', function() {
|
||||
var $log = {
|
||||
log: function(){ $log.logs.push(arguments) },
|
||||
warn: function(){ $log.logs.push(arguments) },
|
||||
info: function(){ $log.logs.push(arguments) },
|
||||
error: function(){ $log.logs.push(arguments) }
|
||||
};
|
||||
|
||||
$log.log.logs = [];
|
||||
$log.warn.logs = [];
|
||||
$log.info.logs = [];
|
||||
$log.error.logs = [];
|
||||
|
||||
return $log;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Mock of the Date type which has its timezone specified via constroctor arg.
|
||||
*
|
||||
@@ -281,7 +370,7 @@ function TzDate(offset, timestamp) {
|
||||
'setYear', 'toDateString', 'toJSON', 'toGMTString', 'toLocaleFormat', 'toLocaleString',
|
||||
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
|
||||
|
||||
angular.foreach(unimplementedMethods, function(methodName) {
|
||||
angular.forEach(unimplementedMethods, function(methodName) {
|
||||
this[methodName] = function() {
|
||||
throw {
|
||||
name: "MethodNotImplemented",
|
||||
+1
-1
@@ -21,4 +21,4 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
(function(window, document, previousOnLoad){
|
||||
(function(window, document){
|
||||
|
||||
+3
-6
@@ -1,9 +1,6 @@
|
||||
|
||||
window.onload = function(){
|
||||
try {
|
||||
if (previousOnLoad) previousOnLoad();
|
||||
} catch(e) {}
|
||||
jqLite(document).ready(function(){
|
||||
angularInit(angularJsConfig(document));
|
||||
};
|
||||
});
|
||||
|
||||
})(window, document, window.onload);
|
||||
})(window, document);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user