Compare commits

..

299 Commits

Author SHA1 Message Date
Igor Minar 5745734991 chore(release): cut 1.1.2 tofu-animation release 2013-01-23 10:54:35 -08:00
Igor Minar 0551aa95f0 docs(changelog): release notes for 1.1.2 and 1.0.4 2013-01-22 22:58:55 -08:00
Igor Minar b62327ec2d docs(ngOpen): fix typo in api docs 2013-01-22 22:58:55 -08:00
Igor Minar 07a58dd766 chore(changelog.js): improve the changelog script 2013-01-22 22:49:00 -08:00
Igor Minar ffe5e01584 revert: fix($resource): Route constructor, updated RegExp
This reverts commit 06ed8ef012.

The reverted commit causes regressions. See comments in the PR:
https://github.com/angular/angular.js/pull/1402#issuecomment-12575399
2013-01-22 16:32:02 -08:00
Igor Minar 3c2e1c5e4d fix(angular.equals): relax the comparison for undefined properties
in 5ae63fd3 the comparison was made consistent but strict, so that

angular.equals({}, {foo: undefined}) // always returns false

this turns out to cause issues for data that is being roundtripped via network
and serialized via JSON because JSON.stringify serializes {foo: undefined} as {}.

Since angular.equals() behaved like this before the 5ae63fd3 in 50% of the cases,
changing the behavior in this way should not introduce any significant issues.

Closes #1648
2013-01-22 07:35:21 -08:00
Igor Minar cdf7781878 chore(Rakefile): skip build parallelization on Travis
Due to a infrastructure change on Travis starting JVMs in forked
processes doesn't currently work. Since we don't really care that
much about the build speed on Travis, I'm going to disable it there.

Related issue: https://github.com/travis-ci/travis-ci/issues/845
2013-01-21 07:50:16 -08:00
sergiopantoja 7e8d3c1736 docs(jqLite): fix typo 2013-01-20 18:39:21 +01:00
Will Moore d30845a757 docs(contribute): adding npm install to step-by-step
npm install is listed in the dependencies section of the contribute guide but
is missing from the step-by-step. This adds it as step 4.
2013-01-18 21:33:10 -08:00
Fredrik Bonander 06ed8ef012 fix($resource): Route constructor, updated RegExp
Update RegExp to allow urlParams with out leading slash (/).
- Will allow reoucese to be loaded from a relative path

Example:
var R = $resource(':path');
R.get({ path : 'data.json' });

Example usage:
Load resources in applications not using webserver, ie local webapp in on a tablet.
2013-01-18 21:28:15 -08:00
Pedro Del Gallego 2f437e8978 feat(scenario): add mouseover method to the ngScenario dsl 2013-01-18 21:24:57 -08:00
Luis Ramón López faf02f0c4d feat(route): Allow using functions as template params in 'when' 2013-01-18 21:20:49 -08:00
David Chang b8bd4d5460 feat(directive): added ng-open boolean directive
Closes# 1797 add ng-open attribute
2013-01-18 21:16:16 -08:00
pavelgj b2f46251ac fix(ngResource): correct leading slash removal.
Fixed an issues with ngResource param substitution where it was incorrectly removing leading slash when param was followed by a non-slash character.
Ex:
'/:foo/:bar.baz/:aux'

params = {
  foo: 'aaa',
  bar: undefined,
  aux: undefined
}

The above params were incorrectly producing '/aaa.baz' but now it results in '/aaa/.baz'.
2013-01-18 20:52:57 -08:00
danilsomsikov a26234f718 fix(ngSwitch): don't leak when destroyed while not attached
The leak can occur when ngSwich is used inside ngRepeat or any other
directive which is destroyed while its transcluded content (which
includes ngSwitch) is not attached to the DOM.

Refactor ngSwitch to use controller instead of storing data on compile
node. This means that we don't need to clean up the jq data cache.
Controller reference is released when the linking fn is released.

Closes #1621
2013-01-18 00:03:28 -08:00
Misko Hevery 61315211da fix(git-validator): support fixup and better errors 2013-01-17 23:52:46 -08:00
Misko Hevery af89daf464 feat(ngResource): support all $http.config actions
This allows the transformation of the $http request in both directions,
headers, caching, and timeout.
2013-01-17 23:08:39 -08:00
Fred Sauer 224d7d6e90 docs(exceptionHandler): document testing
Update src/ng/exceptionHandler.js

Here's an iniitla attempt at documenting how one might write a
test using $exceptionHandlerProvider. The key take-away is the use
of this pattern:

it(...

 module(...
   $exceptionHandlerProvider.mode('log');
 });

 inject(...
 );

});
2013-01-17 23:08:39 -08:00
Jeremy Tymes a179a9a96e feat($parse): allow strict equality in angular expressions
Allows the parser to parse strict equality and inequality
in angular expressions.

Closes #908
2013-01-17 23:08:38 -08:00
Matthew Browne 610a5a0c14 docs(rootScope): correct code examples 2013-01-17 23:08:38 -08:00
Amir H. Hajizamani 4f5583465a docs(cookbook): change prototype methods to scope methods in Buzz
As explained in 'Understanding the Controller Component', Controllers
written for new (post 1.0 RC) versions of Angular need to add methods to
the scope directly, not the function's prototype. Correcting this
example should remove any ambiguity, especially for beginners.
2013-01-18 00:49:41 -05:00
Amir H. Hajizamani 75487ec933 docs(guide): change prototype methods to scope methods in DI examples
As explained in 'Understanding the Controller Component', Controllers
written for new (post 1.0 RC) versions of Angular need to add methods to
the scope directly, not the function's prototype. Correcting this
example should remove any ambiguity, especially for beginners.
2013-01-18 00:49:41 -05:00
Fred Sauer dddb1221fa docs(ngMock.$httpBackend): fix variable declaration 2013-01-18 00:13:39 -05:00
Shai Reznik 69e4d40a76 doc(guide): Fixed typos at the unit tests guide 2013-01-17 23:43:13 -05:00
Shai Reznik d521619c58 doc(guide): Fix examples of $location.html5mode 2013-01-17 23:43:13 -05:00
Igor Minar 5ae63fd385 fix(angular.equals): consistently compare undefined object props
previously:

a = {};
b = {x:undefined};
angular.equals(a, b) == angular.equals(b, a) // returns false.

both should return false because these objects are not equal.

unlike in implemented in #1695 the comparison should be stricter
and return false not true. if we need to relax this in the future
we can always do so.

Closes #1648
2013-01-17 17:48:51 -08:00
Daniel Demmel 8b44324814 docs: recommend using Google CDN 2013-01-17 16:52:05 -08:00
nlaplante 9e991ddb1d feat($log): add $log.debug()
New debug() method with suppressable output via
$logProvider.debugEnabled()

Closes #1592
2013-01-17 16:47:39 -08:00
Matt Rohrer 93070f1488 docs(guide): minor grammar fixes 2013-01-17 19:10:46 -05:00
Gergely Imreh 3c8583e5dd chore(Rakefile): force 32bit JVM mode only when java supports it
Some Java installs don't have '-d32' flag (e.g. OpenJDK which is standard
for some Linux systems), and the closure_compile fails because of forcing
that flag. Test, and only run in faster 32bit mode if supported, or
else just run with no flag (default mode).
2013-01-17 01:34:01 -08:00
Igor Minar 78699c62ea chore(docs): use done() instead of end() in gen-docs.js 2013-01-17 00:51:56 -08:00
Pete Bacon Darwin 49f9e4cef1 fix($compile): do not wrap empty root text nodes in spans
Closes #1059
2013-01-17 00:28:44 -08:00
Pete Bacon Darwin 7e746015ea fix(ngRepeat): correctly apply $last if repeating over object
If the $last property is calculated from the original collectionLength
on an object and properties starting with $ were filtered out, then $last
is never applied and $middle is applied erroniously.

Closes #1789
2013-01-17 00:25:30 -08:00
James deBoer 8c269883fd chore(Rakefile): remove a duplicate file in angularFiles.js 2013-01-16 23:45:09 -08:00
James deBoer a69674b36d chore(Rakefile): generate version.json
Changes 'rake version' to output a version.json file which
contains the structured version info which can be used in other tools.
2013-01-16 23:41:45 -08:00
Igor Minar 8b9e6c3501 fix(scenario): don't trigger input events on IE9
input.enter() should trigger 'change' rather than 'input' event on IE9 because
input events on IE9 are broken and angular doesn't rely on them
2013-01-16 23:26:36 -08:00
Kanwei Li c97c53dbd4 docs(CHANGELOG): fix typo 2013-01-16 21:55:06 +01:00
Martin Probst c6392616ea fix($route): support route params not separated with slashes.
Commit 773ac4a broke support for route parameters that are not seperated
from other route parts by slashes, which this change fixes. It also adds
some documentation about path parameters to the when() method and
escapes all regular expression special characters in the URL, not just
some.
2013-01-16 09:41:02 -08:00
Igor Minar 74dd2f7980 fix($compile): safely create transclude comment nodes
Closes #1740
2013-01-14 21:57:23 -08:00
Lucas Galfasó c0cb9f8c14 doc(directive): Fix typos in dialog widget
Fixes #1799
2013-01-13 10:08:19 +00:00
Igor Minar 7dff7bb696 chore(*): remove obsolete files 2013-01-09 21:28:53 -08:00
Martin Probst cdf6fb19c8 feat($compile): support modifying the DOM structure in postlink fn
Support modifying the DOM structure in the post link function of a directive
by creating a defensive copy of the node list, as opposed to a live DOM list.
This is useful for directives to actually replace their entire DOM fragment,
e.g. with the HTML fragment generated by a 3rd party component (Closure, Bootstrap ...).
Fix the indentation of the compileNodes function (was one too little).
2013-01-09 20:06:22 -08:00
Igor Minar c909f49112 style($compile): fix indentation 2013-01-09 16:54:29 -08:00
Igor Minar cc821502bc fix(date): parse string input as local time unless TZ is specified
previously we were always parsing the string input as UTC which cased issues like:

{{ '2012-04-01' | date:'d MMM yyyy' }} renders as 31 Mar 2012

BREAKING CHANGE: string input without timezone info is now parsed as local time/date

Closes #847
2013-01-09 09:50:43 -08:00
naomiblack 037aefae47 Update docs/content/misc/faq.ngdoc
Updated the canonical video to a recent one. Fixed a typo.
2013-01-09 09:49:44 +00:00
Pete Bacon Darwin febb4c1c35 fix(jqLite): children() should only return elements
The jQuery implementation of children only returns child nodes of the given element that are elements themselves. The previous jqLite implementation was returning all nodes except those that are text nodes. Use jQLite.contents() to get all the child nodes.

The jQuery implementation of contents returns [] if the object has no child nodes.  The previous jqLite implementation was returning undefined, causing a stack overflow in test/testabilityPatch.js when it tried to `cleanup()` a window object.

The testabilityPatch was incorrectly using children() rather than contents() inside cleanup() to iterate down through all the child nodes of the element to clean up.
2013-01-09 09:22:35 +00:00
Keyamoon 76a6047af6 fix(jqLite): make next() ignore non-element nodes
next() is supposed to return the next sibling *element* so it
should ignore text nodes. To achieve this, nextElementSibling()
should be used instead of nextSibling().
2013-01-08 14:54:15 -08:00
Igor Minar b6b7c5a1d6 fix($injector): remove bogus fn arg
getService fn takes only one argument, removing the second one.

Closes #1711
2013-01-08 14:36:03 -08:00
Igor Minar 5b5f35d5e4 refactor($browser): remove faulty 20+ cookies warning
the warning is defunct (and the test is incorrect) so obviously nobody is using
it and it just takes up space.

also the browser behavior varies (ff and chrome allow up to 150 cookies, safari
even more), so it's not very useful.

Closes #1712
2013-01-08 14:23:50 -08:00
Igor Minar 14948cf5d9 revert: fix(a): prevent Opera from incorrectly navigating on link click
This reverts commit c81d8176cc.

This commit causes several issues (#1651, #1674, #1662) and doesn't even
contain a test that proves that anything on Opera got actually fixed.

If the original Opera resurfaces, we'll fix it properly.
2013-01-08 11:47:22 -08:00
kim lokoy 2b0978b07c docs(guide): fix typos in unit test guide 2013-01-07 21:01:01 +01:00
Pawel Kozlowski 1122dc7a5b docs(forms): fix code example for a custom form control
Closes #1021
2013-01-05 22:47:36 +01:00
naomiblack a3a9d4af05 docs(faq): add info on logo reuse and how to get t-shirts and stickers 2013-01-04 18:59:11 +01:00
Jonathan Card 36089931a5 docs(form): minor form doc and example fixes
Form documentation fixes:
- Fix broken form example in docs
- A few small other corrections in form docs.
2013-01-04 09:28:35 +01:00
Per Rovegård 74db36ee94 docs($http): clarify documentation on error status codes
Modify the documentation for $http to correspond to what Angular
considers a success status code.

Closes #1693
2013-01-03 20:49:43 +01:00
Matt Hardy 8d42ce8563 docs(guide): change example controller to properly call greet method on greeter 2012-12-31 12:49:51 +01:00
petrovalex f0c6ebc076 feat($timeout-mock): add verifyNoPendingTasks method
added verifyNoPendingTasks method, which throws error if not all
deferred tasks have been flushed

Closes #1245
2012-12-20 20:39:40 +01:00
Murilo da Silva 59d9b89852 docs(anchorScroll): correct word "location" 2012-12-19 21:01:41 +01:00
John Fletcher 6aac69039e docs(guide): minor English corrections to the Directive guide 2012-12-19 20:35:31 +01:00
Jeremy Tymes 9e96d98345 feat(limitTo): limitTo filter accepts strings
This allows strings to be filtered by limitTo, using the same methods

Closes #653
2012-12-19 20:13:36 +01:00
Miško Hevery 1e13544da8 docs(directive): old syntax 2012-12-18 20:38:43 -08:00
Mark Nadig e03182f018 feat(directive): ng:keydown, ng:keyup
New directives for binding to keydown and keyup events.

Closes #1035
2012-12-18 22:57:58 +01:00
Pawel Kozlowski f2d526190a docs(ngView): fix code example (change template to templateUrl)
Closes #1715
2012-12-17 23:18:12 +01:00
Miško Hevery 039b138042 docs(q): added testing information 2012-12-14 05:49:22 -08:00
Gonzalo Ruiz de Villa 30a9da5dc1 fix($route): correctly extract $routeParams from urls
Routes like '/bar/foovalue/barvalue' matching '/bar/:foo/:bar'
now are well mapped in $routeParams to:
{bar:'barvalue', foo:'foovalue'}

Closes: #1501
Signed-off-by: Gonzalo Ruiz de Villa <gonzaloruizdevilla@gmail.com>
2012-12-14 01:15:15 +01:00
ggoodman 25e1ad9a94 feat(docs): Add angularjs tag to plunks and make private
This is a minor edit to allow Plunks created by way of the docs.angularjs.org site to be appropriately tagged as `angularjs`.
Also, make these generated Plunks private by default.
2012-12-11 19:04:46 +00:00
Peter Evjan 37cced6296 docs(README.md): add missing 'you' and a comma 2012-12-11 19:20:30 +01:00
Romain Neutron a66c968df2 docs(guide): fix injector service code example
Fix syntax and update code to the latest API
2012-12-10 23:19:20 +01:00
Juha Syrjälä c398d7d370 docs($resource): document port number escaping and fix typo
Closes #1243
2012-12-09 17:13:29 +01:00
Eric Case d93533812b docs(tutorial): typo fix commandx -> command 2012-12-08 11:33:07 +01:00
Eric Case ff11061a8f docs($q): typo fix - programing -> programming 2012-12-07 20:45:33 +01:00
János Rusiczki c52bfd37ee doc(concepts): Fix typo in $render() function 2012-12-07 10:03:58 +00:00
Jeremy Tymes f8b755982a docs($http): fix link typo in $http doc
Should be $httpBackend instead of $httpBacked

Closes #1516
2012-12-06 21:25:47 +01:00
Fred Sauer bcc7089b3c docs(mocks): update src/ngMock/angular-mocks.js documentation
Clarify how to use `$exceptionHandlerProvider.mode('log')` in tests
2012-12-06 21:18:49 +01:00
Fred Sauer d43cc3f893 docs(mocks): fix documentation bug: angular.mock.debug 2012-12-06 21:13:51 +01:00
Igor Minar c9d937082c chore(bootstrap-prettify): update urls to code.angularjs.org
Closes #1599
2012-12-05 02:55:43 +01:00
_pants 26adeb119b fix(select): support optgroup + select[multiple] combo
Closes #1553
2012-12-05 02:20:11 +01:00
Stephane Bisson 15183f3e1f feat(e2eRunner): fail when an option to select does not exist 2012-12-01 20:05:42 +01:00
Sudhir Jonathan 2c405f4171 fix($injector): provider can now be defined in the array format
`injector.instantiate` is now called for arrays too, instead of only for functions.

Closes #1452
2012-12-01 18:41:59 +01:00
Sudhir Jonathan 8991680d8a fix($resource): HTTP method should be case-insensitive
Perform call `angular.uppercase` on all given action methods.

Closes #1403
2012-11-30 22:58:11 +01:00
Cezar Berea ec801ac137 refactor($resource): fix indentation and move a method definition
Moved Resource.bind out of the actions forEach
2012-11-30 22:35:57 +01:00
Igor Minar d6da505f4e fix(Scope): ensure that a scope is destroyed only once
Due to bd524fc4 calling $destroy() on a scope mupltiple times cases NPE.

Closes #1627
2012-11-30 13:10:00 +01:00
Daniel Luz 5f7054bf5d docs(directive): correct expression, fix typo and re-wrap lines 2012-11-29 19:40:33 +01:00
Johannes Hansen cf4ed8a145 fix(docs): add missing </div> tag to sourceEdit directive template 2012-11-29 19:40:30 +01:00
Igor Minar d1e48fcbf3 docs(menu): fix the navbar drop down links 2012-11-28 23:56:09 +01:00
Pascal Corpet cc42c99bec feat($resource): allow dynamic default parameters
Default resource params can now be calculated at runtime if defined
via a function.
2012-11-28 16:01:57 +01:00
Igor Minar 3c7bfa77aa chore(release): start 1.1.2 tofu-animation iteration 2012-11-28 14:21:33 +01:00
Igor Minar 2ee0f56c54 chore(release): cutting the 1.1.1 pathological-kerning release 2012-11-27 01:45:35 +01:00
Igor Minar 9b18644f30 docs(CHANGELOG): release notes for 1.0.3 and 1.1.1 releases 2012-11-27 01:43:05 +01:00
Rado Kirov fce100a46c fix($http): only set X-XSFR-TOKEN header for same-domain request
This is needed to prevent CORS preflight checks. The XSFR token
is quite useless for CORS requests anyway.

BREAKING CHANGE: X-XSFR-TOKEN is no longer send for cross domain
requests. This shouldn't affect any known production service.

Closes #1096
2012-11-26 23:58:59 +01:00
Rado Kirov 3a75b1124d fix($http): remove 'X-Requested-With' from header defaults
X-Requested-With header is rarely used in practice and by using
it all the time we are triggering preflight checks for crossdomain
requests.

We could try detecting if we are doing CORS requests or not, but
it doesn't look like it's worth the trouble.

BREAKING CHANGE: X-Requested-With header is not set by $http service
any more. If anyone actually uses this header it's quite easy to add
it back via:

```
myAppModule.config(['$httpProvider', function($httpProvider) {
    $httpProvider.defaults.headers.common["X-Requested-With"] = 'XMLHttpRequest';
}]);
```

Closes #1004
2012-11-26 23:36:40 +01:00
Rado Kirov a32bc40fd7 fix($location): reset $location.$$replace with every watch call
Closes #1111
2012-11-26 23:21:02 +01:00
Igor Minar cfe13b5dac chore(validate-commit-msg): recognize 'revert' as valid commit type 2012-11-26 21:05:38 +01:00
Igor Minar d859dcecea fix(ngClassOdd/ngClassEven): support shrinking/reordering in repeaters
We need to watch $index in addition to cssClasses because only then
we can correctly respond to shrinking or reordered repeaters.

Closes #1076
2012-11-26 20:36:53 +01:00
Igor Minar d3b32a7c94 style(jqLite): better variable names
selector => cssClasses
2012-11-26 20:36:53 +01:00
Igor Minar 1b17dfa693 fix(ngRepeat): support mostly-stable repeating for primitives
I'm reverting changes that were originally done to ngRepeat to fix #933,
because these are now not necessary after the previous changes to keep
ngModel always synced with the DOM.
2012-11-26 20:36:53 +01:00
Igor Minar e6d9bea4f3 fix(ngModel): sync ngModel state with scope state
In cases when we reuse elements in a repeater but associate
them with a new scope (see #933 - repeating over array of
primitives) it's possible for the internal ngModel state and
the scope state to get out of sync. This change ensure that
the two are always sync-ed up even in cases where we
reassociate an element with a different (but similar) scope.

In the case of repeating over array of primitives it's still
possible to run into issue if we iterate over primitives and
use form controls or similar widgets without ngModel - oh well,
we'd likely need a special repeater for primitives to deal
with this properly, even then there might be cornercases.

Closes #933
2012-11-26 20:36:53 +01:00
Igor Minar c8e9105fe6 test(ngRepeat): clean up and improve tests 2012-11-26 20:36:52 +01:00
Igor Minar d644dcfa52 fix(ngRepeat): attempt to simplify and improve existing fix for #933
I'm keeping this in for future reference. The issue with this solution
is that if we shift() the first item in the array, the whole repeater
DOM will be rebuilt from scratch, we need to do better than that.
2012-11-26 20:31:54 +01:00
Igor Minar e7d37ee45a test(ngRepeat): add test for issue #1076 2012-11-26 20:17:11 +01:00
Pawel Kozlowski 733a97adf8 feat(form): add ability to reset a form to pristine state
Retting a form to pristine state will cause all of the nested
form and form controls to be recursively reset as well.

Closes #856
2012-11-26 16:44:34 +01:00
Igor Minar 96ed9ff59a fix(jqLite): support append on document fragment
previously jquery didn't support append on this node type, now it does
(since 1.8.x) so I'm adding this to jqlite as well.
2012-11-26 15:45:04 +01:00
Igor Minar b9a9f91fbf fix(jqLite): fire $destroy event via triggerHandler
in jQuery 1.8.x the data() data structure is changed and events are
not accessible via data().events. Since all we need is to trigger
all event handlers, we can do so via triggerHandler() api instead of
mocking with the internal jQuery data structures.

This fix was originally proposed by PeteAppleton via PR #1512.

Closes #1512
2012-11-26 15:45:04 +01:00
Igor Minar 650fd933df feat(jqLite): add triggerHandler()
we need triggerHandler() to become jQuery 1.8.x compatible.

this is not fully featured triggerHandler() implementation - it doesn't
bother creating new DOM events and passing them into the event handlers.

this is intentional, we don't need access to the native DOM event for our
own purposes and creating these event objects is really tricky.
2012-11-26 15:45:04 +01:00
Igor Minar e249502880 chore(jquery): upgrade to jquery 1.8.2 2012-11-26 15:45:04 +01:00
Iristyle ca3e0e7374 docs(CONTRIBUTING.md): add contrib info file for GitHub 2012-11-25 21:00:14 +01:00
Vojta Jina e6966e05f5 fix(Scope): allow removing a listener during event 2012-11-25 11:39:54 +01:00
Kevin Western 682418f029 docs(README.md): fix "API Docs" link
use direct link to api docs
2012-11-25 01:21:52 +01:00
Dean Sofer c8fd7fd0e2 docs(api): add ngRequired to input/select/textarea directives
Closes #1202
2012-11-25 01:18:10 +01:00
JP Sugarbroad 168db33985 feat($cacheFactory): cache.put now returns the added value
This allows common programming patterns to be expressed with more
concise code.

See #1583 for code examples.
2012-11-25 00:01:34 +01:00
Tom Davis 79af2badcb fix($http): config.param should expand array values properly
Today, calling e.g. $http(url, { params: { a: [1,2,3] } }) results in a query
string like "?a=%5B1%2C2%2C3%5D" which is undesirable. This commit enhances
buildURL to createa query string like "?a=1&a=2&a=3".

BREAKING CHANGE: if the server relied on the buggy behavior then either the
backend should be fixed or a simple serialization of the array should be done
on the client before calling the $http service.

Closes #1363
2012-11-24 22:26:23 +01:00
Zach Dexter 610927d77b feat(linky): allow optional 'target' argument
Closes #1443
2012-11-24 22:21:50 +01:00
Jeremy Tymes 55d15806fb fix($cacheFactory): return undefined when removing non-existent entry
Instead of throwning an exception, remove should return undefined when
cache entry to be removed doesn't exist.

Closes #1497
2012-11-24 21:56:28 +01:00
Adrian Gheorghe 94e1c0391c fix($resource): prevent default params to be shared between actions
Having a $resource defined as:

var R = $resource('/Path', {}, {
  get: {method: 'GET', params: {objId: '1'}},
  perform: {method: 'GET'}
});

was causing both actions to call the same URI (if called in this order):

R.get({}); // => /Path?objId=1
R.perform({}); // => /Path?objId=1
2012-11-24 21:27:24 +01:00
Kris Jenkins b21f4a376d docs(): Fix a couple of typos in the documentation 2012-11-21 23:06:59 +01:00
Dave Clayton f28f283fcf docs(guide/concepts): some typo/grammar fixes 2012-11-17 23:55:32 +01:00
John Hume e362a510e3 docs(guide/directive): fix typo 2012-11-17 23:49:19 +01:00
Uri Goldshtein 8a7f752a80 docs($q): fix missing bracket in the example 2012-11-17 23:45:08 +01:00
Igor Minar af7e0bd0a7 fix(CSP): update to the latest CSP api
window.SecurityPolicy.isActive() is now window.securityPolicy.isActive

since this is available only in Chrome Canary which has already been
updated, we can safely make this change without worrying about
backwards compatilibty.

Closes #1577
2012-11-15 01:46:58 +01:00
Igor Minar bd524fc4e5 fix($rootScope): workaround for Chrome's memleak
Under certain circumstances chrome fails to GC scopes
because of buggy optimizations and caching. Nulling out
references to (not from!) other scopes helps Chrome to
realize that this object should be GC-ed.

This is really just a workaround as the real problem needs
to be fixed in Chrome.

See discusstion at:
https://github.com/angular/angular.js/issues/1313#issuecomment-10378451

And chrome bug at:
https://code.google.com/p/v8/issues/detail?id=2073

Closes #1313
2012-11-14 19:56:28 +01:00
Haralan Dobrev 19a324ce11 docs(angular.module): improve angular.Module#run docs 2012-11-11 11:40:18 +01:00
Jamison Dance cd8b78ebfd docs(guide): fix run-on sentence in modules guide 2012-11-11 11:34:06 +01:00
Jamison Dance 8891757891 docs(tutorial): change module name in step-7 2012-11-11 11:34:06 +01:00
Wes Alvaro 5c5193946d docs($timeout): set return type to Promise instead of *.
The cancel function accepts a Promise, but the timeout function
fails to specify returning a Promise.
2012-11-11 11:31:51 +01:00
Josh Adams ffa6c5195f docs(ngList): fix typo 2012-11-11 11:24:59 +01:00
Josh Adams a758799c7f docs(encodeUriSegment): fix typo 2012-11-11 11:22:43 +01:00
Tim Macfarlane e9253a88b9 docs(guide/directive): fix names in scope '='; easier to grok 2012-11-11 11:20:24 +01:00
Christian Vuerings f3e053cb6f docs(ngHide): Fix typo and make it more in line with ngShow 2012-11-11 10:36:28 +01:00
Anna Vester 04450c48df feat($sanitize): support telephone links
Per http://www.ietf.org/rfc/rfc3966.txt support tel: links
2012-11-11 10:31:27 +01:00
Igor Minar 8650843603 chore(docs): fix docs-scenario.html 2012-11-11 10:31:27 +01:00
Igor Minar e034fa08a8 chore(docs): remove obsolete gae files 2012-11-11 10:31:27 +01:00
Miško Hevery c6b4ab3548 Update docs/content/guide/directive.ngdoc
docs(directive): fix typo
2012-11-05 19:34:20 -08:00
Sudhir Jonathan b429f538a3 chore(testacular): use local testacular version
Making testacular a dependency to avoid having to install it globally.
(Causes npm issues on some machines)
2012-10-31 16:47:28 -07:00
Sudhir Jonathan b3cae4f457 fix(select): select option with a label of 0 is not shown
Bug caused by the use of the `||` operator to replace all non-truthy
values with an empty string. Changed to replace only `undefined` values.

Closes #1401
2012-10-31 15:03:13 -07:00
Igor Minar 7b52a976e1 chore(validate-commit-msg): allow '/' in scope 2012-10-31 14:47:59 -07:00
Fred Sauer 3a624a7ff5 docs(guide/location): fix table formatting
Fix table formatting so headings are bold, rows are separated by lines, and rows have :hover style
2012-10-31 14:47:53 -07:00
Tim Macfarlane b32adb7dea docs(module): fix typo in example
fixed example app, `simpleAppModule` should have been `myAppModule`.
2012-10-31 14:21:28 -07:00
sqwishy trick 271d2bed3a chore(injector): fix typo in injector documentation 2012-10-31 14:19:05 -07:00
Adam Macejak 249a1d84e7 fix(scenario-runner): support data-ng and x-ng based attributes
Prefixed attributes like data-ng-model and x-ng-model were not being
found by the Selector. It was only looking at ng: and ng- prefixed
attributes.
Added a few tests as well to ensure the aforementioned prefixed
attributes are being matched properly.

Closes #1020
2012-10-31 13:58:13 -07:00
Daniel Luz fdf85bfd86 docs(contribute): fix task name for continuous testing 2012-10-31 13:12:49 -07:00
Igor Minar 090e5426ac fix(docs): correctly generate filenames for plunkr/fiddle
previously examples like $http where broken because we would strip part of the
filename (http-hello.html -> http)

we really want to strip only the id suffix that we append to disambiguate
common filenames (like index.html) which appear in many examples.
2012-10-31 13:03:50 -07:00
Shyam Seshadri 7c67b2fb6a feat(docs): add plunkr support
Add option to edit source in Angular Docs in Plunkr in addition to JsFiddle
2012-10-31 12:54:52 -07:00
Daniel Luz 3c9b39ff52 fix(doc): typo on FAQ
Closes #1493
2012-10-31 10:24:12 -07:00
Igor Minar 54b3875ba5 fix($compile): don't look for class directives in empty string
if className is undefined or empty string, don't bother looking for directives in there
2012-10-29 17:49:56 -07:00
Igor Minar 008a782bc8 fix($compile): compilation should not recurse into empty nodes
if a node doesn't have children then don't try to compile these non-existent children
2012-10-29 17:49:36 -07:00
Igor Minar 524c5c8b5d style($compile): better fn names for debugging 2012-10-29 17:46:44 -07:00
Igor Minar b936236fbc refactor(): simplify nodeLinkFn 2012-10-29 17:46:44 -07:00
Igor Minar fc115bfd0d fix($compile): prevent double attr interpolation w/ templateUrl
This fixes the issue that caused two attr interpolation observers
to be registered for the same attribute as a result of isolate
scope definition with attr (@) property for this attribute.

Duplicate observers would then fight with each other updating the
model.

The issue occured only when this directive was used in a repeater
because that's when we clone the template node which caused the
two observers to point to two different sets of $attr instances.

Closes #1166, #836
2012-10-29 17:46:44 -07:00
Braden Shepherdson bca1604c12 fix(currency): Handle not-quite-zero values
IEEE 754 floating point sometimes results in values that are very small,
rather than zero. One example is 1.0 + 1.07 - 2.07, which returns
4.440892098500626e-16 instead of 0.

This change tweaks the number formatting logic so that an exponential
value with a negative exponent that is larger than the precision+1
returns 0 instead. For example: with precision 2, anything with an
exponent of -4, -5 or more would become 0. 9e-3 = 0.009 = 0.01, but 9e-4
= 0.0009 = 0.001 = 0.00. This detail is unlikely to matter since this
quirk is usually only triggered with values very close to zero.

Closes #1469
2012-10-26 08:51:28 -07:00
Braden Shepherdson f4517b500c doc(faq): Add Common Pitfalls section
Describes several common pitfalls new users of Angular fall into that
I've observed in #angularjs.
2012-10-26 08:49:44 -07:00
Braden Shepherdson f54edbbdd4 doc(faq): Fix minor spelling and wording errors 2012-10-26 08:49:44 -07:00
Igor Minar be50e0769a chore(check-size.sh): fix rake target 2012-10-22 12:39:37 -07:00
Igor Minar cf78fb5661 docs(contribute): add CLA note to code submission section 2012-10-19 09:14:05 -07:00
Igor Minar 5c9eb75867 docs(contribute): add visible link to github project 2012-10-19 09:02:06 -07:00
Igor Minar f43cf3b816 chore(jstd-scenario-adapter): remove from our repo
since we don't need the adapter for JsTD (testacular contains its own),
I'm removing this dead code.
2012-10-18 03:26:51 -07:00
Igor Minar 175e727f05 chore(validate-commit-msg): allow * and - in scope string 2012-10-18 03:26:08 -07:00
Igor Minar d938983c06 chore(jasmine): remove Jasmine from our repo
it's bundled with Testacular, so we don't need it here
2012-10-18 03:14:27 -07:00
Igor Minar 8d69f4b93a chore(jstd): remove JsTestDriver from our repo
Testacular FTW!
2012-10-18 03:13:04 -07:00
Igor Minar ca96ec32f9 docs(tutorial): replace JsTD with Testacular + drop snapshots
JsTD references have been replaced with Testacular stuff.

snapshots are PITA to maintain so I'm dropping them, everyone loves the Git
version anyway.
2012-10-18 02:33:45 -07:00
Igor Minar 4f59022582 chore(Rakefile): remove test_out dir when cleaning 2012-10-17 15:44:18 -07:00
Igor Minar 3bd95dbb1a chore(Rakefile): tune JVM for closure compiler
Using the client VM and forcing 32bit mode gives us huge perf boost.

before:

reali   0m8.173s
user    0m39.984s
sys     0m1.408s

after:

real    0m3.000s
user    0m12.687s
sys     0m0.852s
2012-10-17 15:36:51 -07:00
Igor Minar c959fa4fe8 chore(Rakefile): paralelize closure compilation
this speeds up the build by paralelizing closure compilation (the slowest
piece of the build process)

before:

real  0m14.372s
user  0m31.649s
sys   0m1.006s

after:

real  0m8.191s
user  0m40.473s
sys   0m1.378s
2012-10-17 14:38:15 -07:00
Vojta Jina a5d434d857 chore(test): add junit config for testacular 2012-10-17 13:17:16 -07:00
Igor Minar 0ae0591f42 chore(Rakefile): misc_options should support + -> , conversion 2012-10-17 12:48:08 -07:00
Igor Minar 43ac783d35 chore(Rakefile): use exec for webserver
exec unlike system replaces the current process. this way when we kill
the webserver process we don't get scary looking 'rake aborted' error
2012-10-17 12:45:10 -07:00
Misko Hevery c96dc60594 fix(doc): disable directory listing in docs.angularjs.org 2012-10-05 16:43:01 -07:00
Igor Minar b440ad36f3 docs(downloading): update the downloading docs 2012-10-05 03:13:51 -07:00
Vojta Jina 8b2532cec7 chore: add travis config 2012-09-24 23:39:33 -07:00
Vojta Jina 8db47ca7d4 fix($compile): reference local in isolate scope
This was really corner case:
Watcher needs to return changed value, to notify that model might have changed and one more $digest cycle needs to be performed.

The watcher, that takes care of reference binding into an isolate scope ("="), did not return changed value, if the change was from the isolate scope to the parent.

If any other watcher returned change, it worked fine, as this change caused re-digest.

Closes #1272
2012-09-20 18:32:01 -07:00
Vojta Jina bcaa3bb373 docs: load angular from CDN only on production
So that when running the docs locally, eg. during e2e testing, we use the latest build version of angular, rather than the stable one from CDN.

This fixes e2e tests running with Testacular.
2012-09-17 18:08:16 -07:00
Igor Minar 6fc4fdb438 docs(README): update README.md with new rake tasks 2012-09-17 14:51:21 -07:00
Igor Minar 6a5f8c0483 chore(Rakefile): fix test:jquery task 2012-09-17 14:50:51 -07:00
Igor Minar 20c116d9d5 docs(contribute): update contribute docs 2012-09-16 10:40:44 -07:00
Igor Minar 4a04c2ec0c chore(): remove unused files 2012-09-16 10:39:43 -07:00
Igor Minar 89dd566277 docs(contribute): update misc/contribute docs with Testacular info 2012-09-15 08:11:53 -07:00
Igor Minar 9d168f058f chore(testing): Testacular config files + rake tasks
- adds testacular config files for jqlite, jquery, modules and e2e tests
- replaces obsolete JsTD Rake tasks with Testacular onces
- rake tasks are parameterazied so that they can be used locally as well as on CI server

usage:

rake test  # run all tests on Chrome
rake test[Safari+Chrome+Opera]  # run all tests on Safari, Chrome and Opera
rake test[Safari]  # run all tests on Safari
rake test:jqlite # run unit tests using jqlite on Chrome
rake test:jqlite[Safari,"--reporter=dots"]  # run jqlite-based unit tests on Safari with dots reporter
rake autotest:jquery  # start testacular with jquery-based config and watch fs for changes
rake test:e2e # run end to end tests
2012-09-13 16:23:18 -07:00
Miško Hevery 5418564f04 docs(directive): remove reference to old isolation syntax 2012-09-13 11:31:06 -07:00
Misko Hevery b0a05a7531 fix($route): support inline annotation on .resolve 2012-09-11 22:10:26 -07:00
Tom Hughes 209b67df6a feat($http): Allow setting withCredentials on defaults
Closes #1095.
2012-09-11 21:59:31 -07:00
Vojta Jina 2e1539356a chore(scripts): add init-repo script 2012-09-11 17:27:57 -07:00
Misko Hevery 331cd5a8cb fix($evalAsync): have only one global async queue
Having one async queue per scope complicates the matters when users wish to do
partial scope updates, since many services put events on the rootScope. By
having single queue the programing model is simplified.
2012-09-11 16:12:41 -07:00
Brian Ford f2ebfa16b0 docs(guide): fix directive interpolation example code
Closes #1339
2012-09-11 16:12:41 -07:00
Shyam Seshadri 95276a7e10 fix(scenario): emit RunnerBegin event 2012-09-11 16:12:41 -07:00
Vojta Jina 5dbd942bac chore(scripts): add commit-msg hook (validation) 2012-09-11 16:12:40 -07:00
Jimmy Zhuo 84c13d96ff fix(scenario): NPE when no angular loaded in test page 2012-09-11 16:12:40 -07:00
Daniel Luz 79941d2527 docs($rootScope): fix iteration limit described by $watch, it's actually 10 as of now 2012-09-11 15:11:02 -07:00
Daniel Luz 03ebecd5eb docs($rootScope): fix typos and minor wording tweaks on $watch 2012-09-11 15:11:02 -07:00
Daniel Luz 62bb728d07 docs($rootScope): fix quoting on expression 2012-09-11 15:11:02 -07:00
Daniel Luz b8eb843b25 docs($rootScope): standardize on present, third-person actions for descriptions 2012-09-11 15:11:02 -07:00
Daniel Luz 053247e412 docs($rootScope): backquote attribute types too on $on 2012-09-11 15:11:02 -07:00
Daniel Luz 7fa391c979 docs($cacheFactory): fix backquotes on method descriptions 2012-09-11 15:11:01 -07:00
Daniel Luz b01c28c900 docs($rootScope): fix typos on $new 2012-09-11 15:11:01 -07:00
Daniel Luz c0b9e94dec docs($rootScope): fix typo on $eval 2012-09-11 15:11:01 -07:00
Daniel Luz 83fbdd1097 docs($rootScope): fix typos on $watch 2012-09-11 15:11:01 -07:00
Jay Zeng 03042c52b9 docs(ngResource): Spelling typo (agressive => aggressive) 2012-09-11 15:08:55 -07:00
Igor Minar 2a4a8226d1 fix($resource): fix isDefined -> angular.isDefined 2012-09-10 14:49:22 -07:00
sgtpep c81d8176cc fix(a): prevent Opera from incorrectly navigating on link click
we handle the navigation by ourselves, so we need to prevent the default action.

Opera ignores event.preventDefault() call so we must return false.
2012-09-06 16:06:27 -07:00
Kai Groner 04329151d2 fix(FormController): propagate dirty state to parent forms 2012-09-06 16:06:26 -07:00
Jonathan Zacsh a9be003fce chore(docs): get correct location for jasmine-node 2012-09-06 16:06:25 -07:00
Shyam Seshadri ca30fce28c fix(*): name all anonymous watch functions in Angular
This will allow us to see function names in Batarang and debugger.

Closes #1119
2012-09-06 16:06:25 -07:00
Xiangru Chen b6e4a71166 fix(ngSrc): don't set src if value is empty string
Current implementation of ngSrc may lead to empty src attribute when page is loading.

For example:

<img ng-src="{{image.url}}">
can be temporarily rendered as

<img src="">
before the image resource is loaded.

Some browser emits a request to the current page when seeing <img src=""> (Firefox13 and IE8 will, Chromium20 won't), which leads to performance problems.
2012-09-06 16:06:24 -07:00
Misko Hevery d9eff86ef7 fix($injector): more conservative annotation parsing 2012-09-06 16:06:24 -07:00
Pedro Del Gallego 8cb9c99ec0 feat(scenario): add dblclick method to the ngScenario dsl 2012-09-06 16:06:24 -07:00
Iwein Fuld 9473780e77 fix(dateFilter): make timezone optional
Makes the time zone optional in the date filter

Problem with the current R_ISO8601_STR regex was that the time was optional, but the zone was not.
This results in the filter not formatting local date times, which it could easily do.

For example:
2012-08-30 -> formatted
2012-08-30T06:06:06.123Z -> formatted
2012-08-30T06:06:06.123 -> NOT formatted

A simple change in the regex fixes this. Arguably this is closer to the ISO8601 spec which specifies
local dates being in the "current time zone" and not requiring a Z. In any case it behaves more like
a user would expect.
2012-09-06 16:06:23 -07:00
Misko Hevery eb5fd400d3 docs(concept): correct example for creating injector 2012-09-06 16:06:23 -07:00
Godmar Back 0472c5f07e docs(module): fixed module example and corrected typos 2012-09-06 16:06:23 -07:00
Cameron Westland 92558fe411 feat(mocha): support mocha in angular mocks 2012-09-06 16:06:23 -07:00
Gregory Pike d519953a4b feat(ngModel): support ngTrim attribute on input 2012-09-06 16:06:23 -07:00
Benjamín Eidelman 4909d1d39d fix($resource): allow falsy values in URL parameters
Close #1212

when a param value was 0 (or false) it was ignored and removed from url.
after this fix that only happens if the value is undefined or null.
2012-09-06 16:06:22 -07:00
Jay Zeng 7079ff5eb6 docs(module): myAppModule -> simpleAppModule 2012-09-06 16:06:22 -07:00
petrovalex 10e1c759f4 fix($resource): ignore undefined parameters
- $resource should handle multiple params with same name
- ignore slashes of undefined parameters
- fix default parameters issue, mentioned in #875

Closes #875
Closes #782
2012-09-06 16:06:22 -07:00
petrovalex 6c67719dfa fix(ngClassEven/Odd): filtering/ordering and repeater
Closes #1076
2012-09-06 16:06:22 -07:00
Max Martinsson cebd015f78 fix(ngClass): works with class interpolation
Closes #1016
2012-09-06 16:06:21 -07:00
Max Martinsson fbdab513dd feat($resource): support custom headers per action
Closes #736
2012-09-06 16:06:21 -07:00
Zhenbo Zhang f2b7fffdc0 fix(ngRepeat): now works with primitive types
closes #933
2012-09-06 16:06:21 -07:00
petrovalex 42c38b29f7 fix($parser): string concatination with undefined model
Closes #988
2012-09-06 16:06:21 -07:00
Stein Jakob Nordbø f299fd5122 fix(dateFilter): support sub-second precision on dateFilter 2012-09-06 16:06:19 -07:00
Igor Minar 05c88b866b docs($route): rename leftover $afterRouteChange to $routeChangeSuccess 2012-09-06 15:03:35 -07:00
Igor Minar 9b08bfa251 chore(release): prepare 1.1.1 pathological-kerning iteration 2012-09-06 10:41:23 -07:00
Misko Hevery 99a000bac2 fix(docs): broken url to angular-bootstrap 2012-09-04 18:15:00 -07:00
Igor Minar f353fea042 chore(Rakefile): add 'version' rake task to generate version.txt 2012-09-04 16:38:01 -07:00
Igor Minar b1f50307b3 chore(docs): bump up the stable version 2012-09-04 16:31:57 -07:00
Igor Minar d0c0eadedd chore(release): cut the 1.1.0 increase-gravatar release 2012-09-04 11:11:09 -07:00
Igor Minar b8fac353f0 chore(docs): don't rewrite colons in doc filenames 2012-09-04 11:11:09 -07:00
Igor Minar b22308152f chore(Rakefile): zip only the build dir 2012-08-31 13:59:03 -07:00
Igor Minar 5e9041818b revert: fix(ng-repeat) to work with primitive types
this was accidentaly merged in. the commit is not ready yet
and we don't have CLA signature.

This reverts commit 98d489712e.
2012-08-31 13:59:03 -07:00
Igor Minar db861db1f2 docs(changelog): release notes for 1.0.2 and 1.1.0 releases 2012-08-31 13:59:03 -07:00
Jonathan Zacsh b12d1b6813 fix(docs): Making sure gen_docs.sh looks for a globally installed copy of jasmine-node as well as local. 2012-08-30 22:33:30 -07:00
Fernando Correia acb499f820 docs(tutorial): correct typos and clarify a few sections 2012-08-30 22:19:34 -07:00
Brice Burgess 9a710c788d fix(docs): indicate support for passing a string as the controller property on $routeProvider's route object 2012-08-30 22:13:20 -07:00
brettcannon 1b34c6d558 doc(misc) Mention how attribute names map to directive names. 2012-08-30 22:09:16 -07:00
Igor Minar a62c7b8b4e test(locationSpec): fix broken tests after vojta's commit 2012-08-30 16:25:23 -07:00
Sahat Yalkabov 62cfedbe0c doc(module) changed simpleApp to myApp in the Module page guide for consistency 2012-08-30 16:10:39 -07:00
Steve Nicolai 5cb7297a08 doc(devguide) - Fix typos and small grammatical errors in the developer guide. 2012-08-30 16:02:24 -07:00
Igor Minar 0f05516d14 chore(docs): ask GAE to serve docs-keywords.js 2012-08-30 15:58:55 -07:00
Uri Goldshtein f5f1200f25 Loading from Google CDN
As you guys had mansion, we can and need to do it through Google CDN for better performance,
so i've updated it accordingly
2012-08-30 15:49:11 -07:00
Tyson Benson c023c850c3 docs(typos): fix typos in dev guide 2012-08-30 15:43:58 -07:00
German Galvis 5318588d6e fix(scenario): Adding meta tag to avoid cache issues 2012-08-30 15:36:42 -07:00
phil 14c8f6a7ca docs(api): fix typo on home page
Refference -> Reference
2012-08-30 15:31:29 -07:00
csugden 351deb555f Update docs/content/guide/overview.ngdoc
Corrects video information
2012-08-30 15:28:15 -07:00
Jamie Krug 847d2da0f8 fix(docs): Fix typos and improve grammar. 2012-08-30 15:25:21 -07:00
Jamie Krug dbefd671e4 fix(docs): Fix bad links. 2012-08-30 15:25:20 -07:00
Colin Frei aff68a9ddf docs(module) fix typo 2012-08-30 15:22:08 -07:00
Zhenbo Zhang 0a71753ce3 fix(ng-repeat) to work with primitive types 2012-08-30 15:20:40 -07:00
Vojta Jina 1a8642aac2 fix(mocks): free up memory after every spec 2012-08-30 15:18:09 -07:00
Igor Minar 8114d55a15 test(bootstrap): test exception siling during bootstrap
Closes #1018
2012-08-30 15:15:11 -07:00
Igor Minar 9398040a41 test(ngApp): add missing test for [ng-app] bootstrap 2012-08-30 15:15:11 -07:00
Brian Ford d804bbcd51 feat($interpolate): provide contextual error messages
if an exception occurs during interpolation of a string
(e.g. name() in "Hello, {{name()}}!" throws an exception) we now print
an error message with the expression that was being evaluated when the
exception was thrown.
2012-08-30 14:50:22 -07:00
Igor Minar d3fa7a2e9e fix(jqLite): better support for xhtml
it turns out that some stuff doesn't work in xhtml as it does in html.

for example &nbsp; can't be innerHTML-ed and auto-closing of elements
doesn't work.

the reporter of the referenced issue claimed that innerHTML vs text on
script made a difference but that doesn't appear to be true in my testing.

I'm not including test for this because testacular doesn't currently
run tests in xhtml yet.

Closes #1301
2012-08-30 10:53:23 -07:00
Igor Minar 8693eac417 chore(docs): correctly link docs images 2012-08-30 02:26:35 -07:00
Igor Minar e0184d4aef chore(Rakefile): fix the default task 2012-08-29 16:56:48 -07:00
Igor Minar 1702e49548 chore(Rakefile): remove bogus symlink from build 2012-08-29 14:58:10 -07:00
Igor Minar d6706efe7f chore(docs): use symlinks to build docs
so that we can just edit source files without rebuilding docs.

this works for all docs files, except for those that are generated
or rewritten during build.
2012-08-28 16:06:50 -07:00
Igor Minar b08d4b22d2 chore(Rakefile): various build script changes
- restructure rake tasks

  this splits up the concatination and minification into two
  tasks so that we can just build angular.js quickly without wasting
  time with minification which is often not needed when just debugging
  some issue on 3rd party site.

- use symlinks when creating final zip file

- switch from btar to zip

- get rid of version numbers from filenames

- rewrite version numbers in all index files

Closes #1226
2012-08-28 12:38:34 -07:00
Misko Hevery e8ded01cf5 doc($log): correct non-working example 2012-08-27 15:44:38 -07:00
Misko Hevery 7a5f25f667 doc(guide): add concepts 2012-08-27 15:44:38 -07:00
Misko Hevery 96697f464f fix(ngdoc): failing test 2012-08-27 15:44:38 -07:00
Colin Frei 7e18724dfa doc(directive) correct typos 2012-08-27 15:01:50 -07:00
Misko Hevery c269eb3d26 fix(docs) typo 2012-08-27 14:59:19 -07:00
Misko Hevery fa62ea810f fix(ng-list): remove data bound flicker 2012-08-27 14:59:18 -07:00
Misko Hevery bf8ed8a532 doc(misc) updated getting started to reflect the new homepage 2012-08-27 14:59:18 -07:00
Misko Hevery d05a2809a1 doc(guide) simplify the guide home page 2012-08-27 14:59:17 -07:00
Igor Minar fa6c8c3131 chore(Rakefile): rewrite version numbers in all index files 2012-08-27 12:26:04 -07:00
Igor Minar f7ac8ef97a chore(docs): support _escaped_fragment_ hack for crawler 2012-08-25 02:30:55 -07:00
Igor Minar 4a4b28dbf3 chore(docs): use GAE and Google CDN for docs
Short summary: if you use local node server everything should work as before,
if you use GAE, everything should work now as well, but we pull assets from CDN.

- GAE doesn't support ':' in filenames, so I had to replace it with '_'
  but only in the filename, all servers were reconfigured to rewrite the
  urls from : to _ when doing file lookup
- We now pull angular assets from google CDN when deployed on GAE (locally
  or in production). When running on a non GAE server we pull assets from
  ../ directory as before
- Since only certain versions of Angular are available on CDN and we want
  to be able to autodeploy docs, I had to pin down the Angular files
  to a "stable" version when running on GAE
2012-08-24 14:54:35 -07:00
Igor Minar 3e12bc481d docs(a): expose hidden docs
It seems that docs for these directive were previously hidden by accident
2012-08-24 14:54:34 -07:00
johnlindquist 32137cab82 docs(ngRoute): fix typo
aftre -> after
2012-08-23 15:11:07 -04:00
phil f7b4296c38 docs(tutorial): fix typo in step_00
Just removed an extra comma. No big deal.
2012-08-23 01:02:28 -07:00
Igor Minar cab5e1d9b3 fix(docs): update docs top menu links 2012-08-16 11:26:36 -07:00
Igor Minar dfe99836cd fix($compile): denormalize directive templates
Since developers are allowed to customize start/end interpolation
strings, but third-party directive creators don't know about these
customizations, we should standardize on {{ }} in templates of
reusable (third-party) directives. During the compilation, these
templates are then denormalized to use whatever the custom
start/end symbol is, effectively translating the template into the
syntax of the runtime environment.

This addresses an issue raised at http://goo.gl/e8VPV

Existing code should not be affected by this change since project
that do use custom interpolation markers are not expected to use
{{ }} in existing directive templates.
2012-08-13 14:33:56 -07:00
Igor Minar 0f37194fb7 refactor($compile): code cleanup 2012-08-13 09:48:23 -07:00
Brian Ford e85774f709 fix(ngPluralize): fixes ng-pluralize when using non-standard start/end symbols
Closes #1134
2012-08-13 09:48:23 -07:00
Igor Minar 44345c74de style(ngPluralizeSpec): fix indentation 2012-08-13 09:48:22 -07:00
Igor Minar 58f121a5c2 feat($interpolate): expose start/end symbols in run phase
previously the startSymbol() and endSymbol() getters were exposed only via provider
in the config phase
2012-08-13 09:48:22 -07:00
Igor Minar cf6023ef22 docs($interpolateProvider): fixing docs 2012-08-13 09:48:22 -07:00
Igor Minar 2034871764 fix($interpolate): $interpolateProvider.endSymbol() returns startSymbol
I also added missing tests.
2012-08-13 09:48:21 -07:00
Igor Minar 15d283b114 docs($interpolate): fix typo in description 2012-08-13 09:48:21 -07:00
Vojta Jina 9be169365c docs($compileProvider): remove duplicate of .directive() 2012-08-12 10:45:14 -07:00
Vojta Jina 00683a8bbb docs: fix broken links to $compileProvider.directive() 2012-08-12 10:44:29 -07:00
Brian Ford f00b6cca02 fix(docs): fixed documentation for using linky 2012-08-10 16:33:25 -07:00
Brian Ford e05a97c6f5 chore(ngDoc): add support for custom @usage metadata 2012-08-10 16:33:13 -07:00
Brian Ford 2e3651686c fix(docs): added note about using JSON3 as a polyfill for IE7 2012-08-10 16:27:44 -07:00
Brian Ford 536de14821 fix(docs): added note about needing JSON shim for IE7 and earlier 2012-08-10 16:27:44 -07:00
Vojta Jina e0a54f6b20 feat($http): support reponseType
Closes #1013
2012-08-10 16:17:59 -07:00
Igor Minar 9767f7bdd3 fix(option): support option elements in datalist
previously we expected to find option elements only within select element and if
that was not the case we throw an error. This made it impossible to include datalist
element with nested option elements in the template.

Closes #1165
2012-08-10 16:14:30 -07:00
Vojta Jina 167aa0c29c feat($sniffer): auto detect CSP mode
Chrome Canary now has CSP with apis that allow auto-detection. This change
will turn on CSP mode automatically when we detect its presence.

https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-interfaces--experimental
2012-08-10 14:53:53 -07:00
unirgy 4ccd9eb883 docs($rootScope): fix $on listener signature doc
Added args in $on() listener syntax declaration
2012-08-10 14:50:22 -07:00
Igor Minar c0d638a94b test(jqLite): add missing test for $destroy event 2012-08-10 13:04:39 -07:00
Igor Minar 054d40f338 fix(form): prevent page reload when form destroyed
this fix ensures that we prevent the default action on form submission
(full page reload) even in cases when the form is being destroyed as
a result of the submit event handler (e.g. when route change is
triggered).

The fix is more complicated than I'd like it to be mainly because
we need to ensure that we don't create circular references between
js closures and dom elements via DOM event handlers that would then
result in a memory leak.

Also the differences between IE8, IE9 and normal browsers make testing
this ugly.

Closes #1238
2012-08-10 13:03:55 -07:00
Igor Minar 5cec32492c test(form): fix broken preventDefault test
the original test relied on incorrect assumptions about how jasmine async
tests work (when setTimeout is triggered) and how browser reloads a page
(the sequence of events) and thus the test passes even when the default
is not prevented.

this change fixes the test by registering an extra submit event handler
that checks if the default was prevented.

if the default was not prevented, the test will fail and the page will
be reloaded causing the test runner to panic.
2012-08-07 22:07:49 -07:00
Igor Minar c25cb7d488 refactor(formSpec): group preventDefault specs into a describe 2012-08-07 17:23:16 -07:00
Igor Minar 54e4a6ffbf docs(faq): update faq docs 2012-08-07 10:15:54 -07:00
Igor Minar eee9a51fad docs(styles): fix the cog icon alignment 2012-08-06 16:54:18 -07:00
Vojta Jina 77e6d833f6 chore(nodeserver): add font mime type 2012-08-04 12:09:28 -07:00
Vojta Jina 33ad2b4126 docs(guide): hide scenario for directive example
scenario test for this example would be tricky, we need to teach
the runner how to inject mocks first.
2012-07-30 21:21:34 -07:00
Vojta Jina b84eaffd39 docs: fix icons
Copy fontawesome during build
2012-07-30 21:20:28 -07:00
brettcannon a1107e81eb fix(docs): "in depth" -> "in-depth" 2012-07-20 20:54:42 -07:00
JP Sugarbroad e3e8813e3c refactor($injector): move $injector into the providerCache
Better than special-casing '$injector' in createInjector.
2012-07-19 21:56:22 -07:00
Igor Minar 6e2d9711e8 chore(release): start 1.1.0 increase-gravatas iteration 2012-07-19 21:48:58 -07:00
454 changed files with 8667 additions and 52182 deletions
+4 -4
View File
@@ -5,9 +5,9 @@ node_js:
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install -g grunt-cli
- grunt package
- grunt webserver > /dev/null &
- npm install -g testacular@canary
- rake package
- ./nodeserver.sh > /dev/null &
script:
- grunt test --browsers Firefox --reporters=dots
- rake test[Firefox,"--reporters=dots"]
+2 -464
View File
@@ -1,465 +1,3 @@
<a name="1.1.5"></a>
# 1.1.5 triangle-squarification (2013-05-22)
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
out and please give us feedback._
_Note: This release also contains all bug fixes available in [1.0.7](#1.0.7)._
## Features
- **$animator:**
- provide support for custom animation events
([c53d4c94](https://github.com/angular/angular.js/commit/c53d4c94300c97dd005f9a0cbdbfa387294b9026))
- allow to globally disable and enable animations
([5476cb6e](https://github.com/angular/angular.js/commit/5476cb6e9b6d7a16e3a86585bc2db5e63b16cd4d))
- **$http:**
- add support for aborting via timeout promises
([9f4f5937](https://github.com/angular/angular.js/commit/9f4f5937112655a9881d3281da8e72035bc8b180),
[#1159](https://github.com/angular/angular.js/issues/1159))
- add a default content type header for PATCH requests
([f9b897de](https://github.com/angular/angular.js/commit/f9b897de4b5cc438515cbb54519fbdf6242f5858))
- add timeout support for JSONP requests
([cda7b711](https://github.com/angular/angular.js/commit/cda7b71146f6748116ad5bbc9050ee7e79a9ce2b))
- **$parse:** add support for ternary operators to parser
([6798fec4](https://github.com/angular/angular.js/commit/6798fec4390a72b7943a49505f8a245b6016c84b))
- **$q:** add $q.always() method
([6605adf6](https://github.com/angular/angular.js/commit/6605adf6d96cee2ef53dfad24e99d325df732cab))
- **$controller:** support "Controller as" syntax
([cd38cbf9](https://github.com/angular/angular.js/commit/cd38cbf975b501d846e6149d1d993972a1af0053),
[400f9360](https://github.com/angular/angular.js/commit/400f9360bb2f7553c5bd3b1f256a5f3db175b7bc))
- **$injector:** add `has` method for querying
([80341cb9](https://github.com/angular/angular.js/commit/80341cb9badd952fdc80094df4123629313b4cc4),
[#2556](https://github.com/angular/angular.js/issues/2556))
- **Directives:**
- **ngAnimate:**
- add support for CSS3 Animations with working delays and multiple durations
([14757874](https://github.com/angular/angular.js/commit/14757874a7cea7961f31211b245c417bd4b20512))
- cancel previous incomplete animations when new animations take place
([4acc28a3](https://github.com/angular/angular.js/commit/4acc28a310d006c62afe0de8ec82fed21c98c2d6))
- **ngSrcset:** add new ngSrcset directive
([d551d729](https://github.com/angular/angular.js/commit/d551d72924f7c43a043e4760ff05d7389e310f99),
[#2601](https://github.com/angular/angular.js/issues/2601))
- **ngIf:** add directive to remove and recreate DOM elements
([2f96fbd1](https://github.com/angular/angular.js/commit/2f96fbd17577685bc013a4f7ced06664af253944))
- **select:** match options by expression other than object identity
([c32a859b](https://github.com/angular/angular.js/commit/c32a859bdb93699cc080f9affed4bcff63005a64))
- **ngInclude:** $includeContentRequested event
([af0eaa30](https://github.com/angular/angular.js/commit/af0eaa304748f330739a4b0aadb13201126c5407))
- **Mobile:**
- **ngClick:** Add a CSS class while the element is held down via a tap
([52a55ec6](https://github.com/angular/angular.js/commit/52a55ec61895951999cb0d74e706725b965e9c9f))
- **ngSwipe:** Add ngSwipeRight/Left directives to ngMobile
([5e0f876c](https://github.com/angular/angular.js/commit/5e0f876c39099adb6a0300c429b8df1f6b544846))
- **docs:**
- Add FullText search to replace Google search in docs
([3a49b7ee](https://github.com/angular/angular.js/commit/3a49b7eec4836ec9dc1588e6cedda942755dc7bf))
- external links to github, plunkr and jsfiddle available for code examples
([c8197b44](https://github.com/angular/angular.js/commit/c8197b44eb0b4d49acda142f4179876732e1c751))
- add variable type hinting with colors
([404c9a65](https://github.com/angular/angular.js/commit/404c9a653a1e28de1c6dda996875d6616812313a))
- support for HTML table generation from docs code
([b3a62b2e](https://github.com/angular/angular.js/commit/b3a62b2e19b1743df52034d4d7a0405e6a65f925))
- **scenario runner:** adds mousedown and mouseup event triggers to scenario
([629fb373](https://github.com/angular/angular.js/commit/629fb37351ce5778a40a8bc8cd7c1385b382ce75))
## Bug Fixes
- **$animator:** remove dependency on window.setTimeout
([021bdf39](https://github.com/angular/angular.js/commit/021bdf3922b6525bd117e59fb4945b30a5a55341))
- **$controller:** allow dots in a controller name
([de2cdb06](https://github.com/angular/angular.js/commit/de2cdb0658b8b8cff5a59e26c5ec1c9b470efb9b))
- **$location:**
- prevent navigation when event isDefaultPrevented
([2c69a673](https://github.com/angular/angular.js/commit/2c69a6735e8af5d1b9b73fd221274d374e8efdea))
- compare against actual instead of current URL
([a348e90a](https://github.com/angular/angular.js/commit/a348e90aa141921b914f87ec930cd6ebf481a446))
- prevent navigation if already on the URL
([4bd7bedf](https://github.com/angular/angular.js/commit/4bd7bedf48c0c1ebb62f6bd8c85e8ea00f94502b))
- fix URL interception in hash-bang mode
([58ef3230](https://github.com/angular/angular.js/commit/58ef32308f45141c8f7f7cc32a6156cd328ba692),
[#1051](https://github.com/angular/angular.js/issues/1051))
- correctly rewrite Html5 urls
([77ff1085](https://github.com/angular/angular.js/commit/77ff1085554675f1a8375642996e5b1e51f9ed2d))
- **$resource:**
- null default param results in TypeError
([cefbcd47](https://github.com/angular/angular.js/commit/cefbcd470d4c9020cc3487b2326d45058ef831e2))
- collapse empty suffix parameters correctly
([53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336))
- **$rootScope:**
- ensure $watchCollection correctly handles arrayLike objects
([6452707d](https://github.com/angular/angular.js/commit/6452707d4098235bdbde34e790aee05a1b091218))
- **date filter:** correctly format dates with more than 3 sub-second digits
([4f2e3606](https://github.com/angular/angular.js/commit/4f2e36068502f18814fee0abd26951124881f951))
- **jqLite:**
- pass a dummy event into triggerHandler
([0401a7f5](https://github.com/angular/angular.js/commit/0401a7f598ef9a36ffe1f217e1a98961046fa551))
- **Directives:**
- **ngAnimate:**
- eval ng-animate expression on each animation
([fd21c750](https://github.com/angular/angular.js/commit/fd21c7502f0a25364a810c26ebeecb678e5783c5))
- prevent animation on initial page load
([570463a4](https://github.com/angular/angular.js/commit/570463a465fae02efc33e5a1fa963437cdc275dd))
- skip animation on first render
([1351ba26](https://github.com/angular/angular.js/commit/1351ba2632b5011ad6eaddf004a7f0411bea8453))
- **ngPattern:** allow modifiers on inline ng-pattern
([12b6deb1](https://github.com/angular/angular.js/commit/12b6deb1ce99df64e2fc91a06bf05cd7f4a3a475),
[#1437](https://github.com/angular/angular.js/issues/1437))
- **ngRepeat:**
- correctly iterate over array-like objects
([1d8e11dd](https://github.com/angular/angular.js/commit/1d8e11ddfbd6b08ff02df4331f6df125f49da3dc),
[#2546](https://github.com/angular/angular.js/issues/2546))
- prevent initial duplicates
([a0bc71e2](https://github.com/angular/angular.js/commit/a0bc71e27107c58282e71415c4e8d89e916ae99c))
- **ngView:** accidentally compiling leaving content
([9956baed](https://github.com/angular/angular.js/commit/9956baedd73d5e8d0edd04c9eed368bd3988444b))
- **scenario runner:** correct bootstrap issue on IE
([ab755a25](https://github.com/angular/angular.js/commit/ab755a25f9ca3f3f000623071d8de3ddc4b1d78e))
## Breaking Changes
- **$animator/ngAnimate:**
- **$resource:** due to [53061363](https://github.com/angular/angular.js/commit/53061363c7aa1ab9085273d269c6f04ac2162336),
A `/` followed by a `.`, in the last segment of the URL template is now collapsed into a single `.` delimiter.
For example: `users/.json` will become `users.json`. If your server relied upon this sequence then it will no longer
work. In this case you can now escape the `/.` sequence with `/\.`
<a name="1.0.7"></a>
# 1.0.7 monochromatic-rainbow (2013-05-22)
## Bug Fixes
- **$browser:** should use first value for a cookie.
([3952d35a](https://github.com/angular/angular.js/commit/3952d35abe334a0e6afd1f6e34a74d984d1e9d24),
[#2635](https://github.com/angular/angular.js/issues/2635))
- **$cookieStore:** $cookieStore.get now parses blank string as blank string
([cf4729fa](https://github.com/angular/angular.js/commit/cf4729faa3e6e0a5178e2064a6f3cfd345686554))
- **$location:** back-button should fire $locationChangeStart
([dc9a5806](https://github.com/angular/angular.js/commit/dc9a580617a838b63cbf5feae362b6f9cf5ed986),
[#2109](https://github.com/angular/angular.js/issues/2109))
- **$parse:** Fix context access and double function call
([7812ae75](https://github.com/angular/angular.js/commit/7812ae75d578314c1a285e9644fc75812940eb1d),
[#2496](https://github.com/angular/angular.js/issues/2496))
- **dateFilter:** correctly format ISODates on Android<=2.1
([f046f6f7](https://github.com/angular/angular.js/commit/f046f6f73c910998a94f30a4cb4ed087b6325485),
[#2277](https://github.com/angular/angular.js/issues/2277))
- **jqLite:** correct implementation of mouseenter/mouseleave event
([06f2b2a8](https://github.com/angular/angular.js/commit/06f2b2a8cf7e8216ad9ef05f73426271c2d97faa),
[#2131](https://github.com/angular/angular.js/issues/2131))
- **angular.copy/angular.extend:** do not copy $$hashKey in copy/extend functions.
([6d0b325f](https://github.com/angular/angular.js/commit/6d0b325f7f5b9c1f3cfac9b73c6cd5fc3d1e2af0),
[#1875](https://github.com/angular/angular.js/issues/1875))
- **i18n:** escape all chars above \u007f in locale files
([695c54c1](https://github.com/angular/angular.js/commit/695c54c17b3299cd6170c45878b41cb46a577cd2),
[#2417](https://github.com/angular/angular.js/issues/2417))
- **Directives:**
- **ngPluralize:** handle the empty string as a valid override
([67a4a25b](https://github.com/angular/angular.js/commit/67a4a25b890fada0043c1ff98e5437d793f44d0c),
[#2575](https://github.com/angular/angular.js/issues/2575))
- **select:** ensure empty option is not lost in IE9
([4622af3f](https://github.com/angular/angular.js/commit/4622af3f075204e2d5ab33d5bd002074f2d940c9),
[#2150](https://github.com/angular/angular.js/issues/2150))
- **ngModel:** use paste/cut events in IE to support context menu
([363e4cbf](https://github.com/angular/angular.js/commit/363e4cbf649de4c5206f1904ee76f89301ceaab0),
[#1462](https://github.com/angular/angular.js/issues/1462))
- **ngClass:** should remove classes when object is the same but property has changed
([0ac969a5](https://github.com/angular/angular.js/commit/0ac969a5ee1687cfd4517821943f34fe948bb3fc))
- **PhoneCat Tutorial:** renamed Testacular to Karma
([angular-phonecat](https://github.com/angular/angular-phonecat))
<a name="1.1.4"></a>
# 1.1.4 quantum-manipulation (2013-04-03)
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
out and please give us feedback._
_Note: This release also contains all bug fixes available in [1.0.6](#1.0.6)._
## Features
- **$compile:**
- allow directives to modify interpolated attributes
([fe8d893b](https://github.com/angular/angular.js/commit/fe8d893b839e9b14e3e55a3a0523cc1e6355bdd5))
- support for dynamic template generation
([eb53423a](https://github.com/angular/angular.js/commit/eb53423a41136fcda0c5e711f2d104952080354b))
- add attribute binding support via ngAttr*
([cf17c6af](https://github.com/angular/angular.js/commit/cf17c6af475eace31cf52944afd8e10d3afcf6c0),
[#1050](https://github.com/angular/angular.js/issues/1050), [#1925](https://github.com/angular/angular.js/issues/1925))
- `'=?'` makes `'='` binding optional
([ac899d0d](https://github.com/angular/angular.js/commit/ac899d0da59157fa1c6429510791b6c3103d9401),
[#909](https://github.com/angular/angular.js/issues/909), [#1435](https://github.com/angular/angular.js/issues/1435))
- **$q:** `$q.all()` now accepts hash
([e27bb6eb](https://github.com/angular/angular.js/commit/e27bb6eb132a68665c8fca3f5a216b19b1129ba6))
- **$resource:** ability to override url in resource actions
([60f1f099](https://github.com/angular/angular.js/commit/60f1f099fc7e5197808cd6acb7407cdc40f50a3f))
- **$route:** add `caseInsensitiveMatch` option for url matching
([5e18a15f](https://github.com/angular/angular.js/commit/5e18a15fb01d2e81adda68503754289fa9655082))
- **http:**
- support request/response promise chaining
([4ae46814](https://github.com/angular/angular.js/commit/4ae46814ff4e7c0bbcdbbefc0a97277283a84065))
- set custom default cache in $http.defaults.cache
([99f3b70b](https://github.com/angular/angular.js/commit/99f3b70b2d316f5bb39e21249e752c29f49c90ab))
- **JQLite:** `ready()` now supports `document.readyState=='complete'`
([753fc9e5](https://github.com/angular/angular.js/commit/753fc9e58d5e554d4930548558efecc283557eeb))
- **Scenario:** autodisable animations when running e2e tests
([fec4ef38](https://github.com/angular/angular.js/commit/fec4ef38815340e8e5a6b65fd6c08f5c74e701d8))
- **Scope:** add `$watchCollection` method for observing collections
([5eb96855](https://github.com/angular/angular.js/commit/5eb968553a1130461ab8704535691e00eb154ac2))
- **angular.bootstrap:** support deferred bootstrap (mainly useful for tools like test runners and Batarang)
([603fe0d1](https://github.com/angular/angular.js/commit/603fe0d19608ffe1915d8bc23bf412912e7ee1ac))
- **ngMobile:** add ngMobile module with mobile-specific ngClick
([707c65d5](https://github.com/angular/angular.js/commit/707c65d5a228b44ab3aea2fad95516fe6c57169a))
- **Directives:**
- **ngKeypress:** add ngKeypress directive for handling keypress event
([f20646bc](https://github.com/angular/angular.js/commit/f20646bce5f0c914992a78fc2556bda136c27ac9))
- **ngSwitch:** Preserve the order of the elements not in the ng-switch
([e88d6179](https://github.com/angular/angular.js/commit/e88d6179c3a6a137e75fa09de906fc83c6515db2),
[#1074](https://github.com/angular/angular.js/issues/1074))
- **ngAnimate:** add support for animation
([0b6f1ce5](https://github.com/angular/angular.js/commit/0b6f1ce5f89f47f9302ff1e8cd8f4b92f837c413))
- **ngRepeat:** add support for custom tracking of items
([61f2767c](https://github.com/angular/angular.js/commit/61f2767ce65562257599649d9eaf9da08f321655))
## Breaking Changes
- **$route:** due to [6f71e809](https://github.com/angular/angular.js/commit/6f71e809141bf89501e55c378921d6e7ec9512bc),
in $routeChangeStart event, nextRoute.$route property is gone. Use the nextRoute object itself instead of nextRoute.$route.
- **ngRepeat:** due to [61f2767c](https://github.com/angular/angular.js/commit/61f2767ce65562257599649d9eaf9da08f321655), it is now considered an error to have two identical items (identified by the new "track by" expression) in a collection that is fed into the repeater. This behavior was previously tolerated.
- **ngSwitch:** due to [e88d6179](https://github.com/angular/angular.js/commit/e88d6179c3a6a137e75fa09de906fc83c6515db2),
elements not in the ng-switch were rendered after the ng-switch elements. Now they are rendered in-place.
Templates with ngSwitch directives and nested non-ngSwitchWhen elements should be updated to preserve render order.
For example: The following was previously rendered with `<li>1</li>` after `<li>2</li>`:
<ul ng-switch="select">
<li>1</li>
<li ng-switch-when="option">2</li>
</ul>
To keep the old behaviour, use:
<ul ng-switch="select">
<li ng-switch-when="1">2</li>
<li>1</li>
</ul>
<a name="1.0.6"></a>
# 1.0.6 universal-irreversibility (2013-04-04)
## Bug Fixes
- **$compile:**
- compile replace directives in external template
([398691be](https://github.com/angular/angular.js/commit/398691beb3fc40a481afa258d181de06ec0d153c),
[#1859](https://github.com/angular/angular.js/issues/1859))
- whitelist file:// in url sanitization
([7b236b29](https://github.com/angular/angular.js/commit/7b236b29aa3a6f6dfe722815e0a2667d9b7f0899))
- handle elements with no childNodes property
([bec614fd](https://github.com/angular/angular.js/commit/bec614fd90c48c3921a4b659912008574e553b40))
- **$http:** don't encode URL query substring "null" to "+"
([86d191ed](https://github.com/angular/angular.js/commit/86d191ed4aea9015adc71b852223475c5c762c34))
- **$httpBackend:** prevent DOM err due to dereferencing .responseText
([509ec745](https://github.com/angular/angular.js/commit/509ec745fdbb54b54672fbf8595a4958c16f2b53),
[#1922](https://github.com/angular/angular.js/issues/1922))
- **$location:**
- parse FirefoxOS packaged app urls
([3a81dd8b](https://github.com/angular/angular.js/commit/3a81dd8bddbade81c4c9f734813458d0d969a4bf),
[#2112](https://github.com/angular/angular.js/issues/2112))
- correctly rewrite html5 url to hashbang url
([9befe370](https://github.com/angular/angular.js/commit/9befe37014141fbfdf0cded318d28322fc058c13))
- **$route:** make nextRoute.$route private
([6f71e809](https://github.com/angular/angular.js/commit/6f71e809141bf89501e55c378921d6e7ec9512bc),
[#1907](https://github.com/angular/angular.js/issues/1907))
- **mocks:** prevent NPE when module definition outside of it.
([5c735eb4](https://github.com/angular/angular.js/commit/5c735eb4ab07144a62949472ed388cb185099201))
- **dateFilter:** correct timezone date filter for 1/2 hour offsets
([1c1cd4fd](https://github.com/angular/angular.js/commit/1c1cd4fdf6b6d7511c7b8dc61b8042011dc54830))
<a name="1.1.3"></a>
# 1.1.3 radioactive-gargle (2013-02-20)
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
out and please give us feedback._
_Note: This release also contains all bug fixes available in [1.0.5](#1.0.5)._
## Bug Fixes
- **$compile:**
- initialize interpolated attributes before directive linking
([bb8448c0](https://github.com/angular/angular.js/commit/bb8448c011127306df08c7479b66e5afe7a0fa94))
- interpolate @ locals before the link function runs
([2ed53087](https://github.com/angular/angular.js/commit/2ed53087d7dd06d728e333a449265f7685275548))
- **$http:**
- do not encode special characters `@$:,` in params
([288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf))
- **$resource:**
- params should expand array values properly
([2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01))
## Features
- **$http:** allow overriding the XSRF header and cookie name
([8155c3a2](https://github.com/angular/angular.js/commit/8155c3a29ea0eb14806913b8ac08ba7727e1969c))
- **$parse:** added `constant` and `literal` properties
([1ed63858](https://github.com/angular/angular.js/commit/1ed638582d2f2c7f89384d9712f4cfac52cc5b70))
- **$resource:** expose promise based api via $then and $resolved
([dba6bc73](https://github.com/angular/angular.js/commit/dba6bc73e802fdae685a9f351d3e23c7efa8568a))
- **$routeProvider:** add support to catch-all parameters in routes
([7eafbb98](https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5))
- **Scope:**
- expose transcluded and isolate scope info for batarang
([649b8922](https://github.com/angular/angular.js/commit/649b892205615a144dafff9984c0e6ab10ed341d))
- only evaluate constant $watch expressions once
([1d7a95df](https://github.com/angular/angular.js/commit/1d7a95df565192fc02a18b0b297b39dd615eaeb5))
- **angular.noConflict:** added api to restore previous angular namespace reference
([12ba6cec](https://github.com/angular/angular.js/commit/12ba6cec4fb79521101744e02a7e09f9fbb591c4))
- **Directives:**
- **ngSwitch:** support multiple matches on ngSwitchWhen and ngSwitchDefault
([0af17204](https://github.com/angular/angular.js/commit/0af172040e03811c59d01682968241e3df226774),
[#1074](https://github.com/angular/angular.js/issues/1074))
- **Filters:**
- **date:** add `[.,]sss` formatter for milliseconds
([df744f3a](https://github.com/angular/angular.js/commit/df744f3af46fc227a934f16cb63c7a6038e7133b))
- **filter:** add comparison function to filter
([ace54ff0](https://github.com/angular/angular.js/commit/ace54ff08c4593195b49eadb04d258e6409d969e))
## Breaking Changes
- **$http:** due to [288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf),
$http now follows RFC3986 and does not encode special characters like `$@,:` in params.
If your application needs to encode these characters, encode them manually, before sending the request.
- **$resource:** due to [2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01),
if the server relied on the buggy behavior of serializing arrays as http query arguments then
either the backend should be fixed or a simple serialization of the array should be done
on the client before calling the resource service.
<a name="1.0.5"></a>
# 1.0.5 flatulent-propulsion (2013-02-20)
## Bug Fixes
- **$compile:**
- sanitize values bound to `a[href]`
([9532234b](https://github.com/angular/angular.js/commit/9532234bf1c408af9a6fd2c4743fdb585b920531))
- rename $compileNote to compileNode
([92ca7efa](https://github.com/angular/angular.js/commit/92ca7efaa4bc4f37da3008b234e19343a1fa4207),
[#1941](https://github.com/angular/angular.js/issues/1941))
- should not leak memory when there are top level empty text nodes
([791804bd](https://github.com/angular/angular.js/commit/791804bdbfa6da7a39283623bd05628a01cd8720))
- allow startingTag method to handle text / comment nodes
([755beb2b](https://github.com/angular/angular.js/commit/755beb2b66ce9f9f9a218f2355bbaf96d94fbc15))
- **$cookies:** set cookies on Safari&IE when `base[href]` is undefined
([70909245](https://github.com/angular/angular.js/commit/7090924515214752b919b0c5630b3ea5e7c77223),
[#1190](https://github.com/angular/angular.js/issues/1190))
- **$http:**
- patch for Firefox bug w/ CORS and response headers
([e19b04c9](https://github.com/angular/angular.js/commit/e19b04c9ec985821edf1269c628cfa261f81d631),
[#1468](https://github.com/angular/angular.js/issues/1468))
- **$resource:**
- update RegExp to allow urlParams with out leading slash
([b7e1fb05](https://github.com/angular/angular.js/commit/b7e1fb0515798e1b4f3f2426f6b050951bee2617))
- **Directives:**
- **a:** workaround IE bug affecting mailto urls
([37e8b122](https://github.com/angular/angular.js/commit/37e8b12265291918396bfee65d444a8f63697b73),
[#1949](https://github.com/angular/angular.js/issues/1949))
- **ngClass:** keep track of old ngClass value manually
([5f5d4fea](https://github.com/angular/angular.js/commit/5f5d4feadbfa9d8ecc8150041dfd2bca2b2e9fea),
[#1637](https://github.com/angular/angular.js/issues/1637))
- **ngSwitch:** make ngSwitch compatible with controller backwards-compatiblity module
([9b7c1d0f](https://github.com/angular/angular.js/commit/9b7c1d0f7ce442d4ad2ec587e66d2d335e64fa4e))
- **Filters:**
- **date:** invert timezone sign and always display sign
([b001c8ec](https://github.com/angular/angular.js/commit/b001c8ece5472626bf49cf82753e8ac1aafd2513),
[#1261](https://github.com/angular/angular.js/issues/1261))
- **number:** fix formatting when "0" passed as fractionSize
([f5835963](https://github.com/angular/angular.js/commit/f5835963d5982003a713dd354eefd376ed39ac02))
- **scenario runner:** include error messages in XML output
([d46fe3c2](https://github.com/angular/angular.js/commit/d46fe3c23fa269dcc10249148f2af14f3db6b066))
- **Misc:**
- don't use instanceof to detect arrays
([3c2aee01](https://github.com/angular/angular.js/commit/3c2aee01b0b299995eb92f4255159585b0f53c10),
[#1966](https://github.com/angular/angular.js/issues/1966))
- angular.forEach should correctly iterate over objects with length prop
([ec54712f](https://github.com/angular/angular.js/commit/ec54712ff3dab1ade44f94fa82d67edeffa79a1d),
[#1840](https://github.com/angular/angular.js/issues/1840))
<a name="1.1.2"></a>
# 1.1.2 tofu-animation (2013-01-22)
@@ -535,8 +73,6 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
- HTTP method should be case-insensitive
([8991680d](https://github.com/angular/angular.js/commit/8991680d8ab632dda60cd70c780868c803c74509),
[#1403](https://github.com/angular/angular.js/issues/1403))
- correct leading slash removal in resource URLs
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
- **$route:**
- support route params not separated with slashes.
([c6392616](https://github.com/angular/angular.js/commit/c6392616ea5245bd0d2f77dded0b948d9e2637c8))
@@ -564,6 +100,8 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
- **ngRepeat:** correctly apply $last if repeating over object
([7e746015](https://github.com/angular/angular.js/commit/7e746015ea7dec3e9eb81bc4678fa9b6a83bc47c),
[#1789](https://github.com/angular/angular.js/issues/1789))
- **ngResource:** correct leading slash removal.
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
- **ngSwitch:** don't leak when destroyed while not attached
([a26234f7](https://github.com/angular/angular.js/commit/a26234f7183013e2fcc9b35377e181ad96dc9917),
[#1621](https://github.com/angular/angular.js/issues/1621))
-171
View File
@@ -1,171 +0,0 @@
var files = require('./angularFiles').files;
var util = require('./lib/grunt/utils.js');
module.exports = function(grunt) {
//grunt plugins
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadTasks('lib/grunt');
var NG_VERSION = util.getVersion();
var dist = 'angular-'+ NG_VERSION.full;
//global beforeEach
util.init();
//config
grunt.initConfig({
NG_VERSION: NG_VERSION,
connect: {
devserver: {
options: {
port: 8000,
hostname: '0.0.0.0',
base: '.',
keepalive: true,
middleware: function(connect, options){
return [
//uncomment to enable CSP
// util.csp(),
util.rewrite(),
connect.favicon('images/favicon.ico'),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
testserver: {}
},
test: {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js',
modules: 'karma-modules.conf.js',
//NOTE run grunt test:e2e instead and it will start a webserver for you
end2end: 'karma-e2e.conf.js'
},
autotest: {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js'
},
clean: {build: ['build']},
build: {
scenario: {
dest: 'build/angular-scenario.js',
src: [
'lib/jquery/jquery.js',
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
],
styles: {
css: ['css/angular.css', 'css/angular-scenario.css']
}
},
angular: {
dest: 'build/angular.js',
src: util.wrap([files['angularSrc']], 'angular'),
styles: {
css: ['css/angular.css'],
minify: true
}
},
loader: {
dest: 'build/angular-loader.js',
src: util.wrap(['src/loader.js'], 'loader')
},
mocks: {
dest: 'build/angular-mocks.js',
src: ['src/ngMock/angular-mocks.js'],
strict: false
},
sanitize: {
dest: 'build/angular-sanitize.js',
src: util.wrap([
'src/ngSanitize/sanitize.js',
'src/ngSanitize/directive/ngBindHtml.js',
'src/ngSanitize/filter/linky.js',
], 'module')
},
resource: {
dest: 'build/angular-resource.js',
src: util.wrap(['src/ngResource/resource.js'], 'module')
},
cookies: {
dest: 'build/angular-cookies.js',
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
},
bootstrap: {
dest: 'build/angular-bootstrap.js',
src: util.wrap(['src/bootstrap/bootstrap.js'], 'module')
},
bootstrapPrettify: {
dest: 'build/angular-bootstrap-prettify.js',
src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'),
styles: {
css: ['src/bootstrap/google-prettify/prettify.css'],
minify: true
}
}
},
min: {
angular: 'build/angular.js',
cookies: 'build/angular-cookies.js',
loader: 'build/angular-loader.js',
resource: 'build/angular-resource.js',
sanitize: 'build/angular-sanitize.js',
bootstrap: 'build/angular-bootstrap.js',
bootstrapPrettify: 'build/angular-bootstrap-prettify.js',
},
docs: {
process: ['build/docs/*.html', 'build/docs/.htaccess']
},
copy: {
i18n: {
files: [
{ src: 'src/ngLocale/**', dest: 'build/i18n/', expand: true, flatten: true }
]
}
},
compress: {
build: {
options: {archive: 'build/' + dist +'.zip'},
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
}
},
write: {
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
}
});
//alias tasks
grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']);
grunt.registerTask('minify', ['clean', 'build', 'minall']);
grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']);
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('default', ['package']);
};
+12 -6
View File
@@ -21,19 +21,25 @@ Building AngularJS
---------
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
grunt package
rake package
Running Tests
-------------
Running tests requires installation of [Testacular](http://vojtajina.github.com/testacular):
sudo npm install -g testacular
To execute all unit tests, use:
grunt test:unit
rake test:unit
To execute end-to-end (e2e) tests, use:
grunt package
grunt test:e2e
rake package
rake webserver &
rake test:e2e
To learn more about the grunt tasks, run `grunt --help` and also read our
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
To learn more about the rake tasks, run `rake -T` and also read our
[contribution guidelines](http://docs.angularjs.org/misc/contribute) and instructions in this
[commit message](https://github.com/angular/angular.js/commit/9d168f058f9c6d7eeae0daa7cb72ea4e02a0003a).
+355
View File
@@ -0,0 +1,355 @@
require 'yaml'
include FileUtils
## High level flow of the build:
##
## clean -> init -> concat -> minify -> package
##
content = File.open('angularFiles.js', 'r') {|f| f.read }
files = eval(content.gsub(/\};(\s|\S)*/, '}').
gsub(/angularFiles = /, '').
gsub(/:/, '=>').
gsub(/\/\//, '#'));
BUILD_DIR = 'build'
task :default => [:package]
desc 'Init the build workspace'
task :init do
FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
v = YAML::load( File.open( 'version.yaml' ) )
match = v['version'].match(/^([^-]*)(-snapshot)?$/)
NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename, :stable).
new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''),
match[1].split('.')[0],
match[1].split('.')[1],
match[1].split('.')[2].sub(/\D+.*$/, ''),
v['codename'],
v['stable'])
end
desc 'Clean Generated Files'
task :clean do
FileUtils.rm_r(BUILD_DIR, :force => true)
FileUtils.mkdir(BUILD_DIR)
FileUtils.rm_r('test_out', :force => true)
end
desc 'Concat Scenario'
task :concat_scenario => :init do
concat_file('angular-scenario.js', [
'lib/jquery/jquery.js',
'src/ngScenario/angular.prefix',
files['angularSrc'],
files['angularScenario'],
'src/ngScenario/angular.suffix',
], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css'))
end
desc 'Concat AngularJS files'
task :concat => :init do
concat_file('angular.js', [
'src/angular.prefix',
files['angularSrc'],
'src/angular.suffix',
], gen_css('css/angular.css', true))
FileUtils.cp_r 'src/ngLocale', path_to('i18n')
concat_file('angular-loader.js', [
'src/loader.prefix',
'src/loader.js',
'src/loader.suffix'])
concat_module('sanitize', [
'src/ngSanitize/sanitize.js',
'src/ngSanitize/directive/ngBindHtml.js',
'src/ngSanitize/filter/linky.js'])
concat_module('resource', ['src/ngResource/resource.js'])
concat_module('cookies', ['src/ngCookies/cookies.js'])
concat_module('bootstrap', ['src/bootstrap/bootstrap.js'])
concat_module('bootstrap-prettify', ['src/bootstrap/bootstrap-prettify.js',
'src/bootstrap/google-prettify/prettify.js'],
gen_css('src/bootstrap/google-prettify/prettify.css', true))
FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js')
rewrite_file(path_to('angular-mocks.js')) do |content|
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full)
end
end
desc 'Minify JavaScript'
task :minify => [:init, :concat, :concat_scenario] do
[ 'angular.js',
'angular-cookies.js',
'angular-loader.js',
'angular-resource.js',
'angular-sanitize.js',
'angular-bootstrap.js',
'angular-bootstrap-prettify.js'
].each do |file|
unless ENV['TRAVIS']
fork { closure_compile(file) }
else
closure_compile(file)
end
end
Process.waitall
end
desc 'Generate version.txt and version.json files'
task :version => [:init] do
`echo #{NG_VERSION.full} > #{path_to('version.txt')}`
`echo '{
"full": "#{NG_VERSION.full}",
"major": "#{NG_VERSION.major}",
"minor": "#{NG_VERSION.minor}",
"dot": "#{NG_VERSION.dot}",
"codename": "#{NG_VERSION.codename}"\n}' > #{path_to('version.json')}`
end
desc 'Generate docs'
task :docs => [:init] do
`node docs/src/gen-docs.js`
[ path_to('docs/.htaccess'),
path_to('docs/index.html'),
path_to('docs/index-debug.html'),
path_to('docs/index-nocache.html'),
path_to('docs/index-jq.html'),
path_to('docs/index-jq-debug.html'),
path_to('docs/index-jq-nocache.html'),
path_to('docs/docs-scenario.html')
].each do |src|
rewrite_file(src) do |content|
content.sub!('"NG_VERSION_FULL"', NG_VERSION.full).
sub('"NG_VERSION_STABLE"', NG_VERSION.stable)
end
end
end
desc 'Create angular distribution'
task :package => [:clean, :minify, :version, :docs] do
zip_dir = "angular-#{NG_VERSION.full}"
zip_file = "#{zip_dir}.zip"
FileUtils.ln_s BUILD_DIR, zip_dir
%x(zip -r #{zip_file} #{zip_dir})
FileUtils.rm zip_dir
FileUtils.mv zip_file, path_to(zip_file)
puts "Package created: #{path_to(zip_file)}"
end
desc 'Start development webserver'
task :webserver, :port do |t, args|
exec "node lib/nodeserver/server.js #{args[:port]}"
end
desc 'Run all AngularJS tests'
task :test, :browsers, :misc_options do |t, args|
[ 'test:jqlite',
'test:jquery',
'test:modules',
'test:e2e'
].each do |task|
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
end
end
namespace :test do
desc 'Run all unit tests (single run)'
task :unit, :browsers, :misc_options do |t, args|
[ 'test:jqlite',
'test:jquery',
'test:modules'
].each do |task|
Rake::Task[task].invoke(args[:browsers], args[:misc_options])
end
end
desc 'Run jqLite-based unit test suite (single run)'
task :jqlite, :browsers, :misc_options do |t, args|
start_testacular('testacular-jqlite.conf.js', true, args[:browsers], args[:misc_options])
end
desc 'Run jQuery-based unit test suite (single run)'
task :jquery, :browsers, :misc_options do |t, args|
start_testacular('testacular-jquery.conf.js', true, args[:browsers], args[:misc_options])
end
desc 'Run bundled modules unit test suite (single run)'
task :modules, :browsers, :misc_options do |t, args|
start_testacular('testacular-modules.conf.js', true, args[:browsers], args[:misc_options])
end
desc 'Run e2e test suite (single run)'
task :e2e, :browsers, :misc_options do |t, args|
start_testacular('testacular-e2e.conf.js', true, args[:browsers], args[:misc_options])
end
end
namespace :autotest do
desc 'Run jqLite-based unit test suite (autowatch)'
task :jqlite, :browsers, :misc_options do |t, args|
start_testacular('testacular-jqlite.conf.js', false, args[:browsers], args[:misc_options])
end
desc 'Run jQuery-based unit test suite (autowatch)'
task :jquery, :browsers, :misc_options do |t, args|
start_testacular('testacular-jquery.conf.js', false, args[:browsers], args[:misc_options])
end
end
###################
# utility methods #
###################
##
# generates css snippet from a given files and optionally applies simple minification rules
#
def gen_css(cssFile, minify = false)
css = ''
File.open(cssFile, 'r') do |f|
css = f.read
end
if minify
css.gsub! /\n/, ''
css.gsub! /\/\*.*?\*\//, ''
css.gsub! /:\s+/, ':'
css.gsub! /\s*\{\s*/, '{'
css.gsub! /\s*\}\s*/, '}'
css.gsub! /\s*\,\s*/, ','
css.gsub! /\s*\;\s*/, ';'
end
#escape for js
css.gsub! /\\/, "\\\\\\"
css.gsub! /'/, "\\\\'"
css.gsub! /\n/, "\\n"
return %Q{angular.element(document).find('head').append('<style type="text/css">#{css}</style>');}
end
##
# returns path to the file in the build directory
#
def path_to(filename)
return File.join(BUILD_DIR, *filename)
end
##
# returns the 32-bit mode force flags for java compiler if supported, this makes the build much
# faster
#
def java32flags
return '-d32 -client' unless Rake::Win32.windows? || `java -version -d32 2>&1`.match(/Error/i)
end
def closure_compile(filename)
puts "Minifying #{filename} ..."
min_path = path_to(filename.gsub(/\.js$/, '.min.js'))
%x(java \
#{java32flags()} \
-jar lib/closure-compiler/compiler.jar \
--compilation_level SIMPLE_OPTIMIZATIONS \
--language_in ECMASCRIPT5_STRICT \
--js #{path_to(filename)} \
--js_output_file #{min_path})
rewrite_file(min_path) do |content|
content.sub!("'use strict';", "").
sub!(/\(function\([^)]*\)\{/, "\\0'use strict';")
end
end
def concat_file(filename, deps, footer='')
puts "Creating #{filename} ..."
File.open(path_to(filename), 'w') do |f|
concat = 'cat ' + deps.flatten.join(' ')
content = %x{#{concat}}.
gsub('"NG_VERSION_FULL"', NG_VERSION.full).
gsub('"NG_VERSION_MAJOR"', NG_VERSION.major).
gsub('"NG_VERSION_MINOR"', NG_VERSION.minor).
gsub('"NG_VERSION_DOT"', NG_VERSION.dot).
gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename).
gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags
sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag
f.write(content)
f.write(footer)
end
end
def concat_module(name, files, footer='')
concat_file('angular-' + name + '.js', ['src/module.prefix'] + files + ['src/module.suffix'], footer)
end
def rewrite_file(filename)
File.open(filename, File::RDWR) do |f|
content = f.read
content = yield content
raise "File rewrite failed - No content!" unless content
f.truncate 0
f.rewind
f.write content
end
end
def start_testacular(config, singleRun, browsers, misc_options)
sh "./node_modules/testacular/bin/testacular start " +
"#{config} " +
"#{'--single-run=true' if singleRun} " +
"#{'--browsers=' + browsers.gsub('+', ',') if browsers} " +
"#{(misc_options || '').gsub('+', ',')}"
end
+1 -1
View File
@@ -201,7 +201,7 @@ if (exports) {
var files = [];
[].splice.call(arguments, 0).forEach(function(file) {
if (file.match(/karma/)) {
if (file.match(/testacular/)) {
files.push(file);
} else {
angularFiles[file].forEach(function(f) {
+1 -1
View File
@@ -1,5 +1,5 @@
#!/bin/bash
grunt minify
rake minify
gzip -c < build/angular.min.js > build/angular.min.js.gzip
ls -l build/angular.min.*
-4
View File
@@ -38,10 +38,6 @@ initialization.
<html ng-app>
* If IE7 support is required add `id="ng-app"`
<html ng-app id="ng-app">
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
`ng:`.)
+2 -4
View File
@@ -30,7 +30,7 @@ involved.
# Compiler
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
process happens in two phases.
process happens into two phases.
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
function.
@@ -70,8 +70,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
<file name="script.js">
angular.module('drag', []).
directive('draggable', function($document) {
var startX=0, startY=0, x = 0, y = 0;
return function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0;
element.css({
position: 'relative',
border: '1px solid red',
@@ -79,8 +79,6 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
cursor: 'pointer'
});
element.bind('mousedown', function(event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.screenX - x;
startY = event.screenY - y;
$document.bind('mousemove', mousemove);
+10 -10
View File
@@ -61,7 +61,7 @@ This is how we get the ball rolling (refer to the diagram and example below):
The diagram and the example below describe how Angular interacts with the browser's event loop.
1. The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event,
1. The browser's event-loop waits for an event to arrive. An event is a user interactions, timer event,
or network event (response from a server).
2. The event's callback gets executed. This enters the JavaScript context. The callback can
modify the DOM structure.
@@ -74,7 +74,7 @@ applied in Angular execution context will benefit from Angular data-binding, exc
property watching, etc... You can also use $apply() to enter Angular execution context from JavaScript. Keep in
mind that in most places (controllers, services) $apply has already been called for you by the
directive which is handling the event. An explicit call to $apply is needed only when
implementing custom event callbacks, or when working with third-party library callbacks.
implementing custom event callbacks, or when working with a third-party library callbacks.
1. Enter Angular execution context by calling {@link guide/scope scope}`.`{@link
api/ng.$rootScope.Scope#$apply $apply}`(stimulusFn)`. Where `stimulusFn` is
@@ -101,7 +101,7 @@ implementing custom event callbacks, or when working with third-party library ca
re-rendering the DOM to reflect any changes.
Here is the explanation of how the `Hello world` example achieves the data-binding effect when the
Here is the explanation of how the `Hello wold` example achieves the data-binding effect when the
user enters text into the text field.
1. During the compilation phase:
@@ -143,8 +143,8 @@ provides the execution context for expressions. The scopes are nested in a hiera
which closely follow the DOM structure. (See individual directive documentation to see which
directives cause a creation of new scopes.)
The following example demonstrates how the `name` {@link guide/expression expression} will evaluate
into a different value depending on which scope it is evaluated in. The example is followed by
The following example demonstrates how `name` {@link guide/expression expression} will evaluate
into different value depending on which scope it is evaluated in. The example is followed by
a diagram depicting the scope boundaries.
<div class="clear">
@@ -232,9 +232,9 @@ The separation of the controller and the view is important because:
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-model.png">
The model is the data which is merged with the template to produce the view. To be able to
The model is the data which is used merged with the template to produce the view. To be able to
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
other frameworks Angular makes no restrictions or requirements on the model. There are no classes
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
to inherit from or special accessor methods for accessing or changing the model. The model can be
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
@@ -248,9 +248,9 @@ primitive, object hash, or a full object Type. In short the model is a plain Jav
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
The view is what the user sees. The view begins its life as a template, is merged with the
The view is what the users sees. The view begins its life as a template, it is merged with the
model and finally rendered into the browser DOM. Angular takes a very different approach to
rendering the view compared to most other templating systems.
rendering the view, compared to most other templating systems.
* **Others** - Most templating systems begin as an HTML string with special templating markup.
Often the template markup breaks the HTML syntax which means that the template can not be
@@ -425,7 +425,7 @@ function arguments. When angular calls the function, it will use the {@link
api/AUTO.$injector#invoke call} which will automatically fill the function
arguments.
Examine the `ClockCtrl` below, and notice how it lists the dependencies in the constructor. When the
Examine the `ClockCtrl` bellow, and notice how it lists the dependencies in the constructor. When the
{@link api/ng.directive:ngController ng-controller} instantiates
the controller it automatically provides the dependencies. There is no need to create
dependencies, look for dependencies, or even get a reference to the injector.
+1 -127
View File
@@ -97,8 +97,7 @@ the test frame.
Asserts the value of the given `future` satisfies the `matcher`. All API statements return a
`future` object, which get a `value` assigned after they are executed. Matchers are defined using
`angular.scenario.matcher`, and they use the value of futures to run the expectation. For example:
`expect(browser().location().href()).toEqual('http://www.google.com')`. Available matchers
are presented further down this document.
`expect(browser().location().href()).toEqual('http://www.google.com')`
## expect(future).not().{matcher}
Asserts the value of the given `future` satisfies the negation of the `matcher`.
@@ -177,128 +176,3 @@ JavaScript is a dynamically typed language which comes with great power of expre
come with almost no-help from the compiler. For this reason we feel very strongly that any code
written in JavaScript needs to come with a strong set of tests. We have built many features into
angular which makes testing your angular applications easy. So there is no excuse for not testing.
# Matchers
Matchers are used in combination with the `expect(...)` function as described above and can
be negated with `not()`. For instance: `expect(element('h1').text()).not().toEqual('Error')`.
Source: {@link https://github.com/angular/angular.js/blob/master/src/ngScenario/matchers.js}
<pre>
// value and Object comparison following the rules of angular.equals().
expect(value).toEqual(value)
// a simpler value comparison using ===
expect(value).toBe(value)
// checks that the value is defined by checking its type.
expect(value).toBeDefined()
// the following two matchers are using JavaScript's standard truthiness rules
expect(value).toBeTruthy()
expect(value).toBeFalsy()
// verify that the value matches the given regular expression. The regular
// expression may be passed in form of a string or a regular expression
// object.
expect(value).toMatch(expectedRegExp)
// a check for null using ===
expect(value).toBeNull()
// Array.indexOf(...) is used internally to check whether the element is
// contained within the array.
expect(value).toContain(expected)
// number comparison using < and >
expect(value).toBeLessThan(expected)
expect(value).toBeGreaterThan(expected)
</pre>
# Example
See the {@link https://github.com/angular/angular-seed angular-seed} project for more examples.
## Conditional actions with element(...).query(fn)
E2E testing with angular scenario is highly asynchronous and hides a lot of complexity by
queueing actions and expectations that can handle futures. From time to time, you might need
conditional assertions or element selection. Even though you should generally try to avoid this
(as it is can be sign for unstable tests), you can add conditional behavior with
`element(...).query(fn)`. The following code listing shows how this function can be used to delete
added entries (where an entry is some domain object) using the application's web interface.
Imagine the application to be structure into two views:
1. *Overview view* which lists all the added entries in a table and
2. a *detail view* which shows the entries' details and contains a delete button. When clicking the
delete button, the user is redirected back to the *overview page*.
<pre>
beforeEach(function () {
var deleteEntry = function () {
browser().navigateTo('/entries');
// we need to select the <tbody> element as it might be the case that there
// are no entries (and therefore no rows). When the selector does not
// result in a match, the test would be marked as a failure.
element('table tbody').query(function (tbody, done) {
// ngScenario gives us a jQuery lite wrapped element. We call the
// `children()` function to retrieve the table body's rows
var children = tbody.children();
if (children.length > 0) {
// if there is at least one entry in the table, click on the link to
// the entry's detail view
element('table tbody a').click();
// and, after a route change, click the delete button
element('.btn-danger').click();
}
// if there is more than one entry shown in the table, queue another
// delete action.
if (children.length > 1) {
deleteEntry();
}
// remember to call `done()` so that ngScenario can continue
// test execution.
done();
});
};
// start deleting entries
deleteEntry();
});
</pre>
In order to understand what is happening, we should emphasize that ngScenario calls are not
immediately executed, but queued (in ngScenario terms, we would be talking about adding
future actions). If we had only one entry in our table, than the following future actions
would be queued:
<pre>
// delete entry 1
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
</pre>
For two entries, ngScenario would have to work on the following queue:
<pre>
// delete entry 1
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
// delete entry 2
// indented to represent "recursion depth"
browser().navigateTo('/entries');
element('table tbody').query(function (tbody, done) { ... });
element('table tbody a');
element('.btn-danger').click();
</pre>
@@ -3,7 +3,10 @@
@description
In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of
angular {@link scope Scope}, excluding the root scope.
angular {@link scope Scope}, excluding the root scope. When you or Angular create a new
child scope object via the {@link api/ng.$rootScope.Scope#$new scope.$new} API , there is an
option to pass in a controller as a method argument. This will tell Angular to associate the
controller with the new scope and to augment its behavior.
Use controllers to:
@@ -34,8 +37,8 @@ your application as follows:
var myApp = angular.module('myApp',[]);
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
myApp.controller('GreetingCtrl', ['$scope', function(scope) {
scope.greeting = 'Hola!';
}]);
Note also that we use the array notation to explicitly specify the dependency
@@ -79,7 +82,8 @@ instances).
# Associating Controllers with Angular Scope Objects
You can associate controllers with scope objects implicitly via the {@link api/ng.directive:ngController ngController
You can associate controllers with scope objects explicitly via the {@link api/ng.$rootScope.Scope#$new
scope.$new} api or implicitly via the {@link api/ng.directive:ngController ngController
directive} or {@link api/ng.$route $route service}.
@@ -174,9 +178,8 @@ have a look at an example:
<body ng-controller="MainCtrl">
<p>Good {{timeOfDay}}, {{name}}!</p>
<div ng-controller="ChildCtrl">
<p>Good {{timeOfDay}}, {{name}}!</p>
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
</div>
<p>Good {{timeOfDay}}, {{name}}!</p>
<p ng-controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
</body>
function MainCtrl($scope) {
@@ -28,7 +28,7 @@ occurs in controllers:
* Use an {@link expression angular expression} with an assignment operator in templates:
<button ng-click="{{foo='ball'}}">Click me</button>
<button ng-click="{{foos='ball'}}">Click me</button>
* Use {@link api/ng.directive:ngInit ngInit directive} in templates (for toy/example apps
only, not recommended for real applications):
@@ -98,7 +98,7 @@ To configure the `$location` service, retrieve the
- **hashPrefix(prefix)**: {string}<br />
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
default: `""`
default: `'!'`
### Example configuration
<pre>
@@ -619,25 +619,21 @@ to the $location object (using {@link api/ng.directive:input.text
ngModel} directive on an input field), you will need to specify an extra model property
(e.g. `locationPath`) with two watchers which push $location updates in both directions. For
example:
<example>
<file name="index.html">
<div ng-controller="LocationController">
<input type="text" ng-model="locationPath" />
</div>
</file>
<file name="script.js">
function LocationController($scope, $location) {
$scope.$watch('locationPath', function(path) {
$location.path(path);
});
$scope.$watch(function() {
return $location.path();
}, function(path) {
$scope.locationPath = path;
});
}
</file>
</example>
<pre>
<!-- html -->
<input type="text" ng-model="locationPath" />
</pre>
<pre>
// js - controller
$scope.$watch('locationPath', function(path) {
$location.path(path);
});
$scope.$watch('$location.path()', function(path) {
scope.locationPath = path;
});
</pre>
# Related API
@@ -2,7 +2,9 @@
@name Developer Guide: Templates: Understanding Angular Filters
@description
Angular filters format data for display to the user.
Angular filters format data for display to the user. In addition to formatting data, filters can
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
to filtered output.
For example, you might have a data object that needs to be formatted according to the locale before
displaying it to the user. You can pass expressions through a chain of filters like this:
@@ -19,10 +19,6 @@ You can also pass colon-delimited arguments to filters, for example, to display
123 | number:2
Use the same syntax for multiple arguments:
myArray | orderBy:'timestamp':true
Here are some examples that show values before and after applying different filters to an
expression in a binding:
@@ -3,7 +3,7 @@
@description
JavaScript is a dynamically typed language which comes with great power of expression, but it also
comes with almost no-help from the compiler. For this reason we feel very strongly that any code
come with almost no-help from the compiler. For this reason we feel very strongly that any code
written in JavaScript needs to come with a strong set of tests. We have built many features into
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
@@ -97,7 +97,7 @@ function MyClass() {
While no new instance of the dependency is being created, it is fundamentally the same as `new`, in
that there is no good way to intercept the call to `global.xhr` for testing purposes, other then
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
through monkey patching. The basic issue for testing is that global variable needs to be mutated in
order to replace it with call to a mock method. For further explanation why this is bad see: {@link
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ Brittle Global
State & Singletons}
@@ -275,62 +275,7 @@ expect(length('abc')).toEqual(3);
</pre>
## Directives
Directives in angular are responsible for encapsulating complex functionality within custom HTML tags,
attributes, classes or comments. Unit tests are very important for directives because the components
you create with directives may be used throughout your application and in many different contexts.
### Simple HTML Element Directive
Lets start with an angular app with no dependencies.
<pre>
var app = angular.module('myApp', []);
</pre>
Now we can add a directive to our app.
<pre>
app.directive('aGreatEye', function () {
return {
restrict: 'E',
replace: true,
template: '<h1>lidless, wreathed in flame</h1>'
};
});
</pre>
This directive is used as a tag `<a-great-eye></a-great-eye>`. It replaces the entire tag with the
template `<h1>lidless, wreathed in flame</h1>`. Now we are going to write a jasmine unit test to
verify this functionality.
<pre>
describe('Unit testing great quotes', function() {
var $compile;
var $rootScope;
// Load the myApp module, which contains the directive
beforeEach(module('myApp'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Replaces the element with the appropriate content', function() {
// Compile a piece of HTML containing the directive
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
// Check that the compiled element contains the templated content
expect(element.html()).toContain("lidless, wreathed in flame");
});
});
</pre>
We inject the $compile service and $rootScope before each jasmine test. The $compile service is used
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
replaced the content and "lidless, wreathed in flame" is present.
Directives in angular are responsible for updating the DOM when the state of the model changes.
## Mocks
oue
@@ -348,5 +293,4 @@ ou
ou
## Sample project
See the {@link https://github.com/angular/angular-seed angular-seed} project for an example.
uoe
+14 -17
View File
@@ -14,7 +14,7 @@ book.
## DI in a nutshell
There are only three ways an object or a function can get a hold of its dependencies:
There are only three ways how an object or a function can get a hold of its dependencies:
1. The dependency can be created, typically using the `new` operator.
@@ -23,8 +23,8 @@ There are only three ways an object or a function can get a hold of its dependen
3. The dependency can be passed in to where it is needed.
The first two options of creating or looking up dependencies are not optimal because they hard
code the dependency. This make it difficult, if not impossible, to modify the dependencies.
The first two option of creating or looking up dependencies are not optimal, because they hard
code the dependency, making it difficult, if not impossible, to modify the dependencies.
This is especially problematic in tests, where it is often desirable to provide mock dependencies
for test isolation.
@@ -33,7 +33,7 @@ dependency from the component. The dependency is simply handed to the component.
<pre>
function SomeClass(greeter) {
this.greeter = greeter;
this.greeter = greeter
}
SomeClass.prototype.doSomething = function(name) {
@@ -41,18 +41,18 @@ dependency from the component. The dependency is simply handed to the component.
}
</pre>
In the above example `SomeClass` is not concerned with locating the `greeter` dependency, it
In the above example the `SomeClass` is not concerned with locating the `greeter` dependency, it
is simply handed the `greeter` at runtime.
This is desirable, but it puts the responsibility of getting hold of the dependency on the
code that constructs `SomeClass`.
This is desirable, but it puts the responsibility of getting hold of the dependency onto the
code responsible for the construction of `SomeClass`.
To manage the responsibility of dependency creation, each Angular application has an {@link
api/angular.injector injector}. The injector is a service locator that is responsible for
construction and lookup of dependencies.
Here is an example of using the injector service:
Here is an example of using the injector service.
<pre>
// Provide the wiring information in a module
angular.module('myModule', []).
@@ -101,7 +101,7 @@ dependency lookup responsibility to the injector by declaring the dependencies a
</pre>
Notice that by having the `ng-controller` instantiate the class, it can satisfy all of the
dependencies of `MyController` without the controller ever knowing about the injector. This is
dependencies of the `MyController` without the controller ever knowing about the injector. This is
the best outcome. The application code simply ask for the dependencies it needs, without having to
deal with the injector. This setup does not break the Law of Demeter.
@@ -159,18 +159,16 @@ Sometimes using the `$inject` annotation style is not convenient such as when an
directives.
For example:
<pre>
someModule.factory('greeter', function($window) {
...
...;
});
</pre>
Results in code bloat due to needing a temporary variable:
Results in code bloat due to the need of temporary variable:
<pre>
var greeterFactory = function(renamed$window) {
...
...;
};
greeterFactory.$inject = ['$window'];
@@ -179,10 +177,9 @@ Results in code bloat due to needing a temporary variable:
</pre>
For this reason the third annotation style is provided as well.
<pre>
someModule.factory('greeter', ['$window', function(renamed$window) {
...
...;
}]);
</pre>
@@ -216,7 +213,7 @@ services, and filters. The factory methods are registered with the module, and t
of declaring factories is:
<pre>
angular.module('myModule', []).
angualar.module('myModule', []).
config(['depProvider', function(depProvider){
...
}]).
+21 -35
View File
@@ -39,11 +39,11 @@ the following example.
</script>
<div ng-controller="Ctrl1">
Hello <input ng-model='name'> <hr/>
&lt;span ng:bind="name"&gt; <span ng:bind="name"></span> <br/>
&lt;span ng_bind="name"&gt; <span ng_bind="name"></span> <br/>
&lt;span ng-bind="name"&gt; <span ng-bind="name"></span> <br/>
&lt;span data-ng-bind="name"&gt; <span data-ng-bind="name"></span> <br/>
&lt;span x-ng-bind="name"&gt; <span x-ng-bind="name"></span> <br/>
&ltspan ng:bind="name"&gt <span ng:bind="name"></span> <br/>
&ltspan ng_bind="name"&gt <span ng_bind="name"></span> <br/>
&ltspan ng-bind="name"&gt <span ng-bind="name"></span> <br/>
&ltspan data-ng-bind="name"&gt <span data-ng-bind="name"></span> <br/>
&ltspan x-ng-bind="name"&gt <span x-ng-bind="name"></span> <br/>
</div>
</doc:source>
<doc:scenario>
@@ -95,7 +95,7 @@ Compilation of HTML happens in three phases:
var $compile = ...; // injected into your code
var scope = ...;
var html = '<div ng-bind="exp"></div>';
var html = '<div ng-bind='exp'></div>';
// Step 1: parse HTML into DOM element
var template = angular.element(html);
@@ -132,7 +132,7 @@ able to quickly stamp out new `li`s for every `action` in `user.actions`. This m
to save a clean copy of the `li` element for cloning purposes and as new `action`s are inserted,
the template `li` element needs to be cloned and inserted into `ul`. But cloning the `li` element
is not enough. It also needs to compile the `li` so that its directives such as
`{{action.description}}` evaluate against the right {@link api/ng.$rootScope.Scope
`{{action.descriptions}}` evaluate against the right {@link api/ng.$rootScope.Scope
scope}. A naive method would be to simply insert a copy of the `li` element and then compile it.
But compiling on every `li` element clone would be slow, since the compilation requires that we
traverse the DOM tree and look for directives and execute them. If we put the compilation inside a
@@ -206,7 +206,7 @@ In this example we will build a directive that displays the current time.
}
// listen on DOM destroy (removal) event, and cancel the next UI update
// to prevent updating time after the DOM element was removed.
// to prevent updating time ofter the DOM element was removed.
element.bind('$destroy', function() {
$timeout.cancel(timeoutId);
});
@@ -225,12 +225,7 @@ In this example we will build a directive that displays the current time.
# Writing directives (long version)
There are different ways to declare a directive. The difference resides in the return
value of the factory function. You can either return a Directive Definition Object
(see below) that defines the directive properties, or just the postLink function
of such an object (all other properties will have the default values).
Here's an example directive declared with a Directive Definition Object:
An example skeleton of the directive is shown here, for the complete list see below.
<pre>
var myModule = angular.module(...);
@@ -244,7 +239,6 @@ Here's an example directive declared with a Directive Definition Object:
transclude: false,
restrict: 'A',
scope: false,
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
@@ -257,11 +251,12 @@ Here's an example directive declared with a Directive Definition Object:
});
</pre>
In most cases you will not need such fine control and so the above can be simplified. You can still
return a Directive Definition Object, but only setting the 'compile' function property of the Object,
and rely on the default values for other properties.
In most cases you will not need such fine control and so the above can be simplified. All of the
different parts of this skeleton are explained in following sections. In this section we are
interested only in some of this skeleton.
Therefore the above can be simplified as:
The first step in simplyfing the code is to rely on the default values. Therefore the above can be
simplified as:
<pre>
var myModule = angular.module(...);
@@ -276,10 +271,8 @@ Therefore the above can be simplified as:
});
</pre>
Finally, most directives concern themselves only with instances, not with template transformations, allowing
further simplification.
Here we only define the postLink function:
Most directives concern themselves only with instances, not with template transformations, allowing
further simplification:
<pre>
var myModule = angular.module(...);
@@ -362,16 +355,10 @@ compiler}. The attributes are:
* `$scope` - Current scope associated with the element
* `$element` - Current element
* `$attrs` - Current attributes object for the element
* `$attrs` - Current attributes obeject for the element
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
`function(cloneLinkingFn)`.
To avoid errors after minification the bracket notation should be used:
<pre>
controller: ['$scope', '$element', '$attrs', '$transclude', function($scope, $element, $attrs, $transclude) { ... }]
</pre>
* `require` - Require another controller be passed into current directive linking function. The
`require` takes a name of the directive controller to pass in. If no such controller can be
found an error is raised. The name can be prefixed with:
@@ -389,8 +376,8 @@ compiler}. The attributes are:
* `M` - Comment: `<!-- directive: my-directive exp -->`
* `template` - replace the current element with the contents of the HTML. The replacement process
migrates all of the attributes / classes from the old element to the new one. See the
{@link guide/directive#Components Creating Components} section below for more information.
migrates all of the attributes / classes from the old element to the new one. See Creating
Widgets section below for more information.
* `templateUrl` - Same as `template` but the template is loaded from the specified URL. Because
the template loading is asynchronous the compilation/linking is suspended until the template
@@ -445,8 +432,8 @@ done in a linking function rather than in a compile function.
A compile function can have a return value which can be either a function or an object.
* returning a (post-link) function - is equivalent to registering the linking function via the
`link` property of the config object when the compile function is empty.
* returning a function - is equivalent to registering the linking function via the `link` property
of the config object when the compile function is empty.
* returning an object with function(s) registered via `pre` and `post` properties - allows you to
control when a linking function should be called during the linking phase. See info about
@@ -614,7 +601,6 @@ restrict: 'E',
replace: true
</pre>
<a name="Components"></a>
# Creating Components
It is often desirable to replace a single directive with a more complex DOM structure. This
+2 -2
View File
@@ -3,7 +3,7 @@
@description
Expressions are JavaScript-like code snippets that are usually placed in bindings such as `{{
expression }}`. Expressions are processed by the {@link api/ng.$parse $parse}
expression }}`. Expressions are processed by {@link api/ng.$parse $parse}
service.
For example, these are all valid expressions in angular:
@@ -179,7 +179,7 @@ angular uses, to differentiate its API names from others. If angular didn't use
`a.length()` would return undefined because neither a nor angular define such a property.
Consider that in a future version of Angular we might choose to add a length method, in which case
the behavior of the expression would change. Worse yet, you, the developer, could create a length
the behavior of the expression would change. Worse yet, you the developer could create a length
property and then we would have a collision. This problem exists because Angular augments existing
objects with additional behavior. By prefixing its additions with $ we are reserving our namespace
so that angular developers and developers who use Angular can develop in harmony without collisions.
+4 -4
View File
@@ -93,10 +93,10 @@ This ensures that the user is not distracted with an error until after interacti
<script>
function Controller($scope) {
$scope.master = {};
$scope.master= {};
$scope.update = function(user) {
$scope.master = angular.copy(user);
$scope.master= angular.copy(user);
};
$scope.reset = function() {
@@ -113,7 +113,7 @@ This ensures that the user is not distracted with an error until after interacti
# Binding to form and control state
A form is an instance of {@link api/ng.directive:form.FormController FormController}.
A form is in instance of {@link api/ng.directive:form.FormController FormController}.
The form instance can optionally be published into the scope using the `name` attribute.
Similarly control is an instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
The control instance can similarly be published into the form instance using the `name` attribute.
@@ -278,7 +278,7 @@ However, if you need more flexibility, you can write your own form control as a
In order for custom control to work with `ngModel` and to achieve two-way data-binding it needs to:
- implement `$render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
- implement `render` method, which is responsible for rendering the data after it passed the {@link api/ng.directive:ngModel.NgModelController#$formatters NgModelController#$formatters},
- call `$setViewValue` method, whenever the user interacts with the control and model needs to be updated. This is usually done inside a DOM Event listener.
See {@link guide/directive $compileProvider.directive} for more info.
+1 -1
View File
@@ -53,7 +53,7 @@ There are two approaches to providing locale rules to Angular:
You can pre-bundle the desired locale file with Angular by concatenating the content of the
locale-specific file to the end of `angular.js` or `angular.min.js` file.
For example on *nix, to create an angular.js file that contains localization rules for german
For example on *nix, to create a an angular.js file that contains localization rules for german
locale, you can do the following:
`cat angular.js i18n/angular-locale_de-ge.js > angular_de-ge.js`
+24 -45
View File
@@ -15,54 +15,33 @@ To make your Angular application work on IE please make sure that:
1. You polyfill JSON.stringify if necessary (IE7 will need this). You can use
[JSON2](https://github.com/douglascrockford/JSON-js) or
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
<pre>
<!doctype html>
<html xmlns:ng="http://angularjs.org">
<head>
<!--[if lte IE 8]>
<script src="/path/to/json2.js"></script>
<![endif]-->
</head>
<body>
...
</body>
</html>
</pre>
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
<pre>
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
...
</html>
</pre>
3. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
2. you **do not** use custom element tags such as `<ng:view>` (use the attribute version
`<div ng-view>` instead), or
4. if you **do use** custom element tags, then you must take these steps to make IE happy:
<pre>
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
<head>
<!--[if lte IE 8]>
<script>
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');
// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
document.createElement('ng:view');
</script>
<![endif]-->
</head>
<body>
...
</body>
</html>
</pre>
3. if you **do use** custom element tags, then you must take these steps to make IE happy:
<pre>
<html xmlns:ng="http://angularjs.org">
<head>
<!--[if lte IE 8]>
<script>
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');
// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
document.createElement('ng:view');
</script>
<![endif]-->
</head>
<body>
...
</body>
</html>
</pre>
The **important** parts are:
+4 -4
View File
@@ -158,9 +158,9 @@ angular.module('myModule', []).
angular.module('myModule', []).
config(function($provide, $compileProvider, $filterProvider) {
$provide.value('a', 123);
$provide.factory('a', function() { return 123; });
$compileProvider.directive('directiveName', ...);
$provide.value('a', 123)
$provide.factory('a', function() { return 123; })
$compileProvider.directive('directiveName', ...).
$filterProvider.register('filterName', ...);
});
</pre>
@@ -180,7 +180,7 @@ ignored in the unit-tests.
Modules can list other modules as their dependencies. Depending on a module implies that required
module needs to be loaded before the requiring module is loaded. In other words the configuration
blocks of the required modules execute before the configuration blocks of the requiring module.
blocks of the required modules execute before the configuration blocks or the requiring module.
The same is true for the run blocks. Each module can only be loaded once, even if multiple other
modules require it.
+3 -3
View File
@@ -90,7 +90,7 @@ concepts which the application developer may face:
<table>
<tr><td>Quantity</td><td>Cost</td></tr>
<tr>
<td><input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required ></td>
<td><input type="integer" min="0" ng-model="qty" required ></td>
<td><input type="number" ng-model="cost" required ></td>
</tr>
</table>
@@ -124,7 +124,7 @@ We load Angular using the `<script>` tag:
From the `ng-model` attribute of the `<input>` tags, Angular automatically sets up two-way data
binding, and we also demonstrate some easy input validation:
Quantity: <input type="number" ng-pattern="/\d+/" step="1" min="0" ng-model="qty" required >
Quantity: <input type="integer" min="0" ng-model="qty" required >
Cost: <input type="number" ng-model="cost" required >
These input widgets look normal enough, but consider these points:
@@ -202,6 +202,6 @@ Angular frees you from the following pain:
# Watch a Presentation About Angular
Here is a presentation on Angular from May 2012. The {@link http://mhevery.github.io/angular-demo-slides/index.html#/list corresponding slides} are also available.
Here is a presentation on Angular from May 2012.
<iframe width="560" height="315" src="http://www.youtube.com/embed/bfrn5VNpwsg" frameborder="0" allowfullscreen></iframe>
+1 -1
View File
@@ -165,7 +165,7 @@ Scopes are attached to the DOM as `$scope` data property, and can be retrieved f
purposes. (It is unlikely that one would need to retrieve scopes in this way inside the
application.) The location where the root scope is attached to the DOM is defined by the location
of {@link api/ng.directive:ngApp `ng-app`} directive. Typically
`ng-app` is placed on the `<html>` element, but it can be placed on other elements as well, if,
`ng-app` is placed an the `<html>` element, but it can be placed on other elements as well, if,
for example, only a portion of the view needs to be controlled by Angular.
To examine the scope in the debugger:
+3
View File
@@ -0,0 +1,3 @@
@ngdoc overview
@name Developer Guide: Type
@description
+20 -37
View File
@@ -81,27 +81,23 @@ Several steps are needed to check out and build AngularJS:
Before you can build AngularJS, you must install or configure the following dependencies on your
machine:
* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed
on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the
Rake website.
* Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is
quite a good source for information on Git.
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
development web server, run tests, and generate a build. Depending on your system, you can install Node either from source or as a
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a
development web server. Depending on your system, you can install Node either from source or as a
pre-packaged bundle.
* {@link http://www.java.com Java}: JavaScript is minified using
{@link https://developers.google.com/closure/ Closure Tools} jar. Make sure you have Java (version 6 or higher) installed
and included in your {@link http://docs.oracle.com/javase/tutorial/essential/environment/paths.html PATH} variable.
Once installed, you'll also need several npms (node packages), which you can install once you checked out a local copy
of the Angular repository (see below) with:
* `cd angular.js`
* `npm install`
* {@link http://gruntjs.com Grunt}: We use Grunt as our build system. Install the grunt command-line tool globally with:
* `sudo npm install -g grunt-cli`
## Creating a Github Account and Forking Angular
@@ -112,7 +108,7 @@ https://github.com/angular/angular.js main angular repository}.
## Building AngularJS
To build AngularJS, you check out the source code and use Grunt to generate the non-minified and
To build AngularJS, you check out the source code and use Rake to generate the non-minified and
minified AngularJS files:
1. To clone your Github repository, run:
@@ -133,11 +129,7 @@ minified AngularJS files:
5. To build AngularJS, run:
grunt package
NOTE: If you're using Windows you must run your command line with administrative privileges (right click, run as
Administrator).
rake package
The build output can be located under the `build` directory. It consists of the following files and
directories:
@@ -166,7 +158,7 @@ made available a local web server based on Node.js.
1. To start the web server, run:
grunt webserver
rake webserver
2. To access the local server, go to this website:
@@ -178,35 +170,33 @@ made available a local web server based on Node.js.
<a name="unit-tests"></a>
## Running the Unit Test Suite
Our unit and integration tests are written with Jasmine and executed with Karma. To run all of the
Our unit and integration tests are written with Jasmine and executed with Testacular. To run all of the
tests once on Chrome run:
grunt test:unit
rake test:unit
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
grunt test:unit --browsers Opera,Firefox
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
rake test:unit[Opera+Firefox]
During development it's however more productive to continuously run unit tests every time the source or test files
change. To execute tests in this mode run:
1. To start the Karma server, capture Chrome browser and run unit tests, run:
1. To start the Testacular server, capture Chrome browser and run unit tests, run:
grunt autotest:jqlite
rake autotest:jqlite
2. To capture more browsers, open this url in the desired browser (url might be different if you have multiple instance
of Karma running, read Karma's console output for the correct url):
of Testacular running, read Testacular's console output for the correct url):
http://localhost:9876/
3. To re-run tests just change any source or test file.
To learn more about all of the preconfigured Grunt tasks run:
To learn more about all of the preconfigured Rake tasks run:
grunt --help
rake -T
## Running the end-to-end Test Suite
@@ -215,7 +205,7 @@ To run the E2E test suite:
1. Start the local web server if it's not running already.
grunt webserver
rake webserver
2. In a browser, go to:
@@ -223,13 +213,7 @@ To run the E2E test suite:
or in terminal run:
grunt test:end2end
For convenience you can also simply run:
grunt test:e2e
This will start the webserver for you and run the tests.
rake test:e2e
@@ -238,8 +222,7 @@ This will start the webserver for you and run the tests.
To create and submit a change:
1. <a name="CLA"></a>
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
1. Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
accepted, the CLA must be signed. It's a quick process, we promise!
For individuals we have a [simple click-through form](http://code.google.com/legal/individual-cla-v1.0.html). For
+1 -2
View File
@@ -51,8 +51,7 @@ Yes. See instructions in {@link downloading}.
### What browsers does Angular work with?
We run our extensive test suite against the following browsers: Safari, Chrome, Firefox, Opera,
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
Explorer Compatibility} for more details in supporting legacy IE browsers.
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari).
### What's Angular's performance like?
+88 -90
View File
@@ -2,42 +2,41 @@
@name Tutorial
@description
<div class="tutorial-page tutorial-page-no-nav">
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
of Android devices, lets you filter the list to see only devices that interest you, and then view
details for any device.
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
the construction of an AngularJS web app. The app you will build is a catalog that displays a list
of Android devices, lets you filter the list to see only devices that interest you, and then view
details for any device.
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
or plug-ins. As you work through the tutorial, you will:
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
or plug-ins. As you work through the tutorial, you will:
* See examples of how to use client-side data binding and dependency injection to build dynamic
views of data that change immediately in response to user actions.
* See how Angular creates listeners on your data without the need for DOM manipulation.
* Learn a better, easier way to test your web apps.
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
easier.
* See examples of how to use client-side data binding and dependency injection to build dynamic
views of data that change immediately in response to user actions.
* See how Angular creates listeners on your data without the need for DOM manipulation.
* Learn a better, easier way to test your web apps.
* Learn how to use Angular services to make common web tasks, such as getting data into your app,
easier.
And all of this works in any browser without modification to the browser!
And all of this works in any browser without modification to the browser!
When you finish the tutorial you will be able to:
When you finish the tutorial you will be able to:
* Create a dynamic application that works in any browser.
* Define the differences between Angular and common JavaScript frameworks.
* Understand how data binding works in AngularJS.
* Use the angular-seed project to quickly boot-strap your own projects.
* Create and run tests.
* Identify resources for learning more about AngularJS.
* Create a dynamic application that works in any browser.
* Define the differences between Angular and common JavaScript frameworks.
* Understand how data binding works in AngularJS.
* Use the angular-seed project to quickly boot-strap your own projects.
* Create and run tests.
* Identify resources for learning more about AngularJS.
The tutorial guides you through the entire process of building a simple application, including
writing and running unit and end-to-end tests. Experiments at the end of each step provide
suggestions for you to learn more about AngularJS and the application you are building.
The tutorial guides you through the entire process of building a simple application, including
writing and running unit and end-to-end tests. Experiments at the end of each step provide
suggestions for you to learn more about AngularJS and the application you are building.
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
{@link misc/started Getting Started} document.
You can go through the whole tutorial in a couple of hours or you may want to spend a pleasant day
really digging into it. If you're looking for a shorter introduction to AngularJS, check out the
{@link misc/started Getting Started} document.
@@ -45,73 +44,72 @@
# Working with the code
# Working with the code
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
environment. The tutorial relies on the use of Git versioning system for source code management.
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
and follow the instructions for setting up your computer.
You can follow this tutorial and hack on the code in either the Mac/Linux or the Windows
environment. The tutorial relies on the use of Git versioning system for source code management.
You don't need to know anything about Git to follow the tutorial. Select one of the tabs below
and follow the instructions for setting up your computer.
<div class="tabbable" show="true">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
<ol>
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
<div class="tabbable" show="true">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
<ol>
<li><p>You will need Node.js and Testacular to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p>
<pre>node --version</pre>
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
don't have it already:</p>
<pre>npm install -g testacular</pre>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current
directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
directory.</p></li>
<li><p>You will need an http server running on your system. Mac and Linux machines typically
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
</ol>
</div>
<div class="tab-pane well" id="git-win" title="Git on Windows">
<ol>
<li><p>You will need Node.js and Testacular to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p>
<pre>node --version</pre>
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
don't have it already:</p>
<pre>npm install -g karma</pre>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current
directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
directory.</p></li>
<li><p>You will need an http server running on your system. Mac and Linux machines typically
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
</ol>
</div>
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
don't have it already:</p>
<pre>npm install -g testacular</pre>
</li>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
<li><p>Change your current directory to angular-phonecat.</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
directory.</p>
<p>You should run all <code>git</code> commands from Git bash.</p>
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
executed from the Windows command line.</li>
<li><p>You need an http server running on your system, but if you don't already have one
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
bundled http server.</p></li>
</ol>
</div>
<div class="tab-pane well" id="git-win" title="Git on Windows">
<ol>
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.8 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p>
<pre>node --version</pre>
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
don't have it already:</p>
<pre>npm install -g karma</pre>
</li>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone git://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the angular-phonecat directory in your current directory.</p></li>
<li><p>Change your current directory to angular-phonecat.</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the angular-phonecat
directory.</p>
<p>You should run all <code>git</code> commands from Git bash.</p>
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
executed from the Windows command line.</li>
<li><p>You need an http server running on your system, but if you don't already have one
already installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple
bundled http server.</p></li>
</ol>
</div>
The last thing to do is to make sure your computer has a web browser and a good text editor
installed. Now, let's get some cool stuff done!
The last thing to do is to make sure your computer has a web browser and a good text editor
installed. Now, let's get some cool stuff done!
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
</div>
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
+2 -2
View File
@@ -109,7 +109,7 @@ __`app/index.html`:__
<html ng-app>
The `ng-app` attribute represents an Angular directive (named `ngApp`; Angular uses
The `ng-app` attribute is represents an Angular directive (named `ngApp`; Angular uses
`name-with-dashes` for attribute names and `camelCase` for the corresponding directive name)
used to flag an element which Angular should consider to be the root element of our application.
This gives application developers the freedom to tell Angular if the entire html page or only a
@@ -127,7 +127,7 @@ being the element on which the `ngApp` directive was defined.
* Double-curly binding with an expression:
Nothing here {{'yet' + '!'}}
Nothing here {{'yet' + '!'}}`
This line demonstrates the core feature of Angular's templating capabilities a binding, denoted
by double-curlies `{{ }}` as well as a simple expression `'yet' + '!'` used in this binding.
+6 -7
View File
@@ -146,25 +146,24 @@ http://pivotal.github.com/jasmine/ Jasmine home page} and on the {@link
https://github.com/pivotal/jasmine/wiki Jasmine wiki}.
The angular-seed project is pre-configured to run all unit tests using {@link
http://karma-runner.github.io/ Karma}. To run the test, do the following:
http://vojtajina.github.com/testacular/ Testacular}. To run the test, do the following:
1. In a _separate_ terminal window or tab, go to the `angular-phonecat` directory and run
`./scripts/test.sh` to start the Karma server (the config file necessary to start the server
is located at `./config/karma.conf.js`).
`./scripts/test.sh` to start the Testacular server.
2. Karma will start a new instance of Chrome browser automatically. Just ignore it and let it run in
the background. Karma will use this browser for test execution.
2. Testacular will start a new instance of Chrome browser automatically. Just ignore it and let it run in
the background. Testacular will use this browser for test execution.
3. You should see the following or similar output in the terminal:
info: Karma server started at http://localhost:9876/
info: Testacular server started at http://localhost:9876/
info (launcher): Starting browser "Chrome"
info (Chrome 22.0): Connected on socket id tPUm9DXcLHtZTKbAEO-n
Chrome 22.0: Executed 1 of 1 SUCCESS (0.093 secs / 0.004 secs)
Yay! The test passed! Or not...
4. To rerun the tests, just change any of the source or test files. Karma will notice the change
4. To rerun the tests, just change any of the source or test files. Testacular will notice the change
and will rerun the tests for you. Now isn't that sweet?
# Experiments
+2 -2
View File
@@ -122,9 +122,9 @@ To run the end-to-end test, open one of the following in a new browser tab:
`http://localhost:[port-number]/[context-path]/test/e2e/runner.html`
* casual reader: {@link http://angular.github.com/angular-phonecat/step-3/test/e2e/runner.html}
Previously we've seen how Karma can be used to execute unit tests. Well, it can also run the
Previously we've seen how Testacular can be used to execute unit tests. Well, it can also run the
end-to-end tests! Use `./scripts/e2e-test.sh` script for that. End-to-end tests are slow, so unlike
with unit tests, Karma will exit after the test run and will not automatically rerun the test
with unit tests, Testacular will exit after the test run and will not automatically rerun the test
suite on every file change. To rerun the test suite, execute the `e2e-test.sh` script again.
This test verifies that the search box and the repeater are correctly wired together. Notice how
+1 -1
View File
@@ -134,7 +134,7 @@ The unit test now verifies that the default ordering property is set.
We used Jasmine's API to extract the controller construction into a `beforeEach` block, which is
shared by all tests in the parent `describe` block.
You should now see the following output in the Karma tab:
You should now see the following output in the Testacular tab:
Chrome 22.0: Executed 2 of 2 SUCCESS (0.021 secs / 0.001 secs)
+1 -4
View File
@@ -138,9 +138,6 @@ describe('PhoneCat controllers', function() {
describe('PhoneListCtrl', function(){
var scope, ctrl, $httpBackend;
// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
// This allows us to inject a service but then attach it to a variable
// with the same name as the service.
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/phones.json').
@@ -211,7 +208,7 @@ Finally, we verify that the default value of `orderProp` is set correctly:
});
</pre>
You should now see the following output in the Karma tab:
You should now see the following output in the Testacular tab:
Chrome 22.0: Executed 2 of 2 SUCCESS (0.028 secs / 0.007 secs)
+1 -1
View File
@@ -147,7 +147,7 @@ __`test/unit/controllersSpec.js`:__
...
</pre>
You should now see the following output in the Karma tab:
You should now see the following output in the Testacular tab:
Chrome 22.0: Executed 3 of 3 SUCCESS (0.039 secs / 0.012 secs)
+1 -1
View File
@@ -110,7 +110,7 @@ describe('filter', function() {
Note that you need to configure our test injector with the `phonecatFilters` module before any of
our filter tests execute.
You should now see the following output in the Karma tab:
You should now see the following output in the Testacular tab:
Chrome 22.0: Executed 4 of 4 SUCCESS (0.034 secs / 0.012 secs)
+1 -1
View File
@@ -214,7 +214,7 @@ describe('PhoneCat controllers', function() {
});
</pre>
You should now see the following output in the Karma tab:
You should now see the following output in the Testacular tab:
Chrome 22.0: Executed 4 of 4 SUCCESS (0.038 secs / 0.01 secs)
+12 -14
View File
@@ -2,22 +2,20 @@
@name Tutorial: The End
@description
<div class="tutorial-page tutorial-page-no-nav">
Our application is now complete. Feel free to experiment with the code further, and jump back to
previous steps using the `git checkout` command.
Our application is now complete. Feel free to experiment with the code further, and jump back to
previous steps using the `git checkout` command.
For more details and examples of the Angular concepts we touched on in this tutorial, see the
{@link guide/ Developer Guide}.
For more details and examples of the Angular concepts we touched on in this tutorial, see the
{@link guide/ Developer Guide}.
For several more examples of code, see the {@link cookbook/ Cookbook}.
For several more examples of code, see the {@link cookbook/ Cookbook}.
When you are ready to start developing a project using Angular, we recommend that you bootstrap
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
When you are ready to start developing a project using Angular, we recommend that you bootstrap
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.
We hope this tutorial was useful to you and that you learned enough about Angular to make you want
to learn more. We especially hope you are inspired to go out and develop Angular web apps of your
own, and that you might be interested in {@link misc/contribute contributing} to Angular.
We hope this tutorial was useful to you and that you learned enough about Angular to make you want
to learn more. We especially hope you are inspired to go out and develop Angular web apps of your
own, and that you might be interested in {@link misc/contribute contributing} to Angular.
If you have questions or feedback or just want to say "hi", please post a message at {@link
https://groups.google.com/forum/#!forum/angular}.
</div>
If you have questions or feedback or just want to say "hi", please post a message at {@link
https://groups.google.com/forum/#!forum/angular}.
+5 -23
View File
@@ -55,15 +55,12 @@ describe('ngdoc', function() {
'@name a\n' +
'@param {*} a short\n' +
'@param {Type} b med\n' +
'@param {Class=} [c=2] long\nline\n' +
'@param {function(number, string=)} d fn with optional arguments');
'@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'},
{name:'d', description:'<p>fn with optional arguments</p>',
type: 'function(number, string=)', optional: false, 'default':undefined}
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
]);
});
@@ -321,9 +318,9 @@ describe('ngdoc', function() {
});
it('should not parse @property without a type', function() {
var doc = new Doc("@property fake", 'test.js', '44');
var doc = new Doc("@property fake");
expect(function() { doc.parse(); }).
toThrow(new Error("Not a valid 'property' format: fake (found in: test.js:44)"));
toThrow(new Error("Not a valid 'property' format: fake"));
});
it('should parse @property with type', function() {
@@ -353,30 +350,15 @@ describe('ngdoc', function() {
describe('@returns', function() {
it('should not parse @returns without type', function() {
var doc = new Doc("@returns lala");
expect(function() { doc.parse(); }).
toThrow();
expect(doc.parse).toThrow();
});
it('should not parse @returns with invalid type', function() {
var doc = new Doc("@returns {xx}x} lala", 'test.js', 34);
expect(function() { doc.parse(); }).
toThrow(new Error("Not a valid 'returns' format: {xx}x} lala (found in: test.js:34)"));
});
it('should parse @returns with type and description', function() {
var doc = new Doc("@name a\n@returns {string} descrip tion");
doc.parse();
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
});
it('should parse @returns with complex type and description', function() {
var doc = new Doc("@name a\n@returns {function(string, number=)} description");
doc.parse();
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<p>description</p>'});
});
it('should transform description of @returns with markdown', function() {
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
doc.parse();
+8 -4
View File
@@ -5,6 +5,10 @@ var reader = require('./reader.js'),
appCache = require('./appCache.js').appCache,
Q = require('qq');
process.on('uncaughtException', function(err) {
console.error(err.stack || err);
});
var start = now();
var docs;
@@ -38,10 +42,10 @@ writer.makeDir('build/docs/', true).then(function() {
function writeTheRest(writesFuture) {
var metadata = ngdoc.metadata(docs);
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
writesFuture.push(writer.symlinkTemplate('font', 'dir'));
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
writesFuture.push(writer.symlinkTemplate('css'));
writesFuture.push(writer.symlinkTemplate('font'));
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img'));
writesFuture.push(writer.symlinkTemplate('js'));
var manifest = 'manifest="/build/docs/appcache.manifest"';
+46 -62
View File
@@ -2,7 +2,7 @@
* All parsing/transformation code goes here. All code here should be sync to ease testing.
*/
var Showdown = require('showdown');
var Showdown = require('../../lib/showdown').Showdown;
var DOM = require('./dom.js').DOM;
var htmlEscape = require('./dom.js').htmlEscape;
var Example = require('./example.js').Example;
@@ -174,7 +174,7 @@ Doc.prototype = {
});
});
text = parts.join('');
text = new Showdown.converter({ extensions : ['table'] }).makeHtml(text);
text = new Showdown.converter().makeHtml(text);
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
return placeholderMap[id];
});
@@ -203,7 +203,7 @@ Doc.prototype = {
flush();
this.shortName = this.name.split(/[\.:#]/).pop().trim();
this.id = this.id || // if we have an id just use it
(((this.file||'').match(/.*(\/|\\)([^(\/|\\)]*)\.ngdoc/)||{})[2]) || // try to extract it from file name
(((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) || // try to extract it from file name
this.name; // default to name
this.description = this.markdown(this.description);
this.example = this.markdown(this.example);
@@ -214,25 +214,23 @@ Doc.prototype = {
if (atName) {
var text = trim(atText.join('\n')), match;
if (atName == 'param') {
match = text.match(/^\{([^}]+)\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
// 1 1 23 3 4 4 5 5 2 6 6
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 + ' (found in: ' + self.file + ':' + self.line + ')');
throw new Error("Not a valid 'param' format: " + text);
}
var optional = (match[1].slice(-1) === '=');
var param = {
name: match[4] || match[3],
description:self.markdown(text.replace(match[0], match[6])),
type: optional ? match[1].substring(0, match[1].length-1) : match[1],
optional: optional,
'default':match[5]
name: match[5] || match[4],
description:self.markdown(text.replace(match[0], match[7])),
type: match[1],
optional: !!match[2],
'default':match[6]
};
self.param.push(param);
} else if (atName == 'returns' || atName == 'return') {
match = text.match(/^\{([^}]+)\}\s+(.*)/);
match = text.match(/^\{([^}=]+)\}\s+(.*)/);
if (!match) {
throw new Error("Not a valid 'returns' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
throw new Error("Not a valid 'returns' format: " + text + ' in ' + self.file + ':' + self.line);
}
self.returns = {
type: match[1],
@@ -247,7 +245,7 @@ Doc.prototype = {
} else if(atName == 'property') {
match = text.match(/^\{(\S+)\}\s+(\S+)(\s+(.*))?/);
if (!match) {
throw new Error("Not a valid 'property' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
throw new Error("Not a valid 'property' format: " + text);
}
var property = new Doc({
type: match[1],
@@ -272,9 +270,8 @@ Doc.prototype = {
self = this;
dom.h(title(this.name), function() {
notice('deprecated', 'Deprecated API', self.deprecated);
dom.tag('a', {href: 'http://github.com/angular/angular.js/edit/master/' + self.file, class: 'improve-docs btn btn-primary'}, 'Improve this doc');
if (self.ngdoc != 'overview') {
dom.h('Description', self.description, dom.html);
}
@@ -386,53 +383,40 @@ Doc.prototype = {
var self = this;
dom.h('Usage', function() {
var restrict = self.restrict || 'AC';
if (restrict.match(/E/)) {
dom.text('This directive can be used as custom element, but be aware of ');
dom.text('as element (see ');
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
dom.text('.');
}
if (self.usage) {
dom.tag('pre', function() {
dom.tag('code', function() {
dom.text(self.usage);
});
dom.text(')');
dom.code(function() {
dom.text('<');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"');
dom.text('>\n</');
dom.text(dashCase(self.shortName));
dom.text('>');
});
}
if (restrict.match(/A/)) {
var element = self.element || 'ANY';
dom.text('as attribute');
dom.code(function() {
dom.text('<' + element + ' ');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"', true);
dom.text('>\n ...\n');
dom.text('</' + element + '>');
});
}
if (restrict.match(/C/)) {
dom.text('as class');
var element = self.element || 'ANY';
dom.code(function() {
dom.text('<' + element + ' class="');
dom.text(dashCase(self.shortName));
renderParams(' ', ': ', ';', true);
dom.text('">\n ...\n');
dom.text('</' + element + '>');
});
} else {
if (restrict.match(/E/)) {
dom.text('as element:');
dom.code(function() {
dom.text('<');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"');
dom.text('>\n</');
dom.text(dashCase(self.shortName));
dom.text('>');
});
}
if (restrict.match(/A/)) {
var element = self.element || 'ANY';
dom.text('as attribute');
dom.code(function() {
dom.text('<' + element + ' ');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"', true);
dom.text('>\n ...\n');
dom.text('</' + element + '>');
});
}
if (restrict.match(/C/)) {
dom.text('as class');
var element = self.element || 'ANY';
dom.code(function() {
dom.text('<' + element + ' class="');
dom.text(dashCase(self.shortName));
renderParams(' ', ': ', ';', true);
dom.text('">\n ...\n');
dom.text('</' + element + '>');
});
}
}
self.html_usage_directiveInfo(dom);
self.html_usage_parameters(dom);
+2 -3
View File
@@ -7,8 +7,7 @@ exports.collect = collect;
var ngdoc = require('./ngdoc.js'),
Q = require('qq'),
qfs = require('q-fs'),
PATH = require('path');
qfs = require('q-fs');
var NEW_LINE = /\n\r?/;
@@ -44,7 +43,7 @@ function collect() {
var work2;
if (file.match(/\.ngdoc$/)) {
work2 = Q.when(qfs.read(file, 'b'), function(content){
var section = '@section ' + file.split(PATH.sep)[2] + '\n';
var section = '@section ' + file.split('/')[2] + '\n';
allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse());
});
}
+1 -1
View File
@@ -4,7 +4,7 @@
# current angular version. If this rule matches the appcache-offline.manifest will be served for
# requests to appcache.manifest
#
# This file must be processed by Grunt in order to replace %ANGULAR_VERSION% with the actual version.
# This file must be processed by Rake in order to replace %ANGULAR_VERSION% with the actual version.
Options -Indexes
RewriteEngine on
-16
View File
@@ -86,10 +86,6 @@ img.AngularJS-small {
/* Content */
/* =============================== */
.improve-docs {
float: right;
}
.hint {
font-size: .7em;
color: #c0c0c0;
@@ -188,15 +184,3 @@ ul.events > li > h3 {
.clear {
clear: both;
}
.tutorial-page {
position:relative;
}
.tutorial-page-no-nav {
padding-top:50px;
}
.tutorial-page-no-nav .improve-docs {
position:absolute;
top:0;
right:0;
}
+2 -2
View File
@@ -7,7 +7,7 @@
headEl = document.head,
angularVersion = {
current: '"NG_VERSION_FULL"', // rewrite during build
cdn: '"NG_VERSION_CDN"'
stable: '"NG_VERSION_STABLE"'
};
addTag('script', {src: path('angular-scenario.js')}, function() {
@@ -34,7 +34,7 @@
function path(name) {
return production
? 'http://code.angularjs.org/' + angularVersion.cdn + '/' + name
? 'http://code.angularjs.org/' + angularVersion.stable + '/' + name
: '../' + name;
}
</script>
+3 -3
View File
@@ -27,7 +27,7 @@
sync = true,
angularVersion = {
current: '"NG_VERSION_FULL"', // rewrite during build
cdn: '"NG_VERSION_CDN"'
stable: '"NG_VERSION_STABLE"'
};
addTag('base', {href: baseUrl});
@@ -48,12 +48,12 @@
if (production) {
if (name.match(/^angular(-\w+)?\.js/) && !name.match(/bootstrap/)) {
name = '//ajax.googleapis.com/ajax/libs/angularjs/' +
angularVersion.cdn +
angularVersion.stable +
'/' +
name.replace(/\.js$/, '.min.js');
} else {
name = 'http://code.angularjs.org/' +
angularVersion.cdn +
angularVersion.stable +
'/' +
name.replace(/\.js$/, '.min.js');
}
+5 -4
View File
@@ -61,21 +61,22 @@ exports.copy = function(from, to, transform) {
exports.symlink = symlink;
function symlink(from, to, type) {
function symlink(from, to) {
return qfs.exists(to).then(function(exists) {
if (!exists) {
return qfs.symbolicLink(to, from, type);
return qfs.symbolicLink(to, from);
}
});
}
exports.symlinkTemplate = symlinkTemplate;
function symlinkTemplate(filename, type) {
function symlinkTemplate(filename) {
var dest = OUTPUT_DIR + filename,
dirDepth = dest.split('/').length,
src = Array(dirDepth).join('../') + 'docs/src/templates/' + filename;
return symlink(src, dest, type);
return symlink(src, dest);
}
+1 -1
View File
@@ -1,5 +1,5 @@
<!doctype html>
<html ng-app="personalLog">
<html ng-app>
<head>
<title>Personal Log</title>
<script src="../../src/loader.js"></script>
+121 -158
View File
@@ -16,14 +16,14 @@
/**
* @fileoverview A utility to get better currency format pattern.
*
* This module implements a new currency format representation model. It
* This module implement a new currency format representation model. It
* provides 3 currency representation forms: global, portable and local. Local
* format is the most popular format people use to represent currency in its
* circulating country without worrying about how it should be distinguished
* from other currencies. Global format is a formal representation in context
* of multiple currencies in same page, it is ISO 4217 currency code. Portable
* format is a compromise between global and local. It looks similar to how
* people would like to see how their currency is being represented in other
* people would like to see how their currencies is being represented in other
* media. While at the same time, it should be distinguishable to world's
* popular currencies (like USD, EUR) and currencies somewhat relevant in the
* area (like CNY in HK, though native currency is HKD). There is no guarantee
@@ -43,14 +43,15 @@ goog.i18n.currency.PRECISION_MASK_ = 0x07;
/**
* Whether the currency sign should be positioned after the number.
* If this flag is set, it means the currency sign should position before
* number.
* @private
*/
goog.i18n.currency.POSITION_FLAG_ = 0x08;
/**
* Whether a space should be inserted between the number and currency sign.
* Should a space to inserted between number and currency sign.
* @private
*/
goog.i18n.currency.SPACE_FLAG_ = 0x20;
@@ -58,8 +59,8 @@ goog.i18n.currency.SPACE_FLAG_ = 0x20;
/**
* This function will add tier2 currency support. Be default, only tier1
* (most popular currencies) are supported. If an application really needs
* to support some of the rarely used currencies, it should call this function
* (most popular currencies) are supportted. If an application really need
* to support some of the rarely used currency, it should call this function
* before any other functions in this namespace.
*/
goog.i18n.currency.addTier2Support = function() {
@@ -74,8 +75,8 @@ goog.i18n.currency.addTier2Support = function() {
* Global currency pattern always uses ISO-4217 currency code as prefix. Local
* currency sign is added if it is different from currency code. Each currency
* is unique in this form. The negative side is that ISO code looks weird in
* some countries as people normally do not use it. Local currency sign
* alleviates the problem, but also makes it a little verbose.
* some countries as poeple normally do not use it. Local currency sign
* alleviate the problem, but also make it a little verbose.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Global currency pattern string for given currency.
@@ -84,6 +85,9 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
var patternNum = info[0];
if (currencyCode == info[1]) {
if ((patternNum & goog.i18n.currency.POSITION_FLAG_) == 0) {
patternNum |= goog.i18n.currency.SPACE_FLAG_;
}
return goog.i18n.currency.getCurrencyPattern_(patternNum, info[1]);
}
return currencyCode + ' ' +
@@ -100,8 +104,10 @@ goog.i18n.currency.getGlobalCurrencyPattern = function(currencyCode) {
*/
goog.i18n.currency.getGlobalCurrencySign = function(currencyCode) {
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
return (currencyCode == info[1]) ? currencyCode :
currencyCode + ' ' + info[1];
if (currencyCode == info[1]) {
return currencyCode;
}
return currencyCode + ' ' + info[1];
};
@@ -122,7 +128,6 @@ goog.i18n.currency.getLocalCurrencyPattern = function(currencyCode) {
/**
* Returns local currency sign string for those applications that need to
* handle currency sign separately.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Local currency sign for given currency.
*/
@@ -151,7 +156,6 @@ goog.i18n.currency.getPortableCurrencyPattern = function(currencyCode) {
/**
* Return portable currency sign string for those applications that need to
* handle currency sign themselves.
*
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {string} Portable currency sign for given currency.
*/
@@ -161,13 +165,10 @@ goog.i18n.currency.getPortableCurrencySign = function(currencyCode) {
/**
* This function returns the default currency sign position. Some applications
* This function returns the default currency sign position. Some application
* may want to handle currency sign and currency amount separately. This
* function can be used in such situations to correctly position the currency
* sign relative to the amount.
*
* To match the behavior of ICU, position is not determined by display locale.
*
* function can be used in such situation to position the currency sign
* relative to amount field correctly.
* @param {string} currencyCode ISO-4217 3-letter currency code.
* @return {boolean} true if currency should be positioned before amount field.
*/
@@ -178,12 +179,13 @@ goog.i18n.currency.isPrefixSignPosition = function(currencyCode) {
/**
* This function constructs the currency pattern. Currency sign is provided. The
* This function construct the currency pattern. Currency sign is provided. The
* pattern information is encoded in patternNum.
*
* @param {number} patternNum Encoded pattern number that has
* currency pattern information.
* @param {string} sign The currency sign that will be used in pattern.
* @param {string} sign the currency sign that will be used in pattern.
*
* @return {string} currency pattern string.
* @private
*/
@@ -209,97 +211,56 @@ goog.i18n.currency.getCurrencyPattern_ = function(patternNum, sign) {
};
/**
* Modify currency pattern string by adjusting precision for given currency.
* Standard currency pattern will have 2 digit after decimal point.
* Examples:
* $#,##0.00 -> $#,##0 (precision == 0)
* $#,##0.00 -> $#,##0.0 (precision == 1)
* $#,##0.00 -> $#,##0.000 (precision == 3)
*
* @param {string} pattern currency pattern string.
* @param {string} currencyCode 3-letter currency code.
* @return {string} modified currency pattern string.
*/
goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
var strParts = ['0'];
var info = goog.i18n.currency.CurrencyInfo[currencyCode];
var precision = info[0] & goog.i18n.currency.PRECISION_MASK_;
if (precision > 0) {
strParts.push('.');
for (var i = 0; i < precision; i++) {
strParts.push('0');
}
}
return pattern.replace(/0.00/g, strParts.join(''));
};
/**
* Tier 1 currency information.
*
* The first number in the array is a combination of the precision mask and
* other flags. The precision mask indicates how many decimal places to show for
* the currency. Valid values are [0..7]. The position flag indicates whether
* the currency sign should be positioned after the number. Valid values are 0
* (before the number) or 16 (after the number). The space flag indicates
* whether a space should be inserted between the currency sign and number.
* Valid values are 0 (no space) and 24 (space).
*
* The number in the array is calculated by adding together the mask and flag
* values. For example:
*
* 0: no precision (0), currency sign first (0), no space (0)
* 2: two decimals precision (2), currency sign first (0), no space (0)
* 18: two decimals precision (2), currency sign last (16), no space (0)
* 42: two decimals precision (2), currency sign last (16), space (24)
*
* @type {!Object.<!Array>}
*/
goog.i18n.currency.CurrencyInfo = {
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
'AED': [2, '\u062F\u002e\u0625', 'DH'],
'ARS': [2, '$', 'AR$'],
'AUD': [2, '$', 'AU$'],
'BDT': [2, '\u09F3', 'Tk'],
'BRL': [2, 'R$', 'R$'],
'CAD': [2, '$', 'C$'],
'CHF': [2, 'CHF', 'CHF'],
'CHF': [2, 'Fr.', 'CHF'],
'CLP': [0, '$', 'CL$'],
'CNY': [2, '¥', 'RMB¥'],
'COP': [0, '$', 'COL$'],
'CRC': [0, '\u20a1', 'CR\u20a1'],
'CZK': [2, 'K\u010d', 'K\u010d'],
'DKK': [18, 'kr', 'kr'],
'COP': [2, '$', 'COL$'],
'CRC': [2, '\u20a1', 'CR'],
'CUP': [2, '$', '$MN'],
'CZK': [10, '', ''],
'DKK': [26, 'kr', 'kr'],
'DOP': [2, '$', 'RD$'],
'EGP': [2, '£', 'LE'],
'EUR': [18, '€', '€'],
'EUR': [26, '€', '€'],
'GBP': [2, '£', 'GB£'],
'HKD': [2, '$', 'HK$'],
'ILS': [2, '\u20AA', 'IL\u20AA'],
'INR': [2, '\u20B9', 'Rs'],
'ISK': [0, 'kr', 'kr'],
'ILS': [10, '\u20AA', 'IL'],
'INR': [2, 'Rs', 'Rs'],
'ISK': [10, 'kr', 'kr'],
'JMD': [2, '$', 'JA$'],
'JPY': [0, '¥', 'JP¥'],
'KRW': [0, '\u20A9', 'KR₩'],
'LKR': [2, 'Rs', 'SLRs'],
'MNT': [0, '\u20AE', 'MN₮'],
'MNT': [2, '\u20AE', 'MN₮'],
'MXN': [2, '$', 'Mex$'],
'MYR': [2, 'RM', 'RM'],
'NOK': [18, 'kr', 'NOkr'],
'NOK': [26, 'kr', 'NOkr'],
'PAB': [2, 'B/.', 'B/.'],
'PEN': [2, 'S/.', 'S/.'],
'PHP': [2, '\u20B1', 'Php'],
'PKR': [0, 'Rs', 'PKRs.'],
'RUB': [42, 'руб.', 'руб.'],
'SAR': [2, 'Rial', 'Rial'],
'SEK': [2, 'kr', 'kr'],
'PHP': [2, 'P', 'PHP'],
'PKR': [2, 'Rs.', 'PKRs.'],
'RUB': [10, 'руб', 'руб'],
'SAR': [2, '\u0633\u002E\u0631', 'SR'],
'SEK': [10, 'kr', 'kr'],
'SGD': [2, '$', 'S$'],
'THB': [2, '\u0e3f', 'THB'],
'TRY': [2, 'TL', 'YTL'],
'TRY': [2, 'YTL', 'YTL'],
'TWD': [2, 'NT$', 'NT$'],
'USD': [2, '$', 'US$'],
'UYU': [2, '$', 'UY$'],
'VND': [0, '\u20AB', 'VN\u20AB'],
'YER': [0, 'Rial', 'Rial'],
'VND': [10, '\u20AB', 'VN'],
'YER': [2, 'YER', 'YER'],
'ZAR': [2, 'R', 'ZAR']
};
@@ -309,114 +270,116 @@ goog.i18n.currency.CurrencyInfo = {
* @type {!Object.<!Array>}
*/
goog.i18n.currency.CurrencyInfoTier2 = {
'AFN': [16, 'Af.', 'AFN'],
'ALL': [0, 'Lek', 'Lek'],
'AMD': [0, 'Dram', 'dram'],
'AFN': [18, '\u060b', 'AFN'],
'ALL': [2, 'Lek', 'Lek'],
'AMD': [10, '\u0564\u0580\u002e', 'dram'],
'ANG': [2, '\u0083', 'NAƒ'],
'AOA': [2, 'Kz', 'Kz'],
'ARS': [2, '$', 'AR$'],
'AWG': [2, 'Afl.', 'Afl.'],
'AZN': [2, 'man.', 'man.'],
'BAM': [18, 'KM', 'KM'],
'AWG': [2, 'ƒ', 'Afl.'],
'AZN': [2, 'm', 'man'],
'BAM': [18, 'КМ', 'KM'],
'BBD': [2, '$', 'Bds$'],
'BGN': [2, 'lev', 'lev'],
'BHD': [3, 'din', 'din'],
'BGN': [10, '\u043b\u0432', 'лв'],
'BHD': [3, '\u0628\u002e\u062f\u002e', 'BD'],
'BIF': [0, 'FBu', 'FBu'],
'BMD': [2, '$', 'BD$'],
'BND': [2, '$', 'B$'],
'BOB': [2, 'Bs', 'Bs'],
'BSD': [2, '$', 'BS$'],
'BOB': [2, 'B$', 'B$'],
'BSD': [2, '$', 'B$'],
'BTN': [2, 'Nu.', 'Nu.'],
'BWP': [2, 'P', 'pula'],
'BYR': [0, 'BYR', 'BYR'],
'BYR': [0, 'Br', 'Br'],
'BZD': [2, '$', 'BZ$'],
'CDF': [2, 'FrCD', 'CDF'],
'CUC': [1, '$', 'CUC$'],
'CUP': [2, '$', 'CU$'],
'CVE': [2, 'CVE', 'Esc'],
'CDF': [2, 'F', 'CDF'],
'CVE': [2, '$', 'Esc'],
'DJF': [0, 'Fdj', 'Fdj'],
'DZD': [2, 'din', 'din'],
'DZD': [2, '\u062f\u062C', 'DA'],
'EEK': [10, 'EEK', 'EEK'],
'ERN': [2, 'Nfk', 'Nfk'],
'ETB': [2, 'Birr', 'Birr'],
'ETB': [2, 'Br', 'Br'],
'FJD': [2, '$', 'FJ$'],
'FKP': [2, '£', 'FK£'],
'GEL': [2, 'GEL', 'GEL'],
'GHS': [2, 'GHS', 'GHS'],
'GHS': [2, '\u20B5', 'GHS¢'],
'GIP': [2, '£', 'GI£'],
'GMD': [2, 'GMD', 'GMD'],
'GMD': [2, 'D', 'GMD'],
'GNF': [0, 'FG', 'FG'],
'GTQ': [2, 'Q', 'GTQ'],
'GYD': [0, '$', 'GY$'],
'GYD': [2, '$', 'GY$'],
'HNL': [2, 'L', 'HNL'],
'HRK': [2, 'kn', 'kn'],
'HTG': [2, 'HTG', 'HTG'],
'HUF': [0, 'Ft', 'Ft'],
'IDR': [0, 'Rp', 'Rp'],
'IQD': [0, 'din', 'IQD'],
'IRR': [0, 'Rial', 'IRR'],
'JOD': [3, 'din', 'JOD'],
'KES': [2, 'Ksh', 'Ksh'],
'KGS': [2, 'KGS', 'KGS'],
'KHR': [2, 'Riel', 'KHR'],
'KMF': [0, 'CF', 'KMF'],
'KPW': [0, '\u20A9KP', 'KPW'],
'KWD': [3, 'din', 'KWD'],
'KYD': [2, '$', 'KY$'],
'KZT': [2, '\u20B8', 'KZT'],
'LAK': [0, '\u20AD', '\u20AD'],
'LBP': [0, '', 'LBP'],
'HTG': [2, 'G', 'HTG'],
'HUF': [10, 'Ft', 'Ft'],
'IDR': [2, 'Rp', 'Rp'],
'IQD': [3, '\u0639\u062F', 'IQD'],
'IRR': [2, '\ufdfc', 'IRR'],
'JOD': [3, 'JOD', 'JOD'],
'KES': [2, 'KSh', 'KSh'],
'KGS': [2, 'som', 'som'],
'KHR': [10, '\u17DB', 'KHR'],
'KMF': [0, 'KMF', 'KMF'],
'KPW': [2, '\u20A9', 'KPW'],
'KWD': [3, '\u062F\u002e\u0643', 'KWD'],
'KYD': [2, '$', 'CI$'],
'KZT': [10, 'KZT', 'KZT'],
'LAK': [2, '\u20AD', 'LA₭'],
'LBP': [2, '\u0644\u002e\u0644', 'LBP'],
'LRD': [2, '$', 'L$'],
'LSL': [2, 'LSL', 'LSL'],
'LTL': [2, 'Lt', 'Lt'],
'LVL': [2, 'Ls', 'Ls'],
'LYD': [3, 'din', 'LD'],
'MAD': [2, 'dh', 'MAD'],
'LSL': [2, 'L', 'LSL'],
'LTL': [10, 'Lt', 'Lt'],
'LVL': [10, 'Ls', 'Ls'],
'LYD': [3, '\u0644\u002e\u062F', 'LD'],
'MAD': [2, '\u0645\u002E\u062F\u002E', 'MAD'],
'MDL': [2, 'MDL', 'MDL'],
'MGA': [0, 'Ar', 'MGA'],
'MKD': [2, 'din', 'MKD'],
'MMK': [0, 'K', 'MMK'],
'MOP': [2, 'MOP', 'MOP$'],
'MRO': [0, 'MRO', 'MRO'],
'MUR': [0, 'MURs', 'MURs'],
'MWK': [2, 'MWK', 'MWK'],
'MGA': [1, 'MGA', 'MGA'],
'MKD': [2, 'MKD', 'MKD'],
'MMK': [2, 'K', 'MMK'],
'MOP': [2, 'MOP$', 'MOP$'],
'MRO': [1, 'UM', 'UM'],
'MUR': [2, 'Rs', 'MURs'],
'MVR': [2, 'Rf', 'MRF'],
'MWK': [2, 'MK', 'MK'],
'MZN': [2, 'MTn', 'MTn'],
'NAD': [2, '$', 'N$'],
'NGN': [2, '\u20A6', 'NG\u20A6'],
'NGN': [2, '\u20A6', 'NG'],
'NIO': [2, 'C$', 'C$'],
'NPR': [2, 'Rs', 'NPRs'],
'NZD': [2, '$', 'NZ$'],
'OMR': [3, 'Rial', 'OMR'],
'PGK': [2, 'PGK', 'PGK'],
'PLN': [2, 'z\u0142', 'z\u0142'],
'PYG': [0, 'Gs', 'PYG'],
'QAR': [2, 'Rial', 'QR'],
'RON': [2, 'RON', 'RON'],
'RSD': [0, 'din', 'RSD'],
'OMR': [3, '\u0639\u002E\u062F\u002E', 'OMR'],
'PGK': [2, 'K', 'PGK'],
'PLN': [10, 'zł', 'zł'],
'PYG': [0, '\u20b2', 'PYG'],
'QAR': [2, '\u0642\u002E\u0631', 'QR'],
'RON': [2, 'L', 'RON'],
'RSD': [2, 'РС\u0414', 'RSD'],
'RWF': [0, 'RF', 'RF'],
'SBD': [2, '$', 'SI$'],
'SCR': [2, 'SCR', 'SCR'],
'SCR': [2, 'SR', 'SCR'],
'SDG': [2, 'SDG', 'SDG'],
'SHP': [2, '£', 'SH£'],
'SLL': [0, 'SLL', 'SLL'],
'SOS': [0, 'SOS', 'SOS'],
'SKK': [10, 'Sk', 'Sk'],
'SLL': [2, 'Le', 'Le'],
'SOS': [2, 'So. Sh.', 'So. Sh.'],
'SRD': [2, '$', 'SR$'],
'STD': [0, 'Db', 'Db'],
'SYP': [16, '£', 'SY£'],
'SZL': [2, 'SZL', 'SZL'],
'TJS': [2, 'Som', 'TJS'],
'TND': [3, 'din', 'DT'],
'STD': [2, 'Db', 'Db'],
'SYP': [18, 'SYP', 'SYP'],
'SZL': [2, 'L', 'SZL'],
'TJS': [2, 'TJS', 'TJS'],
'TMM': [2, 'm', 'TMM'],
'TND': [3, '\u062F\u002e\u062A ', 'DT'],
'TOP': [2, 'T$', 'T$'],
'TTD': [2, '$', 'TT$'],
'TZS': [0, 'TSh', 'TSh'],
'UAH': [2, '\u20B4', 'UAH'],
'UGX': [0, 'UGX', 'UGX'],
'UYU': [1, '$', '$U'],
'UZS': [0, 'so\u02bcm', 'UZS'],
'VEF': [2, 'Bs', 'Bs'],
'VUV': [0, 'VUV', 'VUV'],
'WST': [2, 'WST', 'WST'],
'TZS': [10, 'TZS', 'TZS'],
'UAH': [10, '\u20B4', 'грн'],
'UGX': [2, 'USh', 'USh'],
'UZS': [2, 'UZS', 'UZS'],
'VEF': [2, 'Bs.F', 'Bs.F'],
'VUV': [0, 'Vt', 'Vt'],
'WST': [2, 'WS$', 'WS$'],
'XAF': [0, 'FCFA', 'FCFA'],
'XCD': [2, '$', 'EC$'],
'XOF': [0, 'CFA', 'CFA'],
'XPF': [0, 'FCFP', 'FCFP'],
'ZMK': [0, 'ZMK', 'ZMK']
'XPF': [0, 'F', 'XPF'],
'ZMK': [2, 'ZK', 'ZK'],
'ZWL': [2, '$', 'ZW$']
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+146 -213
View File
@@ -1,4 +1,4 @@
// Copyright 2012 The Closure Library Authors. All Rights Reserved
// Copyright 2011 The Closure Library Authors. All Rights Reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -7,24 +7,35 @@
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Plural rules.
*
* This file is autogenerated by script:
* http://go/generate_pluralrules.py
* http://go/generate_pluralrules.py
* using the --for_closure flag.
*
* Before check in, this file could have been manually edited. This is to
* incorporate changes before we could fix CLDR. All manual modification must be
* documented in this section, and should be removed after those changes land to
* CLDR.
* To reduce the file size (which may cause issues in some JS
* developing environments), this file will only contain locales
* that are usually supported by google products. This is defined as
* closure_tier1_locales and will change (most likely addition)
* over time. Rest of the data can be found in another file named
* "pluralrulesext.js", which will be generated at the
* same time together with this file.
*
* Before checkin, this file could have been manually edited. This is
* to incorporate changes before we could fix CLDR. All manual
* modification must be documented in this section, and should be
* removed after those changes land to CLDR.
*/
goog.provide('goog.i18n.pluralRules');
/**
* Plural pattern keyword
* @enum {string}
@@ -42,7 +53,7 @@ goog.i18n.pluralRules.Keyword = {
/**
* Default plural select rule.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Default value.
* @return {goog.i18n.pluralRules.Keyword} Default plural value.
* @private
*/
goog.i18n.pluralRules.defaultSelect_ = function(n) {
@@ -53,25 +64,25 @@ goog.i18n.pluralRules.defaultSelect_ = function(n) {
/**
* Plural select rules for ar locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.arSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == (n | 0) && n % 100 >= 3 && n % 100 <= 10) {
return goog.i18n.pluralRules.Keyword.FEW;
if ((n % 100) >= 3 && (n % 100) <= 10 && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 99) {
return goog.i18n.pluralRules.Keyword.MANY;
if ((n % 100) >= 11 && (n % 100) <= 99 && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -80,13 +91,13 @@ goog.i18n.pluralRules.arSelect_ = function(n) {
/**
* Plural select rules for en locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.enSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -95,13 +106,13 @@ goog.i18n.pluralRules.enSelect_ = function(n) {
/**
* Plural select rules for fil locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.filSelect_ = function(n) {
if (n == 0 || n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -110,13 +121,13 @@ goog.i18n.pluralRules.filSelect_ = function(n) {
/**
* Plural select rules for fr locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.frSelect_ = function(n) {
if (n >= 0 && n <= 2 && n != 2) {
return goog.i18n.pluralRules.Keyword.ONE;
if (n >= 0 && n < 2) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -125,34 +136,16 @@ goog.i18n.pluralRules.frSelect_ = function(n) {
/**
* Plural select rules for lv locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.lvSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n % 10 == 1 && n % 100 != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for iu locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.iuSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
if ((n % 10) == 1 && (n % 100) != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -161,22 +154,16 @@ goog.i18n.pluralRules.iuSelect_ = function(n) {
/**
* Plural select rules for ga locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.gaSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == (n | 0) && n >= 3 && n <= 6) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == (n | 0) && n >= 7 && n <= 10) {
return goog.i18n.pluralRules.Keyword.MANY;
return goog.i18n.pluralRules.Keyword.TWO;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -185,16 +172,17 @@ goog.i18n.pluralRules.gaSelect_ = function(n) {
/**
* Plural select rules for ro locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.roSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 0 || n != 1 && n == (n | 0) && n % 100 >= 1 && n % 100 <= 19) {
return goog.i18n.pluralRules.Keyword.FEW;
if (n == 0 || n != 1 && (n % 100) >= 1 &&
(n % 100) <= 19 && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -203,37 +191,40 @@ goog.i18n.pluralRules.roSelect_ = function(n) {
/**
* Plural select rules for lt locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.ltSelect_ = function(n) {
if (n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) {
return goog.i18n.pluralRules.Keyword.ONE;
if ((n % 10) == 1 && ((n % 100) < 11 || (n % 100) > 19)) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) {
return goog.i18n.pluralRules.Keyword.FEW;
if ((n % 10) >= 2 && (n % 10) <= 9 &&
((n % 100) < 11 || (n % 100) > 19) && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for be locale
* Plural select rules for hr locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.beSelect_ = function(n) {
if (n % 10 == 1 && n % 100 != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
goog.i18n.pluralRules.hrSelect_ = function(n) {
if ((n % 10) == 1 && (n % 100) != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
return goog.i18n.pluralRules.Keyword.FEW;
if ((n % 10) >= 2 && (n % 10) <= 4 &&
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n % 10 == 0 || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 11 && n % 100 <= 14) {
return goog.i18n.pluralRules.Keyword.MANY;
if ((n % 10) == 0 || ((n % 10) >= 5 && (n % 10) <= 9) ||
((n % 100) >= 11 && (n % 100) <= 14) && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -242,16 +233,16 @@ goog.i18n.pluralRules.beSelect_ = function(n) {
/**
* Plural select rules for cs locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.csSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n >= 2 && n <= 4) {
return goog.i18n.pluralRules.Keyword.FEW;
if (n == 2 || n == 3 || n == 4) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -260,19 +251,22 @@ goog.i18n.pluralRules.csSelect_ = function(n) {
/**
* Plural select rules for pl locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.plSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14)) {
return goog.i18n.pluralRules.Keyword.FEW;
if ((n % 10) >= 2 && (n % 10) <= 4 &&
((n % 100) < 12 || (n % 100) > 14) && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n != 1 && (n % 10 == 0 || n % 10 == 1) || n == (n | 0) && n % 10 >= 5 && n % 10 <= 9 || n == (n | 0) && n % 100 >= 12 && n % 100 <= 14) {
return goog.i18n.pluralRules.Keyword.MANY;
if ((n % 10) == 0 || n != 1 && (n % 10) == 1 ||
((n % 10) >= 5 && (n % 10) <= 9 || (n % 100) >= 12 && (n % 100) <= 14) &&
n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -281,19 +275,19 @@ goog.i18n.pluralRules.plSelect_ = function(n) {
/**
* Plural select rules for sl locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.slSelect_ = function(n) {
if (n % 100 == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
if ((n % 100) == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n % 100 == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
if ((n % 100) == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n % 100 == 3 || n % 100 == 4) {
return goog.i18n.pluralRules.Keyword.FEW;
if ((n % 100) == 3 || (n % 100) == 4) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -302,19 +296,19 @@ goog.i18n.pluralRules.slSelect_ = function(n) {
/**
* Plural select rules for mt locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.mtSelect_ = function(n) {
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 0 || n == (n | 0) && n % 100 >= 2 && n % 100 <= 10) {
return goog.i18n.pluralRules.Keyword.FEW;
if (n == 0 || ((n % 100) >= 2 && (n % 100) <= 4 && n == Math.floor(n))) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == (n | 0) && n % 100 >= 11 && n % 100 <= 19) {
return goog.i18n.pluralRules.Keyword.MANY;
if ((n % 100) >= 11 && (n % 100) <= 19 && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -323,13 +317,13 @@ goog.i18n.pluralRules.mtSelect_ = function(n) {
/**
* Plural select rules for mk locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.mkSelect_ = function(n) {
if (n % 10 == 1 && n != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
if ((n % 10) == 1 && n != 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -338,25 +332,25 @@ goog.i18n.pluralRules.mkSelect_ = function(n) {
/**
* Plural select rules for cy locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.cySelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == 3) {
return goog.i18n.pluralRules.Keyword.FEW;
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n == 6) {
return goog.i18n.pluralRules.Keyword.MANY;
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -365,16 +359,16 @@ goog.i18n.pluralRules.cySelect_ = function(n) {
/**
* Plural select rules for lag locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.lagSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n >= 0 && n <= 2 && n != 0 && n != 2) {
return goog.i18n.pluralRules.Keyword.ONE;
if (n > 0 && n < 2) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -383,16 +377,16 @@ goog.i18n.pluralRules.lagSelect_ = function(n) {
/**
* Plural select rules for shi locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.shiSelect_ = function(n) {
if (n >= 0 && n <= 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == (n | 0) && n >= 2 && n <= 10) {
return goog.i18n.pluralRules.Keyword.FEW;
if (n >= 2 && n <= 10 && n == Math.floor(n)) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -401,91 +395,25 @@ goog.i18n.pluralRules.shiSelect_ = function(n) {
/**
* Plural select rules for br locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale specific plural value.
* @private
*/
goog.i18n.pluralRules.brSelect_ = function(n) {
if (n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if ((n % 10 == 3 || n % 10 == 4 || n % 10 == 9) && ((n % 100 < 10 || n % 100 > 19) && (n % 100 < 70 || n % 100 > 79) && (n % 100 < 90 || n % 100 > 99))) {
return goog.i18n.pluralRules.Keyword.FEW;
}
if (n % 1000000 == 0 && n != 0) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for ksh locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.kshSelect_ = function(n) {
if (n == 0) {
return goog.i18n.pluralRules.Keyword.ZERO;
return goog.i18n.pluralRules.Keyword.ZERO;
}
if (n == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for tzm locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.tzmSelect_ = function(n) {
if (n == 0 || n == 1 || n == (n | 0) && n >= 11 && n <= 99) {
return goog.i18n.pluralRules.Keyword.ONE;
if (n == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for gv locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.gvSelect_ = function(n) {
if (n % 10 == 1 || n % 10 == 2 || n % 20 == 0) {
return goog.i18n.pluralRules.Keyword.ONE;
if (n == 3) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for gd locale
*
* @param {number} n The count of items.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.gdSelect_ = function(n) {
if (n == 1 || n == 11) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (n == 2 || n == 12) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (n == (n | 0) && (n >= 3 && n <= 10 || n >= 13 && n <= 19)) {
return goog.i18n.pluralRules.Keyword.FEW;
if (n == 6) {
return goog.i18n.pluralRules.Keyword.MANY;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
@@ -495,6 +423,7 @@ goog.i18n.pluralRules.gdSelect_ = function(n) {
* Selected plural rules by locale.
*/
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
if (goog.LOCALE == 'am') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.filSelect_;
}
@@ -628,7 +557,7 @@ if (goog.LOCALE == 'hi') {
}
if (goog.LOCALE == 'hr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
}
if (goog.LOCALE == 'hu') {
@@ -652,7 +581,7 @@ if (goog.LOCALE == 'it') {
}
if (goog.LOCALE == 'iw') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.defaultSelect_;
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'ja') {
@@ -683,6 +612,10 @@ if (goog.LOCALE == 'ml') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'mo') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.roSelect_;
}
if (goog.LOCALE == 'mr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
@@ -728,7 +661,7 @@ if (goog.LOCALE == 'ro') {
}
if (goog.LOCALE == 'ru') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
}
if (goog.LOCALE == 'sk') {
@@ -744,7 +677,7 @@ if (goog.LOCALE == 'sq') {
}
if (goog.LOCALE == 'sr') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
}
if (goog.LOCALE == 'sv') {
@@ -776,7 +709,7 @@ if (goog.LOCALE == 'tr') {
}
if (goog.LOCALE == 'uk') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.beSelect_;
goog.i18n.pluralRules.select = goog.i18n.pluralRules.hrSelect_;
}
if (goog.LOCALE == 'ur') {
-5
View File
@@ -1,5 +0,0 @@
#!/bin/bash
set -e
PARENT_DIR="$(dirname "$0")"
jasmine-node "$PARENT_DIR"/spec/
-261
View File
@@ -1,261 +0,0 @@
var closureI18nExtractor = require('../src/closureI18nExtractor.js');
var converter = require('../src/converter.js');
findLocaleId = closureI18nExtractor.findLocaleId;
extractNumberSymbols = closureI18nExtractor.extractNumberSymbols;
extractCurrencySymbols = closureI18nExtractor.extractCurrencySymbols;
extractDateTimeSymbols = closureI18nExtractor.extractDateTimeSymbols;
function newTestLocaleInfo() {
return { fr_CA: {
DATETIME_FORMATS: {
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
'octobre', 'novembre', 'décembre'],
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
'nov.', 'déc.'],
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
AMPMS: ['AM', 'PM'],
medium: 'yyyy-MM-dd HH:mm:ss',
short: 'yy-MM-dd HH:mm',
fullDate: 'EEEE d MMMM y',
longDate: 'd MMMM y',
mediumDate: 'yyyy-MM-dd',
shortDate: 'yy-MM-dd',
mediumTime: 'HH:mm:ss',
shortTime: 'HH:mm'
},
NUMBER_FORMATS: {
"DECIMAL_SEP": ".",
"GROUP_SEP": ",",
"PATTERNS": [{
"minInt": 1,
"minFrac": 0,
"macFrac": 0,
"posPre": "",
"posSuf": "",
"negPre": "-",
"negSuf": "",
"gSize": 3,
"lgSize": 3,
"maxFrac": 3
}, {
"minInt": 1,
"minFrac": 2,
"macFrac": 0,
"posPre": "¤",
"posSuf": "",
"negPre": "¤-",
"negSuf": "",
"gSize": 3,
"lgSize": 3,
"maxFrac": 2
}],
"CURRENCY_SYM": "£"
}}};
}
describe("findLocaleId", function () {
it("should find the id from numbers", function() {
expect(findLocaleId("NumberFormatSymbols_en_GB", "num")).toEqual("en_GB");
});
it("should find the id from datetime", function() {
expect(findLocaleId("DateTimeSymbols_en_ISO", "datetime")).toEqual("en_ISO");
});
it("should throw an error otherwise", function() {
expect(function() {
findLocaleId("str", "otherwise")
}).toThrow("unknown type in findLocaleId: otherwise");
});
});
describe("extractNumberSymbols", function () {
it("should extract number data", function() {
var CONTENT = [
"goog.provide('goog.i18n.NumberFormatSymbols_en_GB');",
"goog.i18n.NumberFormatSymbols_en_GB = {",
"DECIMAL_SEP: '.',",
"GROUP_SEP: ',',",
"PERCENT: '%',",
"ZERO_DIGIT: '0',",
"PLUS_SIGN: '+',",
"MINUS_SIGN: '-',",
"EXP_SYMBOL: 'E',",
"PERMILL: '\u2030',",
"INFINITY: '\u221E',",
"NAN: 'NaN',",
"DECIMAL_PATTERN: '#,##0.###',",
"SCIENTIFIC_PATTERN: '#E0',",
"PERCENT_PATTERN: '#,##0%',",
"CURRENCY_PATTERN: '\u00A4#,##0.00',",
"DEF_CURRENCY_CODE: 'GBP' };"
].join('\n');
var currencySymbols = {'GBP':[2, '£', 'GB£']};
var expectedNumberFormats = converter.convertNumberData(
{
DECIMAL_SEP:'.',
GROUP_SEP:',',
DECIMAL_PATTERN:'#,##0.###',
CURRENCY_PATTERN:'\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'GBP'
}, currencySymbols
);
var localeInfo = {};
extractNumberSymbols(CONTENT, localeInfo, currencySymbols);
expect(localeInfo).toEqual({
'en_GB': { NUMBER_FORMATS: expectedNumberFormats }
});
})
});
describe("extractCurrencySymbols", function () {
it("should extract currency data", function() {
var CONTENT = [
"goog.i18n.currency.CurrencyInfo = {",
" 'GBP':[2, '£', 'GB£'],",
"};",
"goog.i18n.currency.CurrencyInfoTier2 = {",
" 'AOA':[2, 'Kz', 'Kz'],",
"};"
].join('\n');
var localeInfo = {};
expect(extractCurrencySymbols(CONTENT)).toEqual({
'GBP':[2, '£', 'GB£'],
'AOA':[2, 'Kz', 'Kz']
});
});
});
describe("extractDateTimeSymbols", function () {
it("should extract date time data", function() {
var CONTENT = [
"goog.i18n.DateTimeSymbols_fr_CA = {",
" ERAS: ['av. J.-C.', 'ap. J.-C.'],",
" ERANAMES: ['avant Jésus-Christ', 'après Jésus-Christ'],",
" NARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O', 'N', 'D'],",
" STANDALONENARROWMONTHS: ['J', 'F', 'M', 'A', 'M', 'J', 'J', 'A', 'S', 'O',",
" 'N', 'D'],",
" MONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet',",
" 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
" STANDALONEMONTHS: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin',",
" 'juillet', 'août', 'septembre', 'octobre', 'novembre', 'décembre'],",
" SHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.',",
" 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
" STANDALONESHORTMONTHS: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin',",
" 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'],",
" WEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',",
" 'samedi'],",
" STANDALONEWEEKDAYS: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi',",
" 'vendredi', 'samedi'],",
" SHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],",
" STANDALONESHORTWEEKDAYS: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.',",
" 'sam.'],",
" NARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
" STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'M', 'J', 'V', 'S'],",
" SHORTQUARTERS: ['T1', 'T2', 'T3', 'T4'],",
" QUARTERS: ['1er trimestre', '2e trimestre', '3e trimestre', '4e trimestre'],",
" AMPMS: ['AM', 'PM'],",
" DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'yyyy-MM-dd', 'yy-MM-dd'],",
" TIMEFORMATS: ['HH \\'h\\' mm \\'min\\' ss \\'s\\' zzzz', 'HH:mm:ss z',",
" 'HH:mm:ss', 'HH:mm'],",
" FIRSTDAYOFWEEK: 6,",
" WEEKENDRANGE: [5, 6],",
" FIRSTWEEKCUTOFFDAY: 2",
"};"
].join('\n');
var localeInfo = {};
var expectedLocaleInfo = {
fr_CA: {
DATETIME_FORMATS: {
MONTH: ['janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'septembre',
'octobre', 'novembre', 'décembre'],
SHORTMONTH: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.',
'nov.', 'déc.'],
DAY: ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'],
SHORTDAY: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'],
AMPMS: ['AM', 'PM'],
medium: 'yyyy-MM-dd HH:mm:ss',
short: 'yy-MM-dd HH:mm',
fullDate: 'EEEE d MMMM y',
longDate: 'd MMMM y',
mediumDate: 'yyyy-MM-dd',
shortDate: 'yy-MM-dd',
mediumTime: 'HH:mm:ss',
shortTime: 'HH:mm'
}
}
};
extractDateTimeSymbols(CONTENT, localeInfo);
expect(localeInfo).toEqual(expectedLocaleInfo);
})
});
describe("pluralExtractor", function() {
it("should output PLURAL_CAT in the output string code", function() {
var localeIds = ["fr_CA"];
var content = (
"goog.provide('goog.i18n.pluralRules');\n" +
"\n" +
"goog.i18n.pluralRules.Keyword = {\n" +
" ZERO: 'zero',\n" +
" ONE: 'one',\n" +
" TWO: 'two',\n" +
" FEW: 'few',\n" +
" MANY: 'many',\n" +
" OTHER: 'other'\n" +
"};\n" +
"\n" +
"goog.i18n.pluralRules.frSelect_ = function(n) {\n" +
" if (n >= 0 && n < 2) {\n" +
" return goog.i18n.pluralRules.Keyword.ONE;\n" +
" }\n" +
" return goog.i18n.pluralRules.Keyword.OTHER;\n" +
"};\n" +
"\n" +
"if (goog.LOCALE == 'fr') {\n" +
" goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;\n" +
"}"
);
var localeInfo = newTestLocaleInfo();
closureI18nExtractor.pluralExtractor(content, localeInfo);
var pluralCat = localeInfo["fr_CA"].pluralCat;
expect(pluralCat).toBeDefined();
// pluralCat is the source text for the pluralCat and contains @@
// placeholders that need to be stripped before evaluation.
// Ref: closureI18nExtractor.pluralExtractor.
pluralCat = pluralCat.replace(/^@@|@@$/g, '');
// pluralCat requires these constants to exist.
var PLURAL_CATEGORY = {
ZERO: "zero", ONE: "one", TWO: "two",
FEW: "few", MANY: "many", OTHER: "other"
};
// Obtain the function by evaluating the source text.
pluralCat = eval("(" + pluralCat + ")");
// Confirm some expectations for pluralCat in fr_CA.
expect(pluralCat(0)).toEqual("one");
expect(pluralCat(3)).toEqual("other");
})
});
describe("serializeContent", function() {
it("should not make any modifications to the content of the locale", function() {
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
expect(eval("(" + serializedContent + ")")).toEqual(newTestLocaleInfo());
});
it("should only have ascii characters", function() {
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo());
expect((/[^\u0001-\u007f]/).test(serializedContent)).toBe(false);
});
});
-2
View File
@@ -24,8 +24,6 @@ describe('parsePattern', function() {
parseAndExpect('#,##,##0.###', '', '-', '', '', 1, 0, 3, 2, 3);
parseAndExpect("#,##0.###;\'\u202A\'-#,##0.###\'\u202C\'",
'', '\u202A-', '', '\u202C', 1, 0, 3, 3, 3);
parseAndExpect('#0.###;#0.###-', '', '', '', '-', 1, 0, 3, 0, 0);
});
it('should parse CURRENCY patterns', function() {
-180
View File
@@ -1,180 +0,0 @@
'use strict';
var converter = require('./converter.js');
exports.extractNumberSymbols = extractNumberSymbols;
exports.extractCurrencySymbols = extractCurrencySymbols;
exports.extractDateTimeSymbols = extractDateTimeSymbols;
exports.pluralExtractor = pluralExtractor;
exports.outputLocale = outputLocale;
exports.correctedLocaleId = correctedLocaleId;
exports.findLocaleId = findLocaleId;
exports.serializeContent = serializeContent;
var goog = { provide: function() {},
require: function() {},
i18n: {currency: {}, pluralRules: {}} };
function findLocaleId(str, type) {
if (type === 'num') {
return (str.match(/^NumberFormatSymbols_(.+)$/) || [])[1];
}
if (type != 'datetime') { throw new Error('unknown type in findLocaleId: ' + type); }
return (str.match(/^DateTimeSymbols_(.+)$/) || [])[1];
}
function getInfoForLocale(localeInfo, localeID) {
if (!localeInfo[localeID]) {
localeInfo[localeID] = {};
//localeIds.push(localeID);
}
return localeInfo[localeID];
}
function extractNumberSymbols(content, localeInfo, currencySymbols) {
//eval script in the current context so that we get access to all the symbols
eval(content.toString());
for (var propName in goog.i18n) {
var localeID = findLocaleId(propName, 'num');
if (localeID) {
var info = getInfoForLocale(localeInfo, localeID);
info.NUMBER_FORMATS =
converter.convertNumberData(goog.i18n[propName], currencySymbols);
}
}
}
function extractCurrencySymbols(content) {
//eval script in the current context so that we get access to all the symbols
eval(content.toString());
var currencySymbols = goog.i18n.currency.CurrencyInfo;
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
return currencySymbols;
}
function extractDateTimeSymbols(content, localeInfo) {
//eval script in the current context so that we get access to all the symbols
eval(content.toString());
for (var propName in goog.i18n) {
var localeID = findLocaleId(propName, 'datetime');
if (localeID) {
var info = getInfoForLocale(localeInfo, localeID);
localeInfo[localeID].DATETIME_FORMATS =
converter.convertDatetimeData(goog.i18n[propName]);
}
}
}
function pluralExtractor(content, localeInfo) {
var contentText = content.toString();
var localeIds = Object.keys(localeInfo);
for (var i = 0; i < localeIds.length; i++) {
//We don't need to care about country ID because the plural rules in more specific id are
//always the same as those in its language ID.
// e.g. plural rules for en_SG is the same as those for en.
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
try {
eval(contentText);
} catch(e) {
console.log("Error in eval(contentText): " + e.stack);
}
if (!goog.i18n.pluralRules.select) {
console.log('No select for lang [' + goog.LOCALE + ']');
continue;
}
var temp = goog.i18n.pluralRules.select.toString().
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
///@@ is a crazy place holder to be replaced before writing to file
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
}
}
function correctedLocaleId(localeID) {
// e.g. from zh_CN to zh-CN, from en_US to en-US
return localeID.replace(/_/g, '-').toLowerCase();
}
function canonicalizeForJsonStringify(unused_key, object) {
// This function is intended to be called as the 2nd argument to
// JSON.stringify. The goal here is to ensure that the generated JSON has
// objects with their keys in ascending order. Without this, it's much
// harder to diff the generated files in src/ngLocale as the order isn't
// exactly consistent. We've gotten lucky in the past.
//
// Iteration order, for string keys, ends up being the same as insertion
// order. Refer :-
// 1. http://ejohn.org/blog/javascript-in-chrome/
// (search for "for loop order").
// Currently all major browsers loop over the properties of an object
// in the order in which they were defined.
// - John Resig
// 2. https://code.google.com/p/v8/issues/detail?id=164
// ECMA-262 does not specify enumeration order. The de facto standard
// is to match insertion order, which V8 also does ...
if (typeof object != "object") {
return object;
}
var result = {};
Object.keys(object).sort().forEach(function(key) {
result[key] = object[key];
});
return result;
}
function serializeContent(localeObj) {
return JSON.stringify(localeObj, canonicalizeForJsonStringify, ' ')
.replace(new RegExp('[\\u007f-\\uffff]', 'g'), function(c) { return '\\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4); })
.replace(/"@@|@@"/g, '');
}
function outputLocale(localeInfo, localeID) {
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
localeObj = localeInfo[localeID],
fallBackObj = localeInfo[fallBackID];
// fallBack to language formats when country format is missing
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
if (!localeObj.NUMBER_FORMATS) {
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
}
// datetimesymbolsext.js provides more top level locales than the other
// files. We process datetimesymbolsext.js because we want the country
// specific formats that are missing from datetimesymbols.js. However, we
// don't want to write locale files that only have dateformat (i.e. missing
// number formats.) So we skip them.
if (!localeObj.NUMBER_FORMATS) {
console.log("Skipping locale %j: Don't have any number formats", localeID);
return null;
}
if (!localeObj.DATETIME_FORMATS) {
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
}
localeObj.id = correctedLocaleId(localeID);
var prefix =
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
'var PLURAL_CATEGORY = {' +
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
'};\n' +
'$provide.value("$locale", ';
var suffix = ');\n}]);';
localeObj = {
DATETIME_FORMATS: localeObj.DATETIME_FORMATS,
NUMBER_FORMATS: localeObj.NUMBER_FORMATS,
pluralCat: localeObj.pluralCat,
id: localeObj.id
};
var content = serializeContent(localeInfo[localeID]);
return prefix + content + suffix;
}
+102 -57
View File
@@ -1,85 +1,130 @@
#!/usr/bin/env node
'use strict';
var Q = require('q'),
var Q = require('qq'),
qfs = require('q-fs'),
converter = require('./converter.js'),
util = require('./util.js'),
closureI18nExtractor = require('./closureI18nExtractor.js'),
localeInfo = {},
localeIds = [],
currencySymbols,
goog = { provide: function() {},
require: function() {},
i18n: {currency: {}, pluralRules: {}} };
createFolder('../../src/ngLocale/').then(function() {
var promiseA = Q.defer(),
promiseB = Q.defer();
var NG_LOCALE_DIR = '../src/ngLocale/';
qfs.read(__dirname + '/../closure/currencySymbols.js', 'b').then(function(content) {
eval(content.toString());
currencySymbols = goog.i18n.currency.CurrencyInfo;
currencySymbols.__proto__ = goog.i18n.currency.CurrencyInfoTier2;
qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
//eval script in the current context so that we get access to all the symbols
eval(content.toString());
for (var propName in goog.i18n) {
var localeID = util.findLocaleId(propName, 'num');
if (localeID) {
if (!localeInfo[localeID]) {
localeInfo[localeID] = {};
localeIds.push(localeID);
}
var convertedData = converter.convertNumberData(goog.i18n[propName], currencySymbols);
localeInfo[localeID].NUMBER_FORMATS = convertedData;
}
}
function readSymbols() {
console.log("Processing currency and number symbols ...");
var numericStagePromise = qfs.read(__dirname + '/../closure/currencySymbols.js', 'b')
.then(function(content) {
var currencySymbols = closureI18nExtractor.extractCurrencySymbols(content);
return qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
closureI18nExtractor.extractNumberSymbols(content, localeInfo, currencySymbols);
});
});
console.log("Processing datetime symbols ...");
var datetimeStagePromise = qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b')
.then(function(content) {
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
return qfs.read(__dirname + '/../closure/datetimeSymbolsExt.js', 'b').then(function(content) {
closureI18nExtractor.extractDateTimeSymbols(content, localeInfo);
});
promiseA.resolve();
});
return Q.all([numericStagePromise, datetimeStagePromise]);
}
function extractPlurals() {
console.log('Extracting Plurals ...');
return qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
closureI18nExtractor.pluralExtractor(content, localeInfo);
});
}
function writeLocaleFiles() {
console.log('Final stage: Writing angular locale files to directory: %j', NG_LOCALE_DIR);
var writePromises = [];
var localeIds = Object.keys(localeInfo);
var num_files = 0;
qfs.read(__dirname + '/../closure/datetimeSymbols.js', 'b').then(function(content) {
eval(content.toString());
for (var propName in goog.i18n) {
var localeID = util.findLocaleId(propName, 'datetime');
if (localeID) {
if (!localeInfo[localeID]) {
localeInfo[localeID] = {};
localeIds.push(localeID);
}
var convertedData = converter.convertDatetimeData(goog.i18n[propName]);
localeInfo[localeID].DATETIME_FORMATS = convertedData;
}
}
promiseB.resolve();
});
return Q.join(promiseA.promise, promiseB.promise, noop);
}).then(function() {
var promise = Q.defer();
qfs.read(__dirname + '/../closure/pluralRules.js').then(function(content) {
for(var i = 0; i < localeIds.length; i++) {
//We don't need to care about country ID because the plural rules in more specific id are
//always the same as those in its language ID.
// e.g. plural rules for en_SG is the same as those for en.
goog.LOCALE = localeIds[i].match(/[^_]+/)[0];
eval(content);
var temp = goog.i18n.pluralRules.select.toString().
replace(/goog.i18n.pluralRules.Keyword/g, 'PLURAL_CATEGORY').replace(/\n/g, '');
///@@ is a crazy place holder to be replaced before writing to file
localeInfo[localeIds[i]].pluralCat = "@@" + temp + "@@";
}
promise.resolve();
});
return promise.promise;
}).then(function() {
localeIds.forEach(function(localeID) {
var content = closureI18nExtractor.outputLocale(localeInfo, localeID);
if (!content) return;
var correctedLocaleId = closureI18nExtractor.correctedLocaleId(localeID);
var filename = NG_LOCALE_DIR + 'angular-locale_' + correctedLocaleId + '.js'
writePromises.push(
qfs.write(filename, content)
.then(function () {
console.log('Wrote ' + filename);
++num_files;
}));
console.log('Writing ' + filename);
var fallBackID = localeID.match(/[A-Za-z]+/)[0],
localeObj = localeInfo[localeID],
fallBackObj = localeInfo[fallBackID];
// fallBack to language formats when country format is missing
// e.g. if NUMBER_FORMATS of en_xyz is not present, use the NUMBER_FORMATS of en instead
if (!localeObj.NUMBER_FORMATS) {
localeObj.NUMBER_FORMATS = fallBackObj.NUMBER_FORMATS;
}
if (!localeObj.DATETIME_FORMATS) {
localeObj.DATETIME_FORMATS = fallBackObj.DATETIME_FORMATS;
}
// e.g. from zh_CN to zh-CN, from en_US to en-US
var correctedLocaleId = localeID.replace(/_/g, '-').toLowerCase();
localeObj.id = correctedLocaleId;
var prefix =
'angular.module("ngLocale", [], ["$provide", function($provide) {\n' +
'var PLURAL_CATEGORY = {' +
'ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"' +
'};\n' +
'$provide.value("$locale", ';
var suffix = ');\n}]);';
var content = JSON.stringify(localeInfo[localeID]).replace(/\¤/g,'\\u00A4').
replace(/"@@|@@"/g, '');
var toWrite = prefix + content + suffix;
qfs.write(__dirname + '/../locale/' + 'angular-locale_' + correctedLocaleId + '.js', toWrite);
});
console.log('Generated %j locale files.', localeIds.length);
return Q.all(writePromises).then(function() { return num_files });
}
console.log('Generated ' + localeIds.length + ' locale files!');
}).end();
function noop() {};
/**
* Make a folder under current directory.
* @param folder {string} name of the folder to be made
*/
function createFolder(folder) {
return qfs.isDirectory(folder).then(function(isDir) {
if (!isDir) return qfs.makeDirectory(folder).then(function() {
console.log('Created directory %j', folder); });
return qfs.isDirectory(__dirname + '/' + folder).then(function(isDir) {
if (!isDir) return qfs.makeDirectory(__dirname + '/' + folder);
});
}
createFolder(NG_LOCALE_DIR)
.then(readSymbols)
.then(extractPlurals)
.then(writeLocaleFiles)
.done(function(num_files) { console.log("Wrote %j files.\nAll Done!", num_files); });
+2 -2
View File
@@ -45,8 +45,8 @@ function parsePattern(pattern) {
}
var groups = integer.split(GROUP_SEP);
p.gSize = groups[1] ? groups[1].length : 0;
p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
p.gSize = groups[1].length;
p.lgSize = (groups[2] || groups[1]).length;
if (negative) {
var trunkLen = positive.length - p.posPre.length - p.posSuf.length,
-5
View File
@@ -1,14 +1,9 @@
#!/bin/bash
set -e # Exit on error.
BASE_DIR=`dirname $0`
cd $BASE_DIR
set -x # Trace commands as they're executed.
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/currency.js > closure/currencySymbols.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbols.js > closure/datetimeSymbols.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/datetimesymbolsext.js > closure/datetimeSymbolsExt.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/numberformatsymbols.js > closure/numberSymbols.js
curl http://closure-library.googlecode.com/svn/trunk/closure/goog/i18n/pluralrules.js > closure/pluralRules.js
+4 -4
View File
@@ -2,7 +2,7 @@
#
# Script to initialize angular repo
# - install required node packages
# - install Karma
# - install Testacular
# - install git hooks
@@ -22,10 +22,10 @@ fi
echo "Installing required npm packages..."
npm install
karma=`which karma 2>&1`
testacular=`which testacular 2>&1`
if [ $? -ne 0 ]; then
echo "Installing Karma..."
npm install -g karma
echo "Installing Testacular..."
npm install -g testacular
fi
echo "Installing git hooks..."
-61
View File
@@ -1,61 +0,0 @@
var util = require('./utils.js');
var spawn = require('child_process').spawn;
module.exports = function(grunt) {
grunt.registerMultiTask('min', 'minify JS files', function(){
util.min.call(util, this.data, this.async());
});
grunt.registerTask('minall', 'minify all the JS files in parallel', function(){
var files = grunt.config('min');
files = Object.keys(files).map(function(key){ return files[key]; });
grunt.util.async.forEach(files, util.min.bind(util), this.async());
});
grunt.registerMultiTask('build', 'build JS files', function(){
util.build.call(util, this.data, this.async());
});
grunt.registerTask('buildall', 'build all the JS files in parallel', function(){
var builds = grunt.config('build');
builds = Object.keys(builds).map(function(key){ return builds[key]; });
grunt.util.async.forEach(builds, util.build.bind(util), this.async());
});
grunt.registerMultiTask('write', 'write content to a file', function(){
grunt.file.write(this.data.file, this.data.val);
grunt.log.ok('wrote to ' + this.data.file);
});
grunt.registerMultiTask('docs', 'create angular docs', function(){
var done = this.async();
var files = this.data;
var docs = spawn('node', ['docs/src/gen-docs.js']);
docs.stdout.pipe(process.stdout);
docs.stderr.pipe(process.stderr);
docs.on('exit', function(code){
if(code !== 0) grunt.fail.warn('Error creating docs');
grunt.file.expand(files).forEach(function(file){
grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false));
});
grunt.log.ok('docs created');
done();
});
});
grunt.registerMultiTask('test', 'Run the unit tests with Karma', function(){
util.startKarma.call(util, this.data, true, this.async());
});
grunt.registerMultiTask('autotest', 'Run and watch the unit tests with Karma', function(){
util.startKarma.call(util, this.data, false, this.async());
});
};
-174
View File
@@ -1,174 +0,0 @@
var fs = require('fs');
var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
module.exports = {
init: function() {
shell.exec('npm install');
},
getVersion: function(){
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
var match = package.version.match(/^([^\-]*)(-snapshot)?$/);
var semver = match[1].split('.');
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
var version = {
full: (match[1] + (match[2] ? '-' + hash : '')),
major: semver[0],
minor: semver[1],
dot: semver[2],
codename: package.codename,
cdn: package.cdnVersion
};
return version;
},
startKarma: function(config, singleRun, done){
var browsers = grunt.option('browsers');
var reporters = grunt.option('reporters');
var noColor = grunt.option('no-colors');
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
singleRun ? '--single-run=true' : '',
reporters ? '--reporters=' + reporters : '',
browsers ? '--browsers=' + browsers : '',
noColor ? '--no-colors' : ''
]);
p.stdout.pipe(process.stdout);
p.stderr.pipe(process.stderr);
p.on('exit', function(code){
if(code !== 0) grunt.fail.warn("Test(s) failed");
done();
});
},
wrap: function(src, name){
src.unshift('src/' + name + '.prefix');
src.push('src/' + name + '.suffix');
return src;
},
addStyle: function(src, styles, minify){
styles = styles.map(processCSS.bind(this)).join('\n');
src += styles;
return src;
function processCSS(file){
var css = fs.readFileSync(file).toString();
if(minify){
css = css
.replace(/\r?\n/g, '')
.replace(/\/\*.*?\*\//g, '')
.replace(/:\s+/g, ':')
.replace(/\s*\{\s*/g, '{')
.replace(/\s*\}\s*/g, '}')
.replace(/\s*\,\s*/g, ',')
.replace(/\s*\;\s*/g, ';');
}
//escape for js
css = css
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/\r?\n/g, '\\n');
return "angular.element(document).find('head').append('<style type=\"text/css\">" + css + "</style>');";
}
},
process: function(src, NG_VERSION, strict){
var processed = src
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
return processed;
},
build: function(config, fn){
var files = grunt.file.expand(config.src);
var styles = config.styles;
//concat
var src = files.map(function(filepath){
return grunt.file.read(filepath);
}).join(grunt.util.normalizelf('\n'));
//process
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
if (styles) processed = this.addStyle(processed, styles.css, styles.minify);
//write
grunt.file.write(config.dest, processed);
grunt.log.ok('File ' + config.dest + ' created.');
fn();
},
singleStrict: function(src, insert, newline){
var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';";
return src
.replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags
.replace(/(\(function\([^)]*\)\s*\{)/, useStrict); // add single strict mode flag
},
min: function(file, done) {
var minFile = file.replace(/\.js$/, '.min.js');
shell.exec(
'java ' +
this.java32flags() + ' ' +
'-jar lib/closure-compiler/compiler.jar ' +
'--compilation_level SIMPLE_OPTIMIZATIONS ' +
'--language_in ECMASCRIPT5_STRICT ' +
'--js ' + file + ' ' +
'--js_output_file ' + minFile,
function(code) {
if (code !== 0) grunt.fail.warn('Error minifying ' + file);
grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n'));
grunt.log.ok(file + ' minified into ' + minFile);
done();
}.bind(this));
},
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
java32flags: function(){
if (process.platform === "win32") return '';
if (shell.exec('java -version -d32 2>&1', {silent: true}).code !== 0) return '';
return ' -d32 -client';
},
//csp connect middleware
csp: function(){
return function(req, res, next){
res.setHeader("X-WebKit-CSP", "default-src 'self';");
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
next();
};
},
//rewrite connect middleware
rewrite: function(){
return function(req, res, next){
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
match;
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
console.log('rewriting', req.url);
req.url = req.url.replace(match[0], '/index.html');
}
next();
};
}
};
+3704 -3668
View File
File diff suppressed because it is too large Load Diff
+2 -4
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
1.7.2
1.8.2

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+273
View File
@@ -0,0 +1,273 @@
var sys = require('sys'),
http = require('http'),
fs = require('fs'),
url = require('url'),
events = require('events');
var DEFAULT_PORT = 8000;
function main(argv) {
new HttpServer({
'GET': createServlet(StaticServlet),
'HEAD': createServlet(StaticServlet)
}).start(Number(argv[2]) || DEFAULT_PORT);
}
function escapeHtml(value) {
return value.toString().
replace('<', '&lt;').
replace('>', '&gt').
replace('"', '&quot;');
}
function createServlet(Class) {
var servlet = new Class();
return servlet.handleRequest.bind(servlet);
}
/**
* An Http server implementation that uses a map of methods to decide
* action routing.
*
* @param {Object} Map of method => Handler function
*/
function HttpServer(handlers) {
this.handlers = handlers;
this.server = http.createServer(this.handleRequest_.bind(this));
}
HttpServer.prototype.start = function(port) {
this.port = port;
this.server.listen(port);
sys.puts('Http Server running at http://127.0.0.1:' + port + '/');
};
HttpServer.prototype.parseUrl_ = function(urlString) {
var parsed = url.parse(urlString);
parsed.pathname = url.resolve('/', parsed.pathname);
return url.parse(url.format(parsed), true);
};
HttpServer.prototype.handleRequest_ = function(req, res) {
var logEntry = req.method + ' ' + req.url;
if (req.headers['user-agent']) {
logEntry += ' ' + req.headers['user-agent'];
}
sys.puts(logEntry);
req.url = this.parseUrl_(req.url);
var handler = this.handlers[req.method];
if (!handler) {
res.writeHead(501);
res.end();
} else {
handler.call(this, req, res);
}
};
/**
* Handles static content.
*/
function StaticServlet() {}
StaticServlet.MimeMap = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
'manifest': 'text/cache-manifest',
// it should be application/font-woff
// but only this silences chrome warnings
'woff': 'font/opentype'
};
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
return String.fromCharCode(parseInt(hex, 16));
});
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
// favicon rewriting
if (path === './favicon.ico')
return self.sendFile_(req, res, './lib/nodeserver/favicon.ico');
// docs rewriting
var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/,
IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/,
match;
if (!IGNORED.test(path) && (match = path.match(REWRITE))) {
path = path.replace(match[0], '/index.html');
sys.puts('Rewrite to ' + path);
}
// end of docs rewriting
fs.stat(path, function(err, stat) {
if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return fs.stat(path + 'index.html', function(err, stat) {
// send index.html if exists
if (!err)
return self.sendFile_(req, res, path + 'index.html');
// list files otherwise
return self.sendDirectory_(req, res, path);
});
return self.sendFile_(req, res, path);
});
};
StaticServlet.prototype.sendError_ = function(req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>Internal Server Error</title>\n');
res.write('<h1>Internal Server Error</h1>');
res.write('<pre>' + escapeHtml(sys.inspect(error)) + '</pre>');
sys.puts('500 Internal Server Error');
sys.puts(sys.inspect(error));
};
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write(
'<p>The requested URL ' +
escapeHtml(path) +
' was not found on this server.</p>'
);
res.end();
sys.puts('404 Not Found: ' + path);
};
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(403, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>403 Forbidden</title>\n');
res.write('<h1>Forbidden</h1>');
res.write(
'<p>You do not have permission to access ' +
escapeHtml(path) + ' on this server.</p>'
);
res.end();
sys.puts('403 Forbidden: ' + path);
};
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
res.writeHead(301, {
'Content-Type': 'text/html',
'Location': redirectUrl
});
res.write('<!doctype html>\n');
res.write('<title>301 Moved Permanently</title>\n');
res.write('<h1>Moved Permanently</h1>');
res.write(
'<p>The document has moved <a href="' +
redirectUrl +
'">here</a>.</p>'
);
res.end();
sys.puts('401 Moved Permanently: ' + redirectUrl);
};
StaticServlet.prototype.sendFile_ = function(req, res, path) {
var self = this;
var file = fs.createReadStream(path);
res.writeHead(200, {
// CSP headers, uncomment to enable CSP
//"X-WebKit-CSP": "default-src 'self';",
//"X-Content-Security-Policy": "default-src 'self'",
'Content-Type': StaticServlet.
MimeMap[path.split('.').pop()] || 'text/plain'
});
if (req.method === 'HEAD') {
res.end();
} else {
file.on('data', res.write.bind(res));
file.on('close', function() {
res.end();
});
file.on('error', function(error) {
self.sendError_(req, res, error);
});
}
};
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
var self = this;
if (path.match(/[^\/]$/)) {
req.url.pathname += '/';
var redirectUrl = url.format(url.parse(url.format(req.url)));
return self.sendRedirect_(req, res, redirectUrl);
}
fs.readdir(path, function(err, files) {
if (err)
return self.sendError_(req, res, error);
if (!files.length)
return self.writeDirectoryIndex_(req, res, path, []);
var remaining = files.length;
files.forEach(function(fileName, index) {
fs.stat(path + '/' + fileName, function(err, stat) {
if (err)
return self.sendError_(req, res, err);
if (stat.isDirectory()) {
files[index] = fileName + '/';
}
if (!(--remaining))
return self.writeDirectoryIndex_(req, res, path, files);
});
});
});
};
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
path = path.substring(1);
res.writeHead(200, {
'Content-Type': 'text/html'
});
if (req.method === 'HEAD') {
res.end();
return;
}
res.write('<!doctype html>\n');
res.write('<title>' + escapeHtml(path) + '</title>\n');
res.write('<style>\n');
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
res.write('</style>\n');
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
res.write('<ol>');
files.forEach(function(fileName) {
if (fileName.charAt(0) !== '.') {
res.write('<li><a href="' +
escapeHtml(fileName) + '">' +
escapeHtml(fileName) + '</a></li>');
}
});
res.write('</ol>');
res.end();
};
// Must be last,
main(process.argv);
+7
View File
@@ -0,0 +1,7 @@
var fs = require('fs');
var vm = require('vm');
var filename = __dirname + '/showdown-0.9.js';
var src = fs.readFileSync(filename);
var Showdown = vm.runInThisContext(src + '\nShowdown;', filename);
exports.Showdown = Showdown;
File diff suppressed because it is too large Load Diff
Executable
+1
View File
@@ -0,0 +1 @@
node lib/nodeserver/server.js $1
+7 -24
View File
@@ -1,27 +1,10 @@
{
"name": "AngularJS",
"version": "1.0.7",
"cdnVersion": "1.0.6",
"codename": "monochromatic-rainbow",
"devDependencies": {
"grunt": "0.4.0",
"grunt-contrib-clean": "0.4.0",
"grunt-contrib-compress": "0.4.1",
"grunt-contrib-connect": "0.1.2",
"grunt-contrib-copy": "0.4.1",
"jasmine-node": "1.2.3",
"q": "~0.9.2",
"q-fs": "0.1.36",
"qq": "0.3.5",
"shelljs": "0.1.2",
"karma": "0.8.4",
"yaml-js": "0.0.5",
"showdown": "0.3.1"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
}
]
"version": "0.0.0",
"dependencies" : {
"testacular" : "canary",
"jasmine-node" : "*",
"q-fs" : "*",
"qq" : "*"
}
}
+36 -100
View File
@@ -28,12 +28,12 @@ var uppercase = function(string){return isString(string) ? string.toUpperCase()
var manualLowercase = function(s) {
return isString(s)
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);})
: s;
};
var manualUppercase = function(s) {
return isString(s)
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);})
: s;
};
@@ -46,8 +46,11 @@ if ('i' !== 'I'.toLowerCase()) {
uppercase = manualUppercase;
}
function fromCharCode(code) {return String.fromCharCode(code);}
var /** holds major version number for IE or NaN for real browsers */
var Error = window.Error,
/** holds major version number for IE or NaN for real browsers */
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
@@ -61,29 +64,6 @@ var /** holds major version number for IE or NaN for real browsers */
nodeName_,
uid = ['0', '0', '0'];
/**
* @private
* @param {*} obj
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
*/
function isArrayLike(obj) {
if (!obj || (typeof obj.length !== 'number')) return false;
// We have on object which has length property. Should we treat it as array?
if (typeof obj.hasOwnProperty != 'function' &&
typeof obj.constructor != 'function') {
// This is here for IE8: it is a bogus object treat it as array;
return true;
} else {
return obj instanceof JQLite || // JQLite
(jQuery && obj instanceof jQuery) || // jQuery
toString.call(obj) !== '[object Object]' || // some browser native object
typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
}
}
/**
* @ngdoc function
* @name angular.forEach
@@ -122,7 +102,7 @@ function forEach(obj, iterator, context) {
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context);
} else if (isArrayLike(obj)) {
} else if (isObject(obj) && isNumber(obj.length)) {
for (key = 0; key < obj.length; key++)
iterator.call(context, obj[key], key);
} else {
@@ -167,7 +147,7 @@ function reverseParams(iteratorFn) {
/**
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
* the number string gets longer over time, and it can also overflow, where as the nextId
* the number string gets longer over time, and it can also overflow, where as the the nextId
* will grow much slower, it is a string, and it will never overflow.
*
* @returns an unique alpha-numeric string
@@ -194,21 +174,6 @@ function nextUid() {
return uid.join('');
}
/**
* Set or clear the hashkey for an object.
* @param obj object
* @param h the hashkey (!truthy to delete the hashkey)
*/
function setHashKey(obj, h) {
if (h) {
obj.$$hashKey = h;
}
else {
delete obj.$$hashKey;
}
}
/**
* @ngdoc function
* @name angular.extend
@@ -220,10 +185,8 @@ function setHashKey(obj, h) {
*
* @param {Object} dst Destination object.
* @param {...Object} src Source object(s).
* @returns {Object} Reference to `dst`.
*/
function extend(dst) {
var h = dst.$$hashKey;
forEach(arguments, function(obj){
if (obj !== dst) {
forEach(obj, function(value, key){
@@ -231,8 +194,6 @@ function extend(dst) {
});
}
});
setHashKey(dst,h);
return dst;
}
@@ -582,19 +543,19 @@ function copy(source, destination){
} else {
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
if (isArray(source)) {
destination.length = 0;
while(destination.length) {
destination.pop();
}
for ( var i = 0; i < source.length; i++) {
destination.push(copy(source[i]));
}
} else {
var h = destination.$$hashKey;
forEach(destination, function(value, key){
delete destination[key];
});
for ( var key in source) {
destination[key] = copy(source[key]);
}
setHashKey(destination,h);
}
}
return destination;
@@ -634,7 +595,7 @@ function shallowCopy(src, dst) {
* During a property comparision, properties of `function` type and properties with names
* that begin with `$` are ignored.
*
* Scope and DOMWindow objects are being compared only by identify (`===`).
* Scope and DOMWindow objects are being compared only be identify (`===`).
*
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
@@ -694,7 +655,7 @@ function sliceArgs(args, startIndex) {
*
* @description
* Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
* `fn`). You can supply optional `args` that are prebound to the function. This feature is also
* `fn`). You can supply optional `args` that are are prebound to the function. This feature is also
* known as [function currying](http://en.wikipedia.org/wiki/Currying).
*
* @param {Object} self Context which `fn` should be evaluated in.
@@ -795,18 +756,9 @@ function startingTag(element) {
// are not allowed to have children. So we just ignore it.
element.html('');
} catch(e) {}
// As Per DOM Standards
var TEXT_NODE = 3;
var elemHtml = jqLite('<div>').append(element).html();
try {
return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
} catch(e) {
return lowercase(elemHtml);
}
return jqLite('<div>').append(element).html().
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
}
@@ -873,7 +825,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
replace((pctEncodeSpaces ? null : /%20/g), '+');
}
@@ -887,10 +839,10 @@ function encodeUriQuery(val, pctEncodeSpaces) {
*
* @description
*
* Use this directive to auto-bootstrap an application. Only
* Use this directive to auto-bootstrap on application. Only
* one directive can be used per HTML document. The directive
* designates the root of the application and is typically placed
* at the root of the page.
* ot the root of the page.
*
* In the example below if the `ngApp` directive would not be placed
* on the `html` element then the document would not be compiled
@@ -962,38 +914,22 @@ function angularInit(element, bootstrap) {
* @returns {AUTO.$injector} Returns the newly created injector for this app.
*/
function bootstrap(element, modules) {
var resumeBootstrapInternal = function() {
element = jqLite(element);
modules = modules || [];
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);
modules.unshift('ng');
var injector = createInjector(modules);
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function(scope, element, compile, injector) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
};
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
return resumeBootstrapInternal();
}
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
resumeBootstrapInternal();
};
element = jqLite(element);
modules = modules || [];
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);
modules.unshift('ng');
var injector = createInjector(modules);
injector.invoke(
['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
}
var SNAKE_CASE_REGEXP = /[A-Z]/g;
@@ -1026,7 +962,7 @@ function bindJQuery() {
}
/**
* throw error if the argument is falsy.
* throw error of the argument is falsy.
*/
function assertArg(arg, name, reason) {
if (!arg) {
+2 -2
View File
@@ -14,8 +14,8 @@
* - `codeName` `{string}` Code name of the release, such as "jiggling-armfat".
*/
var version = {
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by grunt's
major: "NG_VERSION_MAJOR", // package task
full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by rake's
major: "NG_VERSION_MAJOR", // compile task
minor: "NG_VERSION_MINOR",
dot: "NG_VERSION_DOT",
codeName: '"NG_VERSION_CODENAME"'
+1 -1
View File
@@ -222,6 +222,6 @@
function isActuallyNaN(val) {
return (typeof val === 'number') && isNaN(val);
}
}
};
})(window, document);
+18 -19
View File
@@ -62,7 +62,7 @@ function annotate(fn) {
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
assertArgFn(fn[last], 'fn')
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
@@ -96,19 +96,19 @@ function annotate(fn) {
* # Injection Function Annotation
*
* JavaScript does not have annotations, and annotations are needed for dependency injection. The
* following are all valid ways of annotating function with injection arguments and are equivalent.
* following ways are all valid way of annotating function with injection arguments and are equivalent.
*
* <pre>
* // inferred (only works if code not minified/obfuscated)
* $injector.invoke(function(serviceA){});
* $inject.invoke(function(serviceA){});
*
* // annotated
* function explicit(serviceA) {};
* explicit.$inject = ['serviceA'];
* $injector.invoke(explicit);
* $inject.invoke(explicit);
*
* // inline
* $injector.invoke(['serviceA', function(serviceA){}]);
* $inject.invoke(['serviceA', function(serviceA){}]);
* </pre>
*
* ## Inference
@@ -192,7 +192,7 @@ function annotate(fn) {
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
* are supported.
*
* # The `$inject` property
* # The `$injector` property
*
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
* services to be injected into the function.
@@ -225,7 +225,7 @@ function annotate(fn) {
* // ...
* };
* tmpFn.$inject = ['$compile', '$rootScope'];
* injector.invoke(tmpFn);
* injector.invoke(tempFn);
*
* // To better support inline function the inline annotation is supported
* injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
@@ -254,7 +254,7 @@ function annotate(fn) {
* @description
*
* Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance.
* The providers share the same name as the instance they create with `Provider` suffixed to them.
* The providers share the same name as the instance they create with the `Provider` suffixed to them.
*
* A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of
* a service. The Provider can have additional methods which would allow for configuration of the provider.
@@ -278,7 +278,7 @@ function annotate(fn) {
*
* beforeEach(module(function($provide) {
* $provide.provider('greet', GreetProvider);
* }));
* });
*
* it('should greet', inject(function(greet) {
* expect(greet('angular')).toEqual('Hello angular!');
@@ -291,7 +291,9 @@ function annotate(fn) {
* inject(function(greet) {
* expect(greet('angular')).toEqual('Ahoj angular!');
* });
* });
* )};
*
* });
* </pre>
*/
@@ -385,7 +387,7 @@ function annotate(fn) {
*
* @param {string} name The name of the service to decorate.
* @param {function()} decorator This function will be invoked when the service needs to be
* instantiated. The function is called using the {@link AUTO.$injector#invoke
* instanciated. The function is called using the {@link AUTO.$injector#invoke
* injector.invoke} method and is therefore fully injectable. Local injection arguments:
*
* * `$delegate` - The original service instance, which can be monkey patched, configured,
@@ -408,9 +410,10 @@ function createInjector(modulesToLoad) {
decorator: decorator
}
},
providerInjector = createInternalInjector(providerCache, function() {
throw Error("Unknown provider: " + path.join(' <- '));
}),
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function() {
throw Error("Unknown provider: " + path.join(' <- '));
})),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(servicename) {
@@ -487,9 +490,7 @@ function createInjector(modulesToLoad) {
try {
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
var invokeArgs = invokeQueue[i],
provider = invokeArgs[0] == '$injector'
? providerInjector
: providerInjector.get(invokeArgs[0]);
provider = providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
@@ -585,8 +586,6 @@ function createInjector(modulesToLoad) {
var Constructor = function() {},
instance, returnedValue;
// Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
returnedValue = invoke(Type, instance, locals);
+4 -4
View File
@@ -4,10 +4,10 @@ var directive = {};
var service = { value: {} };
var DEPENDENCIES = {
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
'angular.js': 'http://code.angularjs.org/' + angular.version.full + 'angular.min.js',
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-resource.min.js',
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-sanitize.min.js',
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-cookies.min.js'
};
-8
View File
@@ -143,14 +143,6 @@ directive.tabbable = function() {
};
};
directive.table = function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
element[0].className = 'table table-bordered table-striped code-table';
}
};
};
directive.tabPane = function() {
return {
+24 -43
View File
@@ -32,18 +32,18 @@
* - [after()](http://api.jquery.com/after/)
* - [append()](http://api.jquery.com/append/)
* - [attr()](http://api.jquery.com/attr/)
* - [bind()](http://api.jquery.com/bind/) - Does not support namespaces
* - [children()](http://api.jquery.com/children/) - Does not support selectors
* - [bind()](http://api.jquery.com/bind/)
* - [children()](http://api.jquery.com/children/)
* - [clone()](http://api.jquery.com/clone/)
* - [contents()](http://api.jquery.com/contents/)
* - [css()](http://api.jquery.com/css/)
* - [data()](http://api.jquery.com/data/)
* - [eq()](http://api.jquery.com/eq/)
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name.
* - [hasClass()](http://api.jquery.com/hasClass/)
* - [html()](http://api.jquery.com/html/)
* - [next()](http://api.jquery.com/next/) - Does not support selectors
* - [parent()](http://api.jquery.com/parent/) - Does not support selectors
* - [next()](http://api.jquery.com/next/)
* - [parent()](http://api.jquery.com/parent/)
* - [prepend()](http://api.jquery.com/prepend/)
* - [prop()](http://api.jquery.com/prop/)
* - [ready()](http://api.jquery.com/ready/)
@@ -55,7 +55,7 @@
* - [text()](http://api.jquery.com/text/)
* - [toggleClass()](http://api.jquery.com/toggleClass/)
* - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers.
* - [unbind()](http://api.jquery.com/unbind/) - Does not support namespaces
* - [unbind()](http://api.jquery.com/unbind/)
* - [val()](http://api.jquery.com/val/)
* - [wrap()](http://api.jquery.com/wrap/)
*
@@ -353,11 +353,11 @@ var JQLitePrototype = JQLite.prototype = {
// value on get.
//////////////////////////////////////////
var BOOLEAN_ATTR = {};
forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) {
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
BOOLEAN_ATTR[lowercase(value)] = value;
});
var BOOLEAN_ELEMENTS = {};
forEach('input,select,option,textarea,button,form'.split(','), function(value) {
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
BOOLEAN_ELEMENTS[uppercase(value)] = true;
});
@@ -602,43 +602,23 @@ forEach({
if (!eventFns) {
if (type == 'mouseenter' || type == 'mouseleave') {
var contains = document.body.contains || document.body.compareDocumentPosition ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
adown.contains( bup ) :
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
));
} :
function( a, b ) {
if ( b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
}
return false;
};
var counter = 0;
events[type] = [];
// Refer to jQuery's implementation of mouseenter & mouseleave
// Read about mouseenter and mouseleave:
// http://www.quirksmode.org/js/events_mouse.html#link8
var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}
bindFn(element, eventmap[type], function(event) {
var ret, target = this, related = event.relatedTarget;
// For mousenter/leave call the handler if related is outside the target.
// NB: No relatedTarget if the mouse left/entered the browser window
if ( !related || (related !== target && !contains(target, related)) ){
handle(event, type);
}
events.mouseenter = [];
events.mouseleave = [];
bindFn(element, 'mouseover', function(event) {
counter++;
if (counter == 1) {
handle(event, 'mouseenter');
}
});
bindFn(element, 'mouseout', function(event) {
counter --;
if (counter == 0) {
handle(event, 'mouseleave');
}
});
} else {
addEventListenerFn(element, type, handle);
events[type] = [];
@@ -679,8 +659,9 @@ forEach({
append: function(element, node) {
forEach(new JQLite(node), function(child){
if (element.nodeType === 1)
if (element.nodeType === 1 || element.nodeType === 11) {
element.appendChild(child);
}
});
},
+2 -8
View File
@@ -237,7 +237,7 @@ function Browser(window, document, $log, $sniffer) {
*/
self.baseHref = function() {
var href = baseElement.attr('href');
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
};
//////////////////////////////////////////////////////////////
@@ -297,13 +297,7 @@ function Browser(window, document, $log, $sniffer) {
cookie = cookieArray[i];
index = cookie.indexOf('=');
if (index > 0) { //ignore nameless cookies
var name = unescape(cookie.substring(0, index));
// the first value that is seen for a cookie is the most
// specific one. values for the same cookie name that
// follow are for less specific paths.
if (lastCookies[name] === undefined) {
lastCookies[name] = unescape(cookie.substring(index + 1));
}
lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1));
}
}
}
+3 -1
View File
@@ -14,7 +14,7 @@
* @returns {object} Newly created cache object with the following set of methods:
*
* - `{object}` `info()` Returns id, size, and options of cache.
* - `{void}` `put({string} key, {*} value)` Puts a new key-value pair into the cache.
* - `{{*}}` `put({string} key, {*} value)` Puts a new key-value pair into the cache and returns it.
* - `{{*}}` `get({string} key)` Returns cached value for `key` or undefined for cache miss.
* - `{void}` `remove({string} key)` Removes a key-value pair from the cache.
* - `{void}` `removeAll()` Removes all cached values.
@@ -53,6 +53,8 @@ function $CacheFactoryProvider() {
if (size > capacity) {
this.remove(staleEnd.key);
}
return value;
},
+21 -77
View File
@@ -155,8 +155,7 @@ function $CompileProvider($provide) {
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto|file):/;
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
/**
@@ -210,41 +209,11 @@ function $CompileProvider($provide) {
};
/**
* @ngdoc function
* @name ng.$compileProvider#urlSanitizationWhitelist
* @methodOf ng.$compileProvider
* @function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
* absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
* expression. If a match is found the original url is written into the dom. Otherwise the
* absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this.urlSanitizationWhitelist = function(regexp) {
if (isDefined(regexp)) {
urlSanitizationWhitelist = regexp;
return this;
}
return urlSanitizationWhitelist;
};
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
'$controller', '$rootScope', '$document',
'$controller', '$rootScope',
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
$controller, $rootScope, $document) {
$controller, $rootScope) {
var Attributes = function(element, attr) {
this.$$element = element;
@@ -266,8 +235,7 @@ function $CompileProvider($provide) {
*/
$set: function(key, value, writeAttr, attrName) {
var booleanKey = getBooleanAttrName(this.$$element[0], key),
$$observers = this.$$observers,
normalizedVal;
$$observers = this.$$observers;
if (booleanKey) {
this.$$element.prop(key, value);
@@ -286,19 +254,6 @@ function $CompileProvider($provide) {
}
}
// sanitize a[href] values
if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
urlSanitizationNode.setAttribute('href', value);
// href property always returns normalized absolute url, so we can match against that
normalizedVal = urlSanitizationNode.href;
if (!normalizedVal.match(urlSanitizationWhitelist)) {
this[key] = value = 'unsafe:' + normalizedVal;
}
}
if (writeAttr !== false) {
if (value === null || value === undefined) {
this.$$element.removeAttr(attrName);
@@ -342,8 +297,7 @@ function $CompileProvider($provide) {
}
};
var urlSanitizationNode = $document[0].createElement('a'),
startSymbol = $interpolate.startSymbol(),
var startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
? identity
@@ -358,7 +312,7 @@ function $CompileProvider($provide) {
function compile($compileNodes, transcludeFn, maxPriority) {
if (!($compileNodes instanceof jqLite)) {
// jquery always rewraps, whereas we need to preserve the original selector so that we can modify it.
// jquery always rewraps, where as we need to preserve the original selector so that we can modify it.
$compileNodes = jqLite($compileNodes);
}
// We can not compile top level text elements since text nodes can be merged and we will
@@ -376,14 +330,7 @@ function $CompileProvider($provide) {
var $linkNode = cloneConnectFn
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
: $compileNodes;
// Attach scope only to non-text nodes.
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
var node = $linkNode[i];
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
$linkNode.eq(i).data('$scope', scope);
}
}
$linkNode.data('$scope', scope);
safeAddClass($linkNode, 'ng-scope');
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
@@ -410,7 +357,7 @@ function $CompileProvider($provide) {
* functions return values - the linking functions - are combined into a composite linking
* function, which is the a linking function for the node.
*
* @param {NodeList} nodeList an array of nodes or NodeList to compile
* @param {NodeList} nodeList an array of nodes to compile
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the
@@ -433,7 +380,7 @@ function $CompileProvider($provide) {
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
: null;
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes || !nodeList[i].childNodes.length)
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
? null
: compileNodes(nodeList[i].childNodes,
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
@@ -473,7 +420,6 @@ function $CompileProvider($provide) {
(function(transcludeFn) {
return function(cloneFn) {
var transcludeScope = scope.$new();
transcludeScope.$$transcluded = true;
return transcludeFn(transcludeScope, cloneFn).
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
@@ -569,9 +515,9 @@ function $CompileProvider($provide) {
/**
* Once the directives have been collected, their compile functions are executed. This method
* Once the directives have been collected their compile functions is executed. This method
* is responsible for inlining directive templates as well as terminating the application
* of the directives if the terminal directive has been reached.
* of the directives if the terminal directive has been reached..
*
* @param {Array} directives Array of collected directives to execute their compile function.
* this needs to be pre-sorted by priority order.
@@ -579,11 +525,11 @@ function $CompileProvider($provide) {
* @param {Object} templateAttrs The shared attribute function
* @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the
* scope argument is auto-generated to the new child of the transcluded parent scope.
* @param {JQLite} jqCollection If we are working on the root of the compile tree then this
* argument has the root jqLite array so that we can replace nodes on it.
* @param {DOMElement} $rootElement If we are working on the root of the compile tree then this
* argument has the root jqLite array so that we can replace widgets on it.
* @returns linkFn
*/
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection) {
function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) {
var terminalPriority = -Number.MAX_VALUE,
preLinkFns = [],
postLinkFns = [],
@@ -637,7 +583,7 @@ function $CompileProvider($provide) {
$compileNode = templateAttrs.$$element =
jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' '));
compileNode = $compileNode[0];
replaceWith(jqCollection, jqLite($template[0]), compileNode);
replaceWith($rootElement, jqLite($template[0]), compileNode);
childTranscludeFn = compile($template, transcludeFn, terminalPriority);
} else {
$template = jqLite(JQLiteClone(compileNode)).contents();
@@ -661,7 +607,7 @@ function $CompileProvider($provide) {
throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue);
}
replaceWith(jqCollection, $compileNode, compileNode);
replaceWith($rootElement, $compileNode, compileNode);
var newTemplateAttrs = {$attr: {}};
@@ -689,7 +635,7 @@ function $CompileProvider($provide) {
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i),
nodeLinkFn, $compileNode, templateAttrs, jqCollection, directive.replace,
nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace,
childTranscludeFn);
ii = directives.length;
} else if (directive.compile) {
@@ -779,8 +725,6 @@ function $CompileProvider($provide) {
lastValue,
parentGet, parentSet;
scope.$$isolateBindings[scopeName] = mode + attrName;
switch (mode) {
case '@': {
@@ -822,7 +766,7 @@ function $CompileProvider($provide) {
parentGet = $parse(attrs[attrName]);
scope[scopeName] = function(locals) {
return parentGet(parentScope, locals);
};
}
break;
}
@@ -991,8 +935,8 @@ function $CompileProvider($provide) {
}
directives.unshift(derivedSyncDirective);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
while(linkQueue.length) {
@@ -1071,10 +1015,10 @@ function $CompileProvider($provide) {
function addAttrInterpolateDirective(node, directives, value, name) {
var interpolateFn = $interpolate(value, true);
// no interpolation found -> ignore
if (!interpolateFn) return;
directives.push({
priority: 100,
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
+1 -1
View File
@@ -52,7 +52,7 @@ function $ControllerProvider() {
* @description
* `$controller` service is responsible for instantiating controllers.
*
* It's just a simple call to {@link AUTO.$injector $injector}, but extracted into
* It's just simple call to {@link AUTO.$injector $injector}, but extracted into
* a service, so that one can override this service with {@link https://gist.github.com/1649788
* BC version}.
*/
+5 -15
View File
@@ -11,25 +11,15 @@
*
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
* without changing the location or causing page reloads, e.g.:
* `<a href="" ng-click="model.$save()">Save</a>`
* <a href="" ng-click="model.$save()">Save</a>
*/
var htmlAnchorDirective = valueFn({
restrict: 'E',
compile: function(element, attr) {
if (msie <= 8) {
// turn <a href ng-click="..">link</a> into a stylable link in IE
// but only if it doesn't have name attribute, in which case it's an anchor
if (!attr.href && !attr.name) {
attr.$set('href', '');
}
// add a comment node to anchors to workaround IE bug that causes element content to be reset
// to new attribute content if attribute is updated with value containing @ and element also
// contains value with @
// see issue #1949
element.append(document.createComment('IE fix'));
// turn <a href ng-click="..">link</a> into a link in IE
// but only if it doesn't have name attribute, in which case it's an anchor
if (!attr.href) {
attr.$set('href', '');
}
return function(scope, element) {
+34 -4
View File
@@ -66,7 +66,7 @@
it('should execute ng-click but not reload when no href but name specified', function() {
element('#link-5').click();
expect(input('value').val()).toEqual('5');
expect(element('#link-5').attr('href')).toBe(undefined);
expect(element('#link-5').attr('href')).toBe('');
});
it('should only change url when only ng-href', function() {
@@ -272,6 +272,37 @@
* @param {string} expression Angular expression that will be evaluated.
*/
/**
* @ngdoc directive
* @name ng.directive:ngOpen
* @restrict A
*
* @description
* The HTML specs do not require browsers to preserve the special attributes such as open.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce the `ngOpen` directive.
*
* @example
<doc:example>
<doc:source>
Check me check multiple: <input type="checkbox" ng-model="open"><br/>
<details id="details" ng-open="open">
<summary>Show/Hide me</summary>
</details>
</doc:source>
<doc:scenario>
it('should toggle open', function() {
expect(element('#details').prop('open')).toBeFalsy();
input('open').check();
expect(element('#details').prop('open')).toBeTruthy();
});
</doc:scenario>
</doc:example>
*
* @element DETAILS
* @param {string} expression Angular expression that will be evaluated.
*/
var ngAttributeAliasDirectives = {};
@@ -309,9 +340,8 @@ forEach(['src', 'href'], function(attrName) {
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
// to set the property as well to achieve the desired effect.
// we use attr[attrName] value since $set can sanitize the url.
if (msie) element.prop(attrName, attr[attrName]);
// to set the property as well to achieve the desired effect
if (msie) element.prop(attrName, value);
});
}
};
+31 -2
View File
@@ -5,7 +5,8 @@ var nullFormCtrl = {
$addControl: noop,
$removeControl: noop,
$setValidity: noop,
$setDirty: noop
$setDirty: noop,
$setPristine: noop
};
/**
@@ -37,7 +38,8 @@ function FormController(element, attrs) {
var form = this,
parentForm = element.parent().controller('form') || nullFormCtrl,
invalidCount = 0, // used to easily determine if we are valid
errors = form.$error = {};
errors = form.$error = {},
controls = [];
// init state
form.$name = attrs.name;
@@ -61,6 +63,8 @@ function FormController(element, attrs) {
}
form.$addControl = function(control) {
controls.push(control);
if (control.$name && !form.hasOwnProperty(control.$name)) {
form[control.$name] = control;
}
@@ -73,6 +77,8 @@ function FormController(element, attrs) {
forEach(errors, function(queue, validationToken) {
form.$setValidity(validationToken, true, control);
});
arrayRemove(controls, control);
};
form.$setValidity = function(validationToken, isValid, control) {
@@ -120,6 +126,29 @@ function FormController(element, attrs) {
parentForm.$setDirty();
};
/**
* @ngdoc function
* @name ng.directive:form.FormController#$setPristine
* @methodOf ng.directive:form.FormController
*
* @description
* Sets the form to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the form to its pristine
* state (ng-pristine class). This method will also propagate to all the controls contained
* in this form.
*
* Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
* saving or resetting it.
*/
form.$setPristine = function () {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
forEach(controls, function(control) {
control.$setPristine();
});
};
}
+44 -22
View File
@@ -28,6 +28,8 @@ var inputType = {
* patterns defined as scope expressions.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the
* input.
*
* @example
<doc:example>
@@ -35,12 +37,12 @@ var inputType = {
<script>
function Ctrl($scope) {
$scope.text = 'guest';
$scope.word = /^\w*$/;
$scope.word = /^\s*\w*\s*$/;
}
</script>
<form name="myForm" ng-controller="Ctrl">
Single word: <input type="text" name="input" ng-model="text"
ng-pattern="word" required>
ng-pattern="word" required ng-trim="false">
<span class="error" ng-show="myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.pattern">
@@ -69,6 +71,12 @@ var inputType = {
input('text').enter('hello world');
expect(binding('myForm.input.$valid')).toEqual('false');
});
it('should not be trimmed', function() {
input('text').enter('untrimmed ');
expect(binding('text')).toEqual('untrimmed ');
expect(binding('myForm.input.$valid')).toEqual('true');
});
</doc:scenario>
</doc:example>
*/
@@ -85,8 +93,8 @@ var inputType = {
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
* @param {string=} min Sets the `min` validation error key if the value entered is less then `min`.
* @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`.
* @param {string=} required Sets `required` validation error key if the value is not entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -382,7 +390,14 @@ function isEmpty(value) {
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
var listener = function() {
var value = trim(element.val());
var value = element.val();
// By default we will trim the value
// If the attribute ng-trim exists we will avoid trimming
// e.g. <input ng-model="foo" ng-trim="false">
if (toBoolean(attr.ngTrim || 'T')) {
value = trim(value);
}
if (ctrl.$viewValue !== value) {
scope.$apply(function() {
@@ -398,15 +413,6 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
} else {
var timeout;
var deferListener = function() {
if (!timeout) {
timeout = $browser.defer(function() {
listener();
timeout = null;
});
}
};
element.bind('keydown', function(event) {
var key = event.keyCode;
@@ -414,16 +420,16 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// command modifiers arrows
if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
deferListener();
if (!timeout) {
timeout = $browser.defer(function() {
listener();
timeout = null;
});
}
});
// if user paste into input using mouse, we need "change" event to catch it
element.bind('change', listener);
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
if ($sniffer.hasEvent('paste')) {
element.bind('paste cut', deferListener);
}
}
@@ -722,7 +728,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
<tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
<tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
<tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
<tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
<tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br>
<tt>myForm.$valid = {{myForm.$valid}}</tt><br>
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
<tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
@@ -972,6 +978,22 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
parentForm.$setValidity(validationErrorKey, isValid, this);
};
/**
* @ngdoc function
* @name ng.directive:ngModel.NgModelController#$setPristine
* @methodOf ng.directive:ngModel.NgModelController
*
* @description
* Sets the control to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the control to its pristine
* state (ng-pristine class).
*/
this.$setPristine = function () {
this.$dirty = false;
this.$pristine = true;
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
};
/**
* @ngdoc function
@@ -985,7 +1007,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* For example {@link ng.directive:input input} or
* {@link ng.directive:select select} directives call it.
*
* It internally calls all `parsers` and if resulted value is valid, updates the model and
* It internally calls all `formatters` and if resulted value is valid, updates the model and
* calls all registered change listeners.
*
* @param {string} value Value from the view.
+1 -1
View File
@@ -12,7 +12,7 @@
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
* `{{ expression }}` which is similar but less verbose.
*
* One scenario in which the use of `ngBind` is preferred over `{{ expression }}` binding is when
* Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when
* it's desirable to put bindings into template that is momentarily displayed by the browser in its
* raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the
* bindings invisible to the user while the page is loading.
+7 -9
View File
@@ -3,7 +3,6 @@
function classDirective(name, selector) {
name = 'ngClass' + name;
return ngDirective(function(scope, element, attr) {
var oldVal = undefined;
scope.$watch(attr[name], ngClassWatchAction, true);
@@ -15,9 +14,9 @@ function classDirective(name, selector) {
if (name !== 'ngClass') {
scope.$watch('$index', function($index, old$index) {
var mod = $index & 1;
if (mod !== old$index & 1) {
if (mod === selector) {
var mod = $index % 2;
if (mod !== old$index % 2) {
if (mod == selector) {
addClass(scope.$eval(attr[name]));
} else {
removeClass(scope.$eval(attr[name]));
@@ -27,14 +26,13 @@ function classDirective(name, selector) {
}
function ngClassWatchAction(newVal) {
function ngClassWatchAction(newVal, oldVal) {
if (selector === true || scope.$index % 2 === selector) {
if (oldVal && !equals(newVal,oldVal)) {
if (oldVal && (newVal !== oldVal)) {
removeClass(oldVal);
}
addClass(newVal);
}
oldVal = copy(newVal);
}
@@ -67,7 +65,7 @@ function classDirective(name, selector) {
*
* The directive won't add duplicate classes if a particular class was already set.
*
* When the expression changes, the previously added classes are removed and only then the
* When the expression changes, the previously added classes are removed and only then the classes
* new classes are added.
*
* @element ANY
@@ -160,7 +158,7 @@ var ngClassOddDirective = classDirective('Odd', 0);
* @name ng.directive:ngClassEven
*
* @description
* The `ngClassOdd` and `ngClassEven` directives work exactly as
* The `ngClassOdd` and `ngClassEven` works exactly as
* {@link ng.directive:ngClass ngClass}, except it works in
* conjunction with `ngRepeat` and takes affect only on odd (even) rows.
*
+1 -1
View File
@@ -16,7 +16,7 @@
* `angular.min.js` files. Following is the css rule:
*
* <pre>
* [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
* [ng\:cloak], [ng-cloak], .ng-cloak {
* display: none;
* }
* </pre>
+2 -1
View File
@@ -15,7 +15,8 @@
* * Controller The `ngController` directive specifies a Controller class; the class has
* methods that typically express the business logic behind the application.
*
* Note that an alternative way to define controllers is via the {@link ng.$route $route} service.
* Note that an alternative way to define controllers is via the `{@link ng.$route}`
* service.
*
* @element ANY
* @scope
+8 -24
View File
@@ -5,32 +5,16 @@
* @name ng.directive:ngCsp
* @priority 1000
*
* @element html
* @description
* Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
*
* This is necessary when developing things like Google Chrome Extensions.
*
* CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
* For us to be compatible, we just need to implement the "getterFn" in $parse without violating
* any of these restrictions.
*
* AngularJS uses `Function(string)` generated functions as a speed optimization. By applying `ngCsp`
* it is be possible to opt into the CSP compatible mode. When this mode is on AngularJS will
* evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
* be raised.
*
* In order to use this feature put `ngCsp` directive on the root element of the application.
*
* @example
* This example shows how to apply the `ngCsp` directive to the `html` tag.
<pre>
<!doctype html>
<html ng-app ng-csp>
...
...
</html>
</pre>
* This directive should be used on the root element of the application (typically the `<html>`
* element or other element with the {@link ng.directive:ngApp ngApp}
* directive).
*
* If enabled the performance of template expression evaluator will suffer slightly, so don't enable
* this mode unless you need it.
*
* @element html
*/
var ngCspDirective = ['$sniffer', function($sniffer) {
+33 -1
View File
@@ -37,7 +37,7 @@
*/
var ngEventDirectives = {};
forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup'.split(' '),
function(name) {
var directiveName = directiveNormalize('ng-' + name);
ngEventDirectives[directiveName] = ['$parse', function($parse) {
@@ -164,6 +164,38 @@ forEach(
*/
/**
* @ngdoc directive
* @name ng.directive:ngKeydown
*
* @description
* Specify custom behavior on keydown event.
*
* @element ANY
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
*/
/**
* @ngdoc directive
* @name ng.directive:ngKeyup
*
* @description
* Specify custom behavior on keyup event.
*
* @element ANY
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
*/
/**
* @ngdoc directive
* @name ng.directive:ngSubmit
+1 -1
View File
@@ -194,7 +194,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
if (!isNaN(value)) {
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
//check it against pluralization rules in $locale service
if (!(value in whens)) value = $locale.pluralCat(value - offset);
if (!whens[value]) value = $locale.pluralCat(value - offset);
return whensExpFns[value](scope, element, true);
} else {
return '';
+3 -3
View File
@@ -96,7 +96,7 @@ var ngRepeatDirective = ngDirective({
// Same as lastOrder but it has the current state. It will become the
// lastOrder on the next iteration.
nextOrder = new HashQueueMap(),
arrayBound,
arrayLength,
childScope,
key, value, // key/value of iteration
array,
@@ -117,7 +117,7 @@ var ngRepeatDirective = ngDirective({
array = collection || [];
}
arrayBound = array.length-1;
arrayLength = array.length;
// we are not using forEach for perf reasons (trying to avoid #call)
for (index = 0, length = array.length; index < length; index++) {
@@ -154,7 +154,7 @@ var ngRepeatDirective = ngDirective({
childScope.$index = index;
childScope.$first = (index === 0);
childScope.$last = (index === arrayBound);
childScope.$last = (index === (arrayLength - 1));
childScope.$middle = !(childScope.$first || childScope.$last);
if (!last) {
+4 -7
View File
@@ -8,13 +8,11 @@
* @description
* Conditionally change the DOM structure.
*
* @usage
* <ANY ng-switch="expression">
* <ANY ng-switch-when="matchValue1">...</ANY>
* @usageContent
* <ANY ng-switch-when="matchValue1">...</ANY>
* <ANY ng-switch-when="matchValue2">...</ANY>
* ...
* <ANY ng-switch-default>...</ANY>
* </ANY>
*
* @scope
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
@@ -65,10 +63,9 @@ var NG_SWITCH = 'ng-switch';
var ngSwitchDirective = valueFn({
restrict: 'EA',
require: 'ngSwitch',
// asks for $scope to fool the BC controller module
controller: ['$scope', function ngSwitchController() {
controller: function ngSwitchController() {
this.cases = {};
}],
},
link: function(scope, element, attr, ctrl) {
var watchExpr = attr.ngSwitch || attr.on,
selectedTransclude,
+1 -1
View File
@@ -147,7 +147,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
if (current.controller) {
locals.$scope = lastScope;
controller = $controller(current.controller, locals);
element.children().data('$ngControllerController', controller);
element.contents().data('$ngControllerController', controller);
}
link(lastScope);
+10 -13
View File
@@ -27,8 +27,7 @@
* `select` model to be bound to a non-string value. This is because an option element can currently
* be bound to string values only.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string} name assignable expression to data-bind to.
* @param {string=} required The control is considered valid only if value is entered.
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
@@ -123,7 +122,7 @@
var ngOptionsDirective = valueFn({ terminal: true });
var selectDirective = ['$compile', '$parse', function($compile, $parse) {
//0000111110000000000022220000000000000000000000333300000000000000444444444444444440000000005555555555555555500000006666666666666666600000000000000077770
//00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777
var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,
nullModelCtrl = {$setViewValue: noop};
@@ -395,6 +394,10 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (multiple) {
selectedSet = new HashMap(modelValue);
} else if (modelValue === null || nullOption) {
// if we are not multiselect, and we are null then we have to add the nullOption
optionGroups[''].push({selected:modelValue === null, id:'', label:''});
selectedSet = true;
}
// We now build up the list of options we need (we merge later)
@@ -419,14 +422,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
selected: selected // determine if we should be selected
});
}
if (!multiple) {
if (nullOption || modelValue === null) {
// insert null option if we have a placeholder, or the model is null
optionGroups[''].unshift({id:'', label:'', selected:!selectedSet});
} else if (!selectedSet) {
// option could not be found, we have to insert the undefined item
optionGroups[''].unshift({id:'?', label:'', selected:true});
}
if (!multiple && !selectedSet) {
// nothing was selected, we have to insert the undefined item
optionGroups[''].unshift({id:'?', label:'', selected:true});
}
// Now we need to update the list of DOM nodes to match the optionGroups we computed above
@@ -470,8 +468,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id);
}
// lastElement.prop('selected') provided by jQuery has side-effects
if (lastElement[0].selected !== option.selected) {
if (existingOption.element.selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
}
} else {
+1 -1
View File
@@ -19,7 +19,7 @@
*
*/
function $ExceptionHandlerProvider() {
this.$get = ['$log', function($log) {
this.$get = ['$log', function($log){
return function(exception, cause) {
$log.error.apply($log, arguments);
};

Some files were not shown because too many files have changed in this diff Show More