Compare commits

..

368 Commits

Author SHA1 Message Date
Igor Minar de6d4ca1da chore(release): cut 1.1.3 radioactive-gargle release 2013-02-20 12:54:44 -08:00
Igor Minar 776dfc678b docs(changelog): add release notes for 1.0.5 and 1.1.3 2013-02-20 11:16:19 -08:00
Igor Minar 9532234bf1 fix($compile): sanitize values bound to a[href] 2013-02-20 00:06:26 -08:00
Per Rovegård 5f5d4feadb fix(ngClass): keep track of old ngClass value manually
ngClassWatchAction, when called as a $watch function, gets the wrong old
value after it has been invoked previously due to observation of the
interpolated class attribute. As a result it doesn't remove classes
properly. Keeping track of the old value manually seems to fix this.

Closes #1637
2013-02-18 20:25:43 -08:00
Pete Bacon Darwin 791804bdbf fix(compile): should not leak memory when there are top level empty text nodes
The change to prevent <span> elements being wrapped around empty text nodes caused these empty text nodes to have scopes and controllers attached, through jqLite.data() calls, which led to memory leaks and errors in IE8.
Now we exclude all but document nodes and elements from having jqLite.data() set both in the compiler and in ng-view.

Fixes: #1968 and #1876
2013-02-18 12:05:16 +00:00
Luis Ramón López 7eafbb98c6 feat(routeProvider): Add support to catch-all parameters in routes
This allows routeProvider to accept parameters that matches
substrings even when they contain slashes if they are prefixed
with an asterisk instead of a colon.
For example, routes like edit/color/:color/largecode/*largecode
will match with something like this
http://appdomain.com/edit/color/brown/largecode/code/with/slashs.
2013-02-14 21:36:59 -08:00
Pete Bacon Darwin bb8448c011 fix(compile): Initialize interpolated attributes before directive linking 2013-02-14 21:36:59 -08:00
Pete Bacon Darwin 2ed53087d7 fix(compile): Interpolate @ locals before the link function runs
Do a one-off interpolation of @ locals to ensure that the link fn receives attributes that are already interpolated.
2013-02-14 21:36:59 -08:00
Lucas Galfasó 0af172040e feat(ngSwitch): support multiple matches on ngSwitchWhen and ngSwitchDefault
Closes #1074
2013-02-14 19:55:05 -08:00
Will Moore e19b04c9ec fix($httpBackend): patch for Firefox bug w/ CORS and response headers
A workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=608735
In FF getAllResponseHeaders() returns null if the request is the result of CORS.

Tried to format the code so that when a FF patch is released and gains enough
traction it can easily be selected and deleted. Heavily inspired by jQuery's
patch for the same bug. This patch falls short of passing through custom headers
but covers all of the "simple response headers" in the spec at
http://www.w3.org/TR/cors/

This commit should get reverted once Firefox 21 gets out.

Closes #1468
2013-02-14 16:45:30 -08:00
Igor Minar 37e8b12265 fix(a): workaround IE bug affecting mailto urls
Apparently there is a really weird bug in IE6-8 that causes anchor textContent
to be reset with href content when both contain @ symbol.

Inserting a bogus comment node into all anchor elements in IE works around this
browser bug.

I'm fixing the issue via directive because that way we'll fix it for jQuery as
well.

I fixed an e2e test too because it was incorrect.

Closes #1949
2013-02-14 16:42:58 -08:00
Igor Minar 1ace5eb396 style(filter): remove ws 2013-02-14 16:38:43 -08:00
Igor Minar 3c2aee01b0 fix(*): don't use instanceof to detect arrays
this breaks when multiple javascript contexts are involved - like in node-webkit

see original PR: #1966

Closes #1966
2013-02-14 16:37:00 -08:00
Cedric Soulas 37bdcc984a docs($resource): fix bad indentation producing a code block 2013-02-14 15:47:16 -08:00
Ewen Cumming 027f20be1f docs($rootScope): rearrange event listener docs 2013-02-14 15:47:16 -08:00
deboer 9b7c1d0f7c fix(ngSwitch): make ngSwitch compatible with controller BC module
add a $scope to the ngSwitch's controller to fool the controller
BC (backwards compatibility) module used by DFA.
2013-02-14 15:36:03 -08:00
Vineet Kumar 5548328b67 docs($q): fix a few typos 2013-02-14 15:18:58 -08:00
Dylan Pyle 7c6b1e06e8 docs(guide): fix some invalid javascript in directive documentation
Use double quotes to maintain consistency with other HTML
2013-02-14 15:11:04 -08:00
Vojta Jina 288b69a314 fix($http): do not encode special characters @$:, in params
encodeURIComponent is too aggressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt
with regards to the character set (pchar) allowed in path segments so we need
this test to make sure that we don't over-encode the params and break stuff
like buzz api which uses @self.

This is has already been fixed in `$resource`. This commit fixes it in a same way
for `$http` as well.

BREAKING CHANGE: $http does follow 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.
2013-02-14 14:52:46 -08:00
Mark Nadig 2a2123441c fix($resource): params should expand array values properly
Today, calling e.g. var R = $resource('/Path/:a'); R.get({a: 'foo', bar: ['baz1', 'baz2']}); results in a query
string like "/Path/doh?bar=baz1,baz2" which is undesirable. This commit enhances resource to use
$http to encode any non-url parameters resulting in a query string like "/Path/doh?bar=baz1&bar=baz2".

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 resource service.
2013-02-14 14:52:46 -08:00
Daniel Luz 1d7a95df56 feat(scope): only evaluate constant $watch expressions once 2013-02-14 14:43:56 -08:00
Daniel Luz 1ed638582d feat($parse): added constant and literal properties
* `literal` is set to true if the expression's top-level is a JavaScript
  literal (number, string, boolean, null/undefined, array, object), even
  if it contains non-literals inside.

* `constant` is set to true if the expression is known to be made
  entirely of constant values, i.e., evaluating it will always yield the
  same result.

A consequence is that a JSON expression is guaranteed to be both literal
and constant.
2013-02-14 14:43:56 -08:00
Daniel Luz 3b14092135 docs($parse): document function argument types, fix minor typo 2013-02-14 14:43:56 -08:00
Daniel Luz ef268196b9 fix($rootScope): minor typo fixes 2013-02-14 14:43:56 -08:00
James Morrin 12ba6cec4f feat(noConflict): restore previous angular namespace reference 2013-02-14 14:43:55 -08:00
Fredrik Bonander b7e1fb0515 fix(resource): 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-02-14 14:43:55 -08:00
Shyam Seshadri 755beb2b66 fix(compiler): Allow startingTag method to handle text / comment nodes 2013-02-14 14:43:55 -08:00
Trotter Cashion 6d70ff5c8d chore(reakefile): auto install npm packages 2013-02-14 14:43:55 -08:00
Rosina Bignall ace54ff08c feat(filter): Add comparison function to filter
Add optional comparator function argument to $filter('filter')(array,
expression, comparator) such that the comparator function is used to
compare the values and predicates.  When true, defaults to equality.
When missing defaults to substring matching.
2013-02-14 14:43:55 -08:00
Kury Kruitbosch f5835963d5 fix(numberFilter): fix formatting when "0" passed as fractionSize
When checking to add decimal and trialing 0s number filter used to check
trueness of fractionSize. "0" evaluating to true causes "123" to return "123."
2013-02-14 13:15:50 -08:00
Jesse Cooke f3231b9447 docs(guide): Fix typos in concepts/model,view. 2013-02-14 13:05:25 -08:00
Igor Minar a8a3efb5f0 chore(Rakefile): parallelize the build on Travis
now that the forking issue is solved we can run regular build there

https://github.com/travis-ci/travis-ci/issues/845
2013-02-14 12:46:19 -08:00
Cedric Soulas e47f8d2b96 docs($resource): fix missing punctuation 2013-02-14 11:57:33 -08:00
Igor Minar dba6bc73e8 feat($resource): expose promise based api via $then and $resolved
Expose $then and $resolved properties on resource action return values which
allow checking if a promise has been resolved already as well as registering
listeners at any time of the resource object life-cycle.

This commit replaces unreleased commit f3bff27460
which exposed unintuitive $q api instead and didn't expose important stuff
like http headers.
2013-02-11 22:24:21 -08:00
Igor Minar c0a0781425 chore(matchers): fix hasBeenCalledOnceWith matcher
the error message was wrong and misleading
2013-02-11 22:13:15 -08:00
Igor Minar 035e0130f3 test(angular.copy): add tests for scenarios when target is missing 2013-02-11 22:11:07 -08:00
Igor Minar 9e57ce0c7a revert: refactor(angular.copy): use slice(0) to clone arrays
This reverts commit 28273b7f1ef52e46d5bc23c41bc7a1492cf23014o.

slice(0) doesn't perform deep copy of the array so its unsuitable
for our purposes.
2013-02-11 21:58:00 -08:00
Igor Minar 42a5033c56 chore(docs): improve docs parser type
previously we barfed on function type definition with optional arguments
like {function(number=)}

this fixes it

I also added a bunch of code that helps to debug incorrectly parsed docs.
2013-02-11 14:08:16 -08:00
Igor Minar 6b19e7d527 refactor(angular.copy): use array.length=0 to empty arrays 2013-02-11 14:07:49 -08:00
Igor Minar 28273b7f1e refactor(angular.copy): use slice(0) to clone arrays
slice(0) is way faster on most browsers
2013-02-11 14:07:03 -08:00
Igor Minar ec54712ff3 fix(angular.forEach): correctly iterate over objects with length prop
Should handle JQLite, jQuery, NodeList and other objects like arrays
but not other generic objects or instances of user defined types
with length property.

Closes #1840
2013-02-11 12:29:55 -08:00
radu 23dd78f8a4 docs($q): fix typos 2013-02-07 04:16:16 -08:00
Julie d46fe3c23f fix(scenario): include error messages in XML output
Fix the XML output of scenario tests so that it properly includes error
messages from failing specs.
2013-02-07 04:09:52 -08:00
Enrique Paredes 92ca7efaa4 fix($compile): rename $compileNote to compileNode
Directives was observing different instances of Attributes than the one
that interpolation was registered with because we failed to realize
that the compile node and link node were the same (one of them
was a wrapper rather than raw node)

Closes #1941
2013-02-07 02:49:12 -08:00
Fredrik Bonander 7090924515 fix($cookies): set cookies on Safari&IE when base[href] is undefined
Safari and IE don't like being told to store cookies with path set to
undefined. This change ensures that if base[href] (from which cookie path
is derived) is undefined then the cookie path defaults to ''.

The test verifies that the cookie is set instead of checking that cookie has correct path,
this is due to that cookie meta information is not avabile once the cookie is set.

Closes #1190, #1191
2013-02-07 02:36:25 -08:00
Maxim Grach df744f3af4 feat(dateFilter): add [.,]sss formatter for milliseconds
Also Implement getMilliseconds() method of TzDate and
add test for this in ngMock.
2013-02-07 02:28:33 -08:00
Sam McCall 8155c3a29e feat($http): allow overriding the XSRF header and cookie name
Add 'xsrfCookieName' and 'xsrfHeaderName' property to $httpProvider.defaults and
http config object, which give the name of the cookie the XSRF token is found
in, and the name of the header it is sent in, respectively.
This allows interop with servers with built-in XSRF support that use different
names.
The defaults match the current hard-coded values of 'XSRF-TOKEN' and
'X-XSRF-TOKEN'.
2013-02-07 01:48:01 -08:00
Philip Roberts b001c8ece5 fix(date): invert timezone sign and always display sign
This commit fixes #1261 and #1532. This covers
two separate issues:

- Positive timezones were being formatted without
a leading `+` resulting in a formatting string
like: "HH:MM:ssZ" giving "12:13:141000" instead
of "12:13:14+1000". Fixed by checking if timezone
is > 0 and adding a leading "+".

- Timezone output signs were inverted.
mock.TzDate expects the timezone _offset_ as it's
first argument, _not_ the timezone. This means
that a mock.TzDate with a positive offset should
result in a date string with a negative timezone,
and vice-versa.

Closes #1261, #1532
2013-02-07 01:32:04 -08:00
Igor Minar bec4435945 docs(guide): remove stale info about filters changing DOM
as of v0.10.6 this is not the case any more
2013-02-06 14:14:49 -08:00
Thomas Schultz 6fb1054ce6 docs(tutorial): remove extra back-tick character 2013-02-06 21:51:42 +01:00
theotheo a83eced974 docs(module): fix code example 2013-02-06 20:40:06 +01:00
radu 7cc4063303 docs(ngApp): fix typo 2013-02-05 21:29:11 +01:00
Igor Minar d8e242418d docs(contributing): add CLA anchor for deeplinking 2013-02-04 09:38:01 -08:00
PowerKiKi 6518369f25 docs(ngClass): fix typo in description 2013-02-04 10:36:29 +00:00
Brian Ford 649b892205 feat(Scope): expose transcluded and isolate scope info for batarang
test($compile): add test for exposing transclude and isolate scope info to batarang
2013-01-30 10:42:56 -05:00
Dean Sofer e0295cfec4 docs($cookies): added example to $cookies api docs
Better than nothing.
2013-01-29 16:16:17 -08:00
radu 17fc6a70fe docs(nextUid): fix typo
Update src/Angular.js

removed redundant 'the' from nextUid()'s ngdoc
2013-01-29 15:49:10 -08:00
radu 5d0f9ce4c7 docs(tutorial): fix typo
Update docs/content/tutorial/step_00.ngdoc

removed redundant verb
2013-01-29 15:46:32 -08:00
Fred Sauer 250aec71f3 docs(Scope): fix argument docs for $on 2013-01-29 15:38:19 -08:00
metaweta 3b317c5dcb test(ngBindHtml): prevent variable name leak
Add "var" so element is local instead of global

Strict mode doesn't allow undeclared global vars, and these really should be local anyway.
2013-01-29 13:26:06 -08:00
metaweta e4cfb9d938 refactor(Angular.js): prevent Error variable name leak in tests
Remove var Error = window.Error

window.Error is a read-only property in Apps Script.

Igor says, "we should just delete that line instead. I think it was
misko's attempt to get better closure minification, but it turns out
that it's actually hurting us after gzip (I verified it)."
2013-01-29 13:24:33 -08:00
Igor Minar 87ba8221ec chore(release): start 1.1.3 radioactive-gargle iteration 2013-01-29 13:20:43 -08:00
Fred Sauer e34519e93b docs(a): escape sample code in ng a directive 2013-01-26 22:52:16 +01:00
Igor Minar 69be39fccf fix(docs): properly generate angular.js urls in doc examples 2013-01-24 11:36:03 -08:00
Igor Minar deac80a6e8 docs(date): add missing doc about TZ behavior 2013-01-24 10:51:03 -08:00
Igor Minar 0539611bac docs(changelog): correct 1.0.4 release notes 2013-01-24 09:50:27 -08:00
Vineet Kumar d2177ae312 docs($injector): clarify $inject property description
Section heading about `$inject` property refers to it as `$injector` property.
2013-01-24 00:15:32 -05:00
Partap Davis f3bff27460 feat(resource): add $q/$resorved property to Resource 2013-01-23 20:57:26 -08:00
Igor Minar 4df45b20d4 chore(release): update the CDN version 2013-01-23 12:06:40 -08:00
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
473 changed files with 10084 additions and 53542 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"directory": "bower_components",
"json": "bower.json"
}
-2
View File
@@ -12,5 +12,3 @@ angular.js.tmproj
node_modules
angular.xcodeproj
.idea
libpeerconnection.log
npm-debug.log
+7 -13
View File
@@ -1,19 +1,13 @@
language: node_js
node_js:
- 0.10
env:
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
- 0.8
before_script:
- export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
- ./lib/sauce/sauce_connect_setup.sh
- npm install -g grunt-cli
- grunt ci-checks package
- ./lib/sauce/sauce_connect_block.sh
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install -g testacular@canary
- rake package
- ./nodeserver.sh > /dev/null &
script:
- grunt parallel:travis --reporters dots --browsers SL_Chrome
- rake test[Firefox,"--reporters=dots"]
+2 -1007
View File
File diff suppressed because it is too large Load Diff
-228
View File
@@ -1,228 +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.loadNpmTasks('grunt-ddescribe-iit');
grunt.loadNpmTasks('grunt-merge-conflict');
grunt.loadNpmTasks('grunt-parallel');
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,
parallel: {
travis: {
options: {
stream: true,
},
tasks: [
util.parallelTask('test:modules'),
util.parallelTask('test:jquery'),
util.parallelTask('test:jqlite'),
util.parallelTask('test:e2e')
]
}
},
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: {
options: {
// We use end2end task (which does not start the webserver)
// and start the webserver as a separate process (in travis_build.sh)
// to avoid https://github.com/joyent/libuv/issues/826
port: 8000,
hostname: '0.0.0.0',
middleware: function(connect, options){
return [
function(req, resp, next) {
// cache get requests to speed up tests on travis
if (req.method === 'GET') {
resp.setHeader('Cache-control', 'public, max-age=3600');
}
next();
},
connect.favicon('images/favicon.ico'),
connect.static(options.base)
];
}
}
}
},
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']
},
"ddescribe-iit": {
files: [
'test/**/*.js',
'!test/ngScenario/DescribeSpec.js'
]
},
"merge-conflict": {
files: [
'src/**/*',
'test/**/*',
'docs/**/*',
'css/**/*'
]
},
copy: {
i18n: {
files: [
{ src: 'src/ngLocale/**', dest: 'build/i18n/', expand: true, flatten: true }
]
}
},
compress: {
build: {
options: {archive: 'build/' + dist +'.zip', mode: '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('ci-checks', ['ddescribe-iit', 'merge-conflict']);
grunt.registerTask('default', ['package']);
};
+8 -6
View File
@@ -21,19 +21,21 @@ Building AngularJS
---------
[Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run:
grunt package
rake package
Running Tests
-------------
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
%x(npm install)
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|
fork { closure_compile(file) }
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)
Rake::Task[:init].invoke
sh "./node_modules/testacular/bin/testacular start " +
"#{config} " +
"#{'--single-run=true' if singleRun} " +
"#{'--browsers=' + browsers.gsub('+', ',') if browsers} " +
"#{(misc_options || '').gsub('+', ',')}"
end
+53 -19
View File
@@ -94,6 +94,7 @@ angularFiles = {
'test/matchers.js',
'test/ngScenario/*.js',
'test/ngScenario/output/*.js',
'test/ngScenario/jstd-scenario-adapter/*.js',
'test/*.js',
'test/auto/*.js',
'test/bootstrap/*.js',
@@ -108,30 +109,37 @@ angularFiles = {
'test/ngMock/*.js'
],
'karma': [
'jstd': [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'lib/jquery/jquery.js',
'test/jquery_remove.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
'@angularTest',
'example/personalLog/*.js',
'example/personalLog/test/*.js'
],
'karmaExclude': [
'jstdExclude': [
'test/jquery_alias.js',
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js'
],
'karmaScenario': [
'jstdScenario': [
'build/angular-scenario.js',
'build/jstd-scenario-adapter-config.js',
'build/jstd-scenario-adapter.js',
'build/docs/docs-scenario.js'
],
"karmaModules": [
"jstdModules": [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'build/angular.js',
'src/ngMock/angular-mocks.js',
'src/ngCookies/cookies.js',
@@ -148,20 +156,39 @@ angularFiles = {
'test/ngSanitize/filter/*.js'
],
'karmaJquery': [
'jstdPerf': [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'@angularSrc',
'@angularSrcModules',
'src/ngMock/angular-mocks.js',
'perf/data/*.js',
'perf/testUtils.js',
'perf/*.js'
],
'jstdPerfExclude': [
'src/ng/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js'
],
'jstdJquery': [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'lib/jquery/jquery.js',
'test/jquery_alias.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
'@angularTest',
'example/personalLog/*.js',
'example/personalLog/test/*.js'
],
'karmaJqueryExclude': [
'jstdJqueryExclude': [
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
'test/jquery_remove.js'
@@ -169,22 +196,29 @@ angularFiles = {
};
if (exports) {
exports.files = angularFiles;
exports.mergeFilesFor = function() {
exports.files = angularFiles
exports.mergeFiles = function mergeFiles() {
var files = [];
Array.prototype.slice.call(arguments, 0).forEach(function(filegroup) {
angularFiles[filegroup].forEach(function(file) {
// replace @ref
var match = file.match(/^\@(.*)/);
if (match) {
files = files.concat(angularFiles[match[1]]);
} else {
files.push(file);
}
});
[].splice.call(arguments, 0).forEach(function(file) {
if (file.match(/testacular/)) {
files.push(file);
} else {
angularFiles[file].forEach(function(f) {
// replace @ref
var match = f.match(/^\@(.*)/);
if (match) {
var deps = angularFiles[match[1]];
files = files.concat(deps);
} else {
if (!/jstd|jasmine/.test(f)) { //TODO(i): remove once we don't have jstd/jasmine in repo
files.push(f);
}
}
});
}
});
return files;
};
}
}
+1 -1
View File
@@ -192,7 +192,7 @@ var getPreviousTag = function() {
var generate = function(version, file) {
getPreviousTag().then(function(tag) {
console.log('Reading git log since', tag);
readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
console.log('Parsed', commits.length, 'commits');
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
+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.*
+1 -1
View File
@@ -2,7 +2,7 @@
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak {
display: none !important;
display: none;
}
ng\:form {
+2 -2
View File
@@ -10,9 +10,9 @@
}
</script>
<div ng-controller="HelloCntl">
Your name: <input type="text" ng-model="name"/>
Your name: <input type="text" ng-model="name" value="World"/>
<hr/>
Hello {{name || "World"}}!
Hello {{name}}!
</div>
</doc:source>
<doc:scenario>
+11 -17
View File
@@ -7,7 +7,8 @@
This page explains the Angular initialization process and how you can manually initialize Angular
if necessary.
## Angular `<script>` Tag
# Angular `<script>` Tag
This example shows the recommended path for integrating Angular with what we call automatic
initialization.
@@ -37,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:`.)
@@ -49,7 +46,7 @@ initialization.
## Automatic Initialization
# Automatic Initialization
Angular initializes automatically upon `DOMContentLoaded` event, at which point Angular looks for
the {@link api/ng.directive:ngApp `ng-app`} directive which
@@ -76,14 +73,16 @@ will:
## Manual Initialization
# Manual Initialization
If you need to have more control over the initialization process, you can use a manual
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
or the need to perform an operation before Angular compiles a page.
Here is an example of manually initializing Angular:
Here is an example of manually initializing Angular. The example is equivalent to using the {@link
api/ng.directive:ngApp ng-app} directive.
<pre>
<!doctype html>
@@ -93,22 +92,17 @@ Here is an example of manually initializing Angular:
<script src="http://code.angularjs.org/angular.js"></script>
<script>
angular.element(document).ready(function() {
angular.bootstrap(document, ['optionalModuleName']);
angular.bootstrap(document);
});
</script>
</body>
</html>
</pre>
Note that we have provided the name of our application module to be loaded into the injector as the second
parameter of the {@link api/angular.bootstrap} function. This example is equivalent to using the
{@link api/ng.directive:ngApp ng-app} directive, with `ng-app="optionalModuleName"`, as in the automatic
initialization example above.
This is the sequence that your code should follow:
1. After the page and all of the code is loaded, find the root element of your AngularJS
application, which is typically the root of the document.
1. After the page and all of the code is loaded, find the root of the HTML template, which is
typically the root of the document.
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the element into an
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the template into an
executable, bi-directionally bound application.
+14 -15
View File
@@ -6,21 +6,21 @@
Angular's {@link api/ng.$compile HTML compiler} allows the developer to teach the
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
and even create new HTML elements or attributes with custom behavior. Angular calls these behavior
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
extensions {@link api/ng.$compileProvider#directive directives}.
HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
For example if something needs to be centered, there is no need to provide instructions to the
browser how the window size needs to be divided in half so that the center is found, and that this
center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any
browser how the window size needs to be divided in half so that center is found, and that this
center needs to be aligned with the text's center. Simply add `align="center"` attribute to any
element to achieve the desired behavior. Such is the power of declarative language.
But the declarative language is also limited, since it does not allow you to teach the browser new
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
instead of 1/2. What is needed is a way to teach the browser new HTML syntax.
instead of 1/2. What is needed is a way to teach browser new HTML syntax.
Angular comes pre-bundled with common directives which are useful for building any app. We also
expect that you will create directives that are specific to your app. These extensions become a
expect that you will create directives that are specific to your app. These extension become a
Domain Specific Language for building your application.
All of this compilation takes place in the web browser; no server side or pre-compilation step is
@@ -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.
@@ -39,16 +39,17 @@ process happens in two phases.
scope model are reflected in the view, and any user interactions with the view are reflected
in the scope model. This makes the scope model the single source of truth.
Some directives such as {@link api/ng.directive:ngRepeat `ng-repeat`} clone DOM elements once
for each item in a collection. Having a compile and link phase improves performance since the
cloned template only needs to be compiled once, and then linked once for each clone instance.
Some directives such {@link api/ng.directive:ngRepeat
`ng-repeat`} clone DOM elements once for each item in collection. Having a compile and link phase
improves performance since the cloned template only needs to be compiled once, and then linked
once for each clone instance.
# Directive
A directive is a behavior which should be triggered when specific HTML constructs are encountered
during the compilation process. The directives can be placed in element names, attributes, class
names, as well as comments. Here are some equivalent examples of invoking the {@link
A directive is a behavior which should be triggered when specific HTML constructs are encountered in
the compilation process. The directives can be placed in element names, attributes, class names, as
well as comments. Here are some equivalent examples of invoking the {@link
api/ng.directive:ngBind `ng-bind`} directive.
<pre>
@@ -69,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',
@@ -78,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);
+8 -8
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,7 +232,7 @@ 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
to inherit from or special accessor methods for accessing or changing the model. The model can be
@@ -292,7 +292,7 @@ rendering the view compared to most other templating systems.
# Directives
A directive is a behavior or DOM transformation which is triggered by the presence of a custom attribute,
element name, class name or comment. A directive allows you to extend the HTML vocabulary in a
element name, or a class name. A directive allows you to extend the HTML vocabulary in a
declarative fashion. Following is an example which enables data-binding for the `contenteditable`
in HTML.
@@ -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.
+6 -136
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`.
@@ -110,16 +109,16 @@ Scopes the next DSL element selection.
Returns the value of the first binding matching the given `name`.
## input(name).enter(value)
Enters the given `value` in the text field with the corresponding ng-model `name`.
Enters the given `value` in the text field with the given `name`.
## input(name).check()
Checks/unchecks the checkbox with the corresponding ng-model `name`.
Checks/unchecks the checkbox with the given `name`.
## input(name).select(value)
Selects the given `value` in the radio button with the corresponding ng-model `name`.
Selects the given `value` in the radio button with the given `name`.
## input(name).val()
Returns the current value of an input field with the corresponding ng-model `name`.
Returns the current value of an input field with the given `name`.
## repeater(selector, label).count()
Returns the number of rows in the repeater matching the given jQuery `selector`. The `label` is
@@ -136,7 +135,7 @@ the given jQuery `selector`. The `label` is used for test output.
## select(name).option(value)
Picks the option with the given `value` on the select with the given `name`.
## select(name).options(value1, value2...)
## select(name).option(value1, value2...)
Picks the options with the given `values` on the multi select with the given `name`.
## element(selector, label).count()
@@ -177,132 +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 structured 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>
# Caveats
ngScenario does not work with apps that manually bootstrap using angular.bootstrap. You must use the ng-app directive.
@@ -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:
@@ -21,11 +24,9 @@ constructor). Constructors are always applied to an existing scope object.
You set up the initial state of a scope by creating model properties. For example:
<pre>
function GreetingCtrl($scope) {
$scope.greeting = 'Hola!';
}
</pre>
function GreetingCtrl($scope) {
$scope.greeting = 'Hola!';
}
The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
@@ -34,13 +35,11 @@ in the global scope. This is only for demonstration purposes - in a real
application you should use the `.controller` method of your Angular module for
your application as follows:
<pre>
var myApp = angular.module('myApp',[]);
var myApp = angular.module('myApp',[]);
myApp.controller('GreetingCtrl', ['$scope', function($scope) {
$scope.greeting = 'Hola!';
}]);
</pre>
myApp.controller('GreetingCtrl', ['$scope', function(scope) {
scope.greeting = 'Hola!';
}]);
Note also that we use the array notation to explicitly specify the dependency
of the controller on the `$scope` service provided by Angular.
@@ -75,14 +74,16 @@ you have to perform your own manual DOM manipulation, encapsulate the presentati
{@link guide/directive directives}.
- Input formatting — Use {@link forms angular form controls} instead.
- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
- Sharing stateless or stateful code across controllers — Use {@link dev_guide.services angular
- To run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
services} instead.
- Managing the life-cycle of other components (for example, to create service instances).
- To instantiate or manage the life-cycle of other components (for example, to create service
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}.
@@ -177,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) {
@@ -267,7 +267,7 @@ describe('state', function() {
var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
childScope = mainScope.$new();
var childCtrl = $controller(ChildCtrl, {$scope: childScope});
babyScope = childScope.$new();
babyScope = childCtrl.$new();
var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
}));
@@ -28,7 +28,7 @@ occurs in controllers:
* Use an {@link expression angular expression} with an assignment operator in templates:
<button ng-click="{{foo='bar'}}">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>
@@ -212,7 +212,7 @@ In this mode, `$location` uses Hashbang URLs in all browsers.
it('should show example', inject(
function($locationProvider) {
$locationProvider.html5Mode(false);
$locationProvider.hashPrefix('!');
$locationProvider.hashPrefix = '!';
},
function($location) {
// open http://host.com/base/index.html#!/a
@@ -261,7 +261,7 @@ having to worry about whether the browser displaying your app supports the histo
it('should show example', inject(
function($locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
$locationProvider.hashPrefix = '!';
},
function($location) {
// in browser with HTML5 history support:
@@ -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
@@ -57,7 +57,6 @@ myController.$inject = ['$scope','notify'];
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</doc:source>
<doc:scenario>
@@ -99,7 +98,6 @@ function myController($scope, notify) {
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
<input ng-init="message='test'" ng-model="message">
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</doc:source>
</doc:example>
@@ -8,7 +8,7 @@ as the first argument. Any filter arguments are passed in as additional argument
function.
The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case.
text upper-case and assigns color.
<doc:example module="MyReverseModule">
<doc:source>
@@ -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:
+18 -63
View File
@@ -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}
@@ -248,11 +248,10 @@ function PasswordCtrl($scope) {
and the test is straight forward
<pre>
var $scope = {};
var pc = $controller('PasswordCtrl', { $scope: $scope });
$scope.password = 'abc';
$scope.grade();
expect($scope.strength).toEqual('weak');
var pc = new PasswordCtrl();
pc.password('abc');
pc.grade();
expect(pc.strength).toEqual('weak');
</pre>
Notice that the test is not only much shorter but it is easier to follow what is going on. We say
@@ -276,66 +275,22 @@ 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.
Directives in angular are responsible for updating the DOM when the state of the model changes.
### Simple HTML Element Directive
## Mocks
oue
Lets start with an angular app with no dependencies.
## Global State Isolation
oue
<pre>
var app = angular.module('myApp', []);
</pre>
# Preferred way of Testing
uo
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, {{1 + 1}} times</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, {{1 + 1}} times</h1>`. Now we are going to write a jasmine unit test to
verify this functionality. Note that the expression `{{1 + 1}}` times will also be evaluated in the rendered content.
<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);
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
$rootScope.$digest();
// Check that the compiled element contains the templated content
expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
});
});
</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, 2 times" is present.
## JavaScriptTestDriver
ou
## Jasmine
ou
## Sample project
See the {@link https://github.com/angular/angular-seed angular-seed} project for an example.
uoe
+29 -34
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 makes 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,21 +101,21 @@ 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
the best outcome. The application code simply asks for the dependencies it needs, without having to
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.
## Dependency Annotation
# Dependency Annotation
How does the injector know what service needs to be injected?
How does the injector know what service needs to be injected?
The application developer needs to provide annotation information that the injector uses in order
to resolve the dependencies. Throughout Angular, certain API functions are invoked using the
to resolve the dependencies. Throughout Angular certain API functions are invoked using the
injector, as per the API documentation. The injector needs to know what services to inject into
the function. Below are three equivalent ways of annotating your code with service name
information. These can be used interchangeably as you see fit and are equivalent.
### Inferring Dependencies
# Inferring Dependencies
The simplest way to get hold of the dependencies, is to assume that the function parameter names
are the names of the dependencies.
@@ -134,10 +134,10 @@ While straightforward, this method will not work with JavaScript minifiers/obfus
rename the method parameter names. This makes this way of annotating only useful for {@link
http://www.pretotyping.org/ pretotyping}, and demo applications.
### `$inject` Annotation
# `$inject` Annotation
To allow the minifers to rename the function parameters and still be able to inject right services
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
the function needs to be annotate with the `$inject` property. The `$inject` property is an array
of service names to inject.
<pre>
@@ -153,24 +153,22 @@ function declaration.
This method of annotation is useful for controller declarations since it assigns the annotation
information with the function.
### Inline Annotation
# Inline Annotation
Sometimes using the `$inject` annotation style is not convenient such as when annotating
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,46 +177,43 @@ 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>
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
where injection is supported.
## Where can I use DI?
# Where can I use DI?
DI is pervasive throughout Angular. It is typically used in controllers and factory methods.
### DI in controllers
## DI in controllers
Controllers are classes which are responsible for application behavior. The recommended way of
declaring controllers is using the array notation:
declaring controllers is:
<pre>
someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) {
var MyController = function($scope, dep1, dep2) {
...
$scope.aMethod = function() {
...
}
...
}]);
}
MyController.$inject = ['$scope', 'dep1', 'dep2'];
</pre>
This avoids the creation of global functions for controllers and also protects against minification.
### Factory methods
## Factory methods
Factory methods are responsible for creating most objects in Angular. Examples are directives,
services, and filters. The factory methods are registered with the module, and the recommended way
of declaring factories is:
<pre>
angular.module('myModule', []).
angualar.module('myModule', []).
config(['depProvider', function(depProvider){
...
}]).
+23 -39
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>
@@ -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,8 +239,6 @@ Here's an example directive declared with a Directive Definition Object:
transclude: false,
restrict: 'A',
scope: false,
controller: ["$scope", "$element", "$attrs", "$transclude", "otherInjectables",
function($scope, $element, $attrs, $transclude, otherInjectables) { ... }],
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
@@ -258,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(...);
@@ -277,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(...);
@@ -308,9 +300,8 @@ compiler}. The attributes are:
* `priority` - When there are multiple directives defined on a single DOM element, sometimes it
is necessary to specify the order in which the directives are applied. The `priority` is used
to sort the directives before their `compile` functions get called. Priority is defined as a
number. Directives with greater numerical `priority` are compiled first. The order of directives with
the same priority is undefined. The default priority is `0`.
to sort the directives before their `compile` functions get called. Higher `priority` goes
first. The order of directives within the same priority is undefined.
* `terminal` - If set to true then the current `priority` will be the last set of directives
which will execute (any directives at the current priority will still execute
@@ -364,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:
@@ -391,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
@@ -447,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
@@ -498,7 +483,7 @@ The {@link api/ng.$compile.directive.Attributes Attributes} object - passed as a
link() or compile() functions - is a way of accessing:
* *normalized attribute names:* Since a directive such as 'ngBind' can be expressed in many ways
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized access to
such as 'ng:bind', or 'x-ng-bind', the attributes object allows for normalized accessed to
the attributes.
* *directive inter-communication:* All directives share the same instance of the attributes
@@ -616,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
+4 -4
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:
@@ -23,8 +23,8 @@ You can think of Angular expressions as JavaScript expressions with following di
evaluation, unlike in JavaScript where the expressions are evaluated against the global
`window`.
* **Forgiving:** expression evaluation is forgiving to `undefined` and `null`, unlike in JavaScript,
where trying to evaluate undefined properties can generate `ReferenceError` or `TypeError`.
* **Forgiving:** expression evaluation is forgiving to undefined and null, unlike in JavaScript,
where such evaluations generate `NullPointerExceptions`.
* **No Control Flow Statements:** you cannot do any of the following in angular expression:
conditionals, loops, or throw.
@@ -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.
+6 -6
View File
@@ -2,8 +2,8 @@
@name Forms
@description
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
A Form is a collection of controls for the purpose of grouping related controls together.
Controls (`input`, `select`, `textarea`) are a way for user to enter data.
Form is a collection of controls for the purpose of grouping related controls together.
Form and controls provide validation services, so that the user can be notified of invalid input.
This provides a better user experience, because the user gets instant feedback on how to correct the error.
@@ -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:
+1 -19
View File
@@ -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.
@@ -191,24 +191,6 @@ scripts into a VM. There are existing projects which deal with script loading, w
with Angular. Because modules do nothing at load time they can be loaded into the VM in any order
and thus script loaders can take advantage of this property and parallelize the loading process.
## Creation versus Retrieval
Beware that using `angular.module('myModule', [])` will create the module `myModule` and overwrite any
existing module named `myModule`. Use `angular.module('myModule')` to retrieve an existing module.
<pre>
var myModule = angular.module('myModule', []);
// add some directives and services
myModule.service('myService', ...);
myModule.directive('myDirective', ...);
// overwrites both myService and myDirective by creating a new module
var myModule = angular.module('myModule', []);
// throws an error because myOtherModule has yet to be defined
var myModule = angular.module('myOtherModule');
</pre>
# Unit Testing
+18 -18
View File
@@ -8,28 +8,28 @@
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
language and lets you extend HTML's syntax to express your application's components clearly and
succinctly. Out of the box, it eliminates much of the code you currently write through data
binding and dependency injection. And it all happens in JavaScript within the browser, making it
an ideal partner with any server technology.
binding and dependency injection. And it all happens in JavaScript within the browser making it an
ideal partner with any server technology.
Angular is what HTML would have been had it been designed for applications. HTML is a great
declarative language for static documents. It does not contain much in the way of creating
applications, and as a result building web applications is an exercise in *what do I have to do
to trick the browser into doing what I want.*
applications, and as a result building web applications is an exercise in *what do I have to do, so
that I trick the browser in to doing what I want.*
The impedance mismatch between dynamic applications and static documents is often solved with:
The impedance mismatch between dynamic applications and static documents is often solved as:
* **a library** - a collection of functions which are useful when writing web apps. Your code is
* **library** - a collection of functions which are useful when writing web apps. Your code is
in charge and it calls into the library when it sees fit. E.g., `jQuery`.
* **frameworks** - a particular implementation of a web application, where your code fills in
the details. The framework is in charge and it calls into your code when it needs something
app specific. E.g., `knockout`, `ember`, etc.
app specific. E.g., `knockout`, `sproutcore`, etc.
Angular takes another approach. It attempts to minimize the impedance mismatch between document
centric HTML and what an application needs by creating new HTML constructs. Angular teaches the
browser new syntax through a construct we call directives. Examples include:
* Data binding, as in `{{}}`.
* Data binding as in `{{}}`.
* DOM control structures for repeating/hiding DOM fragments.
* Support for forms and form validation.
* Attaching code-behind to DOM elements.
@@ -37,13 +37,13 @@ browser new syntax through a construct we call directives. Examples include:
## A complete client-side solution
## End-to-end solution
Angular is not a single piece in the overall puzzle of building the client-side of a web
application. It handles all of the DOM and AJAX glue code you once wrote by hand and puts it in a
well-defined structure. This makes Angular opinionated about how a CRUD application should be
built. But while it is opinionated, it also tries to make sure that its opinion is just a
starting point you can easily change. Angular comes with the following out-of-the-box:
Angular tries to be an end-to-end solution, when building a web application. This means it is
not a single piece in an overall puzzle of building a web application, but an end-to-end solution.
This makes Angular opinionated about how a CRUD application should be built. But while it is
opinionated, it also tries to make sure that its opinion is just a starting point, which you can
easily change. Angular comes with the following out-of-the-box:
* Everything you need to build a CRUD app in a cohesive set: data-binding, basic templating
directives, form validation, routing, deep-linking, reusable components, dependency injection.
@@ -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:
@@ -134,7 +134,7 @@ These input widgets look normal enough, but consider these points:
Model-View-Controller design pattern.
* Note that the HTML widget {@link api/ng.directive:input input}
has special powers. The input invalidates itself by turning red when you enter invalid data or
leave the input fields blank. These new widget behaviors make it easier to implement field
leave the the input fields blank. These new widget behaviors make it easier to implement field
validation common in CRUD applications.
And finally, the mysterious `{{ double curly braces }}`:
@@ -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
+30 -42
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
@@ -247,27 +231,31 @@ To create and submit a change:
[print, sign and one of scan+email, fax or mail the form](http://code.google.com/legal/corporate-cla-v1.0.html).
2. Create and checkout a new branch off the master branch for your changes:
2. Create a new branch off the master for your changes:
git checkout -b my-fix-branch master
git branch my-fix-branch
3. Create your patch, make sure to have plenty of tests (that pass).
3. Check out the branch:
4. Commit your changes and create a descriptive commit message (the commit message is used to generate release notes,
git checkout my-fix-branch
4. Create your patch, make sure to have plenty of tests (that pass).
5. Commit your changes and create a descriptive commit message (the commit message is used to generate release notes,
please check out our
[commit message conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#)
and our commit message presubmit hook `validate-commit-msg.js`):
git commit -a
5. Push your branch to Github:
6. Push your branch to Github:
git push origin my-fix-branch
6. In Github, send a pull request to `angular:master`.
7. In Github, send a pull request to `angular:master`.
7. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
8. When the patch is reviewed and merged, delete your branch and pull yours — and other — changes
from the main (upstream) repository:
1. To delete the branch in Github, run:
+9 -5
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?
@@ -101,9 +100,9 @@ The MIT License.
### Can I download and use the Angular logo artwork?
Yes! You can find design files in our github repository, under "{@link https://github.com/angular/angular.js/tree/master/images/logo
Yes! You can find design files in our github repository, under "{@link https://github.com/angular/angular.js/tree/master/images/logo
angular.js/images/logo}"
The logo design is licensed under a "{@link http://creativecommons.org/licenses/by-sa/3.0/
The logo design is licensed under a "{@link http://creativecommons.org/licenses/by-sa/3.0/
Creative Commons Attribution-ShareAlike 3.0 Unported License}". If you have some other use in mind, contact us.
### How can I get some AngularJS schwag?
@@ -114,10 +113,15 @@ they'll waive the setup costs, and you can order any quantity you need.
**Stickers**
Contact Tom Witting (or anyone in sales) via email at tom@stickergiant.com, and tell him you want to order some AngularJS
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
As long as the design stays exactly the same, {@link http://www.stickergiant.com StickerGiant} will give you a reorder discount.
**T-shirts**
Contact sales at {@link http://www.customink.com www.customink.com} and tell them you want some shirts with design name "angularjs",
just like past order #2106371. You'll have to give them your own info for billing and shipping.
As long as the design stays exactly the same, CustomInk won't charge for any set up fees, and they'll give you a reorder discount.
## Common Pitfalls
-1
View File
@@ -19,7 +19,6 @@ becoming an Angular expert.
##Watch Videos
If you havent had a chance to watch the videos from the homepage, please check out:
* {@link http://www.youtube.com/watch?v=WuiHuZq_cg4&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Introduction to AngularJS}
* {@link http://www.youtube.com/watch?v=Yg-R1gchccg&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Creating Directives}
* {@link http://www.youtube.com/watch?v=IRelx4-ISbs&list=PL173F1A311439C05D&context=C48ac877ADvjVQa1PpcFONnl4Q5x8hqvT6tRBTE-m0-Ym47jO3PEE%3D Communicating with Servers}
+13 -13
View File
@@ -1,5 +1,5 @@
@ngdoc overview
@name Tutorial: Index
@name Tutorial
@description
A great way to get introduced to AngularJS is to work through this tutorial, which walks you through
@@ -54,24 +54,24 @@ 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
<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
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
don't have it already:</p>
<pre>npm install -g karma</pre>
<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>
<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 <code>angular-phonecat</code>
<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>
@@ -81,24 +81,24 @@ to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></l
<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
<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
<p>Additionally install <a href="http://vojtajina.github.com/testacular">Testacular</a> if you
don't have it already:</p>
<pre>npm install -g karma</pre>
<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 <code>angular-phonecat</code> directory in your current directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<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 <code>angular-phonecat</code>
<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
+3 -3
View File
@@ -13,7 +13,7 @@ angular-seed, and run the application in the browser.
<div class="tabbable" show="true" ng-model="$cookies.platformPreference">
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux" value="gitUnix">
<ol>
<li><p>In <code>angular-phonecat</code> directory, run this command:</p>
<li><p>In angular-phonecat directory, run this command:</p>
<pre>git checkout -f step-0</pre>
<p>This resets your workspace to step 0 of the tutorial app.</p>
<p>You must repeat this for every future step in the tutorial and change the number to
@@ -25,7 +25,7 @@ angular-seed, and run the application in the browser.
<li><b>For node.js users:</b>
<ol>
<li>In a <i>separate</i> terminal tab or window, run
<code>node ./scripts/web-server.js</code> to start the web server.</li>
<code>./scripts/web-server.js</code> to start the web server.</li>
<li>Open a browser window for the app and navigate to <a
href="http://localhost:8000/app/index.html">http://localhost:8000/app/index.html</a></li>
</ol>
@@ -46,7 +46,7 @@ directory.</li>
<div class="tab-pane well" id="git-win" title="Git on Windows" value="gitWin">
<ol>
<li><p>Open Git bash and run this command (in <code>angular-phonecat</code> directory):</p>
<li><p>Open Git bash and run this command (in angular-phonecat directory):</p>
<pre>git checkout -f step-0</pre>
<p>This resets your workspace to step 0 of the tutorial app.</p>
<p>You must repeat this for every future step in the tutorial and change the number to
+10 -11
View File
@@ -134,10 +134,10 @@ describe('PhoneCat controllers', function() {
});
</pre>
The test instantiates our PhoneListCtrl and verifies that its phones array property contains three
records. This example demonstrates how easy it is to create a unit test for code in Angular. Since
testing is such a critical part of software development, we make it easy to create tests in Angular
so that developers are encouraged to write them.
The test verifies that we have three records in the phones array and the example demonstrates how
easy it is to create a unit test for code in Angular. Since testing is such a critical part of
software development, we make it easy to create tests in Angular so that developers are encouraged
to write them.
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
@@ -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
+4 -4
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
@@ -138,12 +138,12 @@ really is that easy to set up any functional, readable, end-to-end test.
* Let's see how we can get the current value of the `query` model to appear in the HTML page title.
You might think you could just add the `{{query}}` to the title tag element as follows:
You might think you could just add the {{query}} to the title tag element as follows:
<title>Google Phone Gallery: {{query}}</title>
However, when you reload the page, you won't see the expected result. This is because the "query"
model lives in the scope, defined by the `ng-controller="PhoneListCtrl"` directive, on the body element:
model lives in the scope defined by the body element:
<body ng-controller="PhoneListCtrl">
+1 -3
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)
@@ -178,8 +178,6 @@ ordering will default to unordered/natural order.
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
text.
* Reverse the sort order by adding a `-` symbol before the sorting value: `<option value="-age">Oldest</option>`
# Summary
Now that you have added list sorting and tested the app, go to {@link step_05 step 5} to learn
+4 -5
View File
@@ -44,7 +44,7 @@ Following is a sample of the file:
We'll use angular's {@link api/ng.$http $http} service in our controller to make an HTTP
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
one of several built-in {@link api/ng angular services} that handle common operations
in web apps. Angular injects these services for you where you need them.
Services are managed by angular's {@link guide/di DI subsystem}. Dependency injection
@@ -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').
@@ -207,9 +204,11 @@ Finally, we verify that the default value of `orderProp` is set correctly:
it('should set the default value of orderProp model', function() {
expect(scope.orderProp).toBe('age');
});
});
});
</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
@@ -53,7 +53,7 @@ When the application bootstraps, Angular creates an injector that will be used f
this app. The injector itself doesn't know anything about what `$http` or `$route` services do, in
fact it doesn't even know about the existence of these services unless it is configured with proper
module definitions. The sole responsibilities of the injector are to load specified module
definition(s), register all service providers defined in these modules, and when asked, inject
definition(s), register all service providers defined in these modules and when asked inject
a specified function with dependencies (services) that it lazily instantiates via their providers.
Providers are objects that provide (create) instances of services and expose configuration APIs
+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)
+3 -2
View File
@@ -37,7 +37,8 @@ angular.module('phonecatFilters', []).filter('checkmark', function() {
</pre>
The name of our filter is "checkmark". The `input` evaluates to either `true` or `false`, and we
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
return one of two unicode characters we have chosen to represent true or false (`\u2713` and
`\u2718`).
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
our main `phonecat` module.
@@ -109,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)
-41
View File
@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
x="0px" y="0px" width="687px" height="176px" viewBox="0 0 687 176" overflow="visible" enable-background="new 0 0 687 176"
xml:space="preserve">
<defs>
</defs>
<path fill="#FFFFFF" d="M179.011,125.328V54.527h9.158l43.322,57.035V54.527h8.666v70.801h-9.158l-43.326-57.536v57.536H179.011z
M179.011,125.328"/>
<path fill="#FFFFFF" d="M310.46,122.554c-5.708,2.182-11.864,3.269-18.467,3.269c-25.644,0-38.469-12.294-38.469-36.887
c0-23.27,12.378-34.908,37.134-34.908c7.096,0,13.7,0.994,19.802,2.976v7.921c-6.103-2.311-12.378-3.468-18.813-3.468
c-19.306,0-28.96,9.162-28.96,27.479c0,19.639,9.504,29.463,28.517,29.463c3.034,0,6.404-0.396,10.103-1.193V93.145h9.154V122.554z
M310.46,122.554"/>
<path fill="#FFFFFF" d="M325.067,97.996V54.523h9.154v43.473c0,13.598,6.768,20.4,20.303,20.4c13.531,0,20.301-6.803,20.301-20.4
V54.523h9.158v43.473c0,18.556-9.82,27.825-29.459,27.825C334.886,125.821,325.067,116.552,325.067,97.996L325.067,97.996z
M325.067,97.996"/>
<path fill="#FFFFFF" d="M409.48,54.523v63.376h37.037v7.425h-46.191V54.523H409.48z M409.48,54.523"/>
<path fill="#FFFFFF" d="M459.736,125.327h-9.504l35.201-80.146l35.199,80.146h-10.15l-9.158-22.282h-23.418l2.527-7.424h17.82
l-13.217-32.088L459.736,125.327z M459.736,125.327"/>
<path fill="#FFFFFF" d="M530.289,125.328V54.527h30.203c13.469,0,20.197,5.659,20.197,16.982c0,9.207-6.578,16.028-19.75,20.445
l24.309,33.374h-12.086l-22.521-31.835v-5.992c13.531-2.151,20.301-7.344,20.301-15.598c0-6.533-3.766-9.801-11.293-9.801h-20.201
v63.226H530.289z M530.289,125.328"/>
<path fill="#B52E31" d="M619.561,54.523v50.405c0,13.603-8.006,20.396-24.016,20.396V117.9c9.902,0,14.857-4.329,14.857-12.973
V54.523H619.561z M619.561,54.523"/>
<path fill="#B52E31" d="M635.896,122.849v-8.418c7.428,2.639,15.447,3.965,24.064,3.965c12.178,0,18.271-4.457,18.271-13.372
c0-7.584-4.492-11.385-13.469-11.385h-9.113c-14.818,0-22.234-6.435-22.234-19.31c0-13.531,9.492-20.303,28.479-20.303
c8.25,0,15.922,0.998,23.021,2.976v8.418c-7.1-2.644-14.771-3.965-23.021-3.965c-12.875,0-19.311,4.293-19.311,12.875
c0,7.588,4.352,11.385,13.066,11.385h9.113c15.08,0,22.627,6.439,22.627,19.31c0,13.864-9.141,20.796-27.43,20.796
C651.344,125.819,643.324,124.826,635.896,122.849L635.896,122.849z M635.896,122.849"/>
<path fill="#B2B2B2" d="M82.688,0L0,29.1l13.066,108.335l69.71,38.314l70.069-38.834l13.062-108.331L82.688,0z M82.688,0"/>
<path fill="#B52E31" d="M157.66,34.846L82.496,9.214v157.381l62.991-34.861L157.66,34.846z M157.66,34.846"/>
<path fill="#E23237" d="M9.279,35.308l11.196,96.889l62.019,34.398V9.211L9.279,35.308z M9.279,35.308"/>
<path fill="#F2F2F2" d="M99.918,87.493L82.632,51.396L67.415,87.493H99.918z M106.508,102.672h-45.82l-10.251,25.64l-19.067,0.352
L82.496,14.929l52.908,113.734h-17.673L106.508,102.672z M106.508,102.672"/>
<path fill="#B2B2B2" d="M82.496,14.929l0.136,36.467l17.268,36.125H82.534l-0.039,15.127l24.012,0.023l11.223,25.996l18.245,0.339
L82.496,14.929z M82.496,14.929"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

+28 -42
View File
@@ -59,10 +59,10 @@ describe('ngdoc', function() {
'@param {function(number, string=)} d fn with optional arguments');
doc.parse();
expect(doc.param).toEqual([
{name:'a', description:'<div class="a-page"><p>short</p></div>', type:'*', optional:false, 'default':undefined},
{name:'b', description:'<div class="a-page"><p>med</p></div>', type:'Type', optional:false, 'default':undefined},
{name:'c', description:'<div class="a-page"><p>long\nline</p></div>', type:'Class', optional:true, 'default':'2'},
{name:'d', description:'<div class="a-page"><p>fn with optional arguments</p></div>',
{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}
]);
});
@@ -72,7 +72,7 @@ describe('ngdoc', function() {
doc.parse();
expect(doc.returns).toEqual({
type: 'Type',
description: '<div class="a-page"><p>text <em>bold</em>.</p></div>'
description: '<p>text <em>bold</em>.</p>'
});
});
@@ -138,30 +138,16 @@ describe('ngdoc', function() {
it('should not replace anything in <pre>, but escape the html escape the content', function() {
expect(new Doc().markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
toEqual(
'<div class="docs-page"><p>bah x\n' +
'<p>bah x\n' +
'<pre class="prettyprint linenums">\n' +
'&lt;b&gt;angular&lt;/b&gt;.k\n' +
'</pre>\n' +
' asdf x</p></div>');
' asdf x</p>');
});
it('should wrap everything inside a container tag', function() {
var doc = new Doc('@name superman').parse();
var content = doc.markdown('hello');
expect(content).toMatch('<div class="superman-page"><p>hello</p></div>');
});
it('should use the content before a colon as the name prefix for the className of the tag container', function() {
var doc = new Doc('@name super: man').parse();
var content = doc.markdown('hello');
expect(content).toMatch('<div class="super-page super-man-page"><p>hello</p></div>');
});
it('should replace text between two <pre></pre> tags', function() {
expect(new Doc().markdown('<pre>x</pre>\n# One\n<pre>b</pre>')).
toMatch('</pre>\n\n<h1 id="one">One</h1>\n\n<pre');
toMatch('</pre>\n\n<h1>One</h1>\n\n<pre');
});
it('should ignore nested doc widgets', function() {
@@ -171,11 +157,11 @@ describe('ngdoc', function() {
'\ngit bla bla\n</doc:tutorial-instruction>\n' +
'</doc:tutorial-instructions>')).toEqual(
'<div class="docs-page"><p>before<div class="tabbable">\n' +
'<p>before<div class="tabbable">\n' +
'<div class="tab-pane well" id="git-mac" ng:model="Git on Mac/Linux">\n' +
'git bla bla\n' +
'</doc:tutorial-instruction>\n' +
'</doc:tutorial-instructions></p></div>');
'</doc:tutorial-instructions></p>');
});
it('should unindent text before processing based on the second line', function() {
@@ -277,7 +263,7 @@ describe('ngdoc', function() {
name : 'number',
optional: false,
'default' : undefined,
description : '<div class="a-page"><p>Number \nto format.</p></div>' }]);
description : '<p>Number \nto format.</p>' }]);
});
it('should parse with default and optional', function() {
@@ -288,7 +274,7 @@ describe('ngdoc', function() {
name : 'fractionSize',
optional: true,
'default' : '2',
description : '<div class="a-page"><p>desc</p></div>' }]);
description : '<p>desc</p>' }]);
});
});
@@ -298,8 +284,8 @@ describe('ngdoc', function() {
doc.ngdoc = 'service';
doc.parse();
expect(doc.requires).toEqual([
{name:'$service', text:'<div class="a-page"><p>for \n<code>A</code></p></div>'},
{name:'$another', text:'<div class="a-page"><p>for <code>B</code></p></div>'}]);
{name:'$service', text:'<p>for \n<code>A</code></p>'},
{name:'$another', text:'<p>for <code>B</code></p>'}]);
expect(doc.html()).toContain('<a href="api/ng.$service">$service</a>');
expect(doc.html()).toContain('<a href="api/ng.$another">$another</a>');
expect(doc.html()).toContain('<p>for \n<code>A</code></p>');
@@ -351,7 +337,7 @@ describe('ngdoc', function() {
var doc = new Doc("@name a\n@property {string} name desc rip tion");
doc.parse();
expect(doc.properties[0].name).toEqual('name');
expect(doc.properties[0].description).toEqual('<div class="a-page"><p>desc rip tion</p></div>');
expect(doc.properties[0].description).toEqual('<p>desc rip tion</p>');
});
it('should parse @property with type and description both', function() {
@@ -359,7 +345,7 @@ describe('ngdoc', function() {
doc.parse();
expect(doc.properties[0].name).toEqual('name');
expect(doc.properties[0].type).toEqual('bool');
expect(doc.properties[0].description).toEqual('<div class="a-page"><p>desc rip tion</p></div>');
expect(doc.properties[0].description).toEqual('<p>desc rip tion</p>');
});
});
@@ -382,26 +368,26 @@ describe('ngdoc', function() {
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: '<div class="a-page"><p>descrip tion</p></div>'});
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: '<div class="a-page"><p>description</p></div>'});
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();
expect(doc.returns).toEqual({type: 'string', description: '<div class="a-page"><p>descrip <em>tion</em></p></div>'});
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip <em>tion</em></p>'});
});
it('should support multiline content', function() {
var doc = new Doc("@name a\n@returns {string} description\n new line\n another line");
doc.parse();
expect(doc.returns).
toEqual({type: 'string', description: '<div class="a-page"><p>description\nnew line\nanother line</p></div>'});
toEqual({type: 'string', description: '<p>description\nnew line\nanother line</p>'});
});
});
@@ -410,18 +396,18 @@ describe('ngdoc', function() {
var doc = new Doc("@name a\n@description <pre><b>abc</b></pre>");
doc.parse();
expect(doc.description).
toBe('<div class="a-page"><pre class="prettyprint linenums">&lt;b&gt;abc&lt;/b&gt;</pre></div>');
toBe('<pre class="prettyprint linenums">&lt;b&gt;abc&lt;/b&gt;</pre>');
});
it('should support multiple pre blocks', function() {
var doc = new Doc("@name a\n@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
doc.parse();
expect(doc.description).
toBe('<div class="a-page"><p>foo \n' +
toBe('<p>foo \n' +
'<pre class="prettyprint linenums">abc</pre>\n\n' +
'<h1 id="bah">bah</h1>\n\n' +
'<h1>bah</h1>\n\n' +
'<p>foo \n' +
'<pre class="prettyprint linenums">cba</pre></div>');
'<pre class="prettyprint linenums">cba</pre>');
});
@@ -464,7 +450,7 @@ describe('ngdoc', function() {
it('should not remove {{}}', function() {
var doc = new Doc('@name a\n@example text {{ abc }}');
doc.parse();
expect(doc.example).toEqual('<div class="a-page"><p>text {{ abc }}</p></div>');
expect(doc.example).toEqual('<p>text {{ abc }}</p>');
});
});
@@ -483,10 +469,10 @@ describe('ngdoc', function() {
doc.parse();
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
'<div>' +
'<div class="a-page"><p>I am self.</p></div>' +
'<p>I am self.</p>' +
'</div>\n');
expect(doc.html()).toContain('<h3>Method\'s <code>this</code></h3>\n' +
'<div><div class="a-page"><p>I am self.</p></div></div>');
'<div><p>I am self.</p></div>');
});
});
});
@@ -497,7 +483,7 @@ describe('ngdoc', function() {
var doc = new Doc('@ngdoc overview\n@name angular\n@description\n#heading\ntext');
doc.parse();
expect(doc.html()).toContain('text');
expect(doc.html()).toContain('<h1 id="heading">heading</h2>');
expect(doc.html()).toContain('<h2>heading</h2>');
expect(doc.html()).not.toContain('Description');
});
});
+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"';
+5 -25
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;
@@ -173,27 +173,8 @@ Doc.prototype = {
'</a>';
});
});
text = parts.join('');
function prepareClassName(text) {
return text.toLowerCase().replace(/[_\W]+/g, '-');
};
var pageClassName, suffix = '-page';
if(this.name) {
var split = this.name.match(/^\s*(.+?)\s*:\s*(.+)/);
if(split && split.length > 1) {
var before = prepareClassName(split[1]);
var after = prepareClassName(split[2]);
pageClassName = before + suffix + ' ' + before + '-' + after + suffix;
}
}
pageClassName = pageClassName || prepareClassName(this.name || 'docs') + suffix;
text = '<div class="' + pageClassName + '">' +
(new Showdown.converter({ extensions : ['table'] }).makeHtml(text)) +
'</div>';
text = new Showdown.converter().makeHtml(text);
text = text.replace(/(?:<p>)?(REPLACEME\d+)(?:<\/p>)?/g, function(_, id) {
return placeholderMap[id];
});
@@ -222,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);
@@ -291,9 +272,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);
}
@@ -407,7 +387,7 @@ Doc.prototype = {
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('This directive can be used as custom element, but we aware of ');
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
dom.text('.');
}
+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
+2 -35
View File
@@ -1,17 +1,8 @@
/* Logo */
.header .brand {
padding-top: 6px;
padding-bottom: 0px;
}
.header .brand img {
img.AngularJS-small {
width: 95px;
height: 25px;
width: 92px;
}
/* end: Logo */
.clear-navbar {
margin-top: 60px;
@@ -95,15 +86,6 @@
/* Content */
/* =============================== */
.improve-docs {
float: right;
position: relative;
}
.improve-docs {
z-index:100;
}
.hint {
font-size: .7em;
color: #c0c0c0;
@@ -202,18 +184,3 @@ ul.events > li > h3 {
.clear {
clear: both;
}
.tutorial-index-page,
.tutorial-the-end-page {
padding-top:50px;
}
.tutorial-page {
position:relative;
}
.tutorial-page .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>
+9 -10
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');
}
@@ -114,8 +114,8 @@
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="http://angularjs.org">
<img class="logo" src="img/angularjs-for-header-only.svg">
<a class="brand" href="http://angularjs.org" style="padding-top: 6px; padding-bottom: 0px;">
<img class="AngularJS-small" src="http://angularjs.org/img/AngularJS-small.png">
</a>
<ul class="nav">
<li class="divider-vertical"></li>
@@ -130,7 +130,6 @@
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
<li><a href="tutorial">Tutorial</a></li>
<li><a href="http://builtwith.angularjs.org/">Case Studies</a></li>
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
<li><a href="misc/faq">FAQ</a></li>
</ul>
</li>
@@ -140,9 +139,9 @@
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="./tutorial/">Tutorial</a></li>
<li><a href="./guide/">Developer Guide</a></li>
<li><a href="./api/">API Reference</a></li>
<li><a href="http://docs.angularjs.org/tutorial">Tutorial</a></li>
<li><a href="http://docs.angularjs.org/guide/">Developer Guide</a></li>
<li><a href="http://docs.angularjs.org/api/">API Reference</a></li>
<li><a href="http://docs.angularjs.org/misc/contribute">Contribute</a></li>
<li><a href="http://code.angularjs.org/">Download</a></li>
</ul>
@@ -204,7 +203,7 @@
<div class="dropdown search"
ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}">
<input type="text" ng-model="search" placeholder="search the docs"
tabindex="1" accesskey="s" class="input-medium search-query" />
tabindex="1" accesskey="s" class="input-medium search-query" focused="focused">
<ul class="dropdown-menu">
<li>
<a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a>
+9 -9
View File
@@ -18,8 +18,8 @@ docsApp.directive.focused = function($timeout) {
scope.$eval(attrs.focused + '=false');
});
});
scope.$eval(attrs.focused + '=true');
};
scope.$eval(attrs.focused + '=true')
}
};
@@ -59,7 +59,7 @@ docsApp.directive.sourceEdit = function(getEmbeddedTemplate) {
openPlunkr(sources);
};
}
};
}
function read(text) {
var files = [];
@@ -127,7 +127,7 @@ docsApp.directive.docTutorialReset = function() {
'</div>\n');
}
};
};
}
docsApp.serviceFactory.angularUrls = function($document) {
@@ -141,7 +141,7 @@ docsApp.serviceFactory.angularUrls = function($document) {
});
return urls;
};
}
docsApp.serviceFactory.formPostData = function($document) {
@@ -174,7 +174,7 @@ docsApp.serviceFactory.openPlunkr = function(templateMerge, formPostData, angula
var scriptDeps = '';
angular.forEach(content.deps, function(file) {
if (file.name !== 'angular.js') {
scriptDeps += ' <script src="' + file.name + '"></script>\n';
scriptDeps += ' <script src="' + file.name + '"></script>\n'
}
});
indexProp = {
@@ -310,7 +310,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
last: this.$last,
active: page1 && this.currentPage == page1 || page2 && this.currentPage == page2
};
};
}
$scope.submitForm = function() {
$scope.bestMatch && $location.path($scope.bestMatch.page.url);
@@ -509,7 +509,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
},
types: [],
filters: []
};
}
modules.push(module);
}
return module;
@@ -560,7 +560,7 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
angular.element(document.getElementById('disqus_thread')).html('');
}
};
}
angular.module('docsApp', ['ngResource', 'ngCookies', 'ngSanitize', 'bootstrap', 'bootstrapPrettify']).
+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/
-266
View File
@@ -1,266 +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);
});
it("should not transform arrays into objects", function() {
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo().fr_CA);
var deserializedLocale = eval("(" + serializedContent + ")");
expect(deserializedLocale.DATETIME_FORMATS.MONTH.length).not.toBe(undefined);
});
});
-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" || Object.prototype.toString.apply(object) === '[object Array]') {
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..."
-25
View File
@@ -1,25 +0,0 @@
var sharedConfig = require('./karma-shared.conf');
module.exports = function(config) {
sharedConfig(config);
config.set({
frameworks: ['ng-scenario'],
files: [
'build/docs/docs-scenario.js'
],
proxies: {
// angular.js, angular-resource.js, etc
'/angular': 'http://localhost:8000/build/angular',
'/': 'http://localhost:8000/build/docs/'
},
junitReporter: {
outputFile: 'test_out/e2e.xml',
suite: 'E2E'
}
});
config.sauceLabs.testName = 'AngularJS: e2e';
};
-18
View File
@@ -1,18 +0,0 @@
var angularFiles = require('./angularFiles');
var sharedConfig = require('./karma-shared.conf');
module.exports = function(config) {
sharedConfig(config);
config.set({
files: angularFiles.mergeFilesFor('karma'),
exclude: angularFiles.mergeFilesFor('karmaExclude'),
junitReporter: {
outputFile: 'test_out/jqlite.xml',
suite: 'jqLite'
}
});
config.sauceLabs.testName = 'AngularJS: jqLite';
};
-18
View File
@@ -1,18 +0,0 @@
var angularFiles = require('./angularFiles');
var sharedConfig = require('./karma-shared.conf');
module.exports = function(config) {
sharedConfig(config);
config.set({
files: angularFiles.mergeFilesFor('karmaJquery'),
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
junitReporter: {
outputFile: 'test_out/jquery.xml',
suite: 'jQuery'
}
});
config.sauceLabs.testName = 'AngularJS: jQuery';
};
-17
View File
@@ -1,17 +0,0 @@
var angularFiles = require('./angularFiles');
var sharedConfig = require('./karma-shared.conf');
module.exports = function(config) {
sharedConfig(config);
config.set({
files: angularFiles.mergeFilesFor('karmaModules', 'angularSrcModules'),
junitReporter: {
outputFile: 'test_out/modules.xml',
suite: 'modules'
}
});
config.sauceLabs.testName = 'AngularJS: modules';
};
-24
View File
@@ -1,24 +0,0 @@
module.exports = function(config) {
config.set({
frameworks: ['jasmine'],
autoWatch: true,
logLevel: config.LOG_INFO,
logColors: true,
browsers: ['Chrome'],
runnerPort: 0,
// config for Travis CI
sauceLabs: {
testName: 'AngularJS',
startConnect: false,
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
},
customLaunchers: {
'SL_Chrome': {
base: 'SauceLabs',
browserName: 'chrome'
}
}
});
};
-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());
});
};
-205
View File
@@ -1,205 +0,0 @@
var fs = require('fs');
var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var version;
module.exports = {
init: function() {
if (!process.env.TRAVIS) {
shell.exec('npm install');
}
},
getVersion: function(){
if (version) return version;
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
// TODO(brian): change `(-|rc)` to `-` in the regex below after bower
// fixes this issue: https://github.com/bower/bower/issues/782
var match = package.version.match(/^([^\-]*)(?:(-|rc)(.+))?$/);
var semver = match[1].split('.');
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
var fullVersion = (match[1] + (match[2] ? '-' + hash : ''));
var numVersion = semver[0] + '.' + semver[1] + '.' + semver[2];
version = {
number: numVersion,
full: fullVersion,
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 port = grunt.option('port');
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
singleRun ? '--single-run=true' : '',
reporters ? '--reporters=' + reporters : '',
browsers ? '--browsers=' + browsers : '',
noColor ? '--no-colors' : '',
port ? '--port=' + port : ''
]);
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();
};
},
parallelTask: function(name) {
var args = [name, '--port=' + this.lastParallelTaskPort];
if (grunt.option('browsers')) {
args.push('--browsers=' + grunt.option('browsers'));
}
if (grunt.option('reporters')) {
args.push('--reporters=' + grunt.option('reporters'));
}
this.lastParallelTaskPort++;
return {grunt: true, args: args};
},
lastParallelTaskPort: 9876
};
+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
@@ -1,7 +0,0 @@
#!/bin/bash
# Wait for Connect to be ready before exiting
while [ ! -f $SAUCE_CONNECT_READY_FILE ]; do
sleep .5
done
-41
View File
@@ -1,41 +0,0 @@
#!/bin/bash
set -e
# Setup and start Sauce Connect for your TravisCI build
# This script requires your .travis.yml to include the following two private env variables:
# SAUCE_USERNAME
# SAUCE_ACCESS_KEY
# Follow the steps at https://saucelabs.com/opensource/travis to set that up.
#
# Curl and run this script as part of your .travis.yml before_script section:
# before_script:
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
CONNECT_DOWNLOAD="Sauce_Connect.zip"
CONNECT_LOG="$CONNECT_DIR/log"
# Get Connect and start it
mkdir -p $CONNECT_DIR
cd $CONNECT_DIR
curl $CONNECT_URL > $CONNECT_DOWNLOAD 2> /dev/null
unzip $CONNECT_DOWNLOAD
rm $CONNECT_DOWNLOAD
ARGS=""
# Set tunnel-id only on Travis, to make local testing easier.
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
fi
if [ ! -z "$SAUCE_CONNECT_READY_FILE" ]; then
ARGS="$ARGS --readyfile $SAUCE_CONNECT_READY_FILE"
fi
echo "Starting Sauce Connect in the background"
echo "Logging into $CONNECT_LOG"
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY > $CONNECT_LOG &
+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
+8 -39
View File
@@ -1,41 +1,10 @@
{
"name": "angularjs",
"version": "1.0.8",
"cdnVersion": "1.0.6",
"codename": "bubble-burst",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
},
"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",
"grunt-parallel": "~0.2.0",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-merge-conflict": "~0.0.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.10",
"karma-jasmine": "~0.1",
"karma-chrome-launcher": "~0.1",
"karma-firefox-launcher": "~0.1",
"karma-ng-scenario": "~0.1",
"karma-junit-reporter": "~0.1",
"karma-sauce-launcher": "~0.1",
"karma-script-launcher": "~0.1",
"yaml-js": "0.0.5",
"showdown": "0.3.1"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
}
]
"name": "AngularJS",
"version": "0.0.0",
"dependencies" : {
"testacular" : "0.5.9",
"jasmine-node" : "1.2.3",
"q-fs" : "0.1.36",
"qq" : "0.3.5"
}
}
+80 -181
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,6 +46,8 @@ 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 */
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
@@ -55,35 +57,32 @@ var /** holds major version number for IE or NaN for real browsers */
push = [].push,
toString = Object.prototype.toString,
_angular = window.angular,
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
nodeName_,
uid = ['0', '0', '0'];
/**
* @private
* @param {*} obj
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
* @ngdoc function
* @name angular.noConflict
* @function
*
* @description
* Restores the previous global value of angular and returns the current instance. Other libraries may already use the
* angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to
* restore the previous namespace and keep a reference to angular.
*
* @return {Object} The current angular namespace
*/
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)
}
function noConflict() {
var a = window.angular;
window.angular = _angular;
return a;
}
/**
* @ngdoc function
* @name angular.forEach
@@ -111,6 +110,30 @@ function isArrayLike(obj) {
* @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
*/
/**
* @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)
}
}
function forEach(obj, iterator, context) {
var key;
if (obj) {
@@ -194,21 +217,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 +228,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 +237,6 @@ function extend(dst) {
});
}
});
setHashKey(dst,h);
return dst;
}
@@ -276,7 +280,7 @@ noop.$inject = [];
*
<pre>
function transformer(transformationFn, value) {
return (transformationFn || angular.identity)(value);
return (transformationFn || identity)(value);
};
</pre>
*/
@@ -403,18 +407,6 @@ function isArray(value) {
function isFunction(value){return typeof value == 'function';}
/**
* Determines if a value is a regular expression object.
*
* @private
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `RegExp`.
*/
function isRegExp(value) {
return toString.apply(value) == '[object RegExp]';
}
/**
* Checks if `obj` is a window object.
*
@@ -442,20 +434,9 @@ function isBoolean(value) {
}
var trim = (function() {
// native trim is way faster: http://jsperf.com/angular-trim-test
// but IE doesn't have it... :-(
// TODO: we should move this into IE/ES5 polyfill
if (!String.prototype.trim) {
return function(value) {
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
};
}
return function(value) {
return isString(value) ? value.trim() : value;
};
})();
function trim(value) {
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
}
/**
* @ngdoc function
@@ -598,8 +579,6 @@ function copy(source, destination){
destination = copy(source, []);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source);
} else if (isObject(source)) {
destination = copy(source, {});
}
@@ -612,14 +591,12 @@ function copy(source, destination){
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;
@@ -647,7 +624,7 @@ function shallowCopy(src, dst) {
* @function
*
* @description
* Determines if two objects or two values are equivalent. Supports value types, regular expressions, arrays and
* Determines if two objects or two values are equivalent. Supports value types, arrays and
* objects.
*
* Two objects or values are considered equivalent if at least one of the following is true:
@@ -655,14 +632,11 @@ function shallowCopy(src, dst) {
* * Both objects or values pass `===` comparison.
* * Both objects or values are of the same type and all of their properties pass `===` comparison.
* * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal)
* * Both values represent the same regular expression (In JavasScript,
* /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
* representation matches).
*
* 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.
@@ -676,7 +650,6 @@ function equals(o1, o2) {
if (t1 == t2) {
if (t1 == 'object') {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) == o2.length) {
for(key=0; key<length; key++) {
if (!equals(o1[key], o2[key])) return false;
@@ -685,10 +658,8 @@ function equals(o1, o2) {
}
} else if (isDate(o1)) {
return isDate(o2) && o1.getTime() == o2.getTime();
} else if (isRegExp(o1) && isRegExp(o2)) {
return o1.toString() == o2.toString();
} else {
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false;
keySet = {};
for(key in o1) {
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
@@ -725,7 +696,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.
@@ -777,15 +748,13 @@ function toJsonReplacer(key, value) {
* @function
*
* @description
* Serializes input into a JSON-formatted string. Properties with leading $ characters will be
* stripped since angular uses this notation internally.
* Serializes input into a JSON-formatted string.
*
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
* @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
* @returns {string|undefined} Jsonified string representing `obj`.
* @returns {string} Jsonified string representing `obj`.
*/
function toJson(obj, pretty) {
if (typeof obj === 'undefined') return undefined;
return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
}
@@ -845,23 +814,6 @@ function startingTag(element) {
/////////////////////////////////////////////////
/**
* Tries to decode the URI component without throwing an exception.
*
* @private
* @param str value potential URI component to check.
* @returns {boolean} True if `value` can be decoded
* with the decodeURIComponent function.
*/
function tryDecodeURIComponent(value) {
try {
return decodeURIComponent(value);
} catch(e) {
// Ignore any invalid uri component
}
}
/**
* Parses an escaped url query string into key-value pairs.
* @returns Object.<(string|boolean)>
@@ -869,12 +821,10 @@ function tryDecodeURIComponent(value) {
function parseKeyValue(/**string*/keyValue) {
var obj = {}, key_value, key;
forEach((keyValue || "").split('&'), function(keyValue){
if ( keyValue ) {
if (keyValue) {
key_value = keyValue.split('=');
key = tryDecodeURIComponent(key_value[0]);
if ( isDefined(key) ) {
obj[key] = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
}
key = decodeURIComponent(key_value[0]);
obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true;
}
});
return obj;
@@ -925,7 +875,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
replace((pctEncodeSpaces ? null : /%20/g), '+');
}
@@ -939,15 +889,11 @@ function encodeUriQuery(val, pctEncodeSpaces) {
*
* @description
*
* Use this directive to auto-bootstrap an application. Only
* one ngApp directive can be used per HTML document. The directive
* 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.
*
* The first ngApp found in the document will be auto-bootstrapped. To use multiple applications in an
* HTML document you must manually bootstrap them using {@link angular.bootstrap}.
* Applications cannot be nested.
*
* In the example below if the `ngApp` directive would not be placed
* on the `html` element then the document would not be compiled
* and the `{{ 1+2 }}` would not be resolved to `3`.
@@ -1013,46 +959,27 @@ function angularInit(element, bootstrap) {
*
* See: {@link guide/bootstrap Bootstrap}
*
* Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
* They must use {@link api/ng.directive:ngApp ngApp}.
*
* @param {Element} element DOM element which is the root of angular application.
* @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules}
* @returns {AUTO.$injector} Returns the newly created injector for this app.
*/
function bootstrap(element, modules) {
var doBootstrap = 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 doBootstrap();
}
window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
angular.resumeBootstrap = function(extraModules) {
forEach(extraModules, function(module) {
modules.push(module);
});
doBootstrap();
};
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;
@@ -1075,10 +1002,9 @@ function bindJQuery() {
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
// Method signature: JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)
JQLitePatchJQueryRemove('remove', true, true, false);
JQLitePatchJQueryRemove('empty', false, false, false);
JQLitePatchJQueryRemove('html', false, false, true);
JQLitePatchJQueryRemove('remove', true);
JQLitePatchJQueryRemove('empty');
JQLitePatchJQueryRemove('html');
} else {
jqLite = JQLite;
}
@@ -1086,7 +1012,7 @@ function bindJQuery() {
}
/**
* throw error if the argument is falsy.
* throw error of the argument is falsy.
*/
function assertArg(arg, name, reason) {
if (!arg) {
@@ -1104,30 +1030,3 @@ function assertArgFn(arg, name, acceptArrayAnnotation) {
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
return arg;
}
/**
* Return the value accessible from the object by path. Any undefined traversals are ignored
* @param {Object} obj starting object
* @param {string} path path to traverse
* @param {boolean=true} bindFnToScope
* @returns value as accessible by path
*/
//TODO(misko): this function needs to be removed
function getter(obj, path, bindFnToScope) {
if (!path) return obj;
var keys = path.split('.');
var key;
var lastInstance = obj;
var len = keys.length;
for (var i = 0; i < len; i++) {
key = keys[i];
if (obj) {
obj = (lastInstance = obj)[key];
}
}
if (!bindFnToScope && isFunction(obj)) {
return bind(lastInstance, obj);
}
return obj;
}
+5 -3
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"'
@@ -48,7 +48,8 @@ function publishExternalAPI(angular){
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {counter: 0}
'callbacks': {counter: 0},
'noConflict': noConflict
});
angularModule = setupModuleLoader(window);
@@ -87,6 +88,7 @@ function publishExternalAPI(angular){
ngPluralize: ngPluralizeDirective,
ngRepeat: ngRepeatDirective,
ngShow: ngShowDirective,
ngSubmit: ngSubmitDirective,
ngStyle: ngStyleDirective,
ngSwitch: ngSwitchDirective,
ngSwitchWhen: ngSwitchWhenDirective,
+1 -1
View File
@@ -222,6 +222,6 @@
function isActuallyNaN(val) {
return (typeof val === 'number') && isNaN(val);
}
}
};
})(window, document);
+17 -18
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
@@ -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);
+3 -13
View File
@@ -183,9 +183,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
return {
terminal: true,
link: function(scope, element, attrs) {
var modules = [],
embedRootScope,
deregisterEmbedRootScope;
var modules = [];
modules.push(['$provide', function($provide) {
$provide.value('$templateCache', $templateCache);
@@ -211,12 +209,10 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
}
}, $delegate);
}]);
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
embedRootScope = $delegate;
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
$provide.decorator('$rootScope', ['$delegate', function(embedRootScope) {
docsRootScope.$watch(function embedRootScopeDigestWatch() {
embedRootScope.$digest();
});
return embedRootScope;
}]);
}]);
@@ -227,12 +223,6 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
event.preventDefault();
}
});
element.bind('$destroy', function() {
deregisterEmbedRootScope();
embedRootScope.$destroy();
});
angular.bootstrap(element, modules);
}
};
-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 {
+52 -83
View File
@@ -26,25 +26,24 @@
* Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never
* raw DOM references.
*
* ## Angular's jqLite
* Angular's lite version of jQuery provides only the following jQuery methods:
* ## Angular's jQuery lite provides the following methods:
*
* - [addClass()](http://api.jquery.com/addClass/)
* - [after()](http://api.jquery.com/after/)
* - [append()](http://api.jquery.com/append/)
* - [attr()](http://api.jquery.com/attr/)
* - [bind()](http://api.jquery.com/bind/) - 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/)
@@ -56,18 +55,12 @@
* - [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/)
*
* ## jQuery/jqLite Extras
* Angular also provides the following additional methods and events to both jQuery and jqLite:
* ## In addtion to the above, Angular provides additional methods to both jQuery and jQuery lite:
*
* ### Events
* - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
* on all DOM nodes being removed. This can be used to clean up and 3rd party bindings to the DOM
* element before it is removed.
* ### Methods
* - `controller(name)` - retrieves the controller of the current element or its parent. By default
* retrieves controller associated with the `ngController` directive. If `name` is provided as
* camelCase directive name, then the controller for this directive will be retrieved (e.g.
@@ -114,38 +107,37 @@ function camelCase(name) {
/////////////////////////////////////////////
// jQuery mutation patch
//
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a
// $destroy event on all DOM nodes being removed.
//
/////////////////////////////////////////////
function JQLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) {
function JQLitePatchJQueryRemove(name, dispatchThis) {
var originalJqFn = jQuery.fn[name];
originalJqFn = originalJqFn.$original || originalJqFn;
removePatch.$original = originalJqFn;
jQuery.fn[name] = removePatch;
function removePatch(param) {
var list = filterElems && param ? [this.filter(param)] : [this],
function removePatch() {
var list = [this],
fireEvent = dispatchThis,
set, setIndex, setLength,
element, childIndex, childLength, children;
element, childIndex, childLength, children,
fns, events;
if (!getterIfNoArguments || param != null) {
while(list.length) {
set = list.shift();
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
element = jqLite(set[setIndex]);
if (fireEvent) {
element.triggerHandler('$destroy');
} else {
fireEvent = !fireEvent;
}
for(childIndex = 0, childLength = (children = element.children()).length;
childIndex < childLength;
childIndex++) {
list.push(jQuery(children[childIndex]));
}
while(list.length) {
set = list.shift();
for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) {
element = jqLite(set[setIndex]);
if (fireEvent) {
element.triggerHandler('$destroy');
} else {
fireEvent = !fireEvent;
}
for(childIndex = 0, childLength = (children = element.children()).length;
childIndex < childLength;
childIndex++) {
list.push(jQuery(children[childIndex]));
}
}
}
@@ -205,7 +197,7 @@ function JQLiteUnbind(element, type, fn) {
removeEventListenerFn(element, type, events[type]);
delete events[type];
} else {
arrayRemove(events[type] || [], fn);
arrayRemove(events[type], fn);
}
}
}
@@ -361,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;
});
@@ -479,15 +471,6 @@ forEach({
val: function(element, value) {
if (isUndefined(value)) {
if (nodeName_(element) === 'SELECT' && element.multiple) {
var result = [];
forEach(element.options, function (option) {
if (option.selected) {
result.push(option.value || option.text);
}
});
return result.length === 0 ? null : result;
}
return element.value;
}
element.value = value;
@@ -619,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] = [];
@@ -696,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);
}
});
},
@@ -705,7 +669,12 @@ forEach({
if (element.nodeType === 1) {
var index = element.firstChild;
forEach(new JQLite(node), function(child){
element.insertBefore(child, index);
if (index) {
element.insertBefore(child, index);
} else {
element.appendChild(child);
index = child;
}
});
}
},
+2 -2
View File
@@ -30,8 +30,8 @@ function setupModuleLoader(window) {
*
* # Module
*
* A module is a collection of services, directives, filters, and configuration information.
* `angular.module` is used to configure the {@link AUTO.$injector $injector}.
* A module is a collocation of services, directives, filters, and configuration information. Module
* is used to configure the {@link AUTO.$injector $injector}.
*
* <pre>
* // Create a new module
+6 -20
View File
@@ -124,8 +124,7 @@ function Browser(window, document, $log, $sniffer) {
//////////////////////////////////////////////////////////////
var lastBrowserUrl = location.href,
baseElement = document.find('base'),
replacedUrl = null;
baseElement = document.find('base');
/**
* @name ng.$browser#url
@@ -160,21 +159,14 @@ function Browser(window, document, $log, $sniffer) {
baseElement.attr('href', baseElement.attr('href'));
}
} else {
if (replace) {
location.replace(url);
replacedUrl = url;
} else {
location.href = url;
replacedUrl = null;
}
if (replace) location.replace(url);
else location.href = url;
}
return self;
// getter
} else {
// - the replacedUrl is a workaround for an IE8-9 issue with location.replace method that doesn't update
// location.href synchronously
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return replacedUrl || location.href.replace(/%27/g,"'");
// the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return location.href.replace(/%27/g,"'");
}
};
@@ -305,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));
}
}
}
+6 -74
View File
@@ -3,20 +3,7 @@
* @name ng.$cacheFactory
*
* @description
* Factory that constructs cache objects and gives access to them.
*
* <pre>
*
* var cache = $cacheFactory('cacheId');
* expect($cacheFactory.get('cacheId')).toBe(cache);
* expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
*
* cache.put("key", "value");
* cache.put("another key", "another value");
*
* expect(cache.info()).toEqual({id: 'cacheId', size: 2}); // Since we've specified no options on creation
*
* </pre>
* Factory that constructs cache objects.
*
*
* @param {string} cacheId Name or id of the newly created cache.
@@ -27,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.
@@ -66,6 +53,8 @@ function $CacheFactoryProvider() {
if (size > capacity) {
this.remove(staleEnd.key);
}
return value;
},
@@ -148,16 +137,6 @@ function $CacheFactoryProvider() {
}
/**
* @ngdoc method
* @name ng.$cacheFactory#info
* @methodOf ng.$cacheFactory
*
* @description
* Get information about all the of the caches that have been created
*
* @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
*/
cacheFactory.info = function() {
var info = {};
forEach(caches, function(cache, cacheId) {
@@ -167,17 +146,6 @@ function $CacheFactoryProvider() {
};
/**
* @ngdoc method
* @name ng.$cacheFactory#get
* @methodOf ng.$cacheFactory
*
* @description
* Get access to a cache object by the `cacheId` used when it was created.
*
* @param {string} cacheId Name or id of a cache to access.
* @returns {object} Cache object identified by the cacheId or undefined if no such cache.
*/
cacheFactory.get = function(cacheId) {
return caches[cacheId];
};
@@ -192,44 +160,8 @@ function $CacheFactoryProvider() {
* @name ng.$templateCache
*
* @description
* The first time a template is used, it is loaded in the template cache for quick retrieval. You can
* load templates directly into the cache in a `script` tag, or by consuming the `$templateCache`
* service directly.
*
* Adding via the `script` tag:
* <pre>
* <html ng-app>
* <head>
* <script type="text/ng-template" id="templateId.html">
* This is the content of the template
* </script>
* </head>
* ...
* </html>
* </pre>
*
* **Note:** the `script` tag containing the template does not need to be included in the `head` of the document, but
* it must be below the `ng-app` definition.
*
* Adding via the $templateCache service:
*
* <pre>
* var myApp = angular.module('myApp', []);
* myApp.run(function($templateCache) {
* $templateCache.put('templateId.html', 'This is the content of the template');
* });
* </pre>
*
* To retrieve the template later, simply use it in your HTML:
* <pre>
* <div ng-include=" 'templateId.html' "></div>
* </pre>
*
* or get it via Javascript:
* <pre>
* $templateCache.get('templateId.html')
* </pre>
*
* Cache used for storing html templates.
*
* See {@link ng.$cacheFactory $cacheFactory}.
*
*/
+23 -19
View File
@@ -156,7 +156,7 @@ function $CompileProvider($provide) {
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):/;
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/;
/**
@@ -166,11 +166,11 @@ function $CompileProvider($provide) {
* @function
*
* @description
* Register a new directive with the compiler.
* Register a new directives with the compiler.
*
* @param {string} name Name of the directive in camel-case. (ie <code>ngBind</code> which will match as
* <code>ng-bind</code>).
* @param {function|Array} directiveFactory An injectable directive factory function. See {@link guide/directive} for more
* @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more
* info.
* @returns {ng.$compileProvider} Self for chaining.
*/
@@ -293,7 +293,7 @@ function $CompileProvider($provide) {
// href property always returns normalized absolute url, so we can match against that
normalizedVal = urlSanitizationNode.href;
if (normalizedVal !== '' && !normalizedVal.match(urlSanitizationWhitelist)) {
if (!normalizedVal.match(urlSanitizationWhitelist)) {
this[key] = value = 'unsafe:' + normalizedVal;
}
}
@@ -358,7 +358,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
@@ -410,7 +410,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 +433,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);
@@ -517,7 +517,7 @@ function $CompileProvider($provide) {
for (var attr, name, nName, value, nAttrs = node.attributes,
j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
attr = nAttrs[j];
if (!msie || msie >= 8 || attr.specified) {
if (attr.specified) {
name = attr.name;
nName = directiveNormalize(name.toLowerCase());
attrsMap[nName] = name;
@@ -569,9 +569,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 +579,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 +637,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 +661,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 +689,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) {
@@ -788,6 +788,10 @@ function $CompileProvider($provide) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = parentScope;
if( attrs[attrName] ) {
// If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn
scope[scopeName] = $interpolate(attrs[attrName])(parentScope);
}
break;
}
@@ -822,7 +826,7 @@ function $CompileProvider($provide) {
parentGet = $parse(attrs[attrName]);
scope[scopeName] = function(locals) {
return parentGet(parentScope, locals);
};
}
break;
}
@@ -992,7 +996,7 @@ function $CompileProvider($provide) {
directives.unshift(derivedSyncDirective);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
while(linkQueue.length) {
@@ -1086,7 +1090,7 @@ function $CompileProvider($provide) {
interpolateFn = $interpolate(attr[name], true);
}
attr[name] = undefined;
attr[name] = interpolateFn(scope);
($$observers[name] || ($$observers[name] = [])).$$inter = true;
(attr.$$observers && attr.$$observers[name].$$scope || scope).
$watch(interpolateFn, function interpolateFnWatchAction(value) {

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