Compare commits

..

134 Commits

Author SHA1 Message Date
chimney-sweeper c6d04b3a3d chore(release): cut v1.2.7 release 2014-01-03 10:28:30 -08:00
Matias Niemelä e31560cf6b docs(CHANGELOG): add v1.2.7 changes 2014-01-03 13:19:38 -05:00
Igor Minar 3d38fff8b4 fix($httpBackend): don't delete xhr.onreadystatechange otherwise Safari :-O 2014-01-03 09:51:05 -08:00
Matias Niemelä bc492c0fc1 fix($animate): ensure class-based animations are always skipped before structural post-digest tasks are run
Closes #5582
2014-01-03 12:14:15 -05:00
Vojta Jina 162144202c chore: set Karma version to 0.11.11
Temporary reverting Karma, as 0.11.12 is causing some problems.
2014-01-03 08:22:17 -08:00
royling 05596527ed docs($filter): fix wrong param order in doc
fix wrong param order in doc for filter comparator
doc function param for filter expression

Closes #5365
Closes #5611
2014-01-03 02:43:22 -08:00
Tiago Ribeiro 5fea3471e8 docs(\$sniffer): fix minor typo
Closes #5544
2014-01-02 23:10:38 -08:00
Gias Kay Lee 131e4014b8 fix($resource): prevent URL template from collapsing into an empty string
if url template would result in an empty string, we should make a request
to '/' instead.

Closes #5455
Closes #5493
2014-01-02 23:07:27 -08:00
Brian Ford 01c5be4681 fix(ngShow/ngHide, ngIf): functions with zero args should be truthy
Previously, expressions that were a function with one or more arguments evaluated to
true, but functions with zero arguments evaluated to false.

This behavior seems both unintentional and undesirable. This patch makes a function
truthy regardless of its number of arguments.

Closes #5414
2014-01-02 22:59:43 -08:00
Igor Minar fd9a03e147 fix(httpBackend): fix 'type mismatch' error on IE8 after each request 2014-01-02 22:47:39 -08:00
Igor Minar 6c17d02bc4 fix($httpBackend): use ActiveX XHR when making PATCH requests on IE8
IE8's native XHR doesn't support PATCH requests, but the ActiveX one does.

I'm also removing the noxhr error doc because nobody will ever get that error.

Closes #2518
Closes #5043
2014-01-02 22:04:32 -08:00
Drew Perttula 6a6f71f238 docs(error/ngRepeat/dupes): fix typo
Closes #5610
2014-01-02 21:34:34 -08:00
Sebastian K cf686285c2 fix($location): $location.path() behaviour when $locationChangeStart is triggered by the browser
Fixed inconsistency in $location.path() behaviour on the $locationChangeStart event when using
back/forward buttons in the browser or manually changing the url in the address bar.
$location.path() now returns the target url in these cases.

Closes #4989
Closes #5089
Closes #5118
Closes #5580
2014-01-02 21:30:25 -08:00
Igor Minar 7dfedb732d style($browser): remove ws, fix typo 2014-01-02 21:30:00 -08:00
Caitlin Potter 760f2fb731 fix($browser): remove base href domain when url begins with '//'
This change prevents an incorrect appBase url from being calculated when the
<base> href's domain begins with '//'.

Closes #5606
2014-01-02 16:36:31 -08:00
Gias Kay Lee c9705b7556 fix(ngRepeat): allow for more flexible coding style in ngRepeat expression
With this change it's possible to split the ng-repeat expression into multiple
lines at any point in the expression where white-space is expected.

Closes #5537
Closes #5598
2014-01-02 16:14:16 -08:00
David Burrows 53ec33f07f docs($compile): fix typo
Closes #5599
2014-01-02 16:08:51 -08:00
Igor Minar 884ef0dbcd fix(Scope): don't let watch deregistration mess up the dirty-checking digest loop
Closes #5525
2014-01-02 15:28:56 -08:00
Igor Minar 010413f90a test(rootScope): reorganize $watch deregistration specs into a describe 2014-01-02 15:28:56 -08:00
Tobias Bosch 4f57236614 fix($httpBackend): Ignore multiple calls to onreadystatechange with readyState=4
On mobile webkit `onreadystatechange` might by called multiple times
with `readyState===4`  caused by xhrs that are resolved while the app is
in the background.

 Fixes #5426.
2014-01-02 14:37:48 -08:00
Olivier Louvignes 50bf029625 fix(animate): remove trailing s 2014-01-02 11:00:31 -05:00
Igor Minar eff52ad877 docs: moar analytics for all md files 2014-01-01 20:51:45 -08:00
Igor Minar e9ee492d35 docs(README): add analytics 2014-01-01 20:41:55 -08:00
Igor Minar e415e916e8 test(compileSpec): fix broken build on FF
FF 26.0 now throws:

"TypeError: NodeList doesn't have an indexed property setter."

when we try to assign to `childNodes[1]`, since this test still works properly
on Chrome and the issue being tested is not a cross-browser issues, I'm
just making the patchability check more robust instead of trying to figure
out how to make this test fully pass on FF.
2013-12-31 01:39:19 -08:00
Igor Minar 07084e1c8b test(injector): add missing test for #5577
Add a missing test for fix that was merged via #5577
2013-12-31 01:24:41 -08:00
Matt Ginzton 186a591228 fix($injector): remove INSTANTIATING entry when done
getService flags services as INSTANTIATING while it calls their
provider factory, in order to detect circular dependencies. If
the service is instantiated correctly, the INSTANTIATING flag is
overwritten with the actual service. However, if the service is
not instantiated correctly, the INSTANTIATING flag should still
be removed, or all further requests for this service will be
mis-detected as a circular dependency.

Closes #4361
Closes #5577
2013-12-31 01:17:43 -08:00
Igor Minar a80049fd0a fix(input): use apply on change event only when one isn't already in progress
Closes #5293
2013-12-31 00:41:15 -08:00
Igor Minar d158dd131e chore(release.sh): push both the release commit and tag 2013-12-30 16:58:28 -08:00
Michał Gołębiowski 1147f21999 fix(input): prevent double $digest when using jQuery trigger.
If an event was performed natively, jQuery sets the isTrigger property.
When triggering event manually, the field is not present. Manually
triggered events are performed synchronously which causes the "$digest
already in progress" error.

Closes #5293
2013-12-30 15:09:49 -08:00
kimwz bddd46c8ec fix($location): re-assign history after BFCache back on Android browser
Closes #5425
2013-12-30 14:58:04 -08:00
Karl Seamon 80e7a45584 perf(Scope): limit propagation of $broadcast to scopes that have listeners for the event
Update $on and $destroy to maintain a count of event keys registered for each scope and its children.
$broadcast will not descend past a node that has a count of 0/undefined for the $broadcasted event key.

Closes #5341
Closes #5371
2013-12-27 23:31:00 -08:00
Caitlin Potter 498365f219 fix(ngRoute): instantiate controller when template is empty
Before this change, $route controllers are not instantiated if the template is falsy, which includes
the empty string. This change tests if the template is not undefined, rather than just falsy, in
order to ensure that templates are instantiated even when the template is empty, which people may
have some reason to do.

This "bug" was reported in http://robb.weblaws.org/2013/06/21/angularjs-vs-emberjs/, as a "gotcha"
for AngularJS / ngRoute.

Closes #5550
2013-12-27 22:45:46 -08:00
Brady Isom 056c849352 fix($sanitize): consider size attribute as valid/allowed attribute
The "size" attribute gets set on <font> elements when using HTML5 rich
text editors, or elements with the contenteditable attribute, that rely
on the 'fontSize' command (execCommand).

Closes #5522
2013-12-27 16:22:35 -08:00
Vojta Jina 7d6e5a2d01 chore: update Karma and SauceLabs launcher
This should improve stability as it contains capture timeout (if a browser does not capture in a given timeout it gets killed) and retry (if a browser fails to start, Karma will try n times before failing).
2013-12-23 17:54:05 -08:00
mkolodny d1c4766d14 docs(guide/forms): update example
Right now, non-integers such as 'aawefwae' are valid.
This ensures that only integers are valid. Hopefully that makes the example more powerful.

Closes #5501
2013-12-20 21:22:15 -08:00
mkolodny 98473835a2 docs(guide/forms): code style changes for an example
Closes #5499
2013-12-20 21:21:18 -08:00
sanfords 870232bd05 style($http): fix a semi-colon 2013-12-20 16:23:06 -08:00
Vojta Jina c31df32ca0 chore(release): update cdn version 2013-12-20 10:29:03 -08:00
Klaus Weiss df2b88e230 docs(guide): fix typo
Closes #5481
2013-12-19 16:12:24 -08:00
Brian Ford 83451d552e chore(release): add codename for 1.2.7 2013-12-19 16:11:36 -08:00
chimney-sweeper af7203e0b8 chore(release): start v1.2.7 2013-12-19 16:02:57 -08:00
chimney-sweeper 98ee3719f9 chore(release): cut v1.2.6 release 2013-12-19 15:50:07 -08:00
Igor Minar 94b5f2dadb chore(release): improve the release script 2013-12-19 15:47:26 -08:00
Brian Ford fe7decd1b0 docs(CHANGELOG): add v1.2.6 changes 2013-12-19 13:55:10 -08:00
Matias Niemelä cef084ade9 feat(ngAnimate): provide configuration support to match specific className values to trigger animations
Closes #5357
Closes #5283
2013-12-19 16:37:29 -05:00
Matias Niemelä 937caab647 feat(jqLite): provide support for element.one() 2013-12-19 14:39:04 -05:00
Matias Niemelä 3fc8017119 style(animateSpec): ensure spacing between specs and describes is consistent 2013-12-19 12:02:00 -05:00
Matias Niemelä 54637a335f fix($animate): use a scheduled timeout in favor of a fallback property to close transitions
With ngAnimate, CSS transitions, that are not properlty triggered, are forceably closed off
by appling a fallback property. The fallback property approach works, however, its styling
itself may effect CSS inheritance or cause the element to render improperly. Therefore, its
best to stick to using a scheduled timeout to run sometime after the highest animation time
has passed.

Closes #5255
Closes #5241
Closes #5405
2013-12-19 12:01:12 -05:00
Josh Kurz 277a5ea05d docs($interval): remind the developer to destroy their intervals
It is essential that users of `$interval` destroy the interval when they are finished.
Otherwise you can get memory leaks.
Often `$intervals` are used in directives or controllers and developers don't think
about what happens when the component is destroyed.
If a directive/controller scope is destroyed, then the $interval should be destroyed as well.
This could cause some issues with developers who assume that the interval will be cleared
for them when the scope is destroyed.

Closes #5377

I believe that the library could/should handle this as well, but thats another issue.
2013-12-19 13:46:30 +00:00
snicolai 9865a7c0ad docs(ngCloak): style name is ng-cloak, not ngCloak
Closes #5374
2013-12-19 13:32:19 +00:00
Mark Jones efba4731e4 docs(booleanAttrs): add @priority to all the boolean directives
Closes #5361
2013-12-19 13:29:19 +00:00
Tony Cronin a965984733 docs(tutorial/step-4): fix typo
Closes #5356
2013-12-19 13:24:19 +00:00
mkolodny 14d3e559d4 docs($injector): use correct spacing convention for CoffeeScript functions
This convention is exhibited by http://coffeescript.org/ and https://github.com/polarmobile/coffeescript-style-guide#functions.

Closes #5354
2013-12-19 13:05:15 +00:00
James Watling 3d156a76e3 docs(ngEventDirs): adding quick examples for new events
Closes #5338
2013-12-19 12:57:33 +00:00
Alexandre Potvin Latreille c7a1d1ab0b fix($compile): remove invalid IE exceptional case for href
It appears that this exceptional case was only valid for IE<8 and that for IE>=8 it
was actually causing a bug with the `ng-href-attr` directive on `<a>` elements.

Closes #5479
2013-12-19 12:22:58 +00:00
Caitlin Potter 26d43cacdc fix($parse): return 'undefined' if a middle key's value is null
Prior to this fix, $parse/$eval would return 'null' if a middle key in
an expression's value is null, when it should be expected to be undefined.

This patch tries to remedy this by returning undefined for middle values in
expressions, when fetching a child of that null value.

For example:

```js
// Given the following object:
$scope.a = {
  b: null
};

// $scope.$eval('a.b.c') returns undefined, whereas previously it would return null
```

Closes #5480
2013-12-19 00:59:22 -08:00
Tobias Bosch 4f5758e666 fix($log): should work in IE8
In IE8, reading `console.log.apply` throws an error.
Catching this errow now.

Fixes #5400. Fixes #5147.
2013-12-18 21:44:00 -08:00
Tobias Bosch 274a6734ef fix(forEach): allow looping over result of querySelectorAll in IE8
In IE8 the result object
of calling `node.querySelectorAll` does not have a `hasOwnPropery`
function. However, it should be usable with `forEach`.

Related to #5400.
2013-12-18 21:44:00 -08:00
Zachary Babtkis f0e3dfd008 docs(guide): fixed *off typo in angular.injector example comment
Closes #5441
2013-12-18 20:57:20 -08:00
Caitlin Potter bc3ff2cecd fix($location): parse xlink:href for SVGAElements
Before this fix, the xlink:href property of an SVG <a> element could not be parsed
on click, as the property is an SVGAnimatedString rather than a DOMString.

This patch parses the xlink:href's animVal into a DOMString in order to prevent
an `Object #<SVGAnimatedString> has no method 'indexOf'` exception from being thrown,
and also to update the location if necessary as expected.

Closes #5472
Closes #5198
Closes #5199
Closes #4098
Closes #1420
2013-12-18 17:16:39 -08:00
oojerryoo 8f329ffb82 fix(closure): add type definition for Scope#$watchCollection
Closes #5475
2013-12-18 16:35:54 -08:00
Karl Seamon 864b2596b2 perf($parse) use a faster path when the number of path parts is low
Use a faster path when the number of path tokens is low (ie the common case).
This results in a better than 19x improvement in the time spent in $parse and
produces output that is about the same speed in chrome and substantially faster
in firefox.
http://jsperf.com/angularjs-parse-getter/6

Closes #5359
2013-12-18 15:44:15 -08:00
Karl Seamon f3a796e522 perf(compile): add class 'ng-scope' before cloning and other micro-optimizations
Add class ng-scope to dom nodes during directive compile rather than link.
Optimize handling of nodeLists.
This results in a savings of about 130ms during the startup of a product within Google.

Closes #5471
2013-12-18 15:31:49 -08:00
oweitz 09f8962df2 docs($resource): fix typo in server response example
The server is supposed to return the same card number as in the client request.
Adjust server response example to the value given in the client request.

Closes #5352
2013-12-18 21:22:21 +00:00
justmiaotou a13c4ba770 docs(tutorial/step-5): fix typo
Closes #5347
2013-12-18 21:15:38 +00:00
Jason Farnsworth 040e743b39 docs(ngInit): fix typo
Closes #5343
2013-12-18 21:14:46 +00:00
Pete Bacon Darwin bf816d3ade docs(guide/directive): improve access to isolate scope information
Closes #5329
2013-12-18 21:14:12 +00:00
Jesse Browne 74b4ab8867 docs(tutorial/step_11): fix indenting in an example
Closes #5322
2013-12-18 21:14:11 +00:00
Kurt Funai 6e2359caa0 docs(contribute): you may need sudo to install globally on unix systems
Closes #5318
2013-12-18 21:14:11 +00:00
Ziang Song 41534816a4 docs(error/noregexp): fix link to ng-pattern
Closes #5313
2013-12-18 21:14:10 +00:00
Kindy Lin 30252a0504 docs($compile): fix param name and improve example variable name
Closes #5310
2013-12-18 21:14:09 +00:00
Chia-liang Kao 3dc18037e8 fix(input): do not hold input for composition on android
Workaround for chrome for android until #2129 is ready.

Closes #5308, #5323
2013-12-18 12:28:07 -08:00
Chia-liang Kao 57d50582aa chore($sniffer): make android variable public 2013-12-18 11:49:39 -08:00
Pete Bacon Darwin 73c66715c9 docs(bootstrap-prettify): fix $timeout issues and update related docs
End 2 end tests wait for all `$timeout`s to be run before completing the test.
This was problematic where we were using timeouts that restarted themselves because
there would never be a point when all timeouts had completed, causing the tests to hang.

To fix this $timeout had been monkey-patched but this caused other issue itself.

Now that we have $interval we don't need to use $timeout handlers that re-trigger the $timeout
so we can ditch the monkey-patch.

This commit tidies up any examples that are using this approach and changes them to use $interval
instead.

Closes #5232
2013-12-17 22:53:28 +00:00
Karl Seamon cb29632a58 perf: use faster check for $$ prefix
Use two calls to charAt instead of substr to detect a $$prefix in the shallowCopy functions.
This makes shallowCopy 25-50% faster (depending on which browser is used).
http://jsperf.com/angular-shallow-copy

Closes #5457
2013-12-17 11:43:57 -08:00
Caitlin Potter 5c97731a22 fix(select): invalidate when 'multiple, required and model is []`
When `multiple` attribute is set on a `<select>` control and the model value is an empty array,
we should invalidate the control.  Previously, this directive was using incorrect logic for
determining if the model was empty.

Closes #5337
2013-12-17 13:10:40 +00:00
Stéphane Reynaud b2e472e7a2 docs(tutorial/step-11): change "last" to "next"
Since step 12 was added, step 11 is not the last. So this is not the last improvement.

Closes #5306
2013-12-17 12:30:59 +00:00
Pete Bacon Darwin 6ac773f350 docs(): fix jshint issues 2013-12-17 12:29:22 +00:00
unclejustin 3174f73336 docs($resource): add example for a custom PUT request
Closes #5302
2013-12-17 12:20:25 +00:00
Grigoriy Beziuk 3c62e4244e docs(tutorial/step-0): fix twitter bootstrap link
The url of twitter bootstrap was outdated.

Closes #5290
2013-12-17 12:15:52 +00:00
Oliver Schmidt c432999572 docs(ng): fix typo and line lengths
Closes #5288
2013-12-17 12:12:14 +00:00
Sharon DiOrio f8d319c11a docs(css): improve definition table style
Text in definition tables are now aligned to the top of the cell. These are used in
the API index page and makes it cleared what headings match what content.

Closes #5286
2013-12-17 12:09:08 +00:00
Pete Bacon Darwin 4f72433392 docs(ngIf): remove invalid comment from CSS
We cannot use valid /* ... */ CSS comments in examples because they break the parsing
of the ngdoc comments.  We can't use inline // comments because these are not valid in
CSS.

We could use the //!annotate extension to the ngdoc parser but this does not seem to be
working.  It is best to simply remove this line.

Closes #5234
2013-12-17 11:59:29 +00:00
Chris Chua 2f91cfd0d2 fix(jqLite): support unbind self within handler
If an event handler unbinds itself, the next event handler on the same
event and element doesn't get executed.

This works fine in jQuery, and since jqLite doesn't support .one, this
might be a common use case.
2013-12-16 16:39:13 -08:00
Vojta Jina d5c5e2b584 chore: run docs unit test only once
Before we would run them twice on Travis. I don't think it should be part of ci-check task.
2013-12-16 13:37:09 -08:00
Rhys Brett-bowen cbb3ce2c30 fix(ngRepeat): allow multiline expressions
allow and pass through new line characters when checking passed in expression

Closes #5000
2013-12-16 10:37:18 -08:00
Tobias Bosch 45af02de04 chore(build): simplify release scripts 2013-12-16 09:49:48 -08:00
Tobias Bosch 6144df52af chore(build): correct updating bower versions 2013-12-16 09:47:23 -08:00
Tobias Bosch b0474cb984 chore(build): remove stale build files 2013-12-13 21:49:22 -08:00
Tobias Bosch 9a4c9e6487 chore(build): correct and refactor release script 2013-12-13 21:49:05 -08:00
Tobias Bosch 11fff8fa0d chore(build): fix fetching for code.angularjs.org 2013-12-13 14:36:24 -08:00
Tobias Bosch da8ab2f928 chore(build): fix release.sh 2013-12-13 14:33:43 -08:00
Tobias Bosch 6d01384a55 chore(build): fixes to release.sh 2013-12-13 14:13:23 -08:00
Tobias Bosch 109ffac975 chore(build): set execute flag on scripts 2013-12-13 13:18:22 -08:00
Tobias Bosch 8c10db3847 chore(build): automate cutting a release, publishing to bower and to code.angular.js 2013-12-13 12:51:13 -08:00
Vojta Jina 03088d6010 docs: fix a broken link 2013-12-13 12:28:02 -08:00
Vojta Jina 18e0768a2b docs: use q-io instead of deprecated q-fs
This recursive process.nextTick error[1] was probably coming from q-fs,
which is not actively maintained. This updates to q-io/fs instead.


[1]: https://travis-ci.org/angular/angular.js/jobs/15391590
2013-12-13 12:23:53 -08:00
Vojta Jina ed4a1fddce chore(travis): force the latest version of selenium
This might solve some flakiness on SL. At least Santi said that ;-)
2013-12-13 12:23:53 -08:00
Jeff Cross cfde6f507c chore(release): start 1.2.6 taco-salsafication iteration 2013-12-13 11:53:09 -08:00
Jeff Cross 3468ad1b61 chore(release): cut 1.2.5 singularity-expansion release 2013-12-13 10:52:13 -08:00
Jeff Cross e9c79cad43 docs(CHANGELOG): add v1.2.5 changes 2013-12-13 10:44:40 -08:00
Igor Minar e455e7d878 docs(TRIAGING): add info about "needs: public api" 2013-12-13 08:53:33 -08:00
Michał Gołębiowski 3410f65e79 perf(jqLite): implement and use the empty method in place of html(‘’)
jQuery's elem.html('') is way slower than elem.empty(). As clearing
element contents happens quite often in certain scenarios, switching
to using .empty() provides a significant performance boost when using
Angular with jQuery.

Closes #4457
2013-12-13 02:07:11 -08:00
Karl Seamon f3de5b6eac perf(a): do not link when href or name exists in template
Change the a directive to link and hookup a click event only when
there is no href or name in the template element.
In a large Google app, this results in about 800 fewer registrations,
saving a small but measurable amount of time and memory.

Closes #5362
2013-12-13 00:31:25 -08:00
Karl Seamon fcd2a8131a perf($resource): use shallow copy instead of angular.copy
Replace calls to angular.copy with calls to a new function, shallowClearAndCopy.
Add calls to copy for cache access in $http in order to prevent modification of cached data.
Results in a measurable improvement to the startup time of complex apps within Google.

Closes #5300
2013-12-13 00:24:37 -08:00
Karl Seamon 62dbe85798 perf: use call and === instead of apply and == in type check functions
Updates isDate et al to use call instead of apply and === instead of ==.
The change to call brings minor performance improvement and === is just
better practice than ==.
http://jsperf.com/call-vs-apply-tostring

Closes #5295
2013-12-13 00:22:47 -08:00
Brian Atkinson 1d5e18b062 fix(closure): add missing FormController extern definitions
Closes #5303
2013-12-13 00:14:31 -08:00
Igor Minar a0ed371389 style($injector): remove ws 2013-12-12 23:51:45 -08:00
Ben Wiklund 05e4fd3488 perf($injector): remove invoke optimization that doesn't work
Closes #5388
2013-12-12 23:49:03 -08:00
Tobias Bosch 30a8b7d0b5 fix(ngInclude): Add template to DOM before linking other directives
The template needs to be added to the DOM before
other directives at the same element as `ngInclude` are linked.

Fixes #5247.
2013-12-12 17:18:44 -08:00
Tobias Bosch f8944efe70 fix(ngView): Add template to DOM before linking other directives
The template needs to be added to the DOM before
other directives at the same element as `ngView` are linked.

Related to #5247.
2013-12-12 17:18:44 -08:00
Tobias Bosch 43072e3812 fix($compile): Allow literals in isolate scope references
When a component uses an isolate scope reference
and the the component is used with an object literal
a new object is created on every evaluation.
Therefore the compiler needs to compare
the values of the parent and the isolate scope
using object equality and not object reference
equality.

Fixes #5296.
2013-12-12 16:30:31 -08:00
Pete Bacon Darwin 9396d55414 docs(ngSwitch): clarify that ngSwitch matches against string literals
Closes #5285
2013-12-12 11:42:49 +00:00
mbrookes 82e97cf53e docs(guide/expression): remove misplaced comma
Closes #5280
2013-12-12 11:25:41 +00:00
Jürgen Walter cf2a7614a4 docs(tutorial/step-07): update path to pages in e2e scenarios
The url paths in the tutorial are not in line with the actual tutorial code

Closes #5264
2013-12-12 11:22:31 +00:00
Vlad GURDIGA 9e538e7c31 docs(ng.$rootScope.Scope): fix API links
Also added a note to the Writing AngularJS Documentation:
https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation/d0c715ef89

Closes #5261
2013-12-12 11:19:16 +00:00
Mattias Holmlund 4ac21ac039 docs(error/transclude/orphan): fix spelling mistakes
Closes #5259
2013-12-12 11:15:31 +00:00
Karl Seamon f69dc16241 fix(angular-mocks): use copy of mock data in $httpBackend
Copy mock data returned from the mock $httpBackend.
This prevents modifications to the response from affecting future responses.
Previously, this misbehavior was being mitigated by the deep copy in $resource, but that no longer exists.
2013-12-11 15:18:45 -08:00
Brian Ford f1a8d419d5 chore(scripts): fix bower script to pull from master before updating 2013-12-11 14:35:23 -08:00
Vojta Jina 8864e54f1f chore(scripts): refactor travis scripts
Refactoring so that it's easier to use both SL/BS just depending on a global switch.
2013-12-11 10:12:18 -08:00
Vojta Jina dc4df93177 chore(travis): run two jobs
Instead of parallelization on a single Travis VM, we use two VMs.
- output is nicer (we don't have to buffer e2e tests and then show it at the end)
- you can easily see faster the result of unit tests (as it's basically a separate build)

We should also make sure we only do the necesary stuff (for install we don't need to do `grunt
package` for unit tests, we only need to generate the docs for e2e tests.
2013-12-11 10:02:14 -08:00
Tobias Bosch 043190f397 docs(TRIAGING): Add Type:Perf label 2013-12-09 17:54:08 -08:00
Tobias Bosch f4d850e168 docs(TRIAGING): Labels cla yes/no are automatically set
The labels are set by a script and
should not be set manually any more.
2013-12-09 17:30:43 -08:00
Andy Ferra 8ec2743ca1 docs(design): guide index formatting
Just my first pass at a more readable format of the guide index.

Note: the styles apply to all content in the docs, not just the guide
index. This is intentional and I feel that the result is positive.
2013-12-09 16:32:10 -08:00
Vojta Jina ecbee8147b style($route): make jshint happy
Fix the broken build and earn a late (french spelling).
2013-12-09 14:57:49 -08:00
miknsh5 f8c6ee3df5 docs(input): remove redundant closing span tag
Closes #5257
2013-12-09 20:52:00 +00:00
hambyiii fe84f7bef8 docs($route): clarify examples of route parameters
Putting route parameter examples in braces was misleading newcomers.

Closes #5243
2013-12-09 20:48:50 +00:00
Robin Böhm d653607162 docs($q): correct typo in the 'Differences Q/$q' section
Closes #5230
2013-12-09 20:46:21 +00:00
gdi2290 082fe180ec docs(contribute) add platform agnostic git install link
Closes #5216
2013-12-09 20:27:08 +00:00
Shane M. Pelletier d3491083a5 docs(tutorial): enlarge clickable area of tutorial nav buttons
Change position of <a> and <li> tags in tutorial nav buttons
partial. This allows the full area of the button to be clicked
rather than just the text.

Closes #5074
Closes #5209
2013-12-09 20:24:54 +00:00
Wesley Cho c3d6ca97e1 docs(guide/forms): clarify how NgModelController can be added to scope
Closes #5200
2013-12-09 20:14:45 +00:00
Matias Niemelä a14266e464 chore(CHANGELOG): introduce perf() section for performance-related commits
Instead of using fix() or chore() when labelling a commit which improves
speed or performance use perf(). Perf commits will be listed in the
CHANGELOG under "Performance Improvements".

For example:
perf($animate): cache all getComputedStyle operations to reduce additional reflows
2013-12-09 11:52:28 -05:00
Joscha Feth b4d44e1298 docs($injector): add example on how to use the element.injector
Closes #5188
2013-12-09 16:32:09 +00:00
Matias Niemelä ca116c35a6 chore(release): start 1.2.5 singularity-expansion iteration 2013-12-06 14:51:45 -05:00
117 changed files with 2482 additions and 897 deletions
+7 -5
View File
@@ -3,13 +3,16 @@ node_js:
- 0.10
env:
matrix:
- JOB=unit
- JOB=e2e
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
- BROWSER_STACK_USERNAME=VojtaJina
- BROWSER_STACK_ACCESS_KEY=HAfHZaypxAc3PEUrUU9a
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
before_script:
- mkdir -p $LOGS_DIR
@@ -18,11 +21,10 @@ before_script:
- grunt bower
- grunt bower
- grunt package-without-bower
- ./lib/sauce/sauce_connect_block.sh
- ./scripts/travis/wait_for_browser_provider.sh
script:
- grunt ci-checks
- ./travis_build.sh
- ./scripts/travis/build.sh
after_script:
- ./travis_print_logs.sh
- ./scripts/travis/print_logs.sh
+172
View File
@@ -1,3 +1,172 @@
<a name="1.2.7"></a>
# 1.2.7 emoji-clairvoyance (2014-01-03)
## Bug Fixes
- **$animate:**
- ensue class-based animations are always skipped before structural post-digest tasks are run
([bc492c0f](https://github.com/angular/angular.js/commit/bc492c0fc17257ddf2bc5964e205379aa766b3d8),
[#5582](https://github.com/angular/angular.js/issues/5582))
- remove trailing `s` from computed transition duration styles
([50bf0296](https://github.com/angular/angular.js/commit/50bf029625d603fc652f0f413e709f43803743db))
- **$http:**
([3d38fff8](https://github.com/angular/angular.js/commit/3d38fff8b4ea2fd60fadef2028ea4dcddfccb1a4))
- use ActiveX XHR when making PATCH requests on IE8
([6c17d02b](https://github.com/angular/angular.js/commit/6c17d02bc4cc02f478775d62e1f9f77da9da82ad),
[#2518](https://github.com/angular/angular.js/issues/2518), [#5043](https://github.com/angular/angular.js/issues/5043))
- fix 'type mismatch' error on IE8 after each request
([fd9a03e1](https://github.com/angular/angular.js/commit/fd9a03e147aac7e952c6dda1f381fd4662276ba2))
- Ignore multiple calls to onreadystatechange with readyState=4
([4f572366](https://github.com/angular/angular.js/commit/4f57236614415eea919221ea5f99c4d8689b3267),
[#5426](https://github.com/angular/angular.js/issues/5426))
- **$injector:** remove the `INSTANTIATING` flag properly when done
([186a5912](https://github.com/angular/angular.js/commit/186a5912288acfff0ee59dae29af83c37c987921),
[#4361](https://github.com/angular/angular.js/issues/4361), [#5577](https://github.com/angular/angular.js/issues/5577))
- **$location:**
- remove base href domain if the URL begins with '//'
([760f2fb7](https://github.com/angular/angular.js/commit/760f2fb73178e56c37397b3c5876f7dac96f0455),
[#5606](https://github.com/angular/angular.js/issues/5606))
- fix $location.path() behaviour when $locationChangeStart is triggered by the browser
([cf686285](https://github.com/angular/angular.js/commit/cf686285c22d528440e173fdb65ad1052d96df3c),
[#4989](https://github.com/angular/angular.js/issues/4989), [#5089](https://github.com/angular/angular.js/issues/5089), [#5118](https://github.com/angular/angular.js/issues/5118), [#5580](https://github.com/angular/angular.js/issues/5580))
- re-assign history after BFCache back on Android browser
([bddd46c8](https://github.com/angular/angular.js/commit/bddd46c8ecf49cfe6c999cd6b4a69b7d7e1f9a33),
[#5425](https://github.com/angular/angular.js/issues/5425))
- **$resource:** prevent URL template from collapsing into an empty string
([131e4014](https://github.com/angular/angular.js/commit/131e4014b831ac81b7979c4523da81ebc5861c70),
[#5455](https://github.com/angular/angular.js/issues/5455), [#5493](https://github.com/angular/angular.js/issues/5493))
- **$sanitize:** consider the `size` attribute as a valid/allowed attribute
([056c8493](https://github.com/angular/angular.js/commit/056c8493521988dbb330c6636135b505737da918),
[#5522](https://github.com/angular/angular.js/issues/5522))
- **Scope:** don't let watch deregistration mess up the dirty-checking digest loop
([884ef0db](https://github.com/angular/angular.js/commit/884ef0dbcdfe614cedc824d079361b53e675d033),
[#5525](https://github.com/angular/angular.js/issues/5525))
- **input:**
- use apply on the change event only when one isn't already in progress
([a80049fd](https://github.com/angular/angular.js/commit/a80049fd0ac858eeeb645a4209cb2a661d0b4c33),
[#5293](https://github.com/angular/angular.js/issues/5293))
- prevent double $digest when using jQuery trigger.
([1147f219](https://github.com/angular/angular.js/commit/1147f21999edf9a434cd8d24865a6455e744d858),
[#5293](https://github.com/angular/angular.js/issues/5293))
- **ngRepeat:** allow for more flexible coding style in ngRepeat expression
([c9705b75](https://github.com/angular/angular.js/commit/c9705b755645a4bfe066243f2ba15a733c3787e1),
[#5537](https://github.com/angular/angular.js/issues/5537), [#5598](https://github.com/angular/angular.js/issues/5598))
- **ngRoute:** instantiate controller when template is empty
([498365f2](https://github.com/angular/angular.js/commit/498365f219f65d6c29bdf2f03610a4d3646009bb),
[#5550](https://github.com/angular/angular.js/issues/5550))
- **ngShow/ngHide, ngIf:** functions with zero args should be truthy
([01c5be46](https://github.com/angular/angular.js/commit/01c5be4681e34cdc5f5c461b7a618fefe8038919),
[#5414](https://github.com/angular/angular.js/issues/5414))
## Performance Improvements
- **Scope:** limit propagation of $broadcast to scopes that have listeners for the event
([80e7a455](https://github.com/angular/angular.js/commit/80e7a4558490f7ffd33d142844b9153a5ed00e86),
[#5341](https://github.com/angular/angular.js/issues/5341), [#5371](https://github.com/angular/angular.js/issues/5371))
<a name="1.2.6"></a>
# 1.2.6 taco-salsafication (2013-12-19)
## Bug Fixes
- **$animate:** use a scheduled timeout in favor of a fallback property to close transitions
([54637a33](https://github.com/angular/angular.js/commit/54637a335f885110efaa702a3bab29c77644b36c),
[#5255](https://github.com/angular/angular.js/issues/5255), [#5241](https://github.com/angular/angular.js/issues/5241), [#5405](https://github.com/angular/angular.js/issues/5405))
- **$compile:** remove invalid IE exceptional case for `href`
([c7a1d1ab](https://github.com/angular/angular.js/commit/c7a1d1ab0b663edffc1ac7b54deea847e372468d),
[#5479](https://github.com/angular/angular.js/issues/5479))
- **$location:** parse xlink:href for SVGAElements
([bc3ff2ce](https://github.com/angular/angular.js/commit/bc3ff2cecd0861766a9e8606f3cc2c582d9875df),
[#5472](https://github.com/angular/angular.js/issues/5472), [#5198](https://github.com/angular/angular.js/issues/5198), [#5199](https://github.com/angular/angular.js/issues/5199), [#4098](https://github.com/angular/angular.js/issues/4098), [#1420](https://github.com/angular/angular.js/issues/1420))
- **$log:** should work in IE8
([4f5758e6](https://github.com/angular/angular.js/commit/4f5758e6669222369889c9e789601d25ff885530),
[#5400](https://github.com/angular/angular.js/issues/5400))
- **$parse:** return `undefined` if an intermetiate property's value is `null`
([26d43cac](https://github.com/angular/angular.js/commit/26d43cacdc106765bd928d41600352198f887aef),
[#5480](https://github.com/angular/angular.js/issues/5480))
- **closure:** add type definition for `Scope#$watchCollection`
([8f329ffb](https://github.com/angular/angular.js/commit/8f329ffb829410e1fd8f86a766929134e736e3e5),
[#5475](https://github.com/angular/angular.js/issues/5475))
- **forEach:** allow looping over result of `querySelectorAll` in IE8
([274a6734](https://github.com/angular/angular.js/commit/274a6734ef1fff543cc50388a0958d1988baeb57))
- **input:** do not hold input for composition on Android
([3dc18037](https://github.com/angular/angular.js/commit/3dc18037e8db8766641a4d39f0fee96077db1fcb),
[#5308](https://github.com/angular/angular.js/issues/5308))
- **jqLite:** support unbind self within handler
([2f91cfd0](https://github.com/angular/angular.js/commit/2f91cfd0d2986899c38641100c1851b2f9d3888a))
- **ngRepeat:** allow multiline expressions
([cbb3ce2c](https://github.com/angular/angular.js/commit/cbb3ce2c309052b951d0cc87e4c6daa9c48a3dd8),
[#5000](https://github.com/angular/angular.js/issues/5000))
- **select:** invalidate when `multiple`, `required`, and model is `[]`
([5c97731a](https://github.com/angular/angular.js/commit/5c97731a22ed87d64712e673efea0e8a05eae65f),
[#5337](https://github.com/angular/angular.js/issues/5337))
## Features
- **jqLite:** provide support for `element.one()`
([937caab6](https://github.com/angular/angular.js/commit/937caab6475e53a7ea0206e992f8a52449232e78))
- **ngAnimate:** provide configuration support to match specific className values to trigger animations
([cef084ad](https://github.com/angular/angular.js/commit/cef084ade9072090259d8c679751cac3ffeaed51),
[#5357](https://github.com/angular/angular.js/issues/5357), [#5283](https://github.com/angular/angular.js/issues/5283))
## Performance Improvements
- **compile:** add class 'ng-scope' before cloning and other micro-optimizations
([f3a796e5](https://github.com/angular/angular.js/commit/f3a796e522afdbd3b640d14426edb2fbfab463c5),
[#5471](https://github.com/angular/angular.js/issues/5471))
- **$parse:** use a faster path when the number of path parts is low
([f4462319](https://github.com/angular/angular.js/commit/864b2596b246470cca9d4e223eaed720f4462319))
- use faster check for `$$` prefix
([06c5cfc7](https://github.com/angular/angular.js/commit/cb29632a5802e930262919b3db64ca4806c5cfc7))
<a name="1.2.5"></a>
# 1.2.5 singularity-expansion (2013-12-13)
## Bug Fixes
- **$compile:** allow literals in isolate scope references
([43072e38](https://github.com/angular/angular.js/commit/43072e3812e32b89b97ad03144577cba50d4b776),
[#5296](https://github.com/angular/angular.js/issues/5296))
- **angular-mocks:** use copy of mock data in $httpBackend
([f69dc162](https://github.com/angular/angular.js/commit/f69dc16241c8b631123ad0b09674f0a5e0ff32fe))
- **closure:** add missing FormController extern definitions
([1d5e18b0](https://github.com/angular/angular.js/commit/1d5e18b062c3e33b2a8d96aa58d905ed2cd48649),
[#5303](https://github.com/angular/angular.js/issues/5303))
- **ngInclude:** add template to DOM before linking other directives
([30a8b7d0](https://github.com/angular/angular.js/commit/30a8b7d0b5d4882c2bf3b20eb696a02f5b667726),
[#5247](https://github.com/angular/angular.js/issues/5247))
- **ngView:** add template to DOM before linking other directives
([f8944efe](https://github.com/angular/angular.js/commit/f8944efe70b81e02704df9b53ea2546c80c73d3b))
## Performance Improvements
- **$injector:** remove invoke optimization that doesn't work
([05e4fd34](https://github.com/angular/angular.js/commit/05e4fd3488b89e670c36869f18defe26deac2efa),
[#5388](https://github.com/angular/angular.js/issues/5388))
- **$resource:** use shallow copy instead of angular.copy
([fcd2a813](https://github.com/angular/angular.js/commit/fcd2a8131a3cb3e59a616bf31e61510b5c3a97d3),
[#5300](https://github.com/angular/angular.js/issues/5300))
- **a:** do not link when href or name exists in template
([f3de5b6e](https://github.com/angular/angular.js/commit/f3de5b6eac90baf649506072162f36dbc6d2f028),
[#5362](https://github.com/angular/angular.js/issues/5362))
- **jqLite:** implement and use the `empty` method in place of `html(‘’)`
([3410f65e](https://github.com/angular/angular.js/commit/3410f65e790a81d457b4f4601a1e760a6f8ede5e),
[#4457](https://github.com/angular/angular.js/issues/4457))
## Breaking Changes
- **angular-mocks:** due to [f69dc162](https://github.com/angular/angular.js/commit/f69dc16241c8b631123ad0b09674f0a5e0ff32fe),
some tests that rely on identity comparison rather than equality comparison in checking mock http responses will be broken,
since now each mock response is a copy of the original response. This is usually fixable by changing a `.toBe()` comparison
to `toEqual()` inside of tests.
<a name="1.2.4"></a>
# 1.2.4 wormhole-blaster (2013-12-06)
@@ -4283,3 +4452,6 @@ with the `$route` service
[module]: http://docs-next.angularjs.org/api/angular.mock.module
[guide2.di]: http://docs-next.angularjs.org/guide/dev_guide.di
[jqLite2]: http://docs.angularjs.org/#!/api/angular.element
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CHANGELOG.md?pixel)](https://github.com/igrigorik/ga-beacon)
+4
View File
@@ -198,6 +198,7 @@ Must be one of the following:
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug or adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
generation
@@ -257,3 +258,6 @@ You can find out more detailed information about contributing in the
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CONTRIBUTING.md?pixel)](https://github.com/igrigorik/ga-beacon)
+11 -1
View File
@@ -4,6 +4,7 @@ var path = require('path');
module.exports = function(grunt) {
//grunt plugins
grunt.loadNpmTasks('grunt-bump');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-connect');
@@ -268,6 +269,15 @@ module.exports = function(grunt) {
write: {
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
},
bump: {
options: {
files: ['package.json'],
commit: false,
createTag: false,
push: false
}
}
});
@@ -287,6 +297,6 @@ module.exports = function(grunt) {
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'test:docgen']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint']);
grunt.registerTask('default', ['package']);
};
+4
View File
@@ -38,3 +38,7 @@ To execute end-to-end (e2e) tests, use:
To learn more about the grunt tasks, run `grunt --help` and also read our
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/README.md?pixel)](https://github.com/igrigorik/ga-beacon)
+11 -3
View File
@@ -10,6 +10,13 @@ The labels are used later on for planning releases.
* these tags can be used for labeling a closed issue/PR with a reason why it was closed. (we can add reasons as we need them, right there are only a few rejection reasons. it doesn't make sense to label issues that were fixed or prs that were merged)
## Automatic processing ##
We have automatic tools (e.g. Mary Poppins) that automatically add comments / labels to issues and PRs.
The following is done automatically and should not be done manually:
* Label "cla: yes" or "cla: no" for pull requests
## Process ##
1. Open list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
@@ -29,8 +36,9 @@ The labels are used later on for planning releases.
* Reproducible on master? - http://code.angularjs.org/snapshot/
1. Non bugs:
* Label "Type: Feature" or "Type: Chore"
* Label "Type: Feature" or "Type: Chore" or "Type: Perf"
* Label "needs: breaking change" - if needed
* Label "needs: public api" - if a new public api is needed
* Understandable? - verify if the description of the request is clear. if not ask for clarification
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
@@ -46,10 +54,10 @@ The labels are used later on for planning releases.
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
1. Label "PRs welcome" for "GH: issue"
* if complexity is small or medium and the problem as well as solution are well captured in the issue
1. Label "cla: yes" for "GH: PR":
* otherwise prompt the contributor to sign the CLA
1. Label "origin: google" for issues from Google
1. Label "high priority" for security issues, major performance regressions or memory leaks
1. Unassign yourself from the issue
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/TRIAGING.md?pixel)](https://github.com/igrigorik/ga-beacon)
+3 -1
View File
@@ -142,6 +142,7 @@ var writeChangelog = function(stream, commits, version) {
var sections = {
fix: {},
feat: {},
perf: {},
breaks: {}
};
@@ -169,6 +170,7 @@ var writeChangelog = function(stream, commits, version) {
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
printSection(stream, 'Bug Fixes', sections.fix);
printSection(stream, 'Features', sections.feat);
printSection(stream, 'Performance Improvements', sections.perf);
printSection(stream, 'Breaking Changes', sections.breaks, false);
}
@@ -186,7 +188,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|^perf|BREAKING', 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);
+43 -1
View File
@@ -762,7 +762,9 @@ angular.Module.requires;
* $parent: angular.Scope,
* $root: angular.Scope,
* $watch: function(
* (string|Function), (string|Function)=, boolean=):function()
* (string|Function), (string|Function)=, boolean=):function(),
* $watchCollection: function(
* (string|Function), (string|Function)=):function()
* }}
*/
angular.Scope;
@@ -834,6 +836,13 @@ angular.Scope.$root;
*/
angular.Scope.$watch = function(exp, opt_listener, opt_objectEquality) {};
/**
* @param {string|Function} exp
* @param {(string|Function)=} opt_listener
* @return {function()}
*/
angular.Scope.$watchCollection = function(exp, opt_listener) {};
/**
* @typedef {{
* currentScope: angular.Scope,
@@ -1441,6 +1450,11 @@ angular.NgModelController.prototype.$viewValue;
*/
angular.FormController = function() {};
/**
* @param {*} control
*/
angular.FormController.prototype.$addControl = function(control) {};
/**
* @type {boolean}
*/
@@ -1456,11 +1470,39 @@ angular.FormController.prototype.$error;
*/
angular.FormController.prototype.$invalid;
/**
* @type {string}
*/
angular.FormController.prototype.$name;
/**
* @type {boolean}
*/
angular.FormController.prototype.$pristine;
/**
* @param {*} control
*/
angular.FormController.prototype.$removeControl = function(control) {};
/**
* @type {function()}
*/
angular.FormController.prototype.$setDirty = function() {};
/**
* @type {function()}
*/
angular.FormController.prototype.$setPristine = function() {};
/**
* @param {string} validationToken
* @param {boolean} isValid
* @param {*} control
*/
angular.FormController.prototype.$setValidity = function(
validationToken, isValid, control) {};
/**
* @type {boolean}
*/
-11
View File
@@ -9,14 +9,3 @@
ng\:form {
display: block;
}
/* The styles below ensure that the CSS transition will ALWAYS
* animate and close. A nasty bug occurs with CSS transitions where
* when the active class isn't set, or if the active class doesn't
* contain any styles to transition to, then, if ngAnimate is used,
* it will appear as if the webpage is broken due to the forever hanging
* animations. The border-spacing (!ie) and zoom (ie) CSS properties are
* used below since they trigger a transition without making the browser
* animate anything and they're both highly underused CSS properties */
.ng-animate-start { border-spacing:1px 1px; -ms-zoom:1.0001; }
.ng-animate-active { border-spacing:0px 0px; -ms-zoom:1; }
+1 -1
View File
@@ -5,7 +5,7 @@ describe('Docs Annotations', function() {
var body;
beforeEach(function() {
body = angular.element(document.body);
body.html('');
body.empty();
});
var normalizeHtml = function(html) {
+2 -12
View File
@@ -28,7 +28,7 @@ function escape(text) {
function setHtmlIe8SafeWay(element, html) {
var newElement = angular.element('<pre>' + html + '</pre>');
element.html('');
element.empty();
element.append(newElement.contents());
return element;
}
@@ -215,17 +215,7 @@ directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location',
}];
this.html5Mode = angular.noop;
});
$provide.decorator('$timeout', ['$rootScope', '$delegate', function($rootScope, $delegate) {
return angular.extend(function(fn, delay) {
if (delay && delay > 50) {
return setTimeout(function() {
$rootScope.$apply(fn);
}, delay);
} else {
return $delegate.apply(this, arguments);
}
}, $delegate);
}]);
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
embedRootScope = $delegate;
+4 -1
View File
@@ -3,6 +3,9 @@
@description
# ng (core module)
The ng module is loaded by default when an AngularJS application is started. The module itself contains the essential components to for an AngularJS application to function. The table below lists a high level breakdown of each of the services/factories, filters, directives and testing components available within this core module.
The ng module is loaded by default when an AngularJS application is started. The module itself
contains the essential components for an AngularJS application to function. The table below
lists a high level breakdown of each of the services/factories, filters, directives and testing
components available within this core module.
<div doc-module-components="ng"></div>
@@ -1,9 +0,0 @@
@ngdoc error
@name $httpBackend:noxhr
@fullName Unsupported XHR
@description
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
supported browser.
+1 -1
View File
@@ -4,4 +4,4 @@
@description
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:select input} directive docs.
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.
+1 -1
View File
@@ -13,7 +13,7 @@ For example the issue can be triggered by this *invalid* code:
<div ng-repeat="value in [4, 4]"></div>
```
To resolve this error either ensure that the items in the collection have unique identity of use the `track by` syntax to specify how to track the association between models and DOM.
To resolve this error either ensure that the items in the collection have unique identity or use the `track by` syntax to specify how to track the association between models and DOM.
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
+2 -2
View File
@@ -3,9 +3,9 @@
@fullName Orphan ngTransclude Directive
@description
Occurs when an `ngTransclude` occurs without a transcluded ancesstor element.
Occurs when an `ngTransclude` occurs without a transcluded ancestor element.
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTranslude` in the driective's template.
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTransclude` in the directive's template.
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
@@ -50,7 +50,7 @@ of which depend on other services that are provided by the Angular framework:
* @param {*} message Message to be logged.
*/
function batchLogModule($provide){
$provide.factory('batchLog', ['$timeout', '$log', function($timeout, $log) {
$provide.factory('batchLog', ['$interval', '$log', function($interval, $log) {
var messageQueue = [];
function log() {
@@ -58,11 +58,10 @@ of which depend on other services that are provided by the Angular framework:
$log('batchLog messages: ', messageQueue);
messageQueue = [];
}
$timeout(log, 50000);
}
// start periodic checking
log();
$interval(log, 50000);
return function(message) {
messageQueue.push(message);
@@ -82,13 +81,13 @@ of which depend on other services that are provided by the Angular framework:
}]);
}
// get the main service to kick of the application
// get the main service to kick off the application
angular.injector([batchLogModule]).get('routeTemplateMonitor');
</pre>
Things to notice in this example:
* The `batchLog` service depends on the built-in {@link api/ng.$timeout $timeout} and
* The `batchLog` service depends on the built-in {@link api/ng.$interval $interval} and
{@link api/ng.$log $log} services, and allows messages to be logged into the
`console.log` in batches.
* The `routeTemplateMonitor` service depends on the built-in {@link api/ngRoute.$route
@@ -163,7 +163,7 @@ function MyClass(xhr) {
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
instead about whoever created the class responsible for passing it in. Since the creator of the
class should be different code than the user of the class, it separates the responsibility of
creation from the logic. This is dependency-injection is in a nutshell.
creation from the logic. This is dependency-injection in a nutshell.
The class above is testable, since in the test we can write:
<pre>
@@ -222,7 +222,7 @@ var pc = new PasswordCtrl();
input.val('abc');
pc.grade();
expect(span.text()).toEqual('weak');
$('body').html('');
$('body').empty();
</pre>
In angular the controllers are strictly separated from the DOM manipulation logic and this results in
+13 -15
View File
@@ -506,6 +506,8 @@ that you explicitly pass in.
<div class="alert alert-warning">
**Note:** Normally, a scope prototypically inherits from its parent. An isolated scope does not.
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
"Isolating the Scope of a Directive"} section for more information about isolate scopes.
</div>
<div class="alert alert-success">
@@ -528,8 +530,10 @@ where:
* `attrs` is an object with the normalized attribute names and their corresponding values.
In our `link` function, we want to update the displayed time once a second, or whenever a user
changes the time formatting string that our directive binds to. We also want to remove the timeout
if the directive is deleted so we don't introduce a memory leak.
changes the time formatting string that our directive binds to. We will use the `$interval` service
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test.
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
<example module="docsTimeDirective">
<file name="script.js">
@@ -537,7 +541,7 @@ if the directive is deleted so we don't introduce a memory leak.
.controller('Ctrl2', function($scope) {
$scope.format = 'M/d/yy h:mm:ss a';
})
.directive('myCurrentTime', function($timeout, dateFilter) {
.directive('myCurrentTime', function($interval, dateFilter) {
function link(scope, element, attrs) {
var format,
@@ -552,20 +556,14 @@ if the directive is deleted so we don't introduce a memory leak.
updateTime();
});
function scheduleUpdate() {
// save the timeoutId for canceling
timeoutId = $timeout(function() {
updateTime(); // update DOM
scheduleUpdate(); // schedule the next update
}, 1000);
}
element.on('$destroy', function() {
$timeout.cancel(timeoutId);
$interval.cancel(timeoutId);
});
// start the UI update process.
scheduleUpdate();
// start the UI update process; save the timeoutId for canceling
timeoutId = $interval(function() {
updateTime(); // update DOM
}, 1000);
}
return {
@@ -583,7 +581,7 @@ if the directive is deleted so we don't introduce a memory leak.
There are a couple of things to note here.
Just like the `module.controller` API, the function argument in `module.directive` is dependency
injected. Because of this, we can use `$timeout` and `dateFilter` inside our directive's `link`
injected. Because of this, we can use `$interval` and `dateFilter` inside our directive's `link`
function.
We register an event `element.on('$destroy', ...)`. What fires this `$destroy` event?
+1 -1
View File
@@ -18,7 +18,7 @@ It might be tempting to think of Angular view expressions as JavaScript expressi
not entirely correct, since Angular does not use a JavaScript `eval()` to evaluate expressions.
You can think of Angular expressions as JavaScript expressions with following differences:
* **Attribute Evaluation:** evaluation of all properties are against the scope, doing the
* **Attribute Evaluation:** evaluation of all properties are against the scope doing the
evaluation, unlike in JavaScript where the expressions are evaluated against the global
`window`.
+11 -6
View File
@@ -33,10 +33,10 @@ In addition it provides an {@link api/ng.directive:ngModel.NgModelController API
<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() {
@@ -115,9 +115,14 @@ This ensures that the user is not distracted with an error until after interacti
A form is an 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.
This implies that the internal state of both the form and the control is available for binding in the view using the standard binding primitives.
Similarly, an input control that has the {@link api/ng.directive:ngModel} directive holds an
instance of {@link api/ng.directive:ngModel.NgModelController NgModelController}.
Such a control instance can be published as a property of the form instance using the `name` attribute
on the input control. The name attribute specifies the name of the property on the form instance.
This implies that the internal state of both the form and the control is available for binding in
the view using the standard binding primitives.
This allows us to extend the above example with these features:
@@ -230,7 +235,7 @@ In the following example we create two directives.
<script>
var app = angular.module('form-example1', []);
var INTEGER_REGEXP = /^\-?\d*$/;
var INTEGER_REGEXP = /^\-?\d+$/;
app.directive('integer', function() {
return {
require: 'ngModel',
+6 -46
View File
@@ -7,19 +7,14 @@
Everything you need to know about AngularJS
* {@link guide/introduction What is AngularJS?}
* {@link guide/concepts Conceptual Overview}
## Tutorials
* {@link tutorial/index Official AngularJS Tutorial}
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
* [For folks with jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
## Core Concepts
@@ -29,63 +24,44 @@ Everything you need to know about AngularJS
In Angular applications, you move the job of filling page templates with data from the server to the client. The result is a system better structured for dynamic page updates. Below are the core features you'll use.
* {@link guide/databinding Data binding}
* {@link guide/expression Expressions}
* {@link guide/directive Directives}
* {@link api/ngRoute.$route Views and routes (see the example)}
* {@link guide/filter Filters}
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
### Application Structure
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
* **App wiring:** {@link guide/di Dependency injection}
* **Exposing model to templates:** {@link guide/scope Scopes}
* **Communicating with servers:** {@link api/ng.$http $http}, {@link api/ngResource.$resource $resource}
### Other AngularJS Features
* **Animation:** {@link guide/animations Core concepts}, {@link api/ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
* **Security:** {@link api/ng.$sce Strict Contextual Escaping}, {@link api/ng.directive:ngCsp Content Security Policy}, {@link api/ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link api/ng.filter:date date filter}, {@link api/ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
* **Mobile:** {@link api/ngTouch Touch events}
### Testing
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
## Specific Topics
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
## Tools
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
## Complementary Libraries
@@ -93,37 +69,26 @@ In Angular applications, you move the job of filling page templates with data fr
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
* **Internationalization:** [angular-translate](http://pascalprecht.github.io/angular-translate/), [angular-gettext](http://angular-gettext.rocketeer.be/)
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/)
## Deployment
### General
### General
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ngmin automation tool](http://www.thinkster.io/pick/XlWneEZCqY/angularjs-ngmin)
* **Tracking:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
### Server-Specific
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html)
* **FireBase:** [AngularFire](http://angularfire.com/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/resources/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
## Learning Resources
@@ -137,18 +102,18 @@ This is a short list of libraries with specific support and documentation for wo
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
###Videos:
* [egghead.io](http://egghead.io/),
* [egghead.io](http://egghead.io/)
* [Angular on YouTube](http://youtube.com/angularjs)
###Courses
* **Free on-line:**
### Courses
* **Free online:**
[thinkster.io](http://thinkster.io),
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
* **Paid on-line:**
* **Paid online:**
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
* **Paid on-site:**
* **Paid onsite:**
[angularbootcamp.com](http://angularbootcamp.com/)
## Getting Help
@@ -156,19 +121,14 @@ This is a short list of libraries with specific support and documentation for wo
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
## Social Channels
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
## Contributing to AngularJS
+10 -4
View File
@@ -18,8 +18,12 @@ watch {@link guide/expression expressions} and propagate events.
propagate any model changes through the system into the view from outside of the "Angular
realm" (controllers, services, Angular event handlers).
- Scopes can be nested to isolate application components while providing access to shared model
properties. A scope (prototypically) inherits properties from its parent scope.
- Scopes can be nested to limit access to the properties of application components while providing
access to shared model properties. Nested scopes are either "child scopes" or "isolate scopes".
A "child scope" (prototypically) inherits properties from its parent scope. An "isolate scope"
does not. See {@link
guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive isolated
scopes} for more information.
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
@@ -259,8 +263,8 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
For mutations to be properly observed, you should make them only within the {@link
api/ng.$rootScope.Scope#methods_$apply scope.$apply()}. (Angular APIs do this
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
or asynchronous work with {@link api/ng.$http $http} or {@link
api/ng.$timeout $timeout} services.
or asynchronous work with {@link api/ng.$http $http}, {@link api/ng.$timeout $timeout}
or {@link api/ng.$interval $interval} services.
4. **Mutation observation**
@@ -306,6 +310,8 @@ api/ng.directive:ngController ng-controller} and {@link
api/ng.directive:ngRepeat ng-repeat}, create new child scopes
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
element by using an `angular.element(aDomElement).scope()` method call.
See the {@link guide/directive#creating-custom-directives_demo_isolating-the-scope-of-a-directive
directives guide} for more information about isolate scopes.
### Controllers and Scopes
+3 -1
View File
@@ -23,7 +23,7 @@ for how to contribute your own code to AngularJS.
Before you can build AngularJS, you must install and configure the following dependencies on your
machine:
* {@link http://git-scm.com/ Git}: The {@link http://help.github.com/mac-git-installation Github Guide to
* {@link http://git-scm.com/ Git}: The {@link https://help.github.com/articles/set-up-git Github Guide to
Installing Git} is a good source of information.
* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a
@@ -46,6 +46,8 @@ and included in your {@link http://docs.oracle.com/javase/tutorial/essential/env
npm install -g bower
```
**Note:** You may need to use sudo (for OSX, *nix, BSD etc) or run your command shell as Administrator (for Windows) to install Grunt &amp;
Bower globally.
## Forking Angular on Github
+1 -1
View File
@@ -179,7 +179,7 @@ For the purposes of this tutorial, we modified the angular-seed with the followi
* Removed the example app
* Added phone images to `app/img/phones/`
* Added phone data files (JSON) to `app/phones/`
* Added [Bootstrap](http://twitter.github.com/bootstrap/) files to `app/css/` and `app/img/`
* Added [Bootstrap](http://getbootstrap.com) files to `app/css/` and `app/img/`
+1 -1
View File
@@ -88,7 +88,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
record. This property is used to order phones by age.
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
not set the default value here, the model would stay uninitialized until our user would pick an
not set the default value here, the model would stay uninitialized until our user picks an
option from the drop down menu.
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
+1 -1
View File
@@ -199,7 +199,7 @@ isolated from the work done in other tests.
* We created a new scope for our controller by calling `$rootScope.$new()`
* We called the injected `$controller` function passing the name of the`PhoneListCtrl` controller
* We called the injected `$controller` function passing the name of the `PhoneListCtrl` controller
and the created scope as parameters.
Because our code now uses the `$http` service to fetch the phone list data in our controller, before
+2 -2
View File
@@ -258,7 +258,7 @@ to various URLs and verify that the correct view was rendered.
<pre>
...
it('should redirect index.html to index.html#/phones', function() {
browser().navigateTo('../../app/index.html');
browser().navigateTo('app/index.html');
expect(browser().location().url()).toBe('/phones');
});
...
@@ -266,7 +266,7 @@ to various URLs and verify that the correct view was rendered.
describe('Phone detail view', function() {
beforeEach(function() {
browser().navigateTo('../../app/index.html#/phones/nexus-s');
browser().navigateTo('app/index.html#/phones/nexus-s');
});
+2 -2
View File
@@ -11,7 +11,7 @@ In this step, you will improve the way our app fetches data.
<div doc-tutorial-reset="11"></div>
The last improvement we will make to our app is to define a custom service that represents a {@link
The next improvement we will make to our app is to define a custom service that represents a {@link
http://en.wikipedia.org/wiki/Representational_State_Transfer RESTful} client. Using this client we
can make XHR requests for data in an easier way, without having to deal with the lower-level {@link
api/ng.$http $http} API, HTTP methods and URLs.
@@ -185,7 +185,7 @@ describe('PhoneCat controllers', function() {
xyzPhoneData = function() {
return {
name: 'phone xyz',
images: ['image/url1.png', 'image/url2.png']
images: ['image/url1.png', 'image/url2.png']
}
};
+1 -1
View File
@@ -3,7 +3,7 @@
*/
exports.appCache = appCache;
var fs = require('q-fs');
var fs = require('q-io/fs');
var Q = require('qq');
function identity($) {return $;}
+4 -4
View File
@@ -74,10 +74,10 @@ function writeTheRest(writesFuture) {
var versions = ngdoc.ngVersions();
var currentVersion = ngdoc.ngCurrentVersion();
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'dir'));
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir'));
writesFuture.push(writer.symlinkTemplate('js', 'dir'));
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'directory'));
writesFuture.push(writer.symlinkTemplate('css', 'directory'));
writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'directory'));
writesFuture.push(writer.symlinkTemplate('js', 'directory'));
var manifest = 'manifest="/build/docs/appcache.manifest"';
+1 -1
View File
@@ -7,7 +7,7 @@ exports.collect = collect;
var ngdoc = require('./ngdoc.js'),
Q = require('qq'),
qfs = require('q-fs'),
qfs = require('q-io/fs'),
PATH = require('path');
var NEW_LINE = /\n\r?/;
+25
View File
@@ -144,6 +144,27 @@
.content h4,
.content h5 {
margin-top: 1em;
letter-spacing: -0.06em;
}
.content h2 {
font-size: 36px;
margin-bottom: .5em;
}
.content h3 {
font-size: 24px;
border-top: 1px solid #eee;
padding-top: .5em;
}
.content h4 {
font-size: 16px;
margin-top: 1.5em;
}
.content ul {
margin-top: .5em;
}
ul.parameters > li > p,
@@ -522,6 +543,10 @@ pre ol li {
margin-bottom:30px;
}
.definition-table td {
vertical-align: top;
}
.component-heading {
text-transform:capitalize;
}
+4 -4
View File
@@ -273,10 +273,10 @@ docsApp.directive.docTutorialNav = function(templateMerge) {
element.addClass('btn-group');
element.addClass('tutorial-nav');
element.append(templateMerge(
'<li class="btn btn-primary"><a href="tutorial/{{prev}}"><i class="icon-step-backward"></i> Previous</a></li>\n' +
'<li class="btn btn-primary"><a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><i class="icon-play"></i> Live Demo</a></li>\n' +
'<li class="btn btn-primary"><a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><i class="icon-search"></i> Code Diff</a></li>\n' +
'<li class="btn btn-primary"><a href="tutorial/{{next}}">Next <i class="icon-step-forward"></i></a></li>', props));
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
}
};
};
+2 -2
View File
@@ -3,7 +3,7 @@
* for testability
*/
var pathUtils = require('path');
var qfs = require('q-fs');
var qfs = require('q-io/fs');
var Q = require('qq');
var OUTPUT_DIR = pathUtils.join('build','docs');
var TEMPLATES_DIR = pathUtils.join('docs','src','templates');
@@ -76,7 +76,7 @@ function symlink(from, to, type) {
// qfs will normalize the path arguments for us here
return qfs.exists(to).then(function(exists) {
if (!exists) {
return qfs.symbolicLink(to, from, type);
return qfs.symbolicLink(to, from, type || 'file');
}
});
}
+6 -2
View File
@@ -1,9 +1,12 @@
#!/bin/bash
echo "#################################"
echo "#### Jenkins Build ############"
echo "#################################"
# Enable tracing and exit on first failure
set -xe
# Define reasonable set of browsers in case we are running manually from commandline
if [[ -z "$BROWSERS" ]]
then
@@ -25,11 +28,12 @@ rm -f angular.js.size
npm install --color false
grunt ci-checks package --no-color
# DOCS generator unit tests #
grunt test:docgen --no-color
# UNIT TESTS #
grunt test:unit --browsers $BROWSERS --reporters=dots,junit --no-colors --no-color
# END TO END TESTS #
grunt test:e2e --browsers $BROWSERS_E2E --reporters=dots,junit --no-colors --no-color
+4 -1
View File
@@ -13,7 +13,10 @@ module.exports = function(config, specificOptions) {
// SauceLabs config for local development.
sauceLabs: {
testName: specificOptions.testName || 'AngularJS',
startConnect: true
startConnect: true,
options: {
'selenium-version': '2.37.0'
}
},
// BrowserStack config for local development.
+7 -2
View File
@@ -36,13 +36,12 @@ module.exports = {
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
var semver = match[1].split('.');
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
var fullVersion = match[1];
if (match[2]) {
fullVersion += '-';
fullVersion += (match[2] == 'snapshot') ? hash : match[2];
fullVersion += (match[2] == 'snapshot') ? getSnapshotSuffix() : match[2];
}
version = {
@@ -55,6 +54,12 @@ module.exports = {
};
return version;
function getSnapshotSuffix() {
var jenkinsBuild = process.env.BUILD_NUMBER || 'local';
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
return 'build.'+jenkinsBuild+'+sha.'+hash;
}
},
+2 -2
View File
@@ -36,8 +36,8 @@ ARGS=""
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"
if [ ! -z "$BROWSER_PROVIDER_READY_FILE" ]; then
ARGS="$ARGS --readyfile $BROWSER_PROVIDER_READY_FILE"
fi
+7 -6
View File
@@ -1,8 +1,8 @@
{
"name": "angularjs",
"version": "1.2.4",
"cdnVersion": "1.2.3",
"codename": "wormhole-baster",
"version": "1.2.7",
"cdnVersion": "1.2.6",
"codename": "emoji-clairvoyance",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
@@ -10,22 +10,23 @@
"devDependencies": {
"grunt": "~0.4.1",
"bower": "~1.2.2",
"grunt-bump": "~0.0.13",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-copy": "~0.4.1",
"jasmine-node": "~1.11.0",
"q": "~0.9.2",
"q-fs": "~0.1.36",
"q-io": "~1.10.6",
"qq": "~0.3.5",
"shelljs": "~0.2.6",
"karma": "~0.11",
"karma": "0.11.11",
"karma-jasmine": "~0.1.0",
"karma-chrome-launcher": "~0.1.0",
"karma-firefox-launcher": "~0.1.0",
"karma-ng-scenario": "~0.1.0",
"karma-junit-reporter": "~0.2.1",
"karma-sauce-launcher": "~0.1.1",
"karma-sauce-launcher": "~0.2.0",
"karma-script-launcher": "~0.1.0",
"yaml-js": "~0.0.8",
"marked": "0.2.9",
-34
View File
@@ -1,34 +0,0 @@
#!/usr/bin/env bash
function catch_errors() {
echo "ERROR. That's life."
exit 1
}
trap catch_errors ERR
TMP_FILE='changelog.tmp'
CHANGELOG_FILE='CHANGELOG.md'
echo "Getting current version..."
VERSION=`./version.js --current`
echo "Generating changelog..."
./changelog.js $VERSION $TMP_FILE
cat $CHANGELOG_FILE >> $TMP_FILE
mv -f $TMP_FILE $CHANGELOG_FILE
echo "Updating version..."
./version.js --remove-snapshot
echo "CONFIRM TO COMMIT"
read WHATEVER
echo "Creating commit..."
git commit version.yaml CHANGELOG.md -m "chore(relase): cutting the v$VERSION release"
echo "Creating tag..."
git tag "v$VERSION"
+3 -11
View File
@@ -1,23 +1,15 @@
# Angular Bower Script
Script for updating the Angular bower repos from a code.angularjs.org package
Requires `node` (for parsing `bower.json`) and `wget` (for fetching the `angular.zip` from `code.angularjs.org`)
Script for updating the Angular bower repos from current local build.
## Instructions
You need to run `./init.sh` the first time you use this script to clone all the repos.
For subsequent updates:
`grunt package`: Build angular locally
```shell
./publish.sh NEW_VERSION
./publish.sh
```
Where `NEW_VERSION` is a version number like `1.2.3`.
## License
MIT
-28
View File
@@ -1,28 +0,0 @@
#!/bin/bash
#
# init all of the bower repos
#
set -e # fail if any command fails
REPOS=(
angular \
angular-animate \
angular-cookies \
angular-i18n \
angular-loader \
angular-mocks \
angular-route \
angular-resource \
angular-sanitize \
angular-scenario \
angular-touch \
)
cd `dirname $0`
for repo in "${REPOS[@]}"
do
git clone git@github.com:angular/bower-$repo.git
done
+36 -35
View File
@@ -1,18 +1,18 @@
#!/bin/bash
#
# update all the things
#
set -e # fail if any command fails
echo "#################################"
echo "#### Update bower ###############"
echo "#################################"
# Enable tracing and exit on first failure
set -xe
# Normalize working dir to script dir
cd `dirname $0`
NEW_VERSION=$1
ZIP_FILE=angular-$NEW_VERSION.zip
ZIP_FILE_URL=http://code.angularjs.org/$NEW_VERSION/angular-$NEW_VERSION.zip
ZIP_DIR=angular-$NEW_VERSION
SCRIPT_DIR=`pwd`
TMP_DIR=../../tmp
BUILD_DIR=../../build
NEW_VERSION=`cat $BUILD_DIR/version.txt`
REPOS=(
angular \
@@ -28,46 +28,42 @@ REPOS=(
angular-touch \
)
#
# download and unzip the file
# clone repos
#
#wget $ZIP_FILE_URL
unzip $ZIP_FILE
for repo in "${REPOS[@]}"
do
echo "-- Cloning bower-$repo"
git clone git@github.com:angular/bower-$repo.git $TMP_DIR/bower-$repo
done
#
# move the files from the zip
# move the files from the build
#
for repo in "${REPOS[@]}"
do
if [ -f $ZIP_DIR/$repo.js ] # ignore i18l
if [ -f $BUILD_DIR/$repo.js ] # ignore i18l
then
cd bower-$repo
git checkout master
echo "-- Updating files in bower-$repo"
cd $TMP_DIR/bower-$repo
git reset --hard HEAD
cd ..
mv $ZIP_DIR/$repo.* bower-$repo/
git checkout master
git fetch --all
git reset --hard origin/master
cd $SCRIPT_DIR
cp $BUILD_DIR/$repo.* $TMP_DIR/bower-$repo/
fi
done
# move i18n files
mv $ZIP_DIR/i18n/*.js bower-angular-i18n/
cp $BUILD_DIR/i18n/*.js $TMP_DIR/bower-angular-i18n/
# move csp.css
mv $ZIP_DIR/angular-csp.css bower-angular
cp $BUILD_DIR/angular-csp.css $TMP_DIR/bower-angular
#
# get the old version number
#
OLD_VERSION=$(node -e "console.log(require('./bower-angular/bower').version)" | sed -e 's/\r//g')
echo $OLD_VERSION
echo $NEW_VERSION
#
# update bower.json
# tag each repo
@@ -75,13 +71,18 @@ echo $NEW_VERSION
for repo in "${REPOS[@]}"
do
cd bower-$repo
pwd
sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" bower.json
echo "-- Updating version in bower-$repo to $NEW_VERSION"
cd $TMP_DIR/bower-$repo
sed -i .tmp -E 's/"(version)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
sed -i .tmp -E 's/"(angular.*)":[ ]*".*"/"\1": "'$NEW_VERSION'"/g' bower.json
# delete tmp files
rm *.tmp
git add -A
echo "-- Committing, tagging and pushing bower-$repo"
git commit -m "v$NEW_VERSION"
git tag v$NEW_VERSION
git push origin master
git push origin v$NEW_VERSION
cd ..
cd $SCRIPT_DIR
done
+18
View File
@@ -0,0 +1,18 @@
# code.angular.js.org Script
Script for updating code.angularjs.org repo from current local build.
Note: For a snapshot build, this will fetch the data from the ci server
and NOT take the local build!
## Instructions
`grunt package`: Build angular locally
```shell
./publish.sh
```
## License
MIT
+62
View File
@@ -0,0 +1,62 @@
#!/bin/bash
echo "#################################"
echo "## Update code.angular.js.org ###"
echo "#################################"
# Enable tracing and exit on first failure
set -xe
# Normalize working dir to script dir
cd `dirname $0`
TMP_DIR=../../tmp
REPO_DIR=$TMP_DIR/code.angularjs.org
BUILD_DIR=../../build
SCRIPT_DIR=`pwd`
NEW_VERSION=`cat $BUILD_DIR/version.txt`
#
# Snapshot builds are kept in a temp directory in code.angularjs.org
# that is filled by calling a php script there.
#
if [[ "$NEW_VERSION" =~ sha ]] ;then
echo "-- updating snapshot version"
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
exit 0;
fi
#
# clone
#
echo "-- Cloning code.angularjs.org"
git clone git@github.com:angular/code.angularjs.org.git $REPO_DIR
#
# copy the files from the build
#
echo "-- Updating code.angularjs.org"
mkdir $REPO_DIR/$NEW_VERSION
cd $REPO_DIR
git reset --hard HEAD
git checkout master
git fetch --all
git reset --hard origin/master
cd $SCRIPT_DIR
cp -r $BUILD_DIR/* $REPO_DIR/$NEW_VERSION/
#
# commit and push
#
echo "-- Committing and pushing code.angularjs.org"
cd $REPO_DIR
git add -A
git commit -m "v$NEW_VERSION"
git push origin master
cd $SCRIPT_DIR
#
# refresh code.angularjs.org from github
#
curl http://code.angularjs.org/gitFetchSite.php
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
echo "############################################"
echo "## Increment version and add "-snapshot" ##"
echo "############################################"
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
echo "Please specify the next version type: patch|minor|major"
exit 1
fi
BUMP_TYPE=$1
# Enable tracing and exit on first failure
set -xe
# Normalize working dir to script dir
cd `dirname $0`/../..
echo "-- increment version "
grunt bump:$BUMP_TYPE
NEXT_VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
sed -i .tmp -E 's/"version": "(.*)"/"version": "\1-snapshot"/' package.json
echo "-- new version: `grep '"version"' package.json`"
echo "-- commit"
git add package.json
git commit -m "chore(release): start v$NEXT_VERSION"
+20
View File
@@ -0,0 +1,20 @@
#!/bin/bash
echo "############################################"
echo "## Remove "-snapshot" from version ########"
echo "############################################"
# Enable tracing and exit on first failure
set -xe
# Normalize working dir to script dir
cd `dirname $0`/../..
echo "-- old version: `grep '"version"' package.json`"
sed -i .tmp -E 's/"version": "(.*)-snapshot"/"version": "\1"/' package.json
VERSION=`sed -En 's/.*"version"[ ]*:[ ]*"(.*)".*/\1/p' package.json`
echo "-- local version: $VERSION"
echo "-- commit and tag with v$VERSION"
git add package.json
git commit -m "chore(release): cut v$VERSION release"
git tag -m "v$VERSION" v$VERSION
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
echo "#################################"
echo "#### Update master ##############"
echo "#################################"
# Enable tracing and exit on first failure
set -xe
cd `dirname $0`/../..
echo "#################################"
echo "#### Jenkins Build ############"
echo "#################################"
./jenkins_build.sh
echo "#################################"
echo "## Update code.angular.js.org ###"
echo "#################################"
./scripts/code.angularjs.org/publish.sh
echo "#################################"
echo "#### Update bower ###############"
echo "#################################"
./scripts/bower/publish.sh
+44
View File
@@ -0,0 +1,44 @@
#!/bin/bash
echo "#################################"
echo "#### Cut release ################"
echo "#################################"
if [ "$1" != "patch" -a "$1" != "minor" -a "$1" != "major" ]; then
echo "Please specify the next version type: patch|minor|major"
exit 1
fi
BUMP_TYPE=$1
# Enable tracing and exit on first failure
set -xe
# Jump onto the master branch and make sure we are using the latest
git checkout -f master
git merge --ff-only origin/master
# Normalize working dir to script dir
cd `dirname $0`/../..
# Bump versions: remove "-snapshot" suffix
./scripts/jenkins/bump-remove-snapshot.sh
# Build
./jenkins_build.sh
# Bump versions: Increment version and add "-snapshot"
./scripts/jenkins/bump-increment.sh $BUMP_TYPE
echo "-- push to Github"
# push the commits to github
git push origin master
# push the release tag
git push origin v`cat build/version.txt`
# Update code.angularjs.org
./scripts/code.angularjs.org/publish.sh
# Update bower
./scripts/bower/publish.sh
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
set -e
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
if [ $JOB = "unit" ]; then
grunt ci-checks
grunt test:docgen
grunt test:promises-aplus
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
elif [ $JOB = "e2e" ]; then
grunt test:e2e --browsers SL_Chrome --reporters dots
else
echo "Unknown job type. Please set JOB=unit or JOB=e2e."
fi
@@ -2,6 +2,6 @@
# Wait for Connect to be ready before exiting
while [ ! -f $SAUCE_CONNECT_READY_FILE ]; do
while [ ! -f $BROWSER_PROVIDER_READY_FILE ]; do
sleep .5
done
+20 -16
View File
@@ -213,7 +213,9 @@ function forEach(obj, iterator, context) {
if (obj) {
if (isFunction(obj)){
for (key in obj) {
if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) {
// Need to check if hasOwnProperty exists,
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
iterator.call(context, obj[key], key);
}
}
@@ -393,7 +395,7 @@ function valueFn(value) {return function() {return value;};}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is undefined.
*/
function isUndefined(value){return typeof value == 'undefined';}
function isUndefined(value){return typeof value === 'undefined';}
/**
@@ -407,7 +409,7 @@ function isUndefined(value){return typeof value == 'undefined';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is defined.
*/
function isDefined(value){return typeof value != 'undefined';}
function isDefined(value){return typeof value !== 'undefined';}
/**
@@ -422,7 +424,7 @@ function isDefined(value){return typeof value != 'undefined';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Object` but not `null`.
*/
function isObject(value){return value != null && typeof value == 'object';}
function isObject(value){return value != null && typeof value === 'object';}
/**
@@ -436,7 +438,7 @@ function isObject(value){return value != null && typeof value == 'object';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `String`.
*/
function isString(value){return typeof value == 'string';}
function isString(value){return typeof value === 'string';}
/**
@@ -450,7 +452,7 @@ function isString(value){return typeof value == 'string';}
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Number`.
*/
function isNumber(value){return typeof value == 'number';}
function isNumber(value){return typeof value === 'number';}
/**
@@ -465,7 +467,7 @@ function isNumber(value){return typeof value == 'number';}
* @returns {boolean} True if `value` is a `Date`.
*/
function isDate(value){
return toString.apply(value) == '[object Date]';
return toString.call(value) === '[object Date]';
}
@@ -481,7 +483,7 @@ function isDate(value){
* @returns {boolean} True if `value` is an `Array`.
*/
function isArray(value) {
return toString.apply(value) == '[object Array]';
return toString.call(value) === '[object Array]';
}
@@ -496,7 +498,7 @@ function isArray(value) {
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Function`.
*/
function isFunction(value){return typeof value == 'function';}
function isFunction(value){return typeof value === 'function';}
/**
@@ -507,7 +509,7 @@ function isFunction(value){return typeof value == 'function';}
* @returns {boolean} True if `value` is a `RegExp`.
*/
function isRegExp(value) {
return toString.apply(value) == '[object RegExp]';
return toString.call(value) === '[object RegExp]';
}
@@ -529,12 +531,12 @@ function isScope(obj) {
function isFile(obj) {
return toString.apply(obj) === '[object File]';
return toString.call(obj) === '[object File]';
}
function isBoolean(value) {
return typeof value == 'boolean';
return typeof value === 'boolean';
}
@@ -638,7 +640,7 @@ function includes(array, obj) {
function indexOf(array, obj) {
if (array.indexOf) return array.indexOf(obj);
for ( var i = 0; i < array.length; i++) {
for (var i = 0; i < array.length; i++) {
if (obj === array[i]) return i;
}
return -1;
@@ -769,7 +771,7 @@ function shallowCopy(src, dst) {
for(var key in src) {
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
// so we don't need to worry about using our custom hasOwnProperty here
if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') {
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
dst[key] = src[key];
}
}
@@ -957,7 +959,9 @@ function fromJson(json) {
function toBoolean(value) {
if (value && value.length !== 0) {
if (typeof value === 'function') {
value = true;
} else if (value && value.length !== 0) {
var v = lowercase("" + value);
value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
} else {
@@ -974,7 +978,7 @@ function startingTag(element) {
try {
// turns out IE does not let you set .html() on elements which
// are not allowed to have children. So we just ignore it.
element.html('');
element.empty();
} catch(e) {}
// As Per DOM Standards
var TEXT_NODE = 3;
+4
View File
@@ -28,6 +28,7 @@
ngHideDirective,
ngIfDirective,
ngIncludeDirective,
ngIncludeFillContentDirective,
ngInitDirective,
ngNonBindableDirective,
ngPluralizeDirective,
@@ -181,6 +182,9 @@ function publishExternalAPI(angular){
ngRequired: requiredDirective,
ngValue: ngValueDirective
}).
directive({
ngInclude: ngIncludeFillContentDirective
}).
directive(ngAttributeAliasDirectives).
directive(ngEventDirectives);
$provide.provider({
+33 -21
View File
@@ -27,6 +27,28 @@
* $rootScope.$digest();
* });
* </pre>
*
* Sometimes you want to get access to the injector of a currently running Angular app
* from outside Angular. Perhaps, you want to inject and compile some markup after the
* application has been bootstrapped. You can do this using extra `injector()` added
* to JQuery/jqLite elements. See {@link angular.element}.
*
* *This is fairly rare but could be the case if a third party library is injecting the
* markup.*
*
* In the following example a new block of HTML containing a `ng-controller`
* directive is added to the end of the document body by JQuery. We then compile and link
* it into the current AngularJS scope.
*
* <pre>
* var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
* $(document.body).append($div);
*
* angular.element(document).injector().invoke(function($compile) {
* var scope = angular.element($div).scope();
* $compile($div)(scope);
* });
* </pre>
*/
@@ -463,15 +485,15 @@ function annotate(fn) {
* {@link AUTO.$provide#methods_service $provide.service(class)} that is defined as a CoffeeScript class.
* <pre>
* class Ping
* constructor: (@$http)->
* send: ()=>
* constructor: (@$http) ->
* send: () =>
* @$http.get('/ping')
*
* $provide.service('ping', ['$http', Ping])
* </pre>
* You would then inject and use this service like this:
* <pre>
* someModule.controller 'Ctrl', ['ping', (ping)->
* someModule.controller 'Ctrl', ['ping', (ping) ->
* ping.send()
* ]
* </pre>
@@ -718,6 +740,11 @@ function createInjector(modulesToLoad) {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
return cache[serviceName] = factory(serviceName);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
} finally {
path.shift();
}
@@ -747,24 +774,9 @@ function createInjector(modulesToLoad) {
fn = fn[length];
}
// Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke
switch (self ? -1 : args.length) {
case 0: return fn();
case 1: return fn(args[0]);
case 2: return fn(args[0], args[1]);
case 3: return fn(args[0], args[1], args[2]);
case 4: return fn(args[0], args[1], args[2], args[3]);
case 5: return fn(args[0], args[1], args[2], args[3], args[4]);
case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
args[8]);
case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7],
args[8], args[9]);
default: return fn.apply(self, args);
}
// http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
}
function instantiate(Type, locals) {
+36 -5
View File
@@ -46,6 +46,7 @@
* - [`contents()`](http://api.jquery.com/contents/)
* - [`css()`](http://api.jquery.com/css/)
* - [`data()`](http://api.jquery.com/data/)
* - [`empty()`](http://api.jquery.com/empty/)
* - [`eq()`](http://api.jquery.com/eq/)
* - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
* - [`hasClass()`](http://api.jquery.com/hasClass/)
@@ -53,6 +54,7 @@
* - [`next()`](http://api.jquery.com/next/) - Does not support selectors
* - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
* - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
* - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
* - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
* - [`prepend()`](http://api.jquery.com/prepend/)
* - [`prop()`](http://api.jquery.com/prop/)
@@ -358,6 +360,15 @@ function jqLiteInheritedData(element, name, value) {
}
}
function jqLiteEmpty(element) {
for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
jqLiteDealoc(childNodes[i]);
}
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
//////////////////////////////////////////
// Functions which are declared directly.
//////////////////////////////////////////
@@ -552,7 +563,9 @@ forEach({
jqLiteDealoc(childNodes[i]);
}
element.innerHTML = value;
}
},
empty: jqLiteEmpty
}, function(fn, name){
/**
* Properties: writes return selection, reads return first value
@@ -562,11 +575,13 @@ forEach({
// jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
// in a way that survives minification.
if (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined) {
// jqLiteEmpty takes no arguments but is a setter.
if (fn !== jqLiteEmpty &&
(((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
if (isObject(arg1)) {
// we are a write, but the object properties are the key/values
for(i=0; i < this.length; i++) {
for (i = 0; i < this.length; i++) {
if (fn === jqLiteData) {
// data() takes the whole object in jQuery
fn(this[i], arg1);
@@ -591,7 +606,7 @@ forEach({
}
} else {
// we are a write, so apply to all children
for(i=0; i < this.length; i++) {
for (i = 0; i < this.length; i++) {
fn(this[i], arg1, arg2);
}
// return self for chaining
@@ -631,7 +646,10 @@ function createEventHandler(element, events) {
return event.defaultPrevented || event.returnValue === false;
};
forEach(events[type || event.type], function(fn) {
// Copy event handlers in case event handlers array is modified during execution.
var eventHandlersCopy = shallowCopy(events[type || event.type] || []);
forEach(eventHandlersCopy, function(fn) {
fn.call(element, event);
});
@@ -727,6 +745,19 @@ forEach({
off: jqLiteOff,
one: function(element, type, fn) {
element = jqLite(element);
//add the listener twice so that when it is called
//you can remove the original function and still be
//able to call element.off(ev, fn) normally
element.on(type, function onFn() {
element.off(type, fn);
element.off(type, onFn);
});
element.on(type, fn);
},
replaceWith: function(element, replaceNode) {
var index, parent = element.parentNode;
jqLiteDealoc(element);
+22
View File
@@ -61,6 +61,28 @@ var $AnimateProvider = ['$provide', function($provide) {
$provide.factory(key, factory);
};
/**
* @ngdoc function
* @name ng.$animateProvider#classNameFilter
* @methodOf ng.$animateProvider
*
* @description
* Sets and/or returns the CSS class regular expression that is checked when performing
* an animation. Upon bootstrap the classNameFilter value is not set at all and will
* therefore enable $animate to attempt to perform an animation on any element.
* When setting the classNameFilter value, animations will only be performed on elements
* that successfully match the filter expression. This in turn can boost performance
* for low-powered devices as well as applications containing a lot of structural operations.
* @param {RegExp=} expression The className expression which will be checked against all animations
* @return {RegExp} The current CSS className expression value. If null then there is no expression value
*/
this.classNameFilter = function(expression) {
if(arguments.length === 1) {
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
}
return this.$$classNameFilter;
};
this.$get = ['$timeout', function($timeout) {
/**
+7 -6
View File
@@ -148,8 +148,9 @@ function Browser(window, document, $log, $sniffer) {
* @param {boolean=} replace Should new url replace current history record ?
*/
self.url = function(url, replace) {
// Android Browser BFCache causes location reference to become stale.
// Android Browser BFCache causes location, history reference to become stale.
if (location !== window.location) location = window.location;
if (history !== window.history) history = window.history;
// setter
if (url) {
@@ -201,7 +202,7 @@ function Browser(window, document, $log, $sniffer) {
* @description
* Register callback function that will be called, when url changes.
*
* It's only called when the url is changed by outside of angular:
* It's only called when the url is changed from outside of angular:
* - user types different url into address bar
* - user clicks on history (forward/back) button
* - user clicks on a link
@@ -243,7 +244,7 @@ function Browser(window, document, $log, $sniffer) {
/**
* @name ng.$browser#baseHref
* @methodOf ng.$browser
*
*
* @description
* Returns current <base href>
* (always relative - without domain)
@@ -252,7 +253,7 @@ function Browser(window, document, $log, $sniffer) {
*/
self.baseHref = function() {
var href = baseElement.attr('href');
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
};
//////////////////////////////////////////////////////////////
@@ -274,13 +275,13 @@ function Browser(window, document, $log, $sniffer) {
* It is not meant to be used directly, use the $cookie service instead.
*
* The return values vary depending on the arguments that the method was called with as follows:
*
*
* - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
* it
* - cookies(name, value) -> set name to value, if value is undefined delete the cookie
* - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
* way)
*
*
* @returns {Object} Hash of all cookies (if called without any parameter)
*/
self.cookies = function(name, value) {
+37 -30
View File
@@ -24,7 +24,7 @@
* @function
*
* @description
* Compiles a piece of HTML string or DOM into a template and produces a template function, which
* Compiles an HTML string or DOM into a template and produces a template function, which
* can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
*
* The compilation is a process of walking the DOM tree and matching DOM elements to
@@ -469,14 +469,14 @@
* example would not point to the clone, but rather to the original template that was cloned. In
* this case, you can access the clone via the cloneAttachFn:
* <pre>
* var templateHTML = angular.element('<p>{{total}}</p>'),
* var templateElement = angular.element('<p>{{total}}</p>'),
* scope = ....;
*
* var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) {
* var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
* //attach the clone to DOM document at the right place
* });
*
* //now we have reference to the cloned DOM via `clone`
* //now we have reference to the cloned DOM via `clonedElement`
* </pre>
*
*
@@ -818,6 +818,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
safeAddClass($compileNodes, 'ng-scope');
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
assertArg(scope, 'scope');
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
@@ -832,12 +833,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// Attach scope only to non-text nodes.
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
var node = $linkNode[i];
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
var node = $linkNode[i],
nodeType = node.nodeType;
if (nodeType === 1 /* element */ || nodeType === 9 /* document */) {
$linkNode.eq(i).data('$scope', scope);
}
}
safeAddClass($linkNode, 'ng-scope');
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
return $linkNode;
@@ -865,15 +867,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
* the rootElement must be set the jqLite collection of the compile root. This is
* needed so that the jqLite collection items can be replaced with widgets.
* @param {number=} max directive priority
* @param {number=} maxPriority Max directive priority.
* @returns {?function} A composite linking function of all of the matched directives or null.
*/
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
previousCompileContext) {
var linkFns = [],
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;
attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound;
for(var i = 0; i < nodeList.length; i++) {
for (var i = 0; i < nodeList.length; i++) {
attrs = new Attributes();
// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
@@ -885,16 +887,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
null, [], [], previousCompileContext)
: null;
if (nodeLinkFn && nodeLinkFn.scope) {
safeAddClass(jqLite(nodeList[i]), 'ng-scope');
}
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
!nodeList[i].childNodes ||
!nodeList[i].childNodes.length)
!(childNodes = nodeList[i].childNodes) ||
!childNodes.length)
? null
: compileNodes(nodeList[i].childNodes,
: compileNodes(childNodes,
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);
linkFns.push(nodeLinkFn);
linkFns.push(childLinkFn);
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
linkFns.push(nodeLinkFn, childLinkFn);
linkFnFound = linkFnFound || nodeLinkFn || childLinkFn;
//use the previous context only for the first element in the virtual group
previousCompileContext = null;
}
@@ -906,9 +911,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var nodeLinkFn, childLinkFn, node, $node, childScope, childTranscludeFn, i, ii, n;
// copy nodeList so that linking doesn't break due to live list updates.
var stableNodeList = [];
for (i = 0, ii = nodeList.length; i < ii; i++) {
stableNodeList.push(nodeList[i]);
var nodeListLength = nodeList.length,
stableNodeList = new Array(nodeListLength);
for (i = 0; i < nodeListLength; i++) {
stableNodeList[i] = nodeList[i];
}
for(i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
@@ -921,7 +927,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (nodeLinkFn.scope) {
childScope = scope.$new();
$node.data('$scope', childScope);
safeAddClass($node, 'ng-scope');
} else {
childScope = scope;
}
@@ -1004,9 +1009,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nName = directiveNormalize(name.toLowerCase());
attrsMap[nName] = name;
attrs[nName] = value = trim((msie && name == 'href')
? decodeURIComponent(node.getAttribute(name, 2))
: attr.value);
attrs[nName] = value = trim(attr.value);
if (getBooleanAttrName(node, nName)) {
attrs[nName] = true; // presence means true
}
@@ -1219,7 +1222,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
});
} else {
$template = jqLite(jqLiteClone(compileNode)).contents();
$compileNode.html(''); // clear contents
$compileNode.empty(); // clear contents
childTranscludeFn = compile($template, transcludeFn);
}
}
@@ -1400,7 +1403,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
optional = (match[2] == '?'),
mode = match[1], // @, =, or &
lastValue,
parentGet, parentSet;
parentGet, parentSet, compare;
isolateScope.$$isolateBindings[scopeName] = mode + attrName;
@@ -1423,6 +1426,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return;
}
parentGet = $parse(attrs[attrName]);
if (parentGet.literal) {
compare = equals;
} else {
compare = function(a,b) { return a === b; };
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
lastValue = isolateScope[scopeName] = parentGet(scope);
@@ -1433,10 +1441,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
lastValue = isolateScope[scopeName] = parentGet(scope);
isolateScope.$watch(function parentValueWatch() {
var parentValue = parentGet(scope);
if (parentValue !== isolateScope[scopeName]) {
if (!compare(parentValue, isolateScope[scopeName])) {
// we are out of sync and need to copy
if (parentValue !== lastValue) {
if (!compare(parentValue, lastValue)) {
// parent changed and it has precedence
isolateScope[scopeName] = parentValue;
} else {
@@ -1445,7 +1452,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}
return lastValue = parentValue;
});
}, null, parentGet.literal);
break;
case '&':
@@ -1647,7 +1654,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
? origAsyncDirective.templateUrl($compileNode, tAttrs)
: origAsyncDirective.templateUrl;
$compileNode.html('');
$compileNode.empty();
$http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
success(function(content) {
+10 -8
View File
@@ -32,13 +32,15 @@ var htmlAnchorDirective = valueFn({
element.append(document.createComment('IE fix'));
}
return function(scope, element) {
element.on('click', function(event){
// if we have no href url, then don't navigate anywhere.
if (!element.attr('href')) {
event.preventDefault();
}
});
};
if (!attr.href && !attr.name) {
return function(scope, element) {
element.on('click', function(event){
// if we have no href url, then don't navigate anywhere.
if (!element.attr('href')) {
event.preventDefault();
}
});
};
}
}
});
+9 -3
View File
@@ -4,6 +4,7 @@
* @ngdoc directive
* @name ng.directive:ngHref
* @restrict A
* @priority 99
*
* @description
* Using Angular markup like `{{hash}}` in an href attribute will
@@ -87,6 +88,7 @@
* @ngdoc directive
* @name ng.directive:ngSrc
* @restrict A
* @priority 99
*
* @description
* Using Angular markup like `{{hash}}` in a `src` attribute doesn't
@@ -112,6 +114,7 @@
* @ngdoc directive
* @name ng.directive:ngSrcset
* @restrict A
* @priority 99
*
* @description
* Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
@@ -137,6 +140,7 @@
* @ngdoc directive
* @name ng.directive:ngDisabled
* @restrict A
* @priority 100
*
* @description
*
@@ -180,6 +184,7 @@
* @ngdoc directive
* @name ng.directive:ngChecked
* @restrict A
* @priority 100
*
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
@@ -214,6 +219,7 @@
* @ngdoc directive
* @name ng.directive:ngReadonly
* @restrict A
* @priority 100
*
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
@@ -223,7 +229,6 @@
* The `ngReadonly` directive solves this problem for the `readonly` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
* @example
<doc:example>
<doc:source>
@@ -249,6 +254,7 @@
* @ngdoc directive
* @name ng.directive:ngSelected
* @restrict A
* @priority 100
*
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
@@ -258,6 +264,7 @@
* The `ngSelected` directive solves this problem for the `selected` atttribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
*
* @example
<doc:example>
<doc:source>
@@ -285,6 +292,7 @@
* @ngdoc directive
* @name ng.directive:ngOpen
* @restrict A
* @priority 100
*
* @description
* The HTML specification does not require browsers to preserve the values of boolean attributes
@@ -294,8 +302,6 @@
* The `ngOpen` directive solves this problem for the `open` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
*
* @example
<doc:example>
<doc:source>
+15 -10
View File
@@ -395,15 +395,17 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// In composition mode, users are still inputing intermediate text buffer,
// hold the listener until composition is done.
// More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
var composing = false;
if (!$sniffer.android) {
var composing = false;
element.on('compositionstart', function() {
composing = true;
});
element.on('compositionstart', function(data) {
composing = true;
});
element.on('compositionend', function() {
composing = false;
});
element.on('compositionend', function() {
composing = false;
});
}
var listener = function() {
if (composing) return;
@@ -417,9 +419,13 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
}
if (ctrl.$viewValue !== value) {
scope.$apply(function() {
if (scope.$$phase) {
ctrl.$setViewValue(value);
});
} else {
scope.$apply(function() {
ctrl.$setViewValue(value);
});
}
}
};
@@ -1432,7 +1438,6 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
id="{{name}}"
name="favorite">
</label>
</span>
<div>You chose {{my.favorite}}</div>
</form>
</doc:source>
+1 -1
View File
@@ -35,7 +35,7 @@
*
* Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
* cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
* class `ngCloak` in addition to the `ngCloak` directive as shown in the example below.
* class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
*
* @element ANY
*
+92 -13
View File
@@ -69,7 +69,14 @@ forEach(
* a dblclick. (The Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-dblclick="count = count + 1" ng-init="count=0">
Increment (on double click)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
@@ -85,7 +92,14 @@ forEach(
* mousedown. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-mousedown="count = count + 1" ng-init="count=0">
Increment (on mouse down)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
@@ -101,7 +115,14 @@ forEach(
* mouseup. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-mouseup="count = count + 1" ng-init="count=0">
Increment (on mouse up)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
/**
@@ -116,7 +137,14 @@ forEach(
* mouseover. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-mouseover="count = count + 1" ng-init="count=0">
Increment (when mouse is over)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
@@ -132,7 +160,14 @@ forEach(
* mouseenter. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-mouseenter="count = count + 1" ng-init="count=0">
Increment (when mouse enters)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
@@ -148,7 +183,14 @@ forEach(
* mouseleave. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-mouseleave="count = count + 1" ng-init="count=0">
Increment (when mouse leaves)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
@@ -164,7 +206,14 @@ forEach(
* mousemove. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<button ng-mousemove="count = count + 1" ng-init="count=0">
Increment (when mouse moves)
</button>
count: {{count}}
</doc:source>
</doc:example>
*/
@@ -180,7 +229,12 @@ forEach(
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<input ng-keydown="count = count + 1" ng-init="count=0">
key down count: {{count}}
</doc:source>
</doc:example>
*/
@@ -196,7 +250,12 @@ forEach(
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<input ng-keyup="count = count + 1" ng-init="count=0">
key up count: {{count}}
</doc:source>
</doc:example>
*/
@@ -212,7 +271,12 @@ forEach(
* keypress. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<input ng-keypress="count = count + 1" ng-init="count=0">
key press count: {{count}}
</doc:source>
</doc:example>
*/
@@ -311,7 +375,12 @@ forEach(
* copy. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
copied: {{copied}}
</doc:source>
</doc:example>
*/
/**
@@ -326,7 +395,12 @@ forEach(
* cut. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
cut: {{cut}}
</doc:source>
</doc:example>
*/
/**
@@ -341,5 +415,10 @@ forEach(
* paste. (Event object is available as `$event`)
*
* @example
* See {@link ng.directive:ngClick ngClick}
<doc:example>
<doc:source>
<input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
pasted: {{paste}}
</doc:source>
</doc:example>
*/
-3
View File
@@ -59,9 +59,6 @@
padding:10px;
}
/&#42;
The transition styles can also be placed on the CSS base class above
&#42;/
.animate-if.ng-enter, .animate-if.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
+27 -7
View File
@@ -147,13 +147,14 @@
* @description
* Emitted every time the ngInclude content is reloaded.
*/
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animate', '$sce',
function($http, $templateCache, $anchorScroll, $compile, $animate, $sce) {
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce',
function($http, $templateCache, $anchorScroll, $animate, $sce) {
return {
restrict: 'ECA',
priority: 400,
terminal: true,
transclude: 'element',
controller: angular.noop,
compile: function(element, attr) {
var srcExp = attr.ngInclude || attr.src,
onloadExp = attr.onload || '',
@@ -187,6 +188,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
$http.get(src, {cache: $templateCache}).success(function(response) {
if (thisChangeId !== changeCounter) return;
var newScope = scope.$new();
ctrl.template = response;
// Note: This will also link all children of ng-include that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
@@ -194,15 +196,14 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, noop);
cleanupLastIncludeContent();
var clone = $transclude(newScope, function(clone) {
cleanupLastIncludeContent();
$animate.enter(clone, null, $element, afterAnimation);
});
currentScope = newScope;
currentElement = clone;
currentElement.html(response);
$animate.enter(currentElement, null, $element, afterAnimation);
$compile(currentElement.contents())(currentScope);
currentScope.$emit('$includeContentLoaded');
scope.$eval(onloadExp);
}).error(function() {
@@ -211,9 +212,28 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
scope.$emit('$includeContentRequested');
} else {
cleanupLastIncludeContent();
ctrl.template = null;
}
});
};
}
};
}];
// This directive is called during the $transclude call of the first `ngInclude` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngInclude
// is called.
var ngIncludeFillContentDirective = ['$compile',
function($compile) {
return {
restrict: 'ECA',
priority: -400,
require: 'ngInclude',
link: function(scope, $element, $attr, ctrl) {
$element.html(ctrl.template);
$compile($element.contents())(scope);
}
};
}];
+1 -1
View File
@@ -10,7 +10,7 @@
* current scope.
*
* <div class="alert alert-error">
* The only appropriate use of `ngInit` for aliasing special properties of
* The only appropriate use of `ngInit` is for aliasing special properties of
* {@link api/ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
* should use {@link guide/controller controllers} rather than `ngInit`
* to initialize values on a scope.
+2 -2
View File
@@ -203,7 +203,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude){
var expression = $attr.ngRepeat;
var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
lhs, rhs, valueIdentifier, keyIdentifier,
hashFnLocals = {$id: hashKey};
@@ -215,7 +215,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
lhs = match[1];
rhs = match[2];
trackByExp = match[4];
trackByExp = match[3];
if (trackByExp) {
trackByExpGetter = $parse(trackByExp);
+13 -5
View File
@@ -6,19 +6,26 @@
* @restrict EA
*
* @description
* The ngSwitch directive is used to conditionally swap DOM structure on your template based on a scope expression.
* Elements within ngSwitch but without ngSwitchWhen or ngSwitchDefault directives will be preserved at the location
* The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
* Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
* as specified in the template.
*
* The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
* from the template cache), ngSwitch simply choses one of the nested elements and makes it visible based on which element
* from the template cache), `ngSwitch` simply choses one of the nested elements and makes it visible based on which element
* matches the value obtained from the evaluated expression. In other words, you define a container element
* (where you place the directive), place an expression on the **on="..." attribute**
* (or the **ng-switch="..." attribute**), define any inner elements inside of the directive and place
* (where you place the directive), place an expression on the **`on="..."` attribute**
* (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
* a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
* expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
* attribute is displayed.
*
* <div class="alert alert-info">
* Be aware that the attribute values to match against cannot be expressions. They are interpreted
* as literal string values to match against.
* For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
* value of the expression `$scope.someVal`.
* </div>
* @animations
* enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
@@ -30,6 +37,7 @@
* <ANY ng-switch-default>...</ANY>
* </ANY>
*
*
* @scope
* @priority 800
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
+1 -1
View File
@@ -69,7 +69,7 @@ var ngTranscludeDirective = ngDirective({
link: function($scope, $element, $attrs, controller) {
controller.$transclude(function(clone) {
$element.html('');
$element.empty();
$element.append(clone);
});
}
+5 -13
View File
@@ -221,18 +221,10 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
// required validator
if (multiple && (attr.required || attr.ngRequired)) {
var requiredValidator = function(value) {
ngModelCtrl.$setValidity('required', !attr.required || (value && value.length));
return value;
if (multiple) {
ngModelCtrl.$isEmpty = function(value) {
return !value || value.length === 0;
};
ngModelCtrl.$parsers.push(requiredValidator);
ngModelCtrl.$formatters.unshift(requiredValidator);
attr.$observe('required', function() {
requiredValidator(ngModelCtrl.$viewValue);
});
}
if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
@@ -333,13 +325,13 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
// becomes the compilation root
nullOption.removeClass('ng-scope');
// we need to remove it before calling selectElement.html('') because otherwise IE will
// we need to remove it before calling selectElement.empty() because otherwise IE will
// remove the label from the element. wtf?
nullOption.remove();
}
// clear contents, we'll add what's needed based on the model
selectElement.html('');
selectElement.empty();
selectElement.on('change', function() {
scope.$apply(function() {
+4 -4
View File
@@ -25,21 +25,21 @@
* property of the object. That's equivalent to the simple substring match with a `string`
* as described above.
*
* - `function`: A predicate function can be used to write arbitrary filters. The function is
* - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
* called for each element of `array`. The final result is an array of those elements that
* the predicate returned true for.
*
* @param {function(expected, actual)|true|undefined} comparator Comparator which is used in
* @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
* determining if the expected value (from the filter expression) and actual value (from
* the object in the array) should be considered a match.
*
* Can be one of:
*
* - `function(expected, actual)`:
* - `function(actual, expected)`:
* The function will be given the object value and the predicate value to compare and
* should return true if the item should be included in filtered result.
*
* - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`.
* - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
* this is essentially strict comparison of expected and actual.
*
* - `false|undefined`: A short hand for a function which will look for a substring match in case
+5 -4
View File
@@ -222,7 +222,7 @@ function $HttpProvider() {
* will result in the success callback being called. Note that if the response is a redirect,
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
*
* # Calling $http from outside AngularJS
* The `$http` service will not actually send the request until the next `$digest()` is
* executed. Normally this is not an issue, since almost all the time your call to `$http` will
@@ -409,19 +409,20 @@ function $HttpProvider() {
* return responseOrNewPromise
* }
* return $q.reject(rejection);
* };
* }
* }
* };
* });
*
* $httpProvider.interceptors.push('myHttpInterceptor');
*
*
* // register the interceptor via an anonymous factory
* // alternatively, register the interceptor via an anonymous factory
* $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
* return {
* 'request': function(config) {
* // same as above
* },
*
* 'response': function(response) {
* // same as above
* }
+19 -10
View File
@@ -1,12 +1,12 @@
'use strict';
var XHR = window.XMLHttpRequest || function() {
function createXhr(method) {
// IE8 doesn't support PATCH method, but the ActiveX object does
/* global ActiveXObject */
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
};
return (msie <= 8 && lowercase(method) === 'patch')
? new ActiveXObject('Microsoft.XMLHTTP')
: new window.XMLHttpRequest();
}
/**
@@ -28,11 +28,11 @@ var XHR = window.XMLHttpRequest || function() {
*/
function $HttpBackendProvider() {
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, $document[0]);
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
}];
}
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument) {
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
var ABORTED = -1;
// TODO(vojta): fix the signature
@@ -57,7 +57,9 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
delete callbacks[callbackId];
});
} else {
var xhr = new XHR();
var xhr = createXhr(method);
xhr.open(method, url, true);
forEach(headers, function(value, key) {
if (isDefined(value)) {
@@ -69,7 +71,14 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument)
// response is in the cache. the promise api will ensure that to the app code the api is
// always async
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// onreadystatechange might by called multiple times with readyState === 4 on mobile webkit caused by
// xhrs that are resolved while the app is in the background (see #5426).
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
// continuing
//
// we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
// Safari respectively.
if (xhr && xhr.readyState == 4) {
var responseHeaders = null,
response = null;
+97
View File
@@ -24,6 +24,14 @@ function $IntervalProvider() {
* In tests you can use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
* time.
*
* <div class="alert alert-warning">
* **Note**: Intervals created by this service must be explicitly destroyed when you are finished
* with them. In particular they are not automatically destroyed when a controller's scope or a
* directive's element are destroyed.
* You should take this into consideration and make sure to always cancel the interval at the
* appropriate moment. See the example below for more details on how and when to do this.
* </div>
*
* @param {function()} fn A function that should be called repeatedly.
* @param {number} delay Number of milliseconds between each function call.
@@ -32,6 +40,95 @@ function $IntervalProvider() {
* @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
* will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
* @returns {promise} A promise which will be notified on each iteration.
*
* @example
<doc:example module="time">
<doc:source>
<script>
function Ctrl2($scope,$interval) {
$scope.format = 'M/d/yy h:mm:ss a';
$scope.blood_1 = 100;
$scope.blood_2 = 120;
var stop;
$scope.fight = function() {
// Don't start a new fight if we are already fighting
if ( angular.isDefined(stop) ) return;
stop = $interval(function() {
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
$scope.blood_1 = $scope.blood_1 - 3;
$scope.blood_2 = $scope.blood_2 - 4;
} else {
$scope.stopFight();
}
}, 100);
};
$scope.stopFight = function() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
}
};
$scope.resetFight = function() {
$scope.blood_1 = 100;
$scope.blood_2 = 120;
}
$scope.$on('$destroy', function() {
// Make sure that the interval is destroyed too
$scope.stopFight();
});
}
angular.module('time', [])
// Register the 'myCurrentTime' directive factory method.
// We inject $interval and dateFilter service since the factory method is DI.
.directive('myCurrentTime', function($interval, dateFilter) {
// return the directive link function. (compile function not needed)
return function(scope, element, attrs) {
var format, // date format
stopTime; // so that we can cancel the time updates
// used to update the UI
function updateTime() {
element.text(dateFilter(new Date(), format));
}
// watch the expression, and update the UI on change.
scope.$watch(attrs.myCurrentTime, function(value) {
format = value;
updateTime();
});
stopTime = $interval(updateTime, 1000);
// listen on DOM destroy (removal) event, and cancel the next UI update
// to prevent updating time ofter the DOM element was removed.
element.bind('$destroy', function() {
$interval.cancel(stopTime);
});
}
});
</script>
<div>
<div ng-controller="Ctrl2">
Date format: <input ng-model="format"> <hr/>
Current time is: <span my-current-time="format"></span>
<hr/>
Blood 1 : <font color='red'>{{blood_1}}</font>
Blood 2 : <font color='red'>{{blood_2}}</font>
<button type="button" data-ng-click="fight()">Fight</button>
<button type="button" data-ng-click="stopFight()">StopFight</button>
<button type="button" data-ng-click="resetFight()">resetFight</button>
</div>
</div>
</doc:source>
</doc:example>
*/
function interval(fn, delay, count, invokeApply) {
var setInterval = $window.setInterval,
+14 -6
View File
@@ -629,6 +629,13 @@ function $LocationProvider(){
}
var absHref = elm.prop('href');
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
// an animation.
absHref = urlResolve(absHref.animVal).href;
}
var rewrittenUrl = $location.$$rewrite(absHref);
if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
@@ -652,16 +659,17 @@ function $LocationProvider(){
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl) {
if ($location.absUrl() != newUrl) {
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
$location.absUrl()).defaultPrevented) {
$browser.url($location.absUrl());
return;
}
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
$location.$$parse(newUrl);
afterLocationChange(oldUrl);
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
oldUrl).defaultPrevented) {
$location.$$parse(oldUrl);
$browser.url(oldUrl);
} else {
afterLocationChange(oldUrl);
}
});
if (!$rootScope.$$phase) $rootScope.$digest();
}
+9 -2
View File
@@ -139,9 +139,16 @@ function $LogProvider(){
function consoleLog(type) {
var console = $window.console || {},
logFn = console[type] || console.log || noop;
logFn = console[type] || console.log || noop,
hasApply = false;
if (logFn.apply) {
// Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
// The reason behind this is that console.log has type "object" in IE8...
try {
hasApply = !! logFn.apply;
} catch (e) {}
if (hasApply) {
return function() {
var args = [];
forEach(arguments, function(arg) {
+42 -17
View File
@@ -891,19 +891,19 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
? function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key1 ? undefined : pathVal;
pathVal = pathVal[key1];
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key2 ? undefined : pathVal;
pathVal = pathVal[key2];
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key3 ? undefined : pathVal;
pathVal = pathVal[key3];
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key4 ? undefined : pathVal;
pathVal = pathVal[key4];
return pathVal;
@@ -912,7 +912,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope,
promise;
if (pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
if (pathVal && pathVal.then) {
@@ -924,7 +924,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
}
pathVal = pathVal.$$v;
}
if (!key1 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key1 ? undefined : pathVal;
pathVal = pathVal[key1];
if (pathVal && pathVal.then) {
@@ -936,7 +936,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
}
pathVal = pathVal.$$v;
}
if (!key2 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key2 ? undefined : pathVal;
pathVal = pathVal[key2];
if (pathVal && pathVal.then) {
@@ -948,7 +948,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
}
pathVal = pathVal.$$v;
}
if (!key3 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key3 ? undefined : pathVal;
pathVal = pathVal[key3];
if (pathVal && pathVal.then) {
@@ -960,7 +960,7 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
}
pathVal = pathVal.$$v;
}
if (!key4 || pathVal === null || pathVal === undefined) return pathVal;
if (pathVal == null) return key4 ? undefined : pathVal;
pathVal = pathVal[key4];
if (pathVal && pathVal.then) {
@@ -976,6 +976,26 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
};
}
function simpleGetterFn1(key0, fullExp) {
ensureSafeMemberName(key0, fullExp);
return function simpleGetterFn1(scope, locals) {
if (scope == null) return undefined;
return ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
};
}
function simpleGetterFn2(key0, key1, fullExp) {
ensureSafeMemberName(key0, fullExp);
ensureSafeMemberName(key1, fullExp);
return function simpleGetterFn2(scope, locals) {
if (scope == null) return undefined;
scope = ((locals && locals.hasOwnProperty(key0)) ? locals : scope)[key0];
return scope == null ? undefined : scope[key1];
};
}
function getterFn(path, options, fullExp) {
// Check whether the cache has this getter already.
// We can use hasOwnProperty directly on the cache because we ensure,
@@ -988,7 +1008,13 @@ function getterFn(path, options, fullExp) {
pathKeysLength = pathKeys.length,
fn;
if (options.csp) {
// When we have only 1 or 2 tokens, use optimized special case closures.
// http://jsperf.com/angularjs-parse-getter/6
if (!options.unwrapPromises && pathKeysLength === 1) {
fn = simpleGetterFn1(pathKeys[0], fullExp);
} else if (!options.unwrapPromises && pathKeysLength === 2) {
fn = simpleGetterFn2(pathKeys[0], pathKeys[1], fullExp);
} else if (options.csp) {
if (pathKeysLength < 6) {
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp,
options);
@@ -1006,11 +1032,10 @@ function getterFn(path, options, fullExp) {
};
}
} else {
var code = 'var l, fn, p;\n';
var code = 'var p;\n';
forEach(pathKeys, function(key, index) {
ensureSafeMemberName(key, fullExp);
code += 'if(s === null || s === undefined) return s;\n' +
'l=s;\n' +
code += 'if(s == null) return undefined;\n' +
's='+ (index
// we simply dereference 's' on any .dot notation
? 's'
@@ -1033,10 +1058,10 @@ function getterFn(path, options, fullExp) {
/* jshint -W054 */
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
/* jshint +W054 */
evaledFnGetter.toString = function() { return code; };
fn = function(scope, locals) {
evaledFnGetter.toString = valueFn(code);
fn = options.unwrapPromises ? function(scope, locals) {
return evaledFnGetter(scope, locals, promiseWarning);
};
} : evaledFnGetter;
}
// Only cache the value if it's not going to mess up the cache object
+1 -1
View File
@@ -136,7 +136,7 @@
*
* # Differences between Kris Kowal's Q and $q
*
* There are three main differences:
* There are two main differences:
*
* - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
* mechanism in angular, which means faster propagation of resolution or rejection into your
+62 -36
View File
@@ -133,6 +133,7 @@ function $RootScopeProvider(){
this.$$asyncQueue = [];
this.$$postDigestQueue = [];
this.$$listeners = {};
this.$$listenerCount = {};
this.$$isolateBindings = {};
}
@@ -156,11 +157,11 @@ function $RootScopeProvider(){
* @description
* Creates a new child {@link ng.$rootScope.Scope scope}.
*
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
* {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
* scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
* The parent scope will propagate the {@link ng.$rootScope.Scope#methods_$digest $digest()} and
* {@link ng.$rootScope.Scope#methods_$digest $digest()} events. The scope can be removed from the
* scope hierarchy using {@link ng.$rootScope.Scope#methods_$destroy $destroy()}.
*
* {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
* {@link ng.$rootScope.Scope#methods_$destroy $destroy()} must be called on a scope when it is
* desired for the scope and its child scopes to be permanently detached from the parent and
* thus stop participating in model change detection and listener notification by invoking.
*
@@ -192,6 +193,7 @@ function $RootScopeProvider(){
}
child['this'] = child;
child.$$listeners = {};
child.$$listenerCount = {};
child.$parent = this;
child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
child.$$prevSibling = this.$$childTail;
@@ -213,11 +215,11 @@ function $RootScopeProvider(){
* @description
* Registers a `listener` callback to be executed whenever the `watchExpression` changes.
*
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
* - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#methods_$digest
* $digest()} and should return the value that will be watched. (Since
* {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
* {@link ng.$rootScope.Scope#methods_$digest $digest()} reruns when it detects changes the
* `watchExpression` can execute multiple times per
* {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
* {@link ng.$rootScope.Scope#methods_$digest $digest()} and should be idempotent.)
* - The `listener` is called only when the value from the current `watchExpression` and the
* previous call to `watchExpression` are not equal (with the exception of the initial run,
* see below). The inequality is determined according to
@@ -229,13 +231,13 @@ function $RootScopeProvider(){
* iteration limit is 10 to prevent an infinite loop deadlock.
*
*
* If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
* If you want to be notified whenever {@link ng.$rootScope.Scope#methods_$digest $digest} is called,
* you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
* can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
* can execute multiple times per {@link ng.$rootScope.Scope#methods_$digest $digest} cycle when a
* change is detected, be prepared for multiple calls to your listener.)
*
* After a watcher is registered with the scope, the `listener` fn is called asynchronously
* (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
* (via {@link ng.$rootScope.Scope#methods_$evalAsync $evalAsync}) to initialize the
* watcher. In rare cases, this is undesirable because the listener is called when the result
* of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
* can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
@@ -299,7 +301,7 @@ function $RootScopeProvider(){
*
*
* @param {(function()|string)} watchExpression Expression that is evaluated on each
* {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
* {@link ng.$rootScope.Scope#methods_$digest $digest} cycle. A change in the return value triggers
* a call to the `listener`.
*
* - `string`: Evaluated as {@link guide/expression expression}
@@ -351,6 +353,7 @@ function $RootScopeProvider(){
return function() {
arrayRemove(array, watcher);
lastDirtyWatch = null;
};
},
@@ -397,7 +400,7 @@ function $RootScopeProvider(){
*
* @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The
* expression value should evaluate to an object or an array which is observed on each
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
* {@link ng.$rootScope.Scope#methods_$digest $digest} cycle. Any shallow change within the
* collection will trigger a call to the `listener`.
*
* @param {function(newCollection, oldCollection, scope)} listener a callback function that is
@@ -502,9 +505,9 @@ function $RootScopeProvider(){
* @function
*
* @description
* Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
* its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
* the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
* Processes all of the {@link ng.$rootScope.Scope#methods_$watch watchers} of the current scope and
* its children. Because a {@link ng.$rootScope.Scope#methods_$watch watcher}'s listener can change
* the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#methods_$watch watchers}
* until no more listeners are firing. This means that it is possible to get into an infinite
* loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
* iterations exceeds 10.
@@ -512,12 +515,12 @@ function $RootScopeProvider(){
* Usually, you don't call `$digest()` directly in
* {@link ng.directive:ngController controllers} or in
* {@link ng.$compileProvider#methods_directive directives}.
* Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
* Instead, you should call {@link ng.$rootScope.Scope#methods_$apply $apply()} (typically from within
* a {@link ng.$compileProvider#methods_directive directives}), which will force a `$digest()`.
*
* If you want to be notified whenever `$digest()` is called,
* you can register a `watchExpression` function with
* {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
* {@link ng.$rootScope.Scope#methods_$watch $watch()} with no `listener`.
*
* In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
*
@@ -672,7 +675,7 @@ function $RootScopeProvider(){
*
* @description
* Removes the current scope (and all of its children) from the parent scope. Removal implies
* that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
* that calls to {@link ng.$rootScope.Scope#methods_$digest $digest()} will no longer
* propagate to the current scope and its children. Removal also implies that the current
* scope is eligible for garbage collection.
*
@@ -696,6 +699,8 @@ function $RootScopeProvider(){
this.$$destroyed = true;
if (this === $rootScope) return;
forEach(this.$$listenerCount, bind(null, decrementListenerCount, this));
if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
@@ -754,7 +759,7 @@ function $RootScopeProvider(){
*
* - it will execute after the function that scheduled the evaluation (preferably before DOM
* rendering).
* - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
* - at least one {@link ng.$rootScope.Scope#methods_$digest $digest cycle} will be performed after
* `expression` execution.
*
* Any exceptions from the execution of the expression are forwarded to the
@@ -799,7 +804,7 @@ function $RootScopeProvider(){
* framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
* Because we are calling into the angular framework we need to perform proper scope life
* cycle of {@link ng.$exceptionHandler exception handling},
* {@link ng.$rootScope.Scope#$digest executing watches}.
* {@link ng.$rootScope.Scope#methods_$digest executing watches}.
*
* ## Life cycle
*
@@ -820,11 +825,11 @@ function $RootScopeProvider(){
* Scope's `$apply()` method transitions through the following stages:
*
* 1. The {@link guide/expression expression} is executed using the
* {@link ng.$rootScope.Scope#$eval $eval()} method.
* {@link ng.$rootScope.Scope#methods_$eval $eval()} method.
* 2. Any exceptions from the execution of the expression are forwarded to the
* {@link ng.$exceptionHandler $exceptionHandler} service.
* 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
* expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
* 3. The {@link ng.$rootScope.Scope#methods_$watch watch} listeners are fired immediately after the
* expression was executed using the {@link ng.$rootScope.Scope#methods_$digest $digest()} method.
*
*
* @param {(string|function())=} exp An angular expression to be executed.
@@ -858,7 +863,7 @@ function $RootScopeProvider(){
* @function
*
* @description
* Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
* Listens on events of a given type. See {@link ng.$rootScope.Scope#methods_$emit $emit} for
* discussion of event life cycle.
*
* The event listener function format is: `function(event, args...)`. The `event` object
@@ -885,8 +890,18 @@ function $RootScopeProvider(){
}
namedListeners.push(listener);
var current = this;
do {
if (!current.$$listenerCount[name]) {
current.$$listenerCount[name] = 0;
}
current.$$listenerCount[name]++;
} while ((current = current.$parent));
var self = this;
return function() {
namedListeners[indexOf(namedListeners, listener)] = null;
decrementListenerCount(self, 1, name);
};
},
@@ -899,20 +914,20 @@ function $RootScopeProvider(){
*
* @description
* Dispatches an event `name` upwards through the scope hierarchy notifying the
* registered {@link ng.$rootScope.Scope#$on} listeners.
* registered {@link ng.$rootScope.Scope#methods_$on} listeners.
*
* The event life cycle starts at the scope on which `$emit` was called. All
* {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
* {@link ng.$rootScope.Scope#methods_$on listeners} listening for `name` event on this scope get
* notified. Afterwards, the event traverses upwards toward the root scope and calls all
* registered listeners along the way. The event will stop propagating if one of the listeners
* cancels it.
*
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* Any exception emitted from the {@link ng.$rootScope.Scope#methods_$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to emit.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
* @return {Object} Event object (see {@link ng.$rootScope.Scope#methods_$on}).
*/
$emit: function(name, args) {
var empty = [],
@@ -968,19 +983,19 @@ function $RootScopeProvider(){
*
* @description
* Dispatches an event `name` downwards to all child scopes (and their children) notifying the
* registered {@link ng.$rootScope.Scope#$on} listeners.
* registered {@link ng.$rootScope.Scope#methods_$on} listeners.
*
* The event life cycle starts at the scope on which `$broadcast` was called. All
* {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
* {@link ng.$rootScope.Scope#methods_$on listeners} listening for `name` event on this scope get
* notified. Afterwards, the event propagates to all direct and indirect scopes of the current
* scope and calls all registered listeners along the way. The event cannot be canceled.
*
* Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
* Any exception emitted from the {@link ng.$rootScope.Scope#methods_$on listeners} will be passed
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to broadcast.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
* @return {Object} Event object, see {@link ng.$rootScope.Scope#methods_$on}
*/
$broadcast: function(name, args) {
var target = this,
@@ -998,8 +1013,7 @@ function $RootScopeProvider(){
listeners, i, length;
//down while you can, then up and next sibling or up and next sibling until back at root
do {
current = next;
while ((current = next)) {
event.currentScope = current;
listeners = current.$$listeners[name] || [];
for (i=0, length = listeners.length; i<length; i++) {
@@ -1021,12 +1035,14 @@ function $RootScopeProvider(){
// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
// this piece should be kept in sync with the traversal in $digest
if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
// (though it differs due to having the extra check for $$listenerCount)
if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
(current !== target && current.$$nextSibling)))) {
while(current !== target && !(next = current.$$nextSibling)) {
current = current.$parent;
}
}
} while ((current = next));
}
return event;
}
@@ -1055,6 +1071,16 @@ function $RootScopeProvider(){
return fn;
}
function decrementListenerCount(current, count, name) {
do {
current.$$listenerCount[name] -= count;
if (current.$$listenerCount[name] === 0) {
delete current.$$listenerCount[name];
}
} while ((current = current.$parent));
}
/**
* function used as an initial value for watchers.
* because it's unique we can easily tell it apart from other values
+2 -1
View File
@@ -59,7 +59,7 @@ function $SnifferProvider() {
// http://code.google.com/p/android/issues/detail?id=17471
// https://github.com/angular/angular.js/issues/904
// older webit browser (533.9) on Boxee box has exactly the same problem as Android has
// older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
// so let's not use the history API also
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
// jshint -W018
@@ -85,6 +85,7 @@ function $SnifferProvider() {
vendorPrefix: vendorPrefix,
transitions : transitions,
animations : animations,
android: android,
msie : msie,
msieDocumentMode: documentMode
};
-87
View File
@@ -32,93 +32,6 @@ function $TimeoutProvider() {
* @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
* promise will be resolved with is the return value of the `fn` function.
*
* @example
<doc:example module="time">
<doc:source>
<script>
function Ctrl2($scope,$timeout) {
$scope.format = 'M/d/yy h:mm:ss a';
$scope.blood_1 = 100;
$scope.blood_2 = 120;
var stop;
$scope.fight = function() {
stop = $timeout(function() {
if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
$scope.blood_1 = $scope.blood_1 - 3;
$scope.blood_2 = $scope.blood_2 - 4;
$scope.fight();
} else {
$timeout.cancel(stop);
}
}, 100);
};
$scope.stopFight = function() {
$timeout.cancel(stop);
};
$scope.resetFight = function() {
$scope.blood_1 = 100;
$scope.blood_2 = 120;
}
}
angular.module('time', [])
// Register the 'myCurrentTime' directive factory method.
// We inject $timeout and dateFilter service since the factory method is DI.
.directive('myCurrentTime', function($timeout, dateFilter) {
// return the directive link function. (compile function not needed)
return function(scope, element, attrs) {
var format, // date format
timeoutId; // timeoutId, so that we can cancel the time updates
// used to update the UI
function updateTime() {
element.text(dateFilter(new Date(), format));
}
// watch the expression, and update the UI on change.
scope.$watch(attrs.myCurrentTime, function(value) {
format = value;
updateTime();
});
// schedule update in one second
function updateLater() {
// save the timeoutId for canceling
timeoutId = $timeout(function() {
updateTime(); // update DOM
updateLater(); // schedule another update
}, 1000);
}
// listen on DOM destroy (removal) event, and cancel the next UI update
// to prevent updating time ofter the DOM element was removed.
element.bind('$destroy', function() {
$timeout.cancel(timeoutId);
});
updateLater(); // kick off the UI update process.
}
});
</script>
<div>
<div ng-controller="Ctrl2">
Date format: <input ng-model="format"> <hr/>
Current time is: <span my-current-time="format"></span>
<hr/>
Blood 1 : <font color='red'>{{blood_1}}</font>
Blood 2 : <font color='red'>{{blood_2}}</font>
<button type="button" data-ng-click="fight()">Fight</button>
<button type="button" data-ng-click="stopFight()">StopFight</button>
<button type="button" data-ng-click="resetFight()">resetFight</button>
</div>
</div>
</doc:source>
</doc:example>
*/
function timeout(fn, delay, invokeApply) {
var deferred = $q.defer(),
+98 -41
View File
@@ -288,6 +288,13 @@ angular.module('ngAnimate', ['ng'])
});
});
var classNameFilter = $animateProvider.classNameFilter();
var isAnimatableClassName = !classNameFilter
? function() { return true; }
: function(className) {
return classNameFilter.test(className);
};
function lookup(name) {
if (name) {
var matches = [],
@@ -569,17 +576,20 @@ angular.module('ngAnimate', ['ng'])
and the onComplete callback will be fired once the animation is fully complete.
*/
function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, doneCallback) {
var node = extractElementNode(element);
var currentClassName, classes, node = extractElementNode(element);
if(node) {
currentClassName = node.className;
classes = currentClassName + ' ' + className;
}
//transcluded directives may sometimes fire an animation using only comment nodes
//best to catch this early on to prevent any animation operations from occurring
if(!node) {
if(!node || !isAnimatableClassName(classes)) {
fireDOMOperation();
closeAnimation();
return;
}
var currentClassName = node.className;
var classes = currentClassName + ' ' + className;
var animationLookup = (' ' + classes).replace(/\s+/g,'.');
if (!parentElement) {
parentElement = afterElement ? afterElement.parent() : element.parent();
@@ -600,9 +610,14 @@ angular.module('ngAnimate', ['ng'])
}
var animations = [];
//only add animations if the currently running animation is not structural
//or if there is no animation running at all
if(!ngAnimateState.running || !(isClassBased && ngAnimateState.structural)) {
var allowAnimations = isClassBased ?
!ngAnimateState.disabled && (!ngAnimateState.running || !ngAnimateState.structural) :
true;
if(allowAnimations) {
forEach(matches, function(animation) {
//add the animation to the queue to if it is allowed to be cancelled
if(!animation.allowCancel || animation.allowCancel(element, animationEvent, className)) {
@@ -881,27 +896,73 @@ angular.module('ngAnimate', ['ng'])
var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount';
var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey';
var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data';
var NG_ANIMATE_FALLBACK_CLASS_NAME = 'ng-animate-start';
var NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME = 'ng-animate-active';
var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3;
var CLOSING_TIME_BUFFER = 1.5;
var ONE_SECOND = 1000;
var animationCounter = 0;
var lookupCache = {};
var parentCounter = 0;
var animationReflowQueue = [], animationTimer, timeOut = false;
function afterReflow(callback) {
animationReflowQueue.push(callback);
var animationReflowQueue = [];
var animationElementQueue = [];
var animationTimer;
var closingAnimationTime = 0;
var timeOut = false;
function afterReflow(element, callback) {
$timeout.cancel(animationTimer);
animationReflowQueue.push(callback);
var node = extractElementNode(element);
element = angular.element(node);
animationElementQueue.push(element);
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
closingAnimationTime = Math.max(closingAnimationTime,
(elementData.maxDelay + elementData.maxDuration) * CLOSING_TIME_BUFFER * ONE_SECOND);
//by placing a counter we can avoid an accidental
//race condition which may close an animation when
//a follow-up animation is midway in its animation
elementData.animationCount = animationCounter;
animationTimer = $timeout(function() {
forEach(animationReflowQueue, function(fn) {
fn();
});
//copy the list of elements so that successive
//animations won't conflict if they're added before
//the closing animation timeout has run
var elementQueueSnapshot = [];
var animationCounterSnapshot = animationCounter;
forEach(animationElementQueue, function(elm) {
elementQueueSnapshot.push(elm);
});
$timeout(function() {
closeAllAnimations(elementQueueSnapshot, animationCounterSnapshot);
elementQueueSnapshot = null;
}, closingAnimationTime, false);
animationReflowQueue = [];
animationElementQueue = [];
animationTimer = null;
lookupCache = {};
closingAnimationTime = 0;
animationCounter++;
}, 10, false);
}
function closeAllAnimations(elements, count) {
forEach(elements, function(element) {
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
if(elementData && elementData.animationCount == count) {
(elementData.closeAnimationFn || noop)();
}
});
}
function getElementAnimationDetails(element, cacheKey) {
var data = cacheKey ? lookupCache[cacheKey] : null;
if(!data) {
@@ -1007,6 +1068,7 @@ angular.module('ngAnimate', ['ng'])
timeout is empty (this would cause a flicker bug normally
in the page. There is also no point in performing an animation
that only has a delay and no duration */
var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay);
var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration);
if(maxDuration === 0) {
element.removeClass(className);
@@ -1016,13 +1078,9 @@ angular.module('ngAnimate', ['ng'])
//temporarily disable the transition so that the enter styles
//don't animate twice (this is here to avoid a bug in Chrome/FF).
var activeClassName = '';
if(timings.transitionDuration > 0) {
element.addClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
activeClassName += NG_ANIMATE_FALLBACK_ACTIVE_CLASS_NAME + ' ';
blockTransitions(element);
} else {
timings.transitionDuration > 0 ?
blockTransitions(element) :
blockKeyframeAnimations(element);
}
forEach(className.split(' '), function(klass, i) {
activeClassName += (i > 0 ? ' ' : '') + klass + '-active';
@@ -1032,6 +1090,7 @@ angular.module('ngAnimate', ['ng'])
className : className,
activeClassName : activeClassName,
maxDuration : maxDuration,
maxDelay : maxDelay,
classes : className + ' ' + activeClassName,
timings : timings,
stagger : stagger,
@@ -1066,30 +1125,28 @@ angular.module('ngAnimate', ['ng'])
}
function animateRun(element, className, activeAnimationComplete) {
var data = element.data(NG_ANIMATE_CSS_DATA_KEY);
var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY);
var node = extractElementNode(element);
if(node.className.indexOf(className) == -1 || !data) {
if(node.className.indexOf(className) == -1 || !elementData) {
activeAnimationComplete();
return;
}
var timings = data.timings;
var stagger = data.stagger;
var maxDuration = data.maxDuration;
var activeClassName = data.activeClassName;
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * 1000;
var timings = elementData.timings;
var stagger = elementData.stagger;
var maxDuration = elementData.maxDuration;
var activeClassName = elementData.activeClassName;
var maxDelayTime = Math.max(timings.transitionDelay, timings.animationDelay) * ONE_SECOND;
var startTime = Date.now();
var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT;
var ii = data.ii;
var ii = elementData.ii;
var applyFallbackStyle, style = '', appliedStyles = [];
var style = '', appliedStyles = [];
if(timings.transitionDuration > 0) {
var propertyStyle = timings.transitionPropertyStyle;
if(propertyStyle.indexOf('all') == -1) {
applyFallbackStyle = true;
var fallbackProperty = $sniffer.msie ? '-ms-zoom' : 'border-spacing';
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ', ' + fallbackProperty + '; ';
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ', ' + timings.transitionDuration + 's; ';
style += CSS_PREFIX + 'transition-property: ' + propertyStyle + ';';
style += CSS_PREFIX + 'transition-duration: ' + timings.transitionDurationStyle + ';';
appliedStyles.push(CSS_PREFIX + 'transition-property');
appliedStyles.push(CSS_PREFIX + 'transition-duration');
}
@@ -1098,10 +1155,6 @@ angular.module('ngAnimate', ['ng'])
if(ii > 0) {
if(stagger.transitionDelay > 0 && stagger.transitionDuration === 0) {
var delayStyle = timings.transitionDelayStyle;
if(applyFallbackStyle) {
delayStyle += ', ' + timings.transitionDelay + 's';
}
style += CSS_PREFIX + 'transition-delay: ' +
prepareStaggerDelay(delayStyle, stagger.transitionDelay, ii) + '; ';
appliedStyles.push(CSS_PREFIX + 'transition-delay');
@@ -1124,11 +1177,16 @@ angular.module('ngAnimate', ['ng'])
element.on(css3AnimationEvents, onAnimationProgress);
element.addClass(activeClassName);
elementData.closeAnimationFn = function() {
onEnd();
activeAnimationComplete();
};
return onEnd;
// This will automatically be called by $animate so
// there is no need to attach this internally to the
// timeout done method.
return function onEnd(cancelled) {
function onEnd(cancelled) {
element.off(css3AnimationEvents, onAnimationProgress);
element.removeClass(activeClassName);
animateClose(element, className);
@@ -1136,7 +1194,7 @@ angular.module('ngAnimate', ['ng'])
for (var i in appliedStyles) {
node.style.removeProperty(appliedStyles[i]);
}
};
}
function onAnimationProgress(event) {
event.stopPropagation();
@@ -1202,7 +1260,7 @@ angular.module('ngAnimate', ['ng'])
//data from the element which will not make the 2nd animation
//happen in the first place
var cancel = preReflowCancellation;
afterReflow(function() {
afterReflow(element, function() {
unblockTransitions(element);
unblockKeyframeAnimations(element);
//once the reflow is complete then we point cancel to
@@ -1218,7 +1276,6 @@ angular.module('ngAnimate', ['ng'])
function animateClose(element, className) {
element.removeClass(className);
element.removeClass(NG_ANIMATE_FALLBACK_CLASS_NAME);
element.removeData(NG_ANIMATE_CSS_DATA_KEY);
}
@@ -1237,7 +1294,7 @@ angular.module('ngAnimate', ['ng'])
//make the element super hidden and override any CSS style values
clone.attr('style','position:absolute; top:-9999px; left:-9999px');
clone.removeAttr('id');
clone.html('');
clone.empty();
forEach(oldClasses.split(' '), function(klass) {
clone.removeClass(klass);
@@ -1268,7 +1325,7 @@ angular.module('ngAnimate', ['ng'])
beforeAddClass : function(element, className, animationCompleted) {
var cancellationMethod = animateBefore(element, suffixClasses(className, '-add'));
if(cancellationMethod) {
afterReflow(function() {
afterReflow(element, function() {
unblockTransitions(element);
unblockKeyframeAnimations(element);
animationCompleted();
@@ -1285,7 +1342,7 @@ angular.module('ngAnimate', ['ng'])
beforeRemoveClass : function(element, className, animationCompleted) {
var cancellationMethod = animateBefore(element, suffixClasses(className, '-remove'));
if(cancellationMethod) {
afterReflow(function() {
afterReflow(element, function() {
unblockTransitions(element);
unblockKeyframeAnimations(element);
animationCompleted();
+7 -2
View File
@@ -1087,7 +1087,8 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
var definitions = [],
expectations = [],
responses = [],
responsesPush = angular.bind(responses, responses.push);
responsesPush = angular.bind(responses, responses.push),
copy = angular.copy;
function createResponse(status, data, headers) {
if (angular.isFunction(status)) return status;
@@ -1119,7 +1120,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
function handleResponse() {
var response = wrapped.response(method, url, data, headers);
xhr.$$respHeaders = response[2];
callback(response[0], response[1], xhr.getAllResponseHeaders());
callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders());
}
function handleTimeout() {
@@ -1571,6 +1572,10 @@ function MockHttpExpectation(method, url, data, headers) {
};
}
function createMockXhr() {
return new MockXhr();
}
function MockXhr() {
// hack for testing $http, $httpBackend
+54 -6
View File
@@ -24,6 +24,25 @@ function lookupDottedPath(obj, path) {
return obj;
}
/**
* Create a shallow copy of an object and clear other fields from the destination
*/
function shallowClearAndCopy(src, dst) {
dst = dst || {};
angular.forEach(dst, function(value, key){
delete dst[key];
});
for (var key in src) {
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc overview
* @name ngResource
@@ -71,7 +90,7 @@ function lookupDottedPath(obj, path) {
* when a param value needs to be obtained for a request (unless the param was overridden).
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
* excess keys are appended to the url seapph query after the `?`.
*
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
@@ -218,7 +237,7 @@ function lookupDottedPath(obj, path) {
newCard.name = "Mike Smith";
newCard.$save();
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'01234', name: 'Mike Smith'};
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
expect(newCard.id).toEqual(789);
* </pre>
*
@@ -253,6 +272,35 @@ function lookupDottedPath(obj, path) {
});
});
</pre>
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
* <pre>
* var app = angular.module('app', ['ngResource', 'ngRoute']);
*
* // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null,
* {
* 'update': { method:'PUT' }
* });
* }]);
*
* // In our controller we get the ID from the URL using ngRoute and $routeParams
* // We pass in $routeParams and our Notes factory along with $scope
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
* // First get a note object from the factory
* var note = Notes.get({ id:$routeParams.id });
* $id = note.id;
*
* // Now call update passing in the ID first then the object you are updating
* Notes.update({ id:$id }, note);
*
* // This will PUT /notes/ID with the note object in the request payload
* }]);
* </pre>
*/
angular.module('ngResource', ['ng']).
factory('$resource', ['$http', '$q', function($http, $q) {
@@ -353,7 +401,7 @@ angular.module('ngResource', ['ng']).
});
// strip trailing slashes and set the url
url = url.replace(/\/+$/, '');
url = url.replace(/\/+$/, '') || '/';
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
@@ -393,7 +441,7 @@ angular.module('ngResource', ['ng']).
}
function Resource(value){
copy(value || {}, this);
shallowClearAndCopy(value || {}, this);
}
forEach(actions, function(action, name) {
@@ -465,7 +513,7 @@ angular.module('ngResource', ['ng']).
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if ( angular.isArray(data) !== (!!action.isArray) ) {
if (angular.isArray(data) !== (!!action.isArray)) {
throw $resourceMinErr('badcfg', 'Error in resource configuration. Expected ' +
'response to contain an {0} but got an {1}',
action.isArray?'array':'object', angular.isArray(data)?'array':'object');
@@ -477,7 +525,7 @@ angular.module('ngResource', ['ng']).
value.push(new Resource(item));
});
} else {
copy(data, value);
shallowClearAndCopy(data, value);
value.$promise = promise;
}
}
+48 -28
View File
@@ -1,6 +1,8 @@
'use strict';
ngRouteModule.directive('ngView', ngViewFactory);
ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc directive
@@ -166,8 +168,8 @@ ngRouteModule.directive('ngView', ngViewFactory);
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory( $route, $anchorScroll, $animate) {
return {
restrict: 'ECA',
terminal: true,
@@ -197,8 +199,9 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
var locals = $route.current && $route.current.locals,
template = locals && locals.$template;
if (template) {
if (angular.isDefined(template)) {
var newScope = scope.$new();
var current = $route.current;
// Note: This will also link all children of ng-view that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
@@ -206,34 +209,18 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, angular.noop);
clone.html(template);
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
});
cleanupLastView();
});
cleanupLastView();
var link = $compile(clone.contents()),
current = $route.current;
currentScope = current.scope = newScope;
currentElement = clone;
if (current.controller) {
locals.$scope = currentScope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
currentScope[current.controllerAs] = controller;
}
clone.data('$ngControllerController', controller);
clone.children().data('$ngControllerController', controller);
}
link(currentScope);
currentScope = current.scope = newScope;
currentScope.$emit('$viewContentLoaded');
currentScope.$eval(onloadExp);
} else {
@@ -243,3 +230,36 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
}
};
}
// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
function ngViewFillContentFactory($compile, $controller, $route) {
return {
restrict: 'ECA',
priority: -400,
link: function(scope, $element) {
var current = $route.current,
locals = current.locals;
$element.html(locals.$template);
var link = $compile($element.contents());
if (current.controller) {
locals.$scope = scope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}
link(scope);
}
};
}
+4 -4
View File
@@ -52,13 +52,13 @@ function $RouteProvider(){
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon (`:name`). All characters up
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star (`:name*`).
* All characters are eagerly stored in `$routeParams` under the given `name`
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark (`:name?`).
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashs/edit` and extract:
+1 -1
View File
@@ -206,7 +206,7 @@ var validAttrs = angular.extend({}, uriAttrs, makeMap(
'abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,'+
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,'+
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,'+
'scope,scrolling,shape,span,start,summary,target,title,type,'+
'scope,scrolling,shape,size,span,start,summary,target,title,type,'+
'valign,value,vspace,width'));
function makeMap(str) {
+2 -1
View File
@@ -239,7 +239,8 @@ function callerFile(offset) {
* To work around this we instead use our own handler that fires a real event.
*/
(function(fn){
var parentTrigger = fn.trigger;
// We need a handle to the original trigger function for input tests.
var parentTrigger = fn._originalTrigger = fn.trigger;
fn.trigger = function(type) {
if (/(click|change|keydown|blur|input|mousedown|mouseup)/.test(type)) {
var processDefaults = [];
-5
View File
@@ -1,5 +0,0 @@
#!/usr/bin/env bash
./version.js --minor-bump
VERSION=`./version.js --curent`
git commit -a -m "chore(relase): start v$VERSION iteration"
+15
View File
@@ -504,6 +504,21 @@ describe('angular', function() {
expect(log).toEqual(['0:a', '1:c']);
});
if (document.querySelectorAll) {
it('should handle the result of querySelectorAll in IE8 as it has no hasOwnProperty function', function() {
document.body.innerHTML = "<p>" +
"<a name='x'>a</a>" +
"<a name='y'>b</a>" +
"<a name='x'>c</a>" +
"</p>";
var htmlCollection = document.querySelectorAll('[name="x"]'),
log = [];
forEach(htmlCollection, function(value, key) { log.push(key + ':' + value.innerHTML)});
expect(log).toEqual(['0:a', '1:c']);
});
}
it('should handle arguments objects like arrays', function() {
var args,
+11
View File
@@ -74,6 +74,17 @@ describe('injector', function() {
});
it('should not corrupt the cache when an object fails to get instantiated', function() {
expect(function() {
injector.get('idontexist');
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
expect(function() {
injector.get('idontexist');
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist");
});
it('should provide path to the missing provider', function() {
providers('a', function(idontexist) {return 1;});
providers('b', function(a) {return 2;});
+1 -2
View File
@@ -29,8 +29,7 @@ beforeEach(function() {
bindJQuery();
}
angular.element(document.body).html('').removeData();
angular.element(document.body).empty().removeData();
});
afterEach(function() {
+100 -2
View File
@@ -322,7 +322,7 @@ describe('jqLite', function() {
});
it('should emit $destroy event if an element is removed via html()', inject(function(log) {
it('should emit $destroy event if an element is removed via html(\'\')', inject(function(log) {
var element = jqLite('<div><span>x</span></div>');
element.find('span').on('$destroy', log.fn('destroyed'));
@@ -333,6 +333,17 @@ describe('jqLite', function() {
}));
it('should emit $destroy event if an element is removed via empty()', inject(function(log) {
var element = jqLite('<div><span>x</span></div>');
element.find('span').on('$destroy', log.fn('destroyed'));
element.empty();
expect(element.html()).toBe('');
expect(log).toEqual('destroyed');
}));
it('should retrieve all data if called without params', function() {
var element = jqLite(a);
expect(element.data()).toEqual({});
@@ -786,7 +797,7 @@ describe('jqLite', function() {
});
it('should read/write value', function() {
it('should read/write a value', function() {
var element = jqLite('<div>abc</div>');
expect(element.length).toEqual(1);
expect(element[0].innerHTML).toEqual('abc');
@@ -797,6 +808,16 @@ describe('jqLite', function() {
});
describe('empty', function() {
it('should write a value', function() {
var element = jqLite('<div>abc</div>');
expect(element.length).toEqual(1);
expect(element.empty() == element).toBeTruthy();
expect(element.html()).toEqual('');
});
});
describe('on', function() {
it('should bind to window on hashchange', function() {
if (jqLite.fn) return; // don't run in jQuery
@@ -1099,6 +1120,26 @@ describe('jqLite', function() {
});
it('should deregister specific listener within the listener and call subsequent listeners', function() {
var aElem = jqLite(a),
clickSpy = jasmine.createSpy('click'),
clickOnceSpy = jasmine.createSpy('clickOnce').andCallFake(function() {
aElem.off('click', clickOnceSpy);
});
aElem.on('click', clickOnceSpy);
aElem.on('click', clickSpy);
browserTrigger(a, 'click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy).toHaveBeenCalledOnce();
browserTrigger(a, 'click');
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
});
it('should deregister specific listener for multiple types separated by spaces', function() {
var aElem = jqLite(a),
masterSpy = jasmine.createSpy('master'),
@@ -1136,6 +1177,63 @@ describe('jqLite', function() {
}
});
describe('one', function() {
it('should only fire the callback once', function() {
var element = jqLite(a);
var spy = jasmine.createSpy('click');
element.one('click', spy);
browserTrigger(element, 'click');
expect(spy).toHaveBeenCalledOnce();
browserTrigger(element, 'click');
expect(spy).toHaveBeenCalledOnce();
});
it('should deregister when off is called', function() {
var element = jqLite(a);
var spy = jasmine.createSpy('click');
element.one('click', spy);
element.off('click', spy);
browserTrigger(element, 'click');
expect(spy).not.toHaveBeenCalled();
});
it('should return the same event object just as on() does', function() {
var element = jqLite(a);
var eventA, eventB;
element.on('click', function(event) {
eventA = event;
});
element.one('click', function(event) {
eventB = event;
});
browserTrigger(element, 'click');
expect(eventA).toEqual(eventB);
});
it('should not remove other event handlers of the same type after execution', function() {
var element = jqLite(a);
var calls = [];
element.one('click', function(event) {
calls.push('one');
});
element.on('click', function(event) {
calls.push('on');
});
browserTrigger(element, 'click');
browserTrigger(element, 'click');
expect(calls).toEqual(['one','on','on']);
});
});
describe('replaceWith', function() {
it('should replaceWith', function() {
+5
View File
@@ -609,5 +609,10 @@ describe('browser', function() {
fakeDocument.basePath = 'http://host.com/base/path/index.html';
expect(browser.baseHref()).toEqual('/base/path/index.html');
});
it('should remove domain from <base href> beginning with \'//\'', function() {
fakeDocument.basePath = '//google.com/base/path/';
expect(browser.baseHref()).toEqual('/base/path/');
});
});
});
+73 -6
View File
@@ -170,26 +170,26 @@ describe('$compile', function() {
// First with only elements at the top level
element = jqLite('<div><div></div></div>');
$compile(element.contents())($rootScope);
element.html('');
element.empty();
expect(calcCacheSize()).toEqual(0);
// Next with non-empty text nodes at the top level
// (in this case the compiler will wrap them in a <span>)
element = jqLite('<div>xxx</div>');
$compile(element.contents())($rootScope);
element.html('');
element.empty();
expect(calcCacheSize()).toEqual(0);
// Next with comment nodes at the top level
element = jqLite('<div><!-- comment --></div>');
$compile(element.contents())($rootScope);
element.html('');
element.empty();
expect(calcCacheSize()).toEqual(0);
// Finally with empty text nodes at the top level
element = jqLite('<div> \n<div></div> </div>');
$compile(element.contents())($rootScope);
element.html('');
element.empty();
expect(calcCacheSize()).toEqual(0);
});
@@ -202,9 +202,14 @@ describe('$compile', function() {
if (msie < 9) return;
element = jqLite('<div>{{1+2}}</div>');
element[0].childNodes[1] = {nodeType: 3, nodeName: 'OBJECT', textContent: 'fake node'};
if (!element[0].childNodes[1]) return; //browser doesn't support this kind of mocking
try {
element[0].childNodes[1] = {nodeType: 3, nodeName: 'OBJECT', textContent: 'fake node'};
} catch(e) {
} finally {
if (!element[0].childNodes[1]) return; //browser doesn't support this kind of mocking
}
expect(element[0].childNodes[1].textContent).toBe('fake node');
$compile(element)($rootScope);
@@ -2492,6 +2497,62 @@ describe('$compile', function() {
expect(lastRefValueInParent).toBe('new');
}));
describe('literal objects', function() {
it('should copy parent changes', inject(function() {
compile('<div><span my-component reference="{name: name}">');
$rootScope.name = 'a';
$rootScope.$apply();
expect(componentScope.reference).toEqual({name: 'a'});
$rootScope.name = 'b';
$rootScope.$apply();
expect(componentScope.reference).toEqual({name: 'b'});
}));
it('should not change the component when parent does not change', inject(function() {
compile('<div><span my-component reference="{name: name}">');
$rootScope.name = 'a';
$rootScope.$apply();
var lastComponentValue = componentScope.reference;
$rootScope.$apply();
expect(componentScope.reference).toBe(lastComponentValue);
}));
it('should complain when the component changes', inject(function() {
compile('<div><span my-component reference="{name: name}">');
$rootScope.name = 'a';
$rootScope.$apply();
componentScope.reference = {name: 'b'};
expect(function() {
$rootScope.$apply();
}).toThrowMinErr("$compile", "nonassign", "Expression '{name: name}' used with directive 'myComponent' is non-assignable!");
}));
it('should work for primitive literals', inject(function() {
test('1', 1);
test('null', null);
test('undefined', undefined);
test("'someString'", 'someString');
function test(literalString, literalValue) {
compile('<div><span my-component reference="'+literalString+'">');
$rootScope.$apply();
expect(componentScope.reference).toBe(literalValue);
dealoc(element);
}
}));
});
});
@@ -4188,6 +4249,12 @@ describe('$compile', function() {
expect(element.attr('test3')).toBe('Misko');
}));
it('should work with the "href" attribute', inject(function($compile, $rootScope) {
$rootScope.value = 'test';
element = $compile('<a ng-attr-href="test/{{value}}"></a>')($rootScope);
$rootScope.$digest();
expect(element.attr('href')).toBe('test/test');
}));
it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
$rootScope.name = "Misko";
+26
View File
@@ -58,4 +58,30 @@ describe('a', function() {
expect(element.text()).toBe('hello@you');
});
it('should not link and hookup an event if href is present at compile', function() {
var jq = jQuery || jqLite;
element = jq('<a href="//a.com">hello@you</a>');
var linker = $compile(element);
spyOn(jq.prototype, 'on');
linker($rootScope);
expect(jq.prototype.on).not.toHaveBeenCalled();
});
it('should not link and hookup an event if name is present at compile', function() {
var jq = jQuery || jqLite;
element = jq('<a name="bobby">hello@you</a>');
var linker = $compile(element);
spyOn(jq.prototype, 'on');
linker($rootScope);
expect(jq.prototype.on).not.toHaveBeenCalled();
});
});
+1 -1
View File
@@ -216,7 +216,7 @@ describe('form', function() {
// yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep
// this test small. Imagine that the destroy action will cause a model change (e.g.
// $location change) that will cause some directive to destroy the dom (e.g. ngView+$route)
doc.html('');
doc.empty();
destroyed = true;
}
+48 -13
View File
@@ -477,19 +477,37 @@ describe('input', function() {
expect(scope.name).toEqual('adam');
});
it('should not update the model between "compositionstart" and "compositionend"', function() {
compileInput('<input type="text" ng-model="name" name="alias"" />');
changeInputValueTo('a');
expect(scope.name).toEqual('a');
if (!(msie < 9)) {
browserTrigger(inputElm, 'compositionstart');
changeInputValueTo('adam');
expect(scope.name).toEqual('a');
browserTrigger(inputElm, 'compositionend');
}
changeInputValueTo('adam');
expect(scope.name).toEqual('adam');
});
if (!(msie < 9)) {
describe('compositionevents', function() {
it('should not update the model between "compositionstart" and "compositionend" on non android', inject(function($sniffer) {
$sniffer.android = false;
compileInput('<input type="text" ng-model="name" name="alias"" />');
changeInputValueTo('a');
expect(scope.name).toEqual('a');
browserTrigger(inputElm, 'compositionstart');
changeInputValueTo('adam');
expect(scope.name).toEqual('a');
browserTrigger(inputElm, 'compositionend');
changeInputValueTo('adam');
expect(scope.name).toEqual('adam');
}));
it('should update the model between "compositionstart" and "compositionend" on android', inject(function($sniffer) {
$sniffer.android = true;
compileInput('<input type="text" ng-model="name" name="alias"" />');
changeInputValueTo('a');
expect(scope.name).toEqual('a');
browserTrigger(inputElm, 'compositionstart');
changeInputValueTo('adam');
expect(scope.name).toEqual('adam');
browserTrigger(inputElm, 'compositionend');
changeInputValueTo('adam2');
expect(scope.name).toEqual('adam2');
}));
});
}
describe('"change" event', function() {
function assertBrowserSupportsChangeEvent(inputEventSupported) {
@@ -515,6 +533,23 @@ describe('input', function() {
'event so that form auto complete works',function() {
assertBrowserSupportsChangeEvent(true);
});
if (!_jqLiteMode) {
it('should not cause the double $digest when triggering an event using jQuery', function() {
$sniffer.hasEvent = function(eventName) {
return eventName !== 'input';
};
compileInput('<input type="text" ng-model="name" name="alias" ng-change="change()" />');
scope.field = 'fake field';
scope.$watch('field', function() {
// We need to use _originalTrigger since trigger is modified by Angular Scenario.
inputElm._originalTrigger('change');
});
scope.$apply();
});
}
});
describe('"paste" and "cut" events', function() {

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