Compare commits

..

151 Commits

Author SHA1 Message Date
Wes 37310e024d docs(Courses): fix syntax issue in developer guide
The courses section should use commas between links to differentiate the instances of each link

Closes #10448
2014-12-13 21:28:02 -05:00
Caitlin Potter 306e626196 revert feat($compile): add support for ng-attr with camelCased attributes
This reverts commit a1e7eb6360.

This change broke the stable branch builds.

/CC @petebacondarwin / @wesleycho
2014-12-13 20:39:15 -05:00
Dan Tennery-Spalding f31c7492ec docs(API Reference): corrected two typos - two missing commas
In the ngAnimate section, there were two commas missing from two sentences. This is inconsistent with the grammar used in the rest of the API documentation and made the document (slightly) more difficult to read. The two sentences are shown below, with the new commas added:

1. "Once defined, the animation can be triggered"
                           ^
                    comma added

2. "Once registered, the animation can be triggered"
                              ^
                    comma added

Closes #10447
2014-12-13 20:20:40 -05:00
Wesley Cho a1e7eb6360 feat($compile): add support for ng-attr with camelCased attributes
SVG attributes are case sensitive and some have upper case letters in them
This change ensures that we can identify these, when being used with the `ng-attr`
directive, by encoding upper case letters with a preceding underscore.

For example to apply `ng-attr` to the `viewBox` attribute we could write
`ng-attr-view_box` - or any of the other variants: `ng:attr:view_box`,
`data-ng-attr-view_box`, etc.

Closes #9845
Closes #10194
2014-12-09 12:06:04 +00:00
Tobias Davis 169e5326d1 docs($interval): correcting example code indentation
Bueno!

Closes #10372
2014-12-08 16:30:05 -05:00
Georgios Kalpakas bb3b65374d chore(changelog): add test for addition of trailing newline
Adds tests for the functionality added by #9550.

Closes #10358
2014-12-08 13:05:32 -05:00
Giuseppe Caruso ac9336b35a docs(guide/controllers): Just a typo
Gingerbreak would break testing. :)

Oh my gosh he's right, it totally w/ould. That is so embarrassing!

Closes #10353
2014-12-08 10:51:05 -05:00
Ciro Nunes 75787446ee docs(compile): document $attrs.$normalize
Closes #10345
2014-12-05 14:19:46 -05:00
Caitlin Potter 14409d7a7f style(ngHref): make jscs happy ;-; 2014-11-28 22:29:05 -06:00
Sagar Ranglani 370676d4d0 docs(ngHref): fix poor paragraph construction
It was bad.

In order to improve the docs, the inclusion of the last sentence would help define `ngHref` well.

Closes #10254
2014-11-28 22:10:28 -06:00
Caitlin Potter 4c218de4d3 style(*): IE is a real browser, and chakra is pretty solid
- IE9+ do not have issues with Function.prototype.apply() on builtin fns (asked Brian Terlson)
  (NOTE: there may still be corner cases where builtins will not have `apply()` --- this may
  need to be reverted on complaint).
- HTMLScriptElement#text is an IDL-spec'd attribute, and we use it in all cases --- so the
  comment was sort of nonsense.
- The value of `msie` does not depend on whether the user is using a "real" browser or not.

Closes #10242
2014-11-26 14:39:23 -06:00
Lucas Galfaso 929dd15b9b fix(linky): encode double quotes when serializing email addresses
Email addresses can (under certain restrictions) include double quote
characters. See http://tools.ietf.org/html/rfc3696#section-3.

For example, `"Jo Bloggs"@abc.com` is a valid email address.

When serializing emails to the `href` attribute of an anchor element,
we must HTML encode these double quote characters. See
http://www.w3.org/TR/html-markup/syntax.html#syntax-attr-double-quoted

This commit does not attempt to improve the functionality (i.e. regex)
that attempts to identify email addresses in a general string.

Closes #8945
Closes #8964
Closes #5946
Closes #10090
Closes #9256
2014-11-23 22:23:41 +00:00
Georgios Kalpakas 1b9e408ddb fix($route): fix redirection with optional/eager params
Fixes #9742
Closes #10202
2014-11-23 19:24:39 +01:00
thorn0 7505d126fa chore(docs): regroup version selector options into major branches and latest
Before this change we grouped by the  discontinued stable/unstable distinction.

Closes #10053
2014-11-23 14:50:31 +00:00
Caitlin Potter 7044e55feb style($http): make jscs happy 2014-11-21 00:27:45 -05:00
Dustin Chilson bd9e894fb7 docs($http): describe how to remove a header on a per request basis
Closes #10144
2014-11-21 00:27:38 -05:00
Brian Ford ba7e24ec6c chore(scripts): correctly update package.json 2014-11-20 15:21:33 -08:00
Brian Ford e1f98773c7 chore(scripts): fix 1.2.x tag name 2014-11-20 15:02:40 -08:00
Brian Ford 17d8a520ca chore(scripts): publish 1.2.x releases to npm with correct tag 2014-11-20 14:34:26 -08:00
Jeff Cross 84bf883f6d docs(CHANGELOG): update changelog with 1.2.27 2014-11-20 10:31:49 -08:00
samuel durand 38ff199a3c docs($anchorScrollProvider): document disableAutoScrolling method 2014-11-17 14:46:43 +00:00
Brian Westrich 7ff5ec254e docs(tutorial/step-5): include sort and filter in json view experiment
Closes #10082
2014-11-17 14:34:13 +00:00
Brian Westrich a2f2032a20 docs(tutorial/step-4): "unknown" option is actually blank
The name 'unknown' doesn't appear as a choice, the new choice is just blank.
Side note: once I choose one of the non-blank options, I no longer see the blank option.

Closes #10079
2014-11-17 13:47:56 +00:00
Peter Bacon Darwin 16833d0fb6 fix(select): ensure the label attribute is updated in Internet Explorer
Only changing the `<option>` text value is not enough to trigger a render
change in IE. We need to explicit update the `label` property too.

Closes #9621
Closes #10042
2014-11-15 22:55:55 +00:00
Peter Bacon Darwin 2a8a4e7fad test(select): refactor option elements expectations to use toEqualOption matcher
By using a new matcher our tests become less brittle with respect to unimportant
extra attributes.
2014-11-15 18:49:01 +00:00
Judy Tuan beeb64a6f6 docs(misc/Develop): update required Java version 2014-11-14 20:23:15 +00:00
Caitlin Potter e49e7d50c5 chore($q): make jscs happy
jscs loves to be happy and does not love trailing whitespace.
2014-11-14 11:12:37 -05:00
Bernie Telles 3b3de3f876 docs($q): explain what the $q service does in description
Explain what the $q service does in description, instead of origin document.

The original explanation was less accessible to people new to promises and JS in general.

Closes #10056
2014-11-14 11:12:27 -05:00
Chatchavan Wacharamanotham eec78e78d6 docs(guide/compiler): replaced 'locals' with 'scope'
In "Understanding How Scopes Work with Transcluded Directives" section, a text referred to an
obsolete 'locals' instead of 'scope'.

Closes #10018
2014-11-14 11:03:53 -05:00
Caitlin Potter b1f2917696 test(orderBy): add test cases for ordering array-like objects
Closes #10060
2014-11-14 10:23:46 -05:00
Karol Wypchło 409bcb3810 perf(orderBy): copy array with slice instead of for loop
Use array slice method to copy entire array instead of a for loop
http://jsperf.com/new-array-vs-splice-vs-slice/54

Closes #9942
2014-11-14 10:13:29 -05:00
codef0rmer e49a1433fd docs(guide/Index): add book AngularJS UI Development
Matthias and I wrote a book on AngularJS which might be helpful for Angular developers.

Merci~!

Closes #9971
2014-11-11 14:14:07 -05:00
AlexChan c26b5e3c88 docs(ngModel.NgModelController) use $evalAsync instead of $apply for event handling
Have the apply called safely during events by using `$evalAsync` rather than `$apply`
This will help ensure that an apply for a user directive is not called during a digest cycle.

Closes #9891
2014-11-11 13:59:48 -05:00
Chirayu Krishnappa 4d0614fd0d fix($parse, events): prevent accidental misuse of properties on $event
Closes #9969
2014-11-10 15:35:02 -08:00
Chirayu Krishnappa 756640f5aa fix($parse): add quick check for Function constructor in fast path 2014-11-10 15:34:58 -08:00
Michael James d87b7912df docs($q): added IE8 warning to promise.catch()
Closes #9987
2014-11-10 17:30:47 -05:00
Brian Ford bff8041bd0 docs(guide/index): link to security 2014-11-07 10:33:48 -08:00
Brian Ford e0ee491633 docs($parse): formatting, link to security docs 2014-11-07 10:33:31 -08:00
Brian Ford 7dfe82e135 docs(security): add security doc 2014-11-07 10:33:04 -08:00
Lucas Galfaso 9f2a53b33e docs(CHANGELOG): Document breaking change from 23bc92b1
Document the breaking change from 23bc92b1

Closes #9947
2014-11-06 15:28:10 -05:00
rsperberg 9128eac501 docs(guide/concepts): spell "Angular" with cap "A", fix typos
In these two instances, Angular was spelled with a lower-case "a." All occurrences should be spelled
consistently.

Compound adjectives preceding the noun they modify should generally be hyphenated (cf Chicago Manual
of Style, 6.40), e.g., "so-called directives."

Closes #9896
2014-11-06 14:17:24 -05:00
Adir 85e8d5ea67 docs(guide,tutorial): fix outdated Protractor API link
Link to the gh-pages deployment of protractor docs, it's much easier on the eyes.

Closes #9946
2014-11-06 13:58:55 -05:00
danielmbarlow a36863eea3 docs(tutorial/step_12): added 'see phone-detail change'
This one caught me out for a while because, despite the note underneath, I didn't notice the addition
of <div class="phone-images"> and it's repeater until later.

Closes #9924
2014-11-05 12:14:54 -05:00
danielmbarlow 98da7ade7a docs(tutorial/step_12): small change to overview
The bullet points at the beginning of the article were a little hard to understand because they
didn't follow the grammatical form of the preceding articles. I hope these small modifications make
it a little easier for someone else to read.

Closes #9922
2014-11-05 09:10:36 -05:00
danielmbarlow 846fe1b3ef docs(tutorial/step_05): explain need for $httpBackend.flush in tests
There is an excellent explanation for the need for this in the documentation that may be helpful to
tutorial users, so I added a link to it.

Closes #9919
2014-11-05 09:10:28 -05:00
Julie Ralph f4648abe03 chore(ci): update protractor to version 1.4.0 2014-11-04 17:18:30 -08:00
Tobias Gesellchen 5f9a1122e2 chore(.gitignore): ignore IntelliJ IDEA module files
Ignore IntelliJ IDEA modules files in the git repository

Closes #5273
2014-11-04 12:47:26 -05:00
Tero Parviainen 1afeb37756 docs(guide/scope): describe watch depths
Describe and visualize the three watch strategies: By reference, by collection items, and by value.

Closes #9388
2014-11-03 12:44:39 -08:00
Julie Ralph 7809e75a56 chore(ci): fix broken sauce connect download url 2014-10-31 09:24:37 -07:00
Uri Goldshtein 1472c31431 docs(guide/index): add link to angular-meteor
Bueno!

Closes #9844
2014-10-30 11:13:43 -04:00
Ralph Samuel d3b839d986 docs(guide/Scopes): add "the" in front of "DOM" on line 42
Closes #9818
2014-10-29 13:51:34 -04:00
ChristianKohler c8c2386296 chore(docs): clarify comment which was copy&paste from dgeni example
It makes people happy, so why not

Closes #9798
2014-10-27 12:24:02 -04:00
alindberg d0b5bfa454 docs(tutorial/tutorial): instructions to install npm on debian
Additional package required for a Debian install

Closes #9749
2014-10-22 14:17:04 -04:00
Martin Hochel 1fa2d56ba3 docs($http): document $httpProvider.interceptors in $httpProvider documentation
☆.。.:・゜☆ HASHBANG #NGEUROPE ☆.。.:・゜☆

Fixes #9366
Closes #9728
2014-10-22 11:26:43 -04:00
marcin-wosinek efedc643d1 docs($templateCache): clarify inline template
Current doc doesn't state required tag location clear enough. It was
[stack overflow|http://stackoverflow.com/a/16125138] where I've found that requirement

Closes #9741
2014-10-22 10:02:49 -04:00
Jackson Ray Hamilton 34230b30b7 docs(guide/bootstrap): close script tag
Closes #9739
2014-10-22 00:45:52 -04:00
Dwayne Crooks 8354d02805 docs(ngModel.NgModelController): remove extra 'to'
Closes #9702
2014-10-20 21:34:13 +01:00
Bastien Caudan 1426b02980 fix(ngMock): $httpBackend should match data containing Date objects correctly
If a response or expectation contained a date object then `$httpBackend.expect`
was not matching correctly.

This commit encodes then decodes the object being matched to ensure consistency.

Closes #5127
2014-10-20 13:07:07 +01:00
lguyot 29aeee2250 docs(tutorial/step-11): remove excess words
Closes #9690
2014-10-20 12:11:14 +01:00
Augustas 6a8348f715 docs(tutorial/step-7) - correct a url
Closes #9688
2014-10-20 11:44:04 +01:00
Peter Bacon Darwin 38e2856889 docs(guide/introduction): remove ambiguous "code-behind" jargon
This commit tries to remove the jargon and explain in plain English what
it means to add "code-behind" via a directive.

Closes #9684
2014-10-20 10:32:16 +01:00
Peter Bacon Darwin a56435f3ae fix($rootScope.$on) check listener existense while deregistering
Check that listener is still present in $$listeners before decrease
$$listenerCount. It fixes problem with incorrect $$listenerCount after
call deregistering function multiple times.

Closes #9666
Closes #9667
2014-10-19 12:10:58 +01:00
Juan M. Cuello 75082c975c docs(error/dupes): Little fix in explanation text.
Little fix in docs/content/error/ngRepeat/dupes.ngdoc.

Closes #9673
2014-10-18 15:39:12 -04:00
Daniel Luz 31b6bfaaf4 docs(ngEventDirs): update remarks on behavior
The event directives haven't stopped propagation by default in a long time.
If that behavior is desired, the handler may use the provided `$event` to call:

    $event.stopPropagation();

Closes #9640
2014-10-16 16:44:48 -04:00
Ciro Nunes 2d74323e3e docs(ngModel): fix anchor text
Closes #9604
2014-10-14 10:44:36 +01:00
Afshin Mokhtari edc2613ed5 docs(CONTRIBUTING): prototypical -> prototypal
Closes #9608
2014-10-14 10:43:10 +01:00
Richard 6eab8ab187 docs(guide/unit-testing): use whitespace to improve readability
☆.。.:・゜☆ Merci (◜௰◝) ☆.。.:・゜☆

Closes #9572
2014-10-12 23:04:02 -04:00
Georgios Kalpakas 80ce046fd1 chore(CHANGELOG): add an extra new line after each item in the "BREAKING CHANGES" list
This ensures that the next item will appear on a new line and be properly
parsed as new list item (and not as the continuation of the current item),
even if the current item does not end with a newline character.
Currently, it would result is something like this:

    - **item 1**: due to ...
      blah1 blah1 blah1- **item 2**: due to...
      blah2 blah2 blah2

instead of the intended:

    - **item 1**: duo to ...
      ...
    - **item 2**: due to ...
      ...
2014-10-10 19:07:22 +01:00
Chris Inch 315f320e2e docs(guide/compiler): add title to the page 2014-10-09 13:54:20 -07:00
Georgios Kalpakas ec4fe1bcab fix($http): add missing shortcut methods and missing docs
There was some inconsistency in version 1.2.25 regarding the definition
and documentation of shortcut methods in `ng.$http` and
`ngMock[E2E].$httpBackend`. Some methods didn't exist (although documented
as existing), whereas some methods did exist, but wheren't documented.
This commit fixes the above errors and adds tests to verify the existence
of all shortcut methods.
More specificcally, the following issues were addressed:
`ng.$http`: Add the missing `patch()` shortcut method and related docs.
`ng.$http` spec: Add test to verify the existence and functionality of the
`patch()` shortcut method.
`ngMock[E2E].$httpBackend`: Add docs for the (existing) `whenPATCH()`
shortcut method, add the missing `expectHEAD()`/`whenHEAD()` shortcut
methods, fix grammatical errors.
`ngMock[E2E].$httpBackend` spec: Add test to verify the existence of shortcut
methods for all HTTP verbs.

Closes #9180
Closes #9321
2014-10-09 20:13:08 +01:00
bolasblack 3303fe41e4 docs(versions): remove the trailing slash in URLs when switch versions
Because `https://docs.angularjs.org/api/` can handler the trailing slash,
but `https://code.angularjs.org/1.2.24/docs/api` can not.

Fix #9043
Closes #9045
2014-10-09 11:53:37 -07:00
thorn0 f22e5fd980 docs(angular.injector): correct return type 2014-10-09 11:49:30 -07:00
Jakub Zych b11d6c792f style(filters) use consistent quote style and strict equality
before change quotes where mixed and one if contained equality operator instead of identity

Closes #9349
2014-10-09 07:11:43 -04:00
Brian Ford e713f36d7c docs(changelog): more release notes for 1.3.0-rc.5 2014-10-08 15:52:27 -07:00
Brian Ford 8162a1d731 docs(changelog): release notes for 1.3.0-rc.5 2014-10-08 13:37:55 -07:00
Izhaki 1e6f8543e5 docs($compile): note template is ignored with element transclusion 2014-10-08 11:30:49 -07:00
Jesse Palmer 0e293d2a97 docs(ngIf): update out-of-date link 2014-10-08 10:43:19 -07:00
Tobias Bosch 434d7a0903 fix($browser): Cache location.href only during page reload phase
Adds caching for url changes while a reload is happening,
as browsers do not allow to read out the new location the browser
is navigating to.

Removes unnecessary caching from $browser, as IE7-IE9 all
have the new hash value in `location.href` after changing it.
There was a wrong assumption in the previous version of this code
introduced by dca23173e2 and d70711481e.

Adds more tests for #6976
Fixes #9235
Closes #9470
2014-10-07 14:55:54 -07:00
Tobias Bosch a6e6438dae fix($browser): don’t use history api when only the hash changes
Fixes a failing test on IE9 caused as a side effect
of 404b95fe30 being merged
before 0656484d3e.

The test should have been independent on the browser running it
and it is now.

Closes #9423
Closes #9424
2014-10-07 14:54:55 -07:00
Tobias Bosch 3691d2c15f fix($browser): don’t use history api when only the hash changes
Fix jshint error
2014-10-07 14:54:55 -07:00
Tobias Bosch 4cee5fde1b fix($browser): don’t use history api when only the hash changes
IE10/11 have the following problem: When changing the url hash
via `history.pushState()` and then reverting the hash via direct
changes to `location.href` (or via a link) does not fire a
`hashchange` nor `popstate` event.

This commit changes the default behavior as follows:
Uses `location.href`/`location.replace` if the new url differs from
the previous url only in the hash fragment or the browser
does not support history API.
Use `history.pushState`/ `history.replaceState` otherwise.

Fixes #9143
Closes #9406
2014-10-07 14:54:55 -07:00
Ben Harold 5d042592fc docs($injector): fix syntax error
There was a missing parenthesis in $injector test example

Closes #9469
2014-10-07 13:40:19 -04:00
Matias Niemelä f619d032c9 fix($animate): clear the GCS cache even when no animation is detected
$animate will cache subsequent calls to GCS in the event that the element
with the same CSS classes and the same parentNode is being animated. Once the
animation is started then $animate waits for one rAF before flushing the GCS
lookup cache. Prior to this fix, if GCS was unable to detect any transitions
or keyframes on the element then it would simply close the animation, but it
would not trigger the rAF code to flush the cache. This issue caused a bug
which made it difficult to detect why certain animations are not allowed to
fire if the element didn't contain any CSS-based animations beforehand.

Closes #8813
2014-10-07 09:44:44 +03:00
Casey Garland 95f5b86240 docs(guide/index): fix broken link 2014-10-06 16:02:25 -07:00
thorn0 483d91a624 docs($injector): injectors aren't functions
Closes #9453
2014-10-06 18:20:01 -04:00
thorn0 e9339935d4 docs($injector): clean up docs for $injector#has
Fixup return type and param info.

Closes #9452
2014-10-06 16:55:49 -04:00
tommyangelo 3e468523b7 docs(ngBindHtml): explain that angular-sanitize.js is needed to depend on ngSanitize
Added comment. You need to include angular-sanitize.js otherwise you cant use ngSanitize!

Closes #9400
2014-10-06 16:47:58 -04:00
Agam Rafaeli 99f3931e3d docs(guide/introduction): guice has moved to github
Closes #9416
2014-10-06 16:33:13 -04:00
Jesse Palmer 80b78f7461 docs(ngIf): wrap ngIf in code tags
Closes #9435
2014-10-06 16:11:05 -04:00
erikrahm 386c179a94 docs(misc/faq): grammatical error fixes
Merci beaucoup

Closes #9451
Closes #9450
2014-10-06 16:07:39 -04:00
thorn0 054893b555 docs($injector): $injector isn't a function
Closes #9448
2014-10-06 20:21:18 +01:00
Tim Kendrick e865726e00 chore(docs): remove unused gruntUtils import from docs config
As of commit 11c5bb7, the gruntUtils import is no longer needed in the
gitData docs config service.

Closes #9446
2014-10-06 20:19:32 +01:00
Bijan Boustani f9370755d4 docs(guide/introduction): fix grammar style
Changed "you would currently have to write" to "you would otherwise have to write".

Seems to make more sense this way since "currently" presupposes that someone new
to Angular would be coming from a different paradigm, which they may or may not be.

Closes #9428
2014-10-06 20:04:47 +01:00
Peter Bacon Darwin f906603dd6 chore(docs): update to dgeni 0.4.1 and dgeni-packages 0.10.1
Adds a new processor to identify dangling links
2014-10-06 18:08:57 +01:00
Tobias Bosch d7c084f9cf docs($location): clarify guide regarding link handling
The trick with setting `<base href=".">` has not worked since Angular 1.2.0.
It is also misleading that it talks about `$routeProvider.otherwise`
which is not important in this case.

Related to #8869
Closes #8908
2014-10-03 17:15:25 -07:00
Peter Bacon Darwin 5847fc48e7 test($location): fix use of browserTrigger
You must now pass `keys` to the function in a config object.
This bug in the test became apparent because in newer browsers, arrays
have a function called `keys()` and this was causing browserTrigger to
fail. Previously it was quietly passing this test despite being wrong.
2014-10-03 17:15:15 -07:00
Tobias Bosch 1ee9b4ef5e fix($location): revert erroneous logic and backport refactorings from master
Backport of 22948807e3 without enforcing the `<base>` tag and without the new handling for links that only contain hash fragments.

Related to #6162
Closes #8492
2014-10-03 17:15:10 -07:00
Tobias Bosch 430082e6bd refactor(locationSpec): make helper functions take an object
Makes tests more readable
2014-10-03 16:53:56 -07:00
Chris Chua fe7d9dedaa fix($browser): handle async href on url change in <=IE9
Closes #9235
2014-10-03 15:33:31 -07:00
Dominic Watson 029ac8cb80 docs(angular.element): css() method does not retrieve computed styles
The jQuery css() getter functionality utilises getComputedStyle() whereas
jqLite only retrieves what is declared inline on an element.

Closes #7599
2014-10-03 11:58:09 +01:00
Peter Bacon Darwin 45b896a16a fix(orderBy): sort by identity if no predicate is given
Closes #5847
Closes #4579
Closes #9403
2014-10-03 09:54:44 +01:00
Pawel Kozlowski f807d7ab4e fix($location): allow 0 in path() and hash() 2014-10-02 21:32:22 +01:00
Jeff Cross 07d62426f6 docs(CHANGELOG.md): update 1.2.26 release name 2014-10-02 09:46:40 -07:00
Jeff Cross 3a142e79bd docs(changelog.md): update changelog for 1.3.0-rc.4 and 1.2.26 2014-10-01 21:25:57 -07:00
Brian Feister b40130abcf docs(minerr/unpr): note that ctrls cant depend on other ctrls 2014-09-30 14:37:06 -07:00
active-low ee988d40d0 docs(guide/concepts): improve readability 2014-09-30 12:57:32 -07:00
Adam Humphrey ad4d26c525 docs(readme): fix formatting 2014-09-30 12:36:58 -07:00
Justin Walsh f58681564d docs(guide/compiler): change span to block element in draggable example
The draggable example does not work as expected in Chrome (37.0.2062.124 m).
The span disappears when dragged beyond what appears to be a small area.
Changing the span to a block element (with a width of 65px) resolves this issue.
An alternative solution would be to change the span to a div.
2014-09-30 12:30:09 -07:00
thorn0 d23f585f4a docs($compile): add header to example 2014-09-30 11:41:19 -07:00
Jeff Cross 1850f59765 revert: fix(input): always format viewValue as a string inputs with text controls
This reverts commit 1b8b41ad23.

This is a breaking change.
2014-09-30 09:21:53 -07:00
Peter Bacon Darwin 1f182853d4 revert: fix($compile): Resolve leak with asynchronous compilation
This reverts commit 5c9c197305.

This "fix" is not yet ready for 1.2.x as it has lots of issues when JQuery
is loaded.
2014-09-29 22:19:25 +01:00
Caitlin Potter 5036ac905b docs(CHANGELOG.md): put <base> in codeblock
Prevent the tag from being processed (and not rendered). Thanks @davidlehn.

Closes #9331
2014-09-29 17:16:36 -04:00
Brian Ford 11d2242df6 fix(select): make ctrl.hasOption method consistent
Prior to this fix, options added to a select by ngOptions would not cause
`selectCtrl.hasOption` to return `true`

Closes #8761
2014-09-29 13:59:54 -07:00
Peter Bacon Darwin cea23db3de chore(npm-shrinkwrap): update to dgeni-packages 0.10.0 2014-09-29 21:56:43 +01:00
Peter Bacon Darwin 5c9c197305 fix($compile): Resolve leak with asynchronous compilation
Stop an asynchronous compilation when this is performed on an
already destroyed scope

Closes #9199
Closes #9079
Closes #8504
Closes #9197
2014-09-29 21:56:43 +01:00
Peter Bacon Darwin ecb2222ea1 chore(testabilityPatch): add jqLiteCacheSize helper 2014-09-29 21:56:43 +01:00
Julie Ralph 276310e5b5 chore(ci): remove repeated tests running from Travis build
In b2902446eb the doce2e tests were moved
into the 'unit' test job on Travis, but only half of this change ever made
it into v1.2.x. This change fixes up the other half, so that the doce2e
tests are run only once.
2014-09-29 10:55:53 -07:00
Julie Ralph 6afe0dba39 chore(e2e): bump protractor to version 1.3.1 2014-09-29 10:21:05 -07:00
Peter Bacon Darwin 038ca9b9bf chore(docs): fix links to github 2014-09-26 21:22:02 +01:00
Sandeep Panda 44c451e9f5 docs(guide/index): add book AngularJS: Novice to Ninja
I wrote a book on AngularJS (AngularJS: Novice to Ninja).

Closes #9293
2014-09-26 12:29:09 -04:00
Caitlin Potter 1b8b41ad23 fix(input): always format viewValue as a string inputs with text controls
Backported from 1eda18365a

NgModel will format all scope-based values to string when setting the viewValue for
the associated input element. The formatting, however, only applies to input elements
that contain a text, email, url or blank input type. In the event of a null or undefined
scope or model value, the viewValue will be set to null or undefined instead of being
converted to an empty string.

Closes #5936
Closes #9277
2014-09-25 13:49:59 -04:00
Peter Bacon Darwin 37f2265b88 chore(docs): minify javascript 2014-09-25 06:05:17 +01:00
Peter Bacon Darwin 05a2a1d395 chore(docs): remove unused code 2014-09-25 05:50:52 +01:00
Peter Bacon Darwin fc913eea89 chore(docs): improve logo rendering performance 2014-09-25 05:45:33 +01:00
Peter Bacon Darwin f839e4944a chore(protractor): annotate $animate to allow tests to run under strict-di 2014-09-25 05:45:26 +01:00
Peter Bacon Darwin 2aead39486 chore(docs): ensure DI annotations are in place 2014-09-25 05:45:03 +01:00
Jeff Cross 54af406c2e docs(changelog): add release notes for 1.3.0-rc.3 2014-09-23 18:50:20 -07:00
Georgios Kalpakas 930bd40bac docs(ngController): Fix priority value mentioned in the docs
The `@priority 500` part was missing from the ngDoc comment, thus the docs mentioned a priority of 0
(instead of the correct 500).

Closes #9070
2014-09-23 16:25:43 -07:00
Peter Bacon Darwin 4aa4005f5f chore(docs): AngularJS 1.2.x doesn't support '$includeContentError' event 2014-09-23 19:26:04 +01:00
Peter Bacon Darwin d5c04121cb chore(docs): refactor the docs app search for better bootup time
This commit refactors how the search index is built. The docsSearch service
is now defined by a provider, which returns a different implementation of
the service depending upon whether the current browser supports WebWorkers
or now.

* **WebWorker supported**: The index is then built and stored in a new worker.
The service posts and receives messages to and from this worker to make
queries on the search index.

* **WebWorker no supported**: The index is built locally but with a 500ms
delay so that the initial page can render before the browser is blocked as
the index is built.

Also the way that the current app is identified has been modified so we can
slim down the js data files (pages-data.js) to again improve startup time.

Closes #9204
Closes #9203
2014-09-23 19:01:22 +01:00
Andrew Delikat e6996cc457 docs(tutorial/step_05): fix typo 2014-09-22 14:55:24 -07:00
William Chen 33793ec3e1 docs(triaging): fix formatting 2014-09-22 13:15:15 -07:00
Bocharsky Victor c9f8fb2559 docs(guide/$location): fix broken link 2014-09-22 13:12:52 -07:00
Christopher Rains a0229803de docs(tutorial/step_02): fix formatting 2014-09-22 13:09:39 -07:00
James Ferguson 38cb2b348e docs(readme): improve readability 2014-09-22 11:41:00 -07:00
Ariel Mashraki c54287bf8d docs(route): remove irrelevant note
Closes #9196
Closes #9200
2014-09-22 13:30:24 -04:00
Jeff Cross a36515289a chore($http): disable flaky JSONP test
See #9185
2014-09-19 17:20:32 -07:00
Peter Bacon Darwin c59da09d3e docs(limitTo): exclude the e2e test that fails on safari
Safari and doesn't like the minus key to be sent to it via Protractor.
Commenting out rather than using xit so that it passes the build
2014-09-19 22:57:11 +01:00
Peter Bacon Darwin 98ceda2194 docs(limitTo): exclude the e2e test that fails on safari
Safari and doesn't like the minus key to be sent to it via Protractor.
2014-09-19 22:40:30 +01:00
Sekib Omazic fbd620e8ae docs(angular.extend) actually only copies own enumerable properties
Closes #9007
2014-09-19 22:00:51 +01:00
Zahid Mahir 4cfc279d39 docs(tutorial/step-3): correct slight grammar issue
Closes #8996
2014-09-19 21:36:17 +01:00
jimmywarting 6a07009c12 docs(limitTo): fix input type in examples
Closes #8989
2014-09-19 21:33:55 +01:00
Sercan Eraslan f3f65caac2 docs(navigation): side navigation footer overlap problem fix
Closes #8923
2014-09-19 19:26:23 +01:00
Luke Schoen 130fe92cda docs(guide/directive): fix grammar 2014-09-18 16:12:37 -07:00
Matt Kim 3d83973fa4 docs(misc/faq): fix typo 2014-09-18 16:11:09 -07:00
Greg Fedirko 3b171a851d docs(guide/$location): improve readability 2014-09-18 16:08:59 -07:00
Luke Schoen 9782bc2aa2 docs(tutorial): improve readability 2014-09-18 16:01:13 -07:00
Christopher Rains 77be7cc7d9 docs(tutorial): fix formatting
- proper case "jQuery" vs "JQuery"
- wrap ng-view in markdown code `ng-view`
2014-09-18 15:56:23 -07:00
jeffavis 3da5847500 docs(guide/bootstrap): fix missing ngController in example 2014-09-18 15:51:59 -07:00
Rahul Doshi 53e6590e88 docs(guide): add angular-localization module to internationalization section
Closes #9158
2014-09-18 18:00:26 -04:00
Jose Martinez d1c05ad832 docs(error/$controller/noscp): fix example
Fix the "correct" example to have the proper syntax for creating the locals
object and provide a more explicit explanation as to how the scope object
should be provided.
2014-09-17 11:10:03 -07:00
108 changed files with 3325 additions and 1483 deletions
+1
View File
@@ -12,6 +12,7 @@ angular.js.tmproj
bower_components/
angular.xcodeproj
.idea
*.iml
.agignore
libpeerconnection.log
npm-debug.log
-1
View File
@@ -11,7 +11,6 @@ env:
- JOB=unit
- JOB=e2e TEST_TARGET=jqlite
- JOB=e2e TEST_TARGET=jquery
- JOB=e2e TEST_TARGET=doce2e
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
+610
View File
@@ -1,3 +1,611 @@
<a name="1.2.27"></a>
# 1.2.27 prime-factorization (2014-11-20)
## Bug Fixes
- **$animate:** clear the GCS cache even when no animation is detected
([f619d032](https://github.com/angular/angular.js/commit/f619d032c932752313c646b5295bad8a68ef3871),
[#8813](https://github.com/angular/angular.js/issues/8813))
- **$browser:**
- Cache `location.href` only during page reload phase
([434d7a09](https://github.com/angular/angular.js/commit/434d7a09039151c1e627ac156213905d06b7df10),
[#9235](https://github.com/angular/angular.js/issues/9235), [#9470](https://github.com/angular/angular.js/issues/9470))
- dont use history api when only the hash changes
([a6e6438d](https://github.com/angular/angular.js/commit/a6e6438dae1ed92b29608d0b8830b0a7fbb624ef),
[#9423](https://github.com/angular/angular.js/issues/9423), [#9424](https://github.com/angular/angular.js/issues/9424))
- handle async href on url change in <=IE9
([fe7d9ded](https://github.com/angular/angular.js/commit/fe7d9dedaa5ec3b3f56d9eb9c513cf99e40121ce),
[#9235](https://github.com/angular/angular.js/issues/9235))
- **$http:** add missing shortcut methods and missing docs
([ec4fe1bc](https://github.com/angular/angular.js/commit/ec4fe1bcab6f981103a10f860a3a00122aa78607),
[#9180](https://github.com/angular/angular.js/issues/9180), [#9321](https://github.com/angular/angular.js/issues/9321))
- **$location:**
- revert erroneous logic and backport refactorings from master
([1ee9b4ef](https://github.com/angular/angular.js/commit/1ee9b4ef5e4a795061d3aa19adefdeb7e0209eeb),
[#8492](https://github.com/angular/angular.js/issues/8492))
- allow 0 in path() and hash()
([f807d7ab](https://github.com/angular/angular.js/commit/f807d7ab4ebd18899154528ea9ed50d5bc25c57a))
- **$parse:** add quick check for Function constructor in fast path
([756640f5](https://github.com/angular/angular.js/commit/756640f5aa8f3fd0084bff50534e23976a6fff00))
- **$parse, events:** prevent accidental misuse of properties on $event
([4d0614fd](https://github.com/angular/angular.js/commit/4d0614fd0da12c5783dfb4956c330edac87e62fe),
[#9969](https://github.com/angular/angular.js/issues/9969))
- **ngMock:** $httpBackend should match data containing Date objects correctly
([1426b029](https://github.com/angular/angular.js/commit/1426b02980badfd322eb960d71bfb1a14d657847),
[#5127](https://github.com/angular/angular.js/issues/5127))
- **orderBy:** sort by identity if no predicate is given
([45b896a1](https://github.com/angular/angular.js/commit/45b896a16abbcbfcdfb9a95c2d10c76a805b57cc),
[#5847](https://github.com/angular/angular.js/issues/5847), [#4579](https://github.com/angular/angular.js/issues/4579), [#9403](https://github.com/angular/angular.js/issues/9403))
- **select:** ensure the label attribute is updated in Internet Explorer
([16833d0f](https://github.com/angular/angular.js/commit/16833d0fb6585117e9978d1accc3ade83e22e797),
[#9621](https://github.com/angular/angular.js/issues/9621), [#10042](https://github.com/angular/angular.js/issues/10042))
## Performance Improvements
- **orderBy:** copy array with slice instead of for loop
([409bcb38](https://github.com/angular/angular.js/commit/409bcb3810a1622178268f7ff7f4130887a1a3dc),
[#9942](https://github.com/angular/angular.js/issues/9942))
<a name="1.3.3"></a>
# 1.3.3 undersea-arithmetic (2014-11-17)
## Bug Fixes
- **$http:** don't parse single space responses as JSON
([6f19a6fd](https://github.com/angular/angular.js/commit/6f19a6fd33ab72d3908e3418fba47ee8e1598fa6),
[#9907](https://github.com/angular/angular.js/issues/9907))
- **minErr:** stringify non-JSON compatible objects in error messages
([cf43ccdf](https://github.com/angular/angular.js/commit/cf43ccdf9b8665a2fd5d6aa52f80cb2d7c9bb7e2),
[#10085](https://github.com/angular/angular.js/issues/10085))
- **$rootScope:** handle cyclic references in scopes when creating error messages
([e80053d9](https://github.com/angular/angular.js/commit/e80053d91fd7c722e092a23d326384de2e552eb6),
[#10085](https://github.com/angular/angular.js/issues/10085))
- **ngRepeat:** support cyclic object references in error messages
([fa12c3c8](https://github.com/angular/angular.js/commit/fa12c3c86af7965d1b9d9a5dd3434755e9e04635),
[#9838](https://github.com/angular/angular.js/issues/9838), [#10065](https://github.com/angular/angular.js/issues/10065), [#10085](https://github.com/angular/angular.js/issues/10085))
- **ngMock:** call $interval callbacks even when invokeApply is false
([d81ff888](https://github.com/angular/angular.js/commit/d81ff8885b77f70c6417d7be3124d86d07447375),
[#10032](https://github.com/angular/angular.js/issues/10032))
- **ngPattern:** match behaviour of native HTML pattern attribute
([85eb9660](https://github.com/angular/angular.js/commit/85eb9660ef67c24d5104a6a1921bedad0bd1b57e),
[#9881](https://github.com/angular/angular.js/issues/9881), [#9888](https://github.com/angular/angular.js/issues/9888))
- **select:** ensure the label attribute is updated in Internet Explorer
([6604c236](https://github.com/angular/angular.js/commit/6604c2361427fba8c43a39dc2e92197390dfbdbe),
[#9621](https://github.com/angular/angular.js/issues/9621), [#10042](https://github.com/angular/angular.js/issues/10042))
## Features
- **$location:** allow to location to be changed during $locationChangeStart
([a9352c19](https://github.com/angular/angular.js/commit/a9352c19ce33f0393d6581547c7ea8dfc2a8b78f),
[#9607](https://github.com/angular/angular.js/issues/9607), [#9678](https://github.com/angular/angular.js/issues/9678))
- **$routeProvider:** allow setting caseInsensitiveMatch on the provider
([0db573b7](https://github.com/angular/angular.js/commit/0db573b7493f76abd94ff65ce660017d617e865b),
[#6477](https://github.com/angular/angular.js/issues/6477), [#9873](https://github.com/angular/angular.js/issues/9873))
## Performance Improvements
- **orderBy:** copy array with slice instead of for loop
([8eabc546](https://github.com/angular/angular.js/commit/8eabc5463c795d87f37e5a9eacbbb14435024061),
[#9942](https://github.com/angular/angular.js/issues/9942))
<a name="1.3.2"></a>
# 1.3.2 cardiovasculatory-magnification (2014-11-07)
## Bug Fixes
- **$compile:** do not rebind parent bound transclude functions
([841c0907](https://github.com/angular/angular.js/commit/841c0907556f525dbc4223609d808319fe0dd7e2),
[#9413](https://github.com/angular/angular.js/issues/9413))
- **$parse:**
- stateful interceptors override an `undefined` expression
([ed99821e](https://github.com/angular/angular.js/commit/ed99821e4dc621864f7e2d9a6b5305fca27fb7fa),
[#9821](https://github.com/angular/angular.js/issues/9821), [#9825](https://github.com/angular/angular.js/issues/9825))
- add quick check for Function constructor in fast path
([e676d642](https://github.com/angular/angular.js/commit/e676d642f5feb8d3ba88944634afb479ba525c36))
- **$parse, events:** prevent accidental misuse of properties on $event
([e057a9aa](https://github.com/angular/angular.js/commit/e057a9aa398ead209bd6bbf76e22d2d5562904fb))
- **ngRoute:** allow proto inherited properties in route params object
([b4770582](https://github.com/angular/angular.js/commit/b4770582f84f26c8ff7f2320a36a6b0ceff6e6cc),
[#8181](https://github.com/angular/angular.js/issues/8181), [#9731](https://github.com/angular/angular.js/issues/9731))
- **select:** use strict comparison for isSelected with selectAs
([9e305948](https://github.com/angular/angular.js/commit/9e305948e4965fb86b0c79985dc6e8c59a9c66af),
[#9639](https://github.com/angular/angular.js/issues/9639), [#9949](https://github.com/angular/angular.js/issues/9949))
## Features
- **ngAria:** announce ngMessages with aria-live
([187e4318](https://github.com/angular/angular.js/commit/187e43185dfb1bce6a318d95958c73cfb789d33c),
[#9834](https://github.com/angular/angular.js/issues/9834))
- **ngMock:** decorator that adds Scope#$countChildScopes and Scope#$countWatchers
([74981c9f](https://github.com/angular/angular.js/commit/74981c9f208b3617cbf00beafd61138d25c5d546),
[#9926](https://github.com/angular/angular.js/issues/9926), [#9871](https://github.com/angular/angular.js/issues/9871))
## Security Note
This release also contains security fixes for expression sandbox bypasses.
These issues affect only applications with known server-side XSS holes that are also using [CSP](https://developer.mozilla.org/en-US/docs/Web/Security/CSP) to secure their client-side code. If your application falls into this rare category, we recommend updating your version of Angular.
We'd like to thank security researches [Sebastian Lekies](https://twitter.com/sebastianlekies), [Jann Horn](http://thejh.net/), and [Gábor Molnár](https://twitter.com/molnar_g) for reporting these issues to us.
We also added a documentation page focused on security, which contains some of the best practices, DOs and DON'Ts. Please check out [https://docs.angularjs.org/guide/security](https://docs.angularjs.org/guide/security).
<a name="1.3.1"></a>
# 1.3.1 spectral-lobster (2014-10-31)
## Bug Fixes
- **$compile:** returning null when an optional controller is not found
([2cd5b4ec](https://github.com/angular/angular.js/commit/2cd5b4ec4409a818ccd33a6fbdeb99a3443a1809),
[#9404](https://github.com/angular/angular.js/issues/9404), [#9392](https://github.com/angular/angular.js/issues/9392))
- **$observe:** check if the attribute is undefined
([531a8de7](https://github.com/angular/angular.js/commit/531a8de72c439d8ddd064874bf364c00cedabb11),
[#9707](https://github.com/angular/angular.js/issues/9707), [#9720](https://github.com/angular/angular.js/issues/9720))
- **$parse:** support dirty-checking objects with null prototype
([28661d1a](https://github.com/angular/angular.js/commit/28661d1a8cc3a8454bad7ae531e027b1256476c9),
[#9568](https://github.com/angular/angular.js/issues/9568))
- **$sce:** use msie instead of $document[0].documentMode
([45252c3a](https://github.com/angular/angular.js/commit/45252c3a545336a0bac93be6ee28cde6afaa3cb4),
[#9661](https://github.com/angular/angular.js/issues/9661))
- **$templateRequest:** ignore JSON Content-Type header and content
([1bd473eb](https://github.com/angular/angular.js/commit/1bd473eb4587900086e0b6b308dcf1dcfe9760d9),
[#5756](https://github.com/angular/angular.js/issues/5756), [#9619](https://github.com/angular/angular.js/issues/9619))
- **i18n:** rename datetimeSymbols to be camelCase
([94f5a285](https://github.com/angular/angular.js/commit/94f5a285bfcf04d800afc462a7a37a3469d77f1a))
- **loader:** fix double spaces
([8b2f1a47](https://github.com/angular/angular.js/commit/8b2f1a47b584ceb98689f48538a2af73cd65dfd8),
[#9630](https://github.com/angular/angular.js/issues/9630))
- **ngMock:** $httpBackend should match data containing Date objects correctly
([1025f6eb](https://github.com/angular/angular.js/commit/1025f6ebf4e5933a12920889be00cd8ac8a106fa),
[#5127](https://github.com/angular/angular.js/issues/5127))
- **ngSanitize:** attribute name: xmlns:href -> xlink:href
([4cccf0f2](https://github.com/angular/angular.js/commit/4cccf0f2a89b002d63cb443e1e7b15f76dcef425),
[#9769](https://github.com/angular/angular.js/issues/9769))
- **select:** assign result of track exp to element value
([4b4098bf](https://github.com/angular/angular.js/commit/4b4098bfcae64f69c70a22393de1f3d9a0d3dc46),
[#9718](https://github.com/angular/angular.js/issues/9718), [#9592](https://github.com/angular/angular.js/issues/9592))
- **templateRequest:** allow empty html template
([52ceec22](https://github.com/angular/angular.js/commit/52ceec2229dc132b76da4e022c91474344f2d906),
[#9581](https://github.com/angular/angular.js/issues/9581))
- **testability:** escape regex chars in `findBindings` if using `exactMatch`
([02aa4f4b](https://github.com/angular/angular.js/commit/02aa4f4b85ee15922a1f2de8ba78f562c18518d0),
[#9595](https://github.com/angular/angular.js/issues/9595), [#9600](https://github.com/angular/angular.js/issues/9600))
## Features
- **$compile:** allow $watchCollection to be used in bi-directional bindings
([40bbc981](https://github.com/angular/angular.js/commit/40bbc9817845bf75581daee5d0ec30980affb0f5),
[#9725](https://github.com/angular/angular.js/issues/9725))
- **ngSanitize:** accept SVG elements and attributes
([a54b25d7](https://github.com/angular/angular.js/commit/a54b25d77999a85701dfc5396fef78e586a99667),
[#9578](https://github.com/angular/angular.js/issues/9578), [#9751](https://github.com/angular/angular.js/issues/9751))
<a name="1.3.0"></a>
# 1.3.0 superluminal-nudge (2014-10-13)
## Bug Fixes
- **$browser:**
- account for IE deserializing history.state on each read
([1efaf3dc](https://github.com/angular/angular.js/commit/1efaf3dc136f822703a9cda55afac7895a923ccb),
[#9587](https://github.com/angular/angular.js/issues/9587), [#9545](https://github.com/angular/angular.js/issues/9545))
- do not decode cookies that do not appear encoded
([9c995905](https://github.com/angular/angular.js/commit/9c9959059eb84f0f1d748b70b50ec47b7d23d065),
[#9211](https://github.com/angular/angular.js/issues/9211), [#9225](https://github.com/angular/angular.js/issues/9225))
- **$http:**
- allow empty json response
([9ba24c54](https://github.com/angular/angular.js/commit/9ba24c54d60e643b1450cc5cfa8f990bd524c130),
[#9532](https://github.com/angular/angular.js/issues/9532), [#9562](https://github.com/angular/angular.js/issues/9562))
- don't run transformData on HEAD methods
([6e4955a3](https://github.com/angular/angular.js/commit/6e4955a3086555d8ca30c29955faa213b39c6f27),
[#9528](https://github.com/angular/angular.js/issues/9528), [#9529](https://github.com/angular/angular.js/issues/9529))
- **$injector:** ensure $get method invoked with provider context
([372fa699](https://github.com/angular/angular.js/commit/372fa6993b2b1b4848aa4be3c3e11f69244fca6f),
[#9511](https://github.com/angular/angular.js/issues/9511), [#9512](https://github.com/angular/angular.js/issues/9512))
- **$location:** use clone of passed search() object
([c7a9009e](https://github.com/angular/angular.js/commit/c7a9009e143299f0e45a85d715ff22fc676d3f93),
[#9445](https://github.com/angular/angular.js/issues/9445))
- **$parse:** stabilize one-time literal expressions correctly
([874cac82](https://github.com/angular/angular.js/commit/874cac825bf29a936cb1b35f9af239687bc5e036))
- **formController:** remove scope reference when form is destroyed
([01f50e1a](https://github.com/angular/angular.js/commit/01f50e1a7b2bff7070616494774ec493f8133204),
[#9315](https://github.com/angular/angular.js/issues/9315))
- **jqLite:** remove native listener when all jqLite listeners were deregistered
([d71fb6f2](https://github.com/angular/angular.js/commit/d71fb6f2713f1a636f6e9c25479870ee9941ad18),
[#9509](https://github.com/angular/angular.js/issues/9509))
- **select:**
- add basic track by and select as support
([addfff3c](https://github.com/angular/angular.js/commit/addfff3c46311f59bdcd100351260006d457316f),
[#6564](https://github.com/angular/angular.js/issues/6564))
- manage select controller options correctly
([2435e2b8](https://github.com/angular/angular.js/commit/2435e2b8f84fde9495b8e9440a2b4f865b1ff541),
[#9418](https://github.com/angular/angular.js/issues/9418))
## Features
- **$anchorScroll:** support a configurable vertical scroll offset
([09c39d2c](https://github.com/angular/angular.js/commit/09c39d2ce687cdf0ac35dbb34a91f0d198c9d83a),
[#9368](https://github.com/angular/angular.js/issues/9368), [#2070](https://github.com/angular/angular.js/issues/2070), [#9360](https://github.com/angular/angular.js/issues/9360))
- **$animate:**
- introduce the $animate.animate() method
([02be700b](https://github.com/angular/angular.js/commit/02be700bda191b454de393f2805916f374a1d764))
- allow $animate to pass custom styles into animations
([e5f4d7b1](https://github.com/angular/angular.js/commit/e5f4d7b10ae5e6a17ab349995451c33b7d294245))
- **currencyFilter:** add fractionSize as optional parameter
([20685ffe](https://github.com/angular/angular.js/commit/20685ffe11036d4d604d13f0d792ca46497af4a1),
[#3642](https://github.com/angular/angular.js/issues/3642), [#3461](https://github.com/angular/angular.js/issues/3461), [#3642](https://github.com/angular/angular.js/issues/3642), [#7922](https://github.com/angular/angular.js/issues/7922))
- **jqLite:** add private jqLiteDocumentLoaded function
([0dd316ef](https://github.com/angular/angular.js/commit/0dd316efea209e5e5de3e456b4e6562f011a1294))
## Breaking Changes
- **$animate:** due to [e5f4d7b1](https://github.com/angular/angular.js/commit/e5f4d7b10ae5e6a17ab349995451c33b7d294245),
staggering animations that use transitions will now
always block the transition from starting (via `transition: 0s none`)
up until the stagger step kicks in. The former behaviour was that the
block was removed as soon as the pending class was added. This fix
allows for styles to be applied in the pending class without causing
an animation to trigger prematurely.
<a name="1.3.0-rc.5"></a>
# 1.3.0-rc.5 impossible-choreography (2014-10-08)
## Bug Fixes
- **$anchorScroll:** don't scroll to top when initializing and location hash is empty
([d5445c60](https://github.com/angular/angular.js/commit/d5445c601fafd6ecd38befeaa4c9ec7bb044127c),
[#8848](https://github.com/angular/angular.js/issues/8848), [#9393](https://github.com/angular/angular.js/issues/9393))
- **$animate:**
- ensure hidden elements with ngShow/ngHide stay hidden during animations
([39d0b368](https://github.com/angular/angular.js/commit/39d0b36826a077f7549a70d0cf3edebe90a10aaa),
[#9103](https://github.com/angular/angular.js/issues/9103), [#9493](https://github.com/angular/angular.js/issues/9493))
- permit class-based animations for leave operations if ngAnimateChildren is enabled
([df1a00b1](https://github.com/angular/angular.js/commit/df1a00b11ac2722f4da441837795985f12682030),
[#8092](https://github.com/angular/angular.js/issues/8092), [#9491](https://github.com/angular/angular.js/issues/9491))
- ensure that class-based animations only consider the most recent DOM operations
([c93924ed](https://github.com/angular/angular.js/commit/c93924ed275a62683b85c82f1c6c2e19d5662c9a),
[#8946](https://github.com/angular/angular.js/issues/8946), [#9458](https://github.com/angular/angular.js/issues/9458))
- abort class-based animations if the element is removed during digest
([613d0a32](https://github.com/angular/angular.js/commit/613d0a3212de8dc01c817ca8526e09c57978a621),
[#8796](https://github.com/angular/angular.js/issues/8796))
- clear the GCS cache even when no animation is detected
([cb85cbce](https://github.com/angular/angular.js/commit/cb85cbcec1c876db6062a0dc0bad80f842782194),
[#8813](https://github.com/angular/angular.js/issues/8813))
- **$browser:**
- Cache `location.href` only during page reload phase
([8ee1ba4b](https://github.com/angular/angular.js/commit/8ee1ba4b94d6fccff06d8781f7ed256c6ce664ff),
[#9235](https://github.com/angular/angular.js/issues/9235), [#9455](https://github.com/angular/angular.js/issues/9455))
- dont use the history API when only the hash changes
([7cb01a80](https://github.com/angular/angular.js/commit/7cb01a80beec669d8f6aae1dc211d2f0b7d4eac4),
[#9423](https://github.com/angular/angular.js/issues/9423), [#9424](https://github.com/angular/angular.js/issues/9424),
[858360b6](https://github.com/angular/angular.js/commit/858360b680a2bb5c19429c1be1c9506700cda476),
[0656484d](https://github.com/angular/angular.js/commit/0656484d3e709c5162570b0dd6473b0b6140e5b2),
[#9143](https://github.com/angular/angular.js/issues/9143), [#9406](https://github.com/angular/angular.js/issues/9406))
- handle async href on url change in <=IE9
([404b95fe](https://github.com/angular/angular.js/commit/404b95fe30a1bcd1313adafbd0018578d5b21d3d),
[#9235](https://github.com/angular/angular.js/issues/9235))
- **$compile:**
- handle the removal of an interpolated attribute
([a75546af](https://github.com/angular/angular.js/commit/a75546afdf41adab786eda30c258190cd4c5f1ae),
[#9236](https://github.com/angular/angular.js/issues/9236), [#9240](https://github.com/angular/angular.js/issues/9240))
- remove comment nodes from templates before asserting single root node
([feba0174](https://github.com/angular/angular.js/commit/feba0174db0f8f929273beb8b90691734a9292e2),
[#9212](https://github.com/angular/angular.js/issues/9212), [#9215](https://github.com/angular/angular.js/issues/9215))
- use the correct namespace for transcluded svg elements
([f3539f3c](https://github.com/angular/angular.js/commit/f3539f3cb5d9477f50f065c6a0ac7d6ca0a31092),
[#9344](https://github.com/angular/angular.js/issues/9344), [#9415](https://github.com/angular/angular.js/issues/9415))
- **$http:** honor application/json response header and parse json primitives
([7b6c1d08](https://github.com/angular/angular.js/commit/7b6c1d08aceba6704a40302f373400aed9ed0e0b),
[#2973](https://github.com/angular/angular.js/issues/2973))
- **$injector:** throw when factory $get method does not return a value
([0d3b69a5](https://github.com/angular/angular.js/commit/0d3b69a5f27b41745b504c7ffd8d72653bac1f85),
[#4575](https://github.com/angular/angular.js/issues/4575), [#9210](https://github.com/angular/angular.js/issues/9210))
- **$location:** allow `0` in `path()` and `hash()`
([b8c5b871](https://github.com/angular/angular.js/commit/b8c5b87119a06edb8e8d1cefad81ee8d1f64f070))
- **form:** fix submit prevention
([86c7d122](https://github.com/angular/angular.js/commit/86c7d1221c706993044583d51a0c61423fee5bcf),
[#3370](https://github.com/angular/angular.js/issues/3370), [#3776](https://github.com/angular/angular.js/issues/3776))
- **ngAnimate:** defer DOM operations for changing classes to postDigest
([667183a8](https://github.com/angular/angular.js/commit/667183a8c79d6ffce571a2be78c05dc76503b222),
[#8234](https://github.com/angular/angular.js/issues/8234), [#9263](https://github.com/angular/angular.js/issues/9263))
- **orderBy:** sort by identity if no predicate is given
([607f016a](https://github.com/angular/angular.js/commit/607f016a0ba705ce40df0164360fb96a9d7f5912),
[#5847](https://github.com/angular/angular.js/issues/5847), [#4579](https://github.com/angular/angular.js/issues/4579), [#9403](https://github.com/angular/angular.js/issues/9403))
- **select:**
- throw for `selectAs` and `trackBy`
([30996f82](https://github.com/angular/angular.js/commit/30996f82afa03cd11771b3267e9367ecf9af6e6d))
- use `$viewValue` instead of `$modelValue`
([f7174169](https://github.com/angular/angular.js/commit/f7174169f4f710d605f6a67f39f90a67a07d4cab),
[#8929](https://github.com/angular/angular.js/issues/8929))
## Features
- **$location:**
- add support for History API state handling ([6fd36dee](https://github.com/angular/angular.js/commit/6fd36deed954b338e48390862971d465148dc1f2),
[#9027](https://github.com/angular/angular.js/issues/9027))
- allow automatic rewriting of links to be disabled
([b3e09be5](https://github.com/angular/angular.js/commit/b3e09be58960b913fee3869bf36e7de3305bbe00),
[#5487](https://github.com/angular/angular.js/issues/5487))
- **$route:** ability to cancel $routeChangeStart event
([f4ff11b0](https://github.com/angular/angular.js/commit/f4ff11b01e6a5f9a9eb25a38d327dfaadbd7c80c),
[#5581](https://github.com/angular/angular.js/issues/5581), [#5714](https://github.com/angular/angular.js/issues/5714), [#9502](https://github.com/angular/angular.js/issues/9502))
## Performance Improvements
- **$animate:**
- access DOM less in resolveElementClasses
([22358cf9](https://github.com/angular/angular.js/commit/22358cf9c703d67f3cf9eb4899404b09578a5fad))
- don't join classes before it's necessary in resolveElementClasses
([003c44ec](https://github.com/angular/angular.js/commit/003c44eceee54c3398b0d2971fd97a512d7f7cec))
- **ngBind:** set textContent rather than using element.text()
([074a146d](https://github.com/angular/angular.js/commit/074a146d8b1ee7c93bf6d5892448a5c2a0143a28),
[#9369](https://github.com/angular/angular.js/issues/9369), [#9396](https://github.com/angular/angular.js/issues/9396))
## Breaking Changes
- **$compile:** due to [feba0174](https://github.com/angular/angular.js/commit/feba0174db0f8f929273beb8b90691734a9292e2),
If a template contains directives within comment nodes, and there is more than a single node in the
template, those comment nodes are removed. The impact of this breaking change is expected to be
quite low.
Closes #9212
Closes #9215
- **ngAnimate:** due to [667183a8](https://github.com/angular/angular.js/commit/667183a8c79d6ffce571a2be78c05dc76503b222),
The `$animate` CSS class API will always defer changes until the end of the next digest. This allows ngAnimate
to coalesce class changes which occur over a short period of time into 1 or 2 DOM writes, rather than
many. This prevents jank in browsers such as IE, and is generally a good thing.
If you find that your classes are not being immediately applied, be sure to invoke `$digest()`.
Closes #8234
Closes #9263
- **$select:** due to [30996f8](https://github.com/angular/angular.js/commit/30996f82afa03cd11771b3267e9367ecf9af6e6d)
`ngOptions` will now throw an error when the comprehension expressions contains both a `select as`
and `track by` expression.
These expressions are fundamentally incompatible because it is not possible to reliably and
consistently determine the parent object of a model, since `select as` can assign any child of a
`value` as the model value.
Prior to refactorings in this release, neither of these expressions worked correctly independently,
and did not work at all when combined.
See #6564
- **$route:** due to [f4ff11b0](https://github.com/angular/angular.js/commit/f4ff11b01e6a5f9a9eb25a38d327dfaadbd7c80c),
Order of events has changed.
Previously: `$locationChangeStart` -> `$locationChangeSuccess`
-> `$routeChangeStart` -> `$routeChangeSuccess`
Now: `$locationChangeStart` -> `$routeChangeStart`
-> `$locationChangeSuccess` -> -> `$routeChangeSuccess`
Fixes #5581
Closes #5714
Closes #9502- **ngAnimate:** due to [667183a8](https://github.com/angular/angular.js/commit/667183a8c79d6ffce571a2be78c05dc76503b222),
The $animate class API will always defer changes until the end of the next digest. This allows ngAnimate
to coalesce class changes which occur over a short period of time into 1 or 2 DOM writes, rather than
many. This prevents jank in browsers such as IE, and is generally a good thing.
If you're finding that your classes are not being immediately applied, be sure to invoke $digest().
Closes #8234
Closes #9263
<a name="1.3.0-rc.4"></a>
# 1.3.0-rc.4 unicorn-hydrafication (2014-10-01)
## Bug Fixes
- **$compile:**
- get $$observe listeners array as own property
([a27d827c](https://github.com/angular/angular.js/commit/a27d827c22b0b6b3ba6b7495cf4fc338c6934b37),
[#9343](https://github.com/angular/angular.js/issues/9343), [#9345](https://github.com/angular/angular.js/issues/9345))
- Resolve leak with asynchronous compilation
([6303c3dc](https://github.com/angular/angular.js/commit/6303c3dcf64685458fc84aa12289f5c9d57f4e47),
[#9199](https://github.com/angular/angular.js/issues/9199), [#9079](https://github.com/angular/angular.js/issues/9079), [#8504](https://github.com/angular/angular.js/issues/8504), [#9197](https://github.com/angular/angular.js/issues/9197))
- connect transclude scopes to their containing scope to prevent memory leaks
([fb0c77f0](https://github.com/angular/angular.js/commit/fb0c77f0b66ed757a56af13f81b943419fdcbd7f),
[#9095](https://github.com/angular/angular.js/issues/9095), [#9281](https://github.com/angular/angular.js/issues/9281))
- sanitize srcset attribute
([ab80cd90](https://github.com/angular/angular.js/commit/ab80cd90661396dbb1c94c5f4dd2d11ee8f6b6af))
- **input:**
- register builtin parsers/formatters before anyone else
([10644432](https://github.com/angular/angular.js/commit/10644432ca9d5da69ce790a8d9e691640f333711),
[#9218](https://github.com/angular/angular.js/issues/9218), [#9358](https://github.com/angular/angular.js/issues/9358))
- correctly handle invalid model values for `input[date/time/…]`
([a0bfdd0d](https://github.com/angular/angular.js/commit/a0bfdd0d60882125f614a91c321f12f730735e7b),
[#8949](https://github.com/angular/angular.js/issues/8949), [#9375](https://github.com/angular/angular.js/issues/9375))
- **ngModel:** do not parse undefined viewValue when validating
([92f05e5a](https://github.com/angular/angular.js/commit/92f05e5a5900713301e64373d7b7daa45a88278b),
[#9106](https://github.com/angular/angular.js/issues/9106), [#9260](https://github.com/angular/angular.js/issues/9260))
- **ngView:** use animation promises ensure that only one leave animation occurs at a time
([3624e380](https://github.com/angular/angular.js/commit/3624e3800fb3ccd2e9ea361a763e20131fd42c29),
[#9355](https://github.com/angular/angular.js/issues/9355), [#7606](https://github.com/angular/angular.js/issues/7606), [#9374](https://github.com/angular/angular.js/issues/9374))
- **select:** make ctrl.hasOption method consistent
([2bcd02dc](https://github.com/angular/angular.js/commit/2bcd02dc1a6b28b357d47c83be3bed5c9a38417c),
[#8761](https://github.com/angular/angular.js/issues/8761))
## Features
- **$compile:** optionally get controllers from ancestors only
([07e3abc7](https://github.com/angular/angular.js/commit/07e3abc7dda872adc3fb25cb3e133f86f494b35d),
[#4518](https://github.com/angular/angular.js/issues/4518), [#4540](https://github.com/angular/angular.js/issues/4540), [#8240](https://github.com/angular/angular.js/issues/8240), [#8511](https://github.com/angular/angular.js/issues/8511))
- **Scope:** allow the parent of a new scope to be specified on creation
([6417a3e9](https://github.com/angular/angular.js/commit/6417a3e9eb7ab0011cefada8db855aa929a64ff8))
## Performance Improvements
- **$rootScope:** moving internal queues out of the Scope instances
([b1192518](https://github.com/angular/angular.js/commit/b119251827cea670051198e1b48af7ee0c9f2a1b),
[#9071](https://github.com/angular/angular.js/issues/9071))
- **benchmark:** add ngBindOnce benchmarks to largetable-bp
([2c8b4648](https://github.com/angular/angular.js/commit/2c8b4648526acf5c2645de8408a6d9ace2144b5f))
- **ngForm,ngModel:** move initial addClass to the compile phase
([b1ee5386](https://github.com/angular/angular.js/commit/b1ee5386d584f208bce6d3b613afdb3bae9df76a),
[#8268](https://github.com/angular/angular.js/issues/8268))
## Breaking Changes
- **$compile:** due to [fb0c77f0](https://github.com/angular/angular.js/commit/fb0c77f0b66ed757a56af13f81b943419fdcbd7f),
`$transclude` functions no longer attach `$destroy` event handlers to the
transcluded content, and so the associated transclude scope will not automatically
be destroyed if you remove a transcluded element from the DOM using direct DOM
manipulation such as the jquery `remove()` method.
If you want to explicitly remove DOM elements inside your directive that have
been compiled, and so potentially contain child (and transcluded) scopes, then
it is your responsibility to get hold of the scope and destroy it at the same time.
The suggested approach is to create a new child scope of your own around any DOM
elements that you wish to manipulate in this way and destroy those scopes if you
remove their contents - any child scopes will then be destroyed and cleaned up
automatically.
Note that all the built-in directives that manipulate the DOM (ngIf, ngRepeat,
ngSwitch, etc) already follow this best practice, so if you only use these for
manipulating the DOM then you do not have to worry about this change.
Closes #9095
Closes #9281
- **$parse:** due to [5572b40b](https://github.com/angular/angular.js/commit/5572b40b15ed06969c8e0e92866c5afd088484b4),
- $scope['this'] no longer exits on the $scope object
- $parse-ed expressions no longer allow chaining 'this' such as this['this'] or $parent['this']
- 'this' in $parse-ed expressions can no longer be overriden, if a variable named 'this' is put on the scope it must be accessed using this['this']
Closes #9105
- **input:** due to [1eda1836](https://github.com/angular/angular.js/commit/1eda18365a348c9597aafba9d195d345e4f13d1e),
(Note: this change landed in 1.3.0-rc.3, but was not considered a breaking change at the time).
For text based inputs (text, email, url), the `$viewValue` will now always be converted to a string,
regardless of what type the value is on the model.
To migrate, any code or expressions that expect the `$viewValue` to be anything other than string
should be updated to expect a string.
- **input:** due to a0bfdd0d60882125f614a91c321f12f730735e7b (see #8949),
Similar to `input[number]` Angular will now throw if the model value
for a `input[date]` is not a `Date` object. Previously, Angular only
showed an empty string instead.
Angular does not set validation errors on the `<input>` in this case
as those errors are shown to the user, but the erroneous state was
caused by incorrect application logic and not by the user.
<a name="1.2.26"></a>
# 1.2.26 captivating-disinterest (2014-10-01)
## Bug Fixes
- **$compile:** Resolve leak with asynchronous compilation
([5c9c1973](https://github.com/angular/angular.js/commit/5c9c19730526d5df6f16c523e578e5305f3796d0),
[#9199](https://github.com/angular/angular.js/issues/9199), [#9079](https://github.com/angular/angular.js/issues/9079), [#8504](https://github.com/angular/angular.js/issues/8504), [#9197](https://github.com/angular/angular.js/issues/9197))
- **select:** make ctrl.hasOption method consistent
([11d2242d](https://github.com/angular/angular.js/commit/11d2242df65b2ade0dabe366a0c42963b6d37df5),
[#8761](https://github.com/angular/angular.js/issues/8761))
<a name="1.3.0-rc.3"></a>
# 1.3.0-rc.3 aggressive-pacification (2014-09-23)
## Bug Fixes
- **ngModel:** support milliseconds in time and datetime
([4b83f6ca](https://github.com/angular/angular.js/commit/4b83f6ca2c15bd65fe2b3894a02c04f9967fbff4),
[#8874](https://github.com/angular/angular.js/issues/8874))
## Features
- **$location:** add ability to opt-out of `<base>` tag requirement in html5Mode
([dc3de7fb](https://github.com/angular/angular.js/commit/dc3de7fb7a14c38b5c3dc7decfafb0b51d422dd1),
[#8934](https://github.com/angular/angular.js/issues/8934))
- **formController:** add $setUntouched to propagate untouched state
([fd899755](https://github.com/angular/angular.js/commit/fd8997551f9ed4431f5e99d61f637139485076b9),
[#9050](https://github.com/angular/angular.js/issues/9050))
- **input:** support dynamic element validation
([729c238e](https://github.com/angular/angular.js/commit/729c238e19ab27deff01448d79342ea53721bfed),
[#4791](https://github.com/angular/angular.js/issues/4791), [#1404](https://github.com/angular/angular.js/issues/1404))
- **ngAria:** add an ngAria module to make a11y easier
([d1434c99](https://github.com/angular/angular.js/commit/d1434c999a66c6bb915ee1a8b091e497d288d940),
[#5486](https://github.com/angular/angular.js/issues/5486))
## Performance Improvements
- **map:** use Array.prototype.map
([a591e8b8](https://github.com/angular/angular.js/commit/a591e8b8d302efefd67bf0d5c4bad300a5f3aded))
## Breaking Changes
- **$location:** due to [dc3de7fb](https://github.com/angular/angular.js/commit/dc3de7fb7a14c38b5c3dc7decfafb0b51d422dd1),
The $location.html5Mode API has changed to allow enabling html5Mode by
passing an object (as well as still supporting passing a boolean). Symmetrically, the
method now returns an object instead of a boolean value.
To migrate, follow the code example below:
Before:
var mode = $locationProvider.html5Mode();
After:
var mode = $locationProvider.html5Mode().enabled;
Fixes #8934
<a name="1.2.25"></a>
# 1.2.25 hypnotic-gesticulation (2014-09-16)
@@ -1012,6 +1620,8 @@ Closes #8230
- **jQuery:** due to [9e7cb3c3](https://github.com/angular/angular.js/commit/9e7cb3c37543008e6236bb5a2c4536df2e1e43a9),
Angular no longer supports jQuery versions below 2.1.1.
- **$q:** due to [23bc92b1](https://github.com/angular/angular.js/commit/23bc92b17df882a907fb326320f0622717fefe7b),
Promises methods are no longer enumerated when using for-loops with `hasOwnProperty` check. E.g. `angular.extends`
<a name="1.2.22"></a>
+4 -4
View File
@@ -65,13 +65,13 @@ Help us to maximize the effort we can spend fixing issues and adding new
features, by not reporting duplicate issues. Providing the following information will increase the
chances of your issue being dealt with quickly:
* **Overview of the issue** - if an error is being thrown a non-minified stack trace helps
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
* **Motivation for or Use Case** - explain why this is a bug for you
* **Angular Version(s)** - is it a regression?
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
* **Reproduce the error** - provide a live example (using [Plunker][plunker] or
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
* **Related issues** - has a similar issue been reported before?
* **Related Issues** - has a similar issue been reported before?
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
causing the problem (line of code or commit)
@@ -172,7 +172,7 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
* **Do not use namespaces**: Instead, wrap the entire angular code base in an anonymous closure and
export our API explicitly rather than implicitly.
* Wrap all code at **100 characters**.
* Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypical
* Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypal
inheritance only when absolutely necessary.
* We **love functions and closures** and, whenever possible, prefer them over objects.
* To write concise code that can be better minified, we **use aliases internally** that map to the
+5 -4
View File
@@ -6,10 +6,11 @@ use good old HTML (or HAML, Jade and friends!) as your template language and let
syntax to express your applications components clearly and succinctly. It automatically
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
binding. To help you structure your application better and make it easy to test, AngularJS teaches
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
it makes development fun!
the browser how to do dependency injection and inversion of control.
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
piece of cake. The best of all: it makes development fun!
* Web site: http://angularjs.org
* Tutorial: http://docs.angularjs.org/tutorial
-3
View File
@@ -26,7 +26,6 @@ This process based on the idea of minimizing user pain
* You can triage older issues as well
* Triage to your heart's content
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
1. Understandable? - verify if the description of the request is clear.
* If not, [close it][] according to the instructions below and go to the last step.
1. Duplicate?
@@ -36,7 +35,6 @@ This process based on the idea of minimizing user pain
* Label `Type: Bug`
* Reproducible? - Steps to reproduce the bug are clear. If they are not, ask for a clarification. If there's no reply after a week, [close it][].
* Reproducible on master? - <http://code.angularjs.org/snapshot/>
1. Non bugs:
* Label `Type: Feature`, `Type: Chore`, or `Type: Perf`
* Belongs in core? Often new features should be implemented as a third-party module rather than an addition to the core.
@@ -59,7 +57,6 @@ This process based on the idea of minimizing user pain
* In rare cases, it's ok to have multiple components.
1. Label `PRs plz!` - These issues are good targets for PRs from the open source community. Apply to issues where the problem and solution are well defined in the comments, and it's not too complex.
1. Label `origin: google` for issues from Google
1. Assign a milestone:
* Backlog - triaged fixes and features, should be the default choice
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
+3 -1
View File
@@ -112,7 +112,7 @@ var printSection = function(stream, title, section, printCommitLinks) {
}
stream.write(')\n');
} else {
stream.write(util.format('%s %s', prefix, commit.subject));
stream.write(util.format('%s %s\n', prefix, commit.subject));
}
});
});
@@ -188,6 +188,7 @@ var getPreviousTag = function() {
var generate = function(version, file) {
getPreviousTag().then(function(tag) {
console.log('Reading git log since', tag);
readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
@@ -201,6 +202,7 @@ var generate = function(version, file) {
// publish for testing
exports.parseRawCommit = parseRawCommit;
exports.printSection = printSection;
// hacky start if not run by jasmine :-D
if (process.argv.join('').indexOf('jasmine-node') === -1) {
+62 -1
View File
@@ -1,4 +1,4 @@
/* global describe: false, it: false, expect: false */
/* global describe: false, beforeEach: false, afterEach: false, it: false, expect: false */
'use strict';
@@ -44,4 +44,65 @@ describe('changelog.js', function() {
expect(msg.breaking).toEqual(' first breaking change\nsomething else\nanother line with more info\n');
});
});
describe('printSection', function() {
var output;
var streamMock = {
write: function(str) {
output += str;
}
};
beforeEach(function() {
output = '';
});
it('should add a new line at the end of each breaking change list item ' +
'when there is 1 item per component', function() {
var title = 'test';
var printCommitLinks = false;
var section = {
module1: [{subject: 'breaking change 1'}],
module2: [{subject: 'breaking change 2'}]
};
var expectedOutput =
'\n' + '## test\n\n' +
'- **module1:** breaking change 1\n' +
'- **module2:** breaking change 2\n' +
'\n';
ch.printSection(streamMock, title, section, printCommitLinks);
expect(output).toBe(expectedOutput);
});
it('should add a new line at the end of each breaking change list item ' +
'when there are multiple items per component', function() {
var title = 'test';
var printCommitLinks = false;
var section = {
module1: [
{subject: 'breaking change 1.1'},
{subject: 'breaking change 1.2'}
],
module2: [
{subject: 'breaking change 2.1'},
{subject: 'breaking change 2.2'}
]
};
var expectedOutput =
'\n' + '## test\n\n' +
'- **module1:**\n' +
' - breaking change 1.1\n' +
' - breaking change 1.2\n' +
'- **module2:**\n' +
' - breaking change 2.1\n' +
' - breaking change 2.2\n' +
'\n';
ch.printSection(streamMock, title, section, printCommitLinks);
expect(output).toBe(expectedOutput);
});
});
});
+4 -4
View File
@@ -316,10 +316,10 @@ iframe.example {
}
.search-results-group.col-group-api { width:30%; }
.search-results-group.col-group-guide { width:30%; }
.search-results-group.col-group-tutorial { width:25%; }
.search-results-group.col-group-guide,
.search-results-group.col-group-tutorial { width:20%; }
.search-results-group.col-group-misc,
.search-results-group.col-group-error { float:right; clear:both; width:15% }
.search-results-group.col-group-error { width:15%; float: right; }
.search-results-group.col-group-api .search-result {
@@ -391,7 +391,6 @@ iframe.example {
position:fixed;
top:120px;
bottom:0;
padding-bottom:120px;
overflow:auto;
}
@@ -412,6 +411,7 @@ iframe.example {
.main-body-grid .side-navigation {
position:relative;
padding-bottom:120px;
}
.main-body-grid .side-navigation.ng-hide {
@@ -1,284 +0,0 @@
'use strict';
var directive = {};
var service = { value: {} };
var DEPENDENCIES = {
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
'angular-route.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-route.min.js',
'angular-animate.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-animate.min.js',
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
};
function escape(text) {
return text.
replace(/\&/g, '&amp;').
replace(/\</g, '&lt;').
replace(/\>/g, '&gt;').
replace(/"/g, '&quot;');
}
/**
* http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
* http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
*/
function setHtmlIe8SafeWay(element, html) {
var newElement = angular.element('<pre>' + html + '</pre>');
element.empty();
element.append(newElement.contents());
return element;
}
directive.jsFiddle = function(getEmbeddedTemplate, escape, script) {
return {
terminal: true,
link: function(scope, element, attr) {
var name = '',
stylesheet = '<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">\n',
fields = {
html: '',
css: '',
js: ''
};
angular.forEach(attr.jsFiddle.split(' '), function(file, index) {
var fileType = file.split('.')[1];
if (fileType == 'html') {
if (index == 0) {
fields[fileType] +=
'<div ng-app' + (attr.module ? '="' + attr.module + '"' : '') + '>\n' +
getEmbeddedTemplate(file, 2);
} else {
fields[fileType] += '\n\n\n <!-- CACHE FILE: ' + file + ' -->\n' +
' <script type="text/ng-template" id="' + file + '">\n' +
getEmbeddedTemplate(file, 4) +
' </script>\n';
}
} else {
fields[fileType] += getEmbeddedTemplate(file) + '\n';
}
});
fields.html += '</div>\n';
setHtmlIe8SafeWay(element,
'<form class="jsfiddle" method="post" action="http://jsfiddle.net/api/post/library/pure/" target="_blank">' +
hiddenField('title', 'AngularJS Example: ' + name) +
hiddenField('css', '</style> <!-- Ugly Hack due to jsFiddle issue: http://goo.gl/BUfGZ --> \n' +
stylesheet +
script.angular +
(attr.resource ? script.resource : '') +
'<style>\n' +
fields.css) +
hiddenField('html', fields.html) +
hiddenField('js', fields.js) +
'<button class="btn btn-primary"><i class="icon-white icon-pencil"></i> Edit Me</button>' +
'</form>');
function hiddenField(name, value) {
return '<input type="hidden" name="' + name + '" value="' + escape(value) + '">';
}
}
}
};
directive.ngSetText = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
return {
restrict: 'CA',
priority: 10,
compile: function(element, attr) {
setHtmlIe8SafeWay(element, escape(getEmbeddedTemplate(attr.ngSetText)));
}
}
}]
directive.ngHtmlWrap = ['reindentCode', 'templateMerge', function(reindentCode, templateMerge) {
return {
compile: function(element, attr) {
var properties = {
head: '',
module: '',
body: element.text()
},
html = "<!doctype html>\n<html ng-app{{module}}>\n <head>\n{{head:4}} </head>\n <body>\n{{body:4}} </body>\n</html>";
angular.forEach((attr.ngHtmlWrap || '').split(' '), function(dep) {
if (!dep) return;
dep = DEPENDENCIES[dep] || dep;
var ext = dep.split(/\./).pop();
if (ext == 'css') {
properties.head += '<link rel="stylesheet" href="' + dep + '" type="text/css">\n';
} else if(ext == 'js') {
properties.head += '<script src="' + dep + '"></script>\n';
} else {
properties.module = '="' + dep + '"';
}
});
setHtmlIe8SafeWay(element, escape(templateMerge(html, properties)));
}
}
}];
directive.ngSetHtml = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
return {
restrict: 'CA',
priority: 10,
compile: function(element, attr) {
setHtmlIe8SafeWay(element, getEmbeddedTemplate(attr.ngSetHtml));
}
}
}];
directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
return {
compile: function (element, attr) {
var fileNames = attr.ngEvalJavascript.split(' ');
angular.forEach(fileNames, function(fileName) {
var script = getEmbeddedTemplate(fileName);
try {
if (window.execScript) { // IE
window.execScript(script || '""'); // IE complains when evaling empty string
} else {
window.eval(script + '//@ sourceURL=' + fileName);
}
} catch (e) {
if (window.console) {
window.console.log(script, '\n', e);
} else {
window.alert(e);
}
}
});
}
};
}];
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer', '$animate',
function($templateCache, $browser, docsRootScope, $location, $sniffer, $animate) {
return {
terminal: true,
link: function(scope, element, attrs) {
var modules = ['ngAnimate'],
embedRootScope,
deregisterEmbedRootScope;
modules.push(['$provide', function($provide) {
$provide.value('$templateCache', $templateCache);
$provide.value('$anchorScroll', angular.noop);
$provide.value('$browser', $browser);
$provide.value('$sniffer', $sniffer);
$provide.value('$animate', $animate);
$provide.provider('$location', function() {
this.$get = ['$rootScope', function($rootScope) {
docsRootScope.$on('$locationChangeSuccess', function(event, oldUrl, newUrl) {
$rootScope.$broadcast('$locationChangeSuccess', oldUrl, newUrl);
});
return $location;
}];
this.html5Mode = angular.noop;
});
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
embedRootScope = $delegate;
// Since we are teleporting the $animate service, which relies on the $$postDigestQueue
// we need the embedded scope to use the same $$postDigestQueue as the outer scope
embedRootScope.$$postDigestQueue = docsRootScope.$$postDigestQueue;
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
embedRootScope.$digest();
});
return embedRootScope;
}]);
}]);
if (attrs.ngEmbedApp) modules.push(attrs.ngEmbedApp);
element.on('click', function(event) {
if (event.target.attributes.getNamedItem('ng-click')) {
event.preventDefault();
}
});
element.bind('$destroy', function() {
deregisterEmbedRootScope();
embedRootScope.$destroy();
});
element.data('$injector', null);
angular.bootstrap(element, modules);
}
};
}];
service.reindentCode = function() {
return function (text, spaces) {
if (!text) return text;
var lines = text.split(/\r?\n/);
var prefix = ' '.substr(0, spaces || 0);
var i;
// remove any leading blank lines
while (lines.length && lines[0].match(/^\s*$/)) lines.shift();
// remove any trailing blank lines
while (lines.length && lines[lines.length - 1].match(/^\s*$/)) lines.pop();
var minIndent = 999;
for (i = 0; i < lines.length; i++) {
var line = lines[0];
var reindentCode = line.match(/^\s*/)[0];
if (reindentCode !== line && reindentCode.length < minIndent) {
minIndent = reindentCode.length;
}
}
for (i = 0; i < lines.length; i++) {
lines[i] = prefix + lines[i].substring(minIndent);
}
lines.push('');
return lines.join('\n');
}
};
service.templateMerge = ['reindentCode', function(indentCode) {
return function(template, properties) {
return template.replace(/\{\{(\w+)(?:\:(\d+))?\}\}/g, function(_, key, indent) {
var value = properties[key];
if (indent) {
value = indentCode(value, indent);
}
return value == undefined ? '' : value;
});
};
}];
service.getEmbeddedTemplate = ['reindentCode', function(reindentCode) {
return function (id) {
var element = document.getElementById(id);
if (!element) {
return null;
}
return reindentCode(angular.element(element).html(), 0);
}
}];
angular.module('bootstrapPrettify', []).directive(directive).factory(service);
+44
View File
@@ -0,0 +1,44 @@
"use strict";
/* jshint browser: true */
/* global importScripts, onmessage: true, postMessage, lunr */
// Load up the lunr library
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
// Create the lunr index - the docs should be an array of object, each object containing
// the path and search terms for a page
var index = lunr(function() {
this.ref('path');
this.field('titleWords', {boost: 50});
this.field('members', { boost: 40});
this.field('keywords', { boost : 20 });
});
// Retrieve the searchData which contains the information about each page to be indexed
var searchData = {};
var searchDataRequest = new XMLHttpRequest();
searchDataRequest.onload = function() {
// Store the pages data to be used in mapping query results back to pages
searchData = JSON.parse(this.responseText);
// Add search terms from each page to the search index
searchData.forEach(function(page) {
index.add(page);
});
postMessage({ e: 'index-ready' });
};
searchDataRequest.open('GET', 'search-data.json');
searchDataRequest.send();
// The worker receives a message everytime the web app wants to query the index
onmessage = function(oEvent) {
var q = oEvent.data.q;
var hits = index.search(q);
var results = [];
// Only return the array of paths to pages
hits.forEach(function(hit) {
results.push(hit.ref);
});
// The results of the query are sent back to the web app via a new message
postMessage({ e: 'query-ready', q: q, d: results });
};
+7 -4
View File
@@ -54,10 +54,13 @@ describe('docs.angularjs.org', function () {
});
it("should display an error if the page does not exist", function() {
browser.get('index-debug.html#!/api/does/not/exist');
expect(element(by.css('h1')).getText()).toBe('Oops!');
});
});
});
describe('Error Handling', function() {
it("should display an error if the page does not exist", function() {
browser.get('index-debug.html#!/api/does/not/exist');
expect(element(by.css('h1')).getText()).toBe('Oops!');
});
});
+3 -3
View File
@@ -6,6 +6,7 @@ angular.module('docsApp', [
'DocsController',
'versionsData',
'pagesData',
'navData',
'directives',
'errors',
'examples',
@@ -13,11 +14,10 @@ angular.module('docsApp', [
'tutorials',
'versions',
'bootstrap',
'bootstrapPrettify',
'ui.bootstrap.dropdown'
])
.config(function($locationProvider) {
.config(['$locationProvider', function($locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
});
}]);
+9 -74
View File
@@ -6,31 +6,10 @@ angular.module('DocsController', [])
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
$scope.openPlunkr = openPlunkr;
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
$scope.fold = function(url) {
if(url) {
$scope.docs_fold = '/notes/' + url;
if(/\/build/.test($window.location.href)) {
$scope.docs_fold = '/build/docs' + $scope.docs_fold;
}
window.scrollTo(0,0);
}
else {
$scope.docs_fold = null;
}
};
var OFFLINE_COOKIE_NAME = 'ng-offline',
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
/**********************************
Publish methods
***********************************/
$scope.navClass = function(navItem) {
return {
active: navItem.href && this.currentPage.path,
@@ -38,55 +17,22 @@ angular.module('DocsController', [])
};
};
$scope.afterPartialLoaded = function() {
$scope.$on('$includeContentLoaded', function() {
var pagePath = $scope.currentPage ? $scope.currentPage.path : $location.path();
$window._gaq.push(['_trackPageview', pagePath]);
};
/** stores a cookie that is used by apache to decide which manifest ot send */
$scope.enableOffline = function() {
//The cookie will be good for one year!
var date = new Date();
date.setTime(date.getTime()+(365*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
var value = angular.version.full;
document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;
//force the page to reload so server can serve new manifest file
window.location.reload(true);
};
/**********************************
Watches
***********************************/
});
$scope.$watch(function docsPathWatch() {return $location.path(); }, function docsPathWatchAction(path) {
var currentPage = $scope.currentPage = NG_PAGES[path];
if ( !currentPage && path.charAt(0)==='/' ) {
// Strip off leading slash
path = path.substr(1);
}
currentPage = $scope.currentPage = NG_PAGES[path];
if ( !currentPage && path.charAt(path.length-1) === '/' && path.length > 1 ) {
// Strip off trailing slash
path = path.substr(0, path.length-1);
}
currentPage = $scope.currentPage = NG_PAGES[path];
if ( !currentPage && /\/index$/.test(path) ) {
// Strip off index from the end
path = path.substr(0, path.length - 6);
}
path = path.replace(/^\/?(.+?)(\/index)?\/?$/, '$1');
currentPage = $scope.currentPage = NG_PAGES[path];
if ( currentPage ) {
$scope.currentArea = currentPage && NG_NAVIGATION[currentPage.area];
$scope.partialPath = 'partials/' + path + '.html';
$scope.currentArea = NG_NAVIGATION[currentPage.area];
var pathParts = currentPage.path.split('/');
var breadcrumb = $scope.breadcrumb = [];
var breadcrumbPath = '';
@@ -98,6 +44,7 @@ angular.module('DocsController', [])
} else {
$scope.currentArea = NG_NAVIGATION['api'];
$scope.breadcrumb = [];
$scope.partialPath = 'Error404.html';
}
});
@@ -107,24 +54,12 @@ angular.module('DocsController', [])
$scope.versionNumber = angular.version.full;
$scope.version = angular.version.full + " " + angular.version.codeName;
$scope.subpage = false;
$scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
$scope.futurePartialTitle = null;
$scope.loading = 0;
$scope.$cookies = $cookies;
$cookies.platformPreference = $cookies.platformPreference || 'gitUnix';
var INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
if (!$location.path() || INDEX_PATH.test($location.path())) {
$location.path('/api').replace();
}
// bind escape to hash reset callback
angular.element(window).on('keydown', function(e) {
if (e.keyCode === 27) {
$scope.$apply(function() {
$scope.subpage = false;
});
}
});
}]);
-24
View File
@@ -1,24 +0,0 @@
angular.module('docsApp.navigationService', [])
.factory('navigationService', function($window) {
var service = {
currentPage: null,
currentVersion: null,
changePage: function(newPage) {
},
changeVersion: function(newVersion) {
//TODO =========
// var currentPagePath = '';
// // preserve URL path when switching between doc versions
// if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
// currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
// }
// $window.location = version.url + currentPagePath;
}
};
});
+123 -64
View File
@@ -10,22 +10,35 @@ angular.module('search', [])
$scope.search = function(q) {
var MIN_SEARCH_LENGTH = 2;
if(q.length >= MIN_SEARCH_LENGTH) {
var results = docsSearch(q);
var totalAreas = 0;
for(var i in results) {
++totalAreas;
}
if(totalAreas > 0) {
$scope.colClassName = 'cols-' + totalAreas;
}
$scope.hasResults = totalAreas > 0;
$scope.results = results;
docsSearch(q).then(function(hits) {
var results = {};
angular.forEach(hits, function(hit) {
var area = hit.area;
var limit = (area == 'api') ? 40 : 14;
results[area] = results[area] || [];
if(results[area].length < limit) {
results[area].push(hit);
}
});
var totalAreas = 0;
for(var i in results) {
++totalAreas;
}
if(totalAreas > 0) {
$scope.colClassName = 'cols-' + totalAreas;
}
$scope.hasResults = totalAreas > 0;
$scope.results = results;
});
}
else {
clearResults();
}
if(!$scope.$$phase) $scope.$apply();
};
$scope.submit = function() {
var result;
for(var i in $scope.results) {
@@ -39,78 +52,124 @@ angular.module('search', [])
$scope.hideResults();
}
};
$scope.hideResults = function() {
clearResults();
$scope.q = '';
};
}])
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch',
function($scope, $location, docsSearch) {
docsSearch($location.path().split(/[\/\.:]/).pop()).then(function(results) {
$scope.results = {};
angular.forEach(results, function(result) {
var area = $scope.results[result.area] || [];
area.push(result);
$scope.results[result.area] = area;
});
});
}])
.factory('lunrSearch', function() {
return function(properties) {
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
var engine = lunr(properties);
return {
store : function(values) {
engine.add(values);
},
search : function(q) {
return engine.search(q);
}
};
};
})
.provider('docsSearch', function() {
.factory('docsSearch', ['$rootScope','lunrSearch', 'NG_PAGES',
function($rootScope, lunrSearch, NG_PAGES) {
if (window.RUNNING_IN_NG_TEST_RUNNER) {
return null;
}
// This version of the service builds the index in the current thread,
// which blocks rendering and other browser activities.
// It should only be used where the browser does not support WebWorkers
function localSearchFactory($http, $timeout, NG_PAGES) {
var index = lunrSearch(function() {
this.ref('id');
this.field('title', {boost: 50});
this.field('members', { boost: 40});
this.field('keywords', { boost : 20 });
});
console.log('Using Local Search Index');
angular.forEach(NG_PAGES, function(page, key) {
if(page.searchTerms) {
index.store({
id : key,
title : page.searchTerms.titleWords,
keywords : page.searchTerms.keywords,
members : page.searchTerms.members
// Create the lunr index
var index = lunr(function() {
this.ref('path');
this.field('titleWords', {boost: 50});
this.field('members', { boost: 40});
this.field('keywords', { boost : 20 });
});
// Delay building the index by loading the data asynchronously
var indexReadyPromise = $http.get('js/search-data.json').then(function(response) {
var searchData = response.data;
// Delay building the index for 500ms to allow the page to render
return $timeout(function() {
// load the page data into the index
angular.forEach(searchData, function(page) {
index.add(page);
});
}, 500);
});
// The actual service is a function that takes a query string and
// returns a promise to the search results
// (In this case we just resolve the promise immediately as it is not
// inherently an async process)
return function(q) {
return indexReadyPromise.then(function() {
var hits = index.search(q);
var results = [];
angular.forEach(hits, function(hit) {
results.push(NG_PAGES[hit.ref]);
});
return results;
});
};
});
}
localSearchFactory.$inject = ['$http', '$timeout', 'NG_PAGES'];
return function(q) {
var results = {
api : [],
tutorial : [],
guide : [],
error : [],
misc : []
// This version of the service builds the index in a WebWorker,
// which does not block rendering and other browser activities.
// It should only be used where the browser does support WebWorkers
function webWorkerSearchFactory($q, $rootScope, NG_PAGES) {
console.log('Using WebWorker Search Index')
var searchIndex = $q.defer();
var results;
var worker = new Worker('js/search-worker.js');
// The worker will send us a message in two situations:
// - when the index has been built, ready to run a query
// - when it has completed a search query and the results are available
worker.onmessage = function(oEvent) {
$rootScope.$apply(function() {
switch(oEvent.data.e) {
case 'index-ready':
searchIndex.resolve();
break;
case 'query-ready':
var pages = oEvent.data.d.map(function(path) {
return NG_PAGES[path];
});
results.resolve(pages);
break;
}
});
};
angular.forEach(index.search(q), function(result) {
var key = result.ref;
var item = NG_PAGES[key];
var area = item.area;
item.path = key;
var limit = area == 'api' ? 40 : 14;
if(results[area].length < limit) {
results[area].push(item);
}
});
return results;
// The actual service is a function that takes a query string and
// returns a promise to the search results
return function(q) {
// We only run the query once the index is ready
return searchIndex.promise.then(function() {
results = $q.defer();
worker.postMessage({ q: q });
return results.promise;
});
};
}
webWorkerSearchFactory.$inject = ['$q', '$rootScope', 'NG_PAGES'];
return {
$get: window.Worker ? webWorkerSearchFactory : localSearchFactory
};
}])
})
.directive('focused', function($timeout) {
return function(scope, element, attrs) {
+15 -16
View File
@@ -1,6 +1,6 @@
angular.module('tutorials', [])
.directive('docTutorialNav', function(templateMerge) {
.directive('docTutorialNav', function() {
var pages = [
'',
'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
@@ -8,23 +8,22 @@ angular.module('tutorials', [])
'step_10', 'step_11', 'step_12', 'the_end'
];
return {
compile: function(element, attrs) {
var seq = 1 * attrs.docTutorialNav,
props = {
seq: seq,
prev: pages[seq],
next: pages[2 + seq],
diffLo: seq ? (seq - 1): '0~1',
diffHi: seq
};
scope: {},
template:
'<a ng-href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
'<a ng-href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
'<a ng-href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>',
link: function(scope, element, attrs) {
var seq = 1 * attrs.docTutorialNav;
scope.seq = seq;
scope.prev = pages[seq];
scope.next = pages[2 + seq];
scope.diffLo = seq ? (seq - 1): '0~1';
scope.diffHi = seq;
element.addClass('btn-group');
element.addClass('tutorial-nav');
element.append(templateMerge(
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-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="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
}
};
})
@@ -47,4 +46,4 @@ angular.module('tutorials', [])
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
'</p>'
};
});
});
+3 -3
View File
@@ -4,6 +4,7 @@ angular.module('versions', [])
.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
$scope.docs_version = NG_VERSIONS[0];
$scope.docs_versions = NG_VERSIONS;
for(var i=0, minor = NaN; i < NG_VERSIONS.length; i++) {
var version = NG_VERSIONS[i];
@@ -15,13 +16,12 @@ angular.module('versions', [])
minor = version.minor;
}
$scope.docs_versions = NG_VERSIONS;
$scope.getGroupName = function(v) {
return v.isLatest ? 'Latest' : (v.isStable ? 'Stable' : 'Unstable');
return v.isLatest ? 'Latest' : ('v' + v.major + '.' + v.minor + '.x');
};
$scope.jumpToDocsVersion = function(version) {
var currentPagePath = $location.path();
var currentPagePath = $location.path().replace(/\/$/, '');
// TODO: We need to do some munging of the path for different versions of the API...
+2 -2
View File
@@ -19,7 +19,7 @@ describe("DocsController", function() {
it("should update the Google Analytics with currentPage path if currentPage exists", inject(function($window) {
$window._gaq = [];
$scope.currentPage = { path: 'a/b/c' };
$scope.afterPartialLoaded();
$scope.$broadcast('$includeContentLoaded');
expect($window._gaq.pop()).toEqual(['_trackPageview', 'a/b/c']);
}));
@@ -27,7 +27,7 @@ describe("DocsController", function() {
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
$window._gaq = [];
spyOn($location, 'path').andReturn('x/y/z');
$scope.afterPartialLoaded();
$scope.$broadcast('$includeContentLoaded');
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
}));
});
+20 -7
View File
@@ -5,8 +5,8 @@ var packagePath = __dirname;
var Package = require('dgeni').Package;
// Create and export a new Dgeni package called dgeni-example. This package depends upon
// the jsdoc and nunjucks packages defined in the dgeni-packages npm module.
// Create and export a new Dgeni package called angularjs. This package depends upon
// the ngdoc,nunjucks and examples packages defined in the dgeni-packages npm module.
module.exports = new Package('angularjs', [
require('dgeni-packages/ngdoc'),
require('dgeni-packages/nunjucks'),
@@ -92,10 +92,7 @@ module.exports = new Package('angularjs', [
}
return docPath;
},
getOutputPath: function(doc) {
return 'partials/' + doc.path +
( doc.fileInfo.baseName === 'index' ? '/index.html' : '.html');
}
outputPathTemplate: 'partials/${path}.html'
});
computePathsProcessor.pathTemplates.push({
@@ -106,10 +103,20 @@ module.exports = new Package('angularjs', [
computePathsProcessor.pathTemplates.push({
docTypes: ['indexPage'],
getPath: function() {},
pathTemplate: '.',
outputPathTemplate: '${id}.html'
});
computePathsProcessor.pathTemplates.push({
docTypes: ['module' ],
pathTemplate: '${area}/${name}',
outputPathTemplate: 'partials/${area}/${name}.html'
});
computePathsProcessor.pathTemplates.push({
docTypes: ['componentGroup' ],
pathTemplate: '${area}/${moduleName}/${groupType}',
outputPathTemplate: 'partials/${area}/${moduleName}/${groupType}.html'
});
computeIdsProcessor.idTemplates.push({
docTypes: ['overview', 'tutorial', 'e2e-test', 'indexPage'],
@@ -124,6 +131,12 @@ module.exports = new Package('angularjs', [
});
})
.config(function(checkAnchorLinksProcessor) {
checkAnchorLinksProcessor.base = '/';
// We are only interested in docs that have an area (i.e. they are pages)
checkAnchorLinksProcessor.checkDoc = function(doc) { return doc.area; };
})
.config(function(
generateIndexPagesProcessor,
+40 -26
View File
@@ -147,24 +147,18 @@ module.exports = function generatePagesDataProcessor(log) {
};
return {
$runAfter: ['paths-computed'],
$runAfter: ['paths-computed', 'generateKeywordsProcessor'],
$runBefore: ['rendering-docs'],
$process: function(docs) {
_(docs)
.filter(function(doc) { return doc.area === 'api' && doc.docType === 'module'; })
.forEach(function(doc) { if ( !doc.path ) {
log.warn('Missing path property for ', doc.id);
}})
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
.tap(function(docs) {
log.debug(docs);
// We are only interested in docs that are in an area
var pages = _.filter(docs, function(doc) {
return doc.area;
});
// We are only interested in docs that are in an area and are not landing pages
var navPages = _.filter(docs, function(page) {
return page.area && page.docType != 'componentGroup';
// We are only interested in pages that are not landing pages
var navPages = _.filter(pages, function(page) {
return page.docType != 'componentGroup';
});
// Generate an object collection of pages that is grouped by area e.g.
@@ -198,28 +192,48 @@ module.exports = function generatePagesDataProcessor(log) {
area.navGroups = navGroupMapper(pages, area);
});
docs.push({
docType: 'nav-data',
id: 'nav-data',
template: 'nav-data.template.js',
outputPath: 'js/nav-data.js',
areas: areas
});
var searchData = _(pages)
.filter(function(page) {
return page.searchTerms;
})
.map(function(page) {
return _.extend({ path: page.path }, page.searchTerms);
})
.value();
docs.push({
docType: 'json-doc',
id: 'search-data-json',
template: 'json-doc.template.json',
outputPath: 'js/search-data.json',
data: searchData
});
// Extract a list of basic page information for mapping paths to partials and for client side searching
var pages = _(docs)
var pageData = _(docs)
.map(function(doc) {
var page = _.pick(doc, [
'docType', 'id', 'name', 'area', 'outputPath', 'path', 'searchTerms'
]);
return page;
return _.pick(doc, ['name', 'area', 'path']);
})
.indexBy('path')
.value();
var docData = {
docs.push({
docType: 'pages-data',
id: 'pages-data',
template: 'pages-data.template.js',
outputPath: 'js/pages-data.js',
areas: areas,
pages: pages
};
docs.push(docData);
pages: pageData
});
}
}
};
};
+1 -1
View File
@@ -19,13 +19,13 @@ module.exports = function debugDeployment(getVersion) {
'../angular-animate.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.js',
'js/angular-bootstrap/bootstrap-prettify.js',
'js/angular-bootstrap/dropdown-toggle.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/versions-data.js',
'js/pages-data.js',
'js/nav-data.js',
'js/docs.js'
],
stylesheets: [
+4 -4
View File
@@ -18,15 +18,15 @@ module.exports = function defaultDeployment(getVersion) {
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.js',
'js/angular-bootstrap/bootstrap-prettify.js',
'js/angular-bootstrap/dropdown-toggle.js',
'js/angular-bootstrap/bootstrap.min.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/versions-data.js',
'js/pages-data.js',
'js/docs.js'
'js/nav-data.js',
'js/docs.min.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
+4 -4
View File
@@ -22,15 +22,15 @@ module.exports = function jqueryDeployment(getVersion) {
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.js',
'js/angular-bootstrap/bootstrap-prettify.js',
'js/angular-bootstrap/dropdown-toggle.js',
'js/angular-bootstrap/bootstrap.min.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/versions-data.js',
'js/pages-data.js',
'js/docs.js'
'js/nav-data.js',
'js/docs.min.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
@@ -21,15 +21,15 @@ module.exports = function productionDeployment(getVersion) {
cdnUrl + '/angular-touch.min.js',
cdnUrl + '/angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'js/angular-bootstrap/bootstrap.js',
'js/angular-bootstrap/bootstrap-prettify.js',
'js/angular-bootstrap/dropdown-toggle.js',
'js/angular-bootstrap/bootstrap.min.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/versions-data.js',
'js/pages-data.js',
'js/docs.js'
'js/nav-data.js',
'js/docs.min.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
-1
View File
@@ -1,6 +1,5 @@
"use strict";
var gruntUtils = require('../../../lib/grunt/utils');
var versionInfo = require('../../../lib/versions/version-info');
/**
+2 -11
View File
@@ -56,15 +56,6 @@
}
})();
// force page reload when new update is available
window.applicationCache && window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
window.location.reload();
}
}, false);
// GA asynchronous tracker
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-8594346-3']);
@@ -85,7 +76,7 @@
<div class="row">
<div class="col-md-9 header-branding">
<a class="brand navbar-brand" href="http://angularjs.org">
<img class="logo" src="img/angularjs-for-header-only.svg">
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
</a>
<ul class="nav navbar-nav">
<li class="divider-vertical"></li>
@@ -219,7 +210,7 @@
</div>
<div class="grid-right">
<div id="loading" ng-show="loading">Loading...</div>
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
<div ng-hide="loading" ng-include="partialPath" autoscroll></div>
</div>
</div>
</section>
@@ -0,0 +1 @@
{$ doc.data | json $}
@@ -0,0 +1,3 @@
// Meta data used by the AngularJS docs app
angular.module('navData', [])
.value('NG_NAVIGATION', {$ doc.areas | json $});
+1 -2
View File
@@ -1,4 +1,3 @@
// Meta data used by the AngularJS docs app
angular.module('pagesData', [])
.value('NG_PAGES', {$ doc.pages | json $})
.value('NG_NAVIGATION', {$ doc.areas | json $});
.value('NG_PAGES', {$ doc.pages | json $});
+2 -2
View File
@@ -148,7 +148,7 @@ or JavaScript callbacks.
{@link ngAnimate CSS-based animations}
</td>
<td>
Follow ngAnimates CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined the animation can be triggered by referencing the CSS class within the HTML template code.
Follow ngAnimates CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined, the animation can be triggered by referencing the CSS class within the HTML template code.
</td>
</tr>
<tr>
@@ -156,7 +156,7 @@ or JavaScript callbacks.
{@link ngAnimate JS-based animations}
</td>
<td>
Use {@link angular.Module#animation module.animation()} to register a JavaScript animation. Once registered the animation can be triggered by referencing the CSS class within the HTML template code.
Use {@link angular.Module#animation module.animation()} to register a JavaScript animation. Once registered, the animation can be triggered by referencing the CSS class within the HTML template code.
</td>
</tr>
</table>
+2 -2
View File
@@ -12,10 +12,10 @@ $controller(MyController);
$controller(MyController, {scope: newScope});
```
To fix the example above please provide a scope to the $controller call:
To fix the example above please provide a scope (using the `$scope` property in the locals object) to the $controller call:
```
$controller(MyController, {$scope, newScope});
$controller(MyController, {$scope: newScope});
```
Please consult the {@link ng.$controller $controller} service api docs to learn more.
+15 -1
View File
@@ -54,4 +54,18 @@ angular.module('myModule')
.directive('myDirective', ['myCoolService', function (myCoolService) {
// This directive definition does not throw unknown provider.
}]);
```
```
Attempting to inject one controller into another will also throw an `Unknown provider` error:
```
angular.module('myModule', [])
.controller('MyFirstController', function() { /* ... */ });
.controller('MySecondController', ['MyFirstController', function(MyFirstController) {
// This controller throws an unknown provider error because
// MyFirstController cannot be injected.
}]);
```
Use the `$controller` service if you want to instantiate controllers yourself.
+1 -1
View File
@@ -15,7 +15,7 @@ For example the issue can be triggered by this *invalid* code:
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:
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:
```
<div ng-repeat="value in [4, 4] track by $index"></div>
+13 -13
View File
@@ -164,7 +164,7 @@ encoded.
`$location` service has two configuration modes which control the format of the URL in the browser
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
HTML5 [History API](http://www.w3.org/TR/html5/history.html). Applications use the same API in
HTML5 [History API](http://www.w3.org/TR/html5/introduction.html#history-0). Applications use the same API in
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
facilitate the browser URL change and history management.
@@ -211,6 +211,10 @@ facilitate the browser URL change and history management.
## Hashbang mode (default mode)
In this mode, `$location` uses Hashbang URLs in all browsers.
Angular also does not intercept and rewrite links in this mode. I.e. links work
as expected and also perform full page reloads when other parts of the url
than the hash fragment was changed.
### Example
@@ -241,7 +245,7 @@ it('should show example', inject(
## HTML5 mode
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
through the HTML5 history API, which allows for use of regular URL path and search segments,
through the HTML5 history API. This allows for use of regular URL path and search segments,
instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the
`$location` service will fall back to using the hashbang URLs automatically. This frees you from
having to worry about whether the browser displaying your app supports the history API or not; the
@@ -250,6 +254,10 @@ having to worry about whether the browser displaying your app supports the histo
- Opening a regular URL in a legacy browser -> redirects to a hashbang URL
- Opening hashbang URL in a modern browser -> rewrites to a regular URL
Note that in this mode, Angular intercepts all links (subject to the "Html link rewriting" rules below)
and updates the url in a way that never performs a full page reload.
### Example
```js
@@ -298,8 +306,8 @@ history API or not; the `$location` service makes this transparent to you.
### Html link rewriting
When you use HTML5 history API mode, you will need different links in different browsers, but all you
have to do is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
When you use HTML5 history API mode, you will not need special hashbang links. All you have to do
is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
When a user clicks on this link,
@@ -314,17 +322,9 @@ reload to the original link.
Example: `<a href="/ext/link?a=b" target="_self">link</a>`
- Absolute links that go to a different domain<br>
Example: `<a href="http://angularjs.org/">link</a>`
- Links starting with '/' that lead to a different base path when base is defined<br>
- Links starting with '/' that lead to a different base path<br>
Example: `<a href="/not-my-base/link">link</a>`
When running Angular in the root of a domain, along side perhaps a normal application in the same
directory, the "otherwise" route handler will try to handle all the URLs, including ones that map
to static files.
To prevent this, you can set your base href for the app to `<base href=".">` and then prefix links
to URLs that should be handled with `.`. Now, links to locations, which are not to be routed by Angular,
are not prefixed with `.` and will not be intercepted by the `otherwise` rule in your `$routeProvider`.
### Server side
+4 -2
View File
@@ -20,7 +20,7 @@ initialization.
<html xmlns:ng="http://angularjs.org" ng-app>
<body>
...
<script src="angular.js">
<script src="angular.js"></script>
</body>
</html>
```
@@ -91,7 +91,9 @@ Here is an example of manually initializing Angular:
<!doctype html>
<html>
<body>
Hello {{greetMe}}!
<div ng-controller="MyController">
Hello {{greetMe}}!
</div>
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
<script>
+7 -3
View File
@@ -3,6 +3,8 @@
@sortOrder 330
@description
# HTML Compiler
<div class="alert alert-warning">
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics.
@@ -12,7 +14,7 @@ If you want a deeper look into Angular's compilation process, you're in the righ
</div>
# Overview
## Overview
Angular's {@link ng.$compile HTML compiler} allows the developer to teach the
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
@@ -85,7 +87,9 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
position: 'relative',
border: '1px solid red',
backgroundColor: 'lightgrey',
cursor: 'pointer'
cursor: 'pointer',
display: 'block',
width: '65px'
});
element.on('mousedown', function(event) {
// Prevent default dragging of selected content
@@ -328,7 +332,7 @@ The first issue we have to solve is that the dialog box template expects `title`
But we would like the template's scope property `title` to be the result of interpolating the
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
widget. To solve the mapping issue we use the `locals` to create local variables which the template
widget. To solve the mapping issue we use the `scope` to create local variables which the template
expects as follows:
```js
+8 -8
View File
@@ -56,10 +56,10 @@ Try out the Live Preview above, and then let's walk through the example and desc
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
<a name="template">"{@link templates template}"</a>. When Angular starts your application, it parses and
processes this new markup from the template using the so called <a name="compiler">"{@link compiler compiler}"</a>.
processes this new markup from the template using the so-called <a name="compiler">"{@link compiler compiler}"</a>.
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
The first kind of new markup are the so called <a name="directive">"{@link directive directives}"</a>.
The first kind of new markup are the so-called <a name="directive">"{@link directive directives}"</a>.
They apply special behavior to attributes or elements in the HTML. In the example above we use the
{@link ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
initializes our application. Angular also defines a directive for the {@link ng.directive:input `input`}
@@ -67,8 +67,8 @@ element that adds extra behavior to the element. The {@link ng.directive:ngModel
stores/updates the value of the input field into/from a variable.
<div class="alert alert-info">
**Custom directives to access the DOM**: In Angular, the only place where an application touches the DOM is
within directives. This is good as artifacts that access the DOM are hard to test.
**Custom directives to access the DOM**: In Angular, the only place where an application should access the DOM is
within directives. This is important because artifacts that access the DOM are hard to test.
If you need to access the DOM directly you should write a custom directive for this. The
{@link directive directives guide} explains how to do this.
</div>
@@ -89,7 +89,7 @@ A filter formats the value of an expression for display to the user.
In the example above, the filter {@link ng.filter:currency `currency`} formats a number
into an output that looks like money.
The important thing in the example is that angular provides _live_ bindings:
The important thing in the example is that Angular provides _live_ bindings:
Whenever the input values change, the value of the expressions are automatically
recalculated and the DOM is updated with their values.
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
@@ -150,13 +150,13 @@ different currencies and also pay the invoice.
What changed?
First, there is a new JavaScript file that contains a so called <a name="controller">"{@link controller controller}"</a>.
First, there is a new JavaScript file that contains a so-called <a name="controller">"{@link controller controller}"</a>.
More exactly, the file contains a constructor function that creates the actual controller instance.
The purpose of controllers is to expose variables and functionality to expressions and directives.
Besides the new file that contains the controller code we also added a
{@link ng.directive:ngController `ng-controller`} directive to the HTML.
This directive tells angular that the new `InvoiceController` is responsible for the element with the directive
This directive tells Angular that the new `InvoiceController` is responsible for the element with the directive
and all of the element's children.
The syntax `InvoiceController as invoice` tells Angular to instantiate the controller
and save it in the variable `invoice` in the current scope.
@@ -263,7 +263,7 @@ services, ...) is created and wired using dependency injection. Within Angular,
the DI container is called the <a name="injector">"{@link di injector}"</a>.
To use DI, there needs to be a place where all the things that should work together are registered.
In Angular, this is the purpose of the so called <a name="module">"{@link module modules}"</a>.
In Angular, this is the purpose of the so-called <a name="module">"{@link module modules}"</a>.
When Angular starts, it will use the configuration of the module with the name defined by the `ng-app` directive,
including the configuration of all modules that this module depends on.
+1 -1
View File
@@ -326,7 +326,7 @@ describe('state', function() {
expect(childScope.timeOfDay).toBe('morning');
expect(childScope.name).toBe('Mattie');
expect(grandChildScope.timeOfDay).toBe('evening');
expect(grandChildScope.name).toBe('Gingerbreak Baby');
expect(grandChildScope.name).toBe('Gingerbread Baby');
});
});
```
+1 -1
View File
@@ -160,7 +160,7 @@ restrictions, you cannot simply write `cx="{{cx}}"`.
With `ng-attr-cx` you can work around this problem.
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
then during the binding will be applied to the corresponding unprefixed attribute. This allows
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
you to bind to attributes that would otherwise be eagerly processed by browsers
(e.g. an SVG element's `circle[cx]` attributes).
+2 -2
View File
@@ -27,8 +27,8 @@ Protractor is a [Node.js](http://nodejs.org) program, and runs end to end tests
written in JavaScript and run with node. Protractor uses [WebDriver](https://code.google.com/p/selenium/wiki/GettingStarted)
to control browsers and simulate user actions.
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/getting-started.md)
or the [api docs](https://github.com/angular/protractor/blob/master/docs/api.md).
For more information on Protractor, view [getting started](http://angular.github.io/protractor/#/getting-started)
or the [api docs](http://angular.github.io/protractor/#/api).
Protractor uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) for its test syntax.
As in unit testing, a test file is comprised of one or
+8 -5
View File
@@ -41,7 +41,7 @@ In Angular applications, you move the job of filling page templates with data fr
### Other AngularJS Features
* **Animation:** {@link guide/animations Core concepts}, {@link ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
* **Security:** {@link ng.$sce Strict Contextual Escaping}, {@link ng.directive:ngCsp Content Security Policy}, {@link ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
* **Security:** {@link guide/security Security Docs}, {@link ng.$sce Strict Contextual Escaping}, {@link ng.directive:ngCsp Content Security Policy}, {@link 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 ng.filter:date date filter}, {@link 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 ngTouch Touch events}
@@ -70,7 +70,7 @@ 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://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/), [angular-localization](http://doshprompt.github.io/angular-localization/)
* **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/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
@@ -89,11 +89,12 @@ This is a short list of libraries with specific support and documentation for wo
* **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), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.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)
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/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)
* **Meteor: **[angular-meteor package](https://github.com/Urigo/angular-meteor)
## Learning Resources
@@ -104,6 +105,8 @@ This is a short list of libraries with specific support and documentation for wo
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
###Videos:
* [egghead.io](http://egghead.io/)
@@ -112,12 +115,12 @@ This is a short list of libraries with specific support and documentation for wo
### Courses
* **Free online:**
[thinkster.io](http://thinkster.io),
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
* **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)
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
* **Paid onsite:**
[angularbootcamp.com](http://angularbootcamp.com/)
+3 -3
View File
@@ -9,7 +9,7 @@
AngularJS is a structural framework for dynamic web apps. It lets you use HTML as your template
language and lets you extend HTML's syntax to express your application's components clearly and
succinctly. Angular's data binding and dependency injection eliminate much of the code you
currently have to write. And it all happens within the browser, making it
would otherwise have to write. And it all happens within the browser, making it
an ideal partner with any server technology.
Angular is what HTML would have been had it been designed for applications. HTML is a great
@@ -33,7 +33,7 @@ browser new syntax through a construct we call directives. Examples include:
* Data binding, as in `{{}}`.
* DOM control structures for repeating/hiding DOM fragments.
* Support for forms and form validation.
* Attaching code-behind to DOM elements.
* Attaching new behavior to DOM elements, such as DOM event handling.
* Grouping of HTML into reusable components.
@@ -103,7 +103,7 @@ Angular frees you from the following pains:
* **Writing tons of initialization code just to get started:** Typically you need to write a lot
of plumbing just to get a basic "Hello World" AJAX app working. With Angular you can bootstrap
your app easily using services, which are auto-injected into your application in a
[Guice](http://code.google.com/p/google-guice/)-like dependency-injection style. This allows you
[Guice](https://github.com/google/guice)-like dependency-injection style. This allows you
to get started developing features quickly. As a bonus, you get full control over the
initialization process in automated tests.
+13 -1
View File
@@ -39,7 +39,7 @@ linking} phase the {@link ng.$compileProvider#directive directives} set up
render the updated value to the DOM.
Both controllers and directives have reference to the scope, but not to each other. This
arrangement isolates the controller from the directive as well as from DOM. This is an important
arrangement isolates the controller from the directive as well as from the DOM. This is an important
point since it makes the controllers view agnostic, which greatly improves the testing story of
the applications.
@@ -339,6 +339,18 @@ the dirty checking function must be efficient. Care should be taken that the dir
function does not do any DOM access, as DOM access is orders of magnitude slower than property
access on JavaScript object.
### Scope `$watch` Depths
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-scope-watch-strategies.png">
Dirty checking can be done with three strategies: By reference, by collection contents, and by value. The strategies differ in the kinds of changes they detect, and in their performance characteristics.
- Watching *by reference* ({@link
ng.$rootScope.Scope#$watch scope.$watch} `(watchExpression, listener)`) detects a change when the whole value returned by the watch expression switches to a new value. If the value is an array or an object, changes inside it are not detected. This is the most efficient stategy.
- Watching *collection contents* ({@link
ng.$rootScope.Scope#$watchCollection scope.$watchCollection} `(watchExpression, listener)`) detects changes that occur inside an array or an object: When items are added, removed, or reordered. The detection is shallow - it does not reach into nested collections. Watching collection contents is more expensive than watching by reference, because copies of the collection contents need to be maintained. However, the strategy attempts to minimize the amount of copying required.
- Watching *by value* ({@link
ng.$rootScope.Scope#$watch scope.$watch} `(watchExpression, listener, true)`) detects any change in an arbitrarily nested data structure. It is the most powerful change detection strategy, but also the most expensive. A full traversal of the nested data structure is needed on each digest, and a full copy of it needs to be held in memory.
## Integration with the browser event loop
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png">
+60
View File
@@ -0,0 +1,60 @@
@ngdoc overview
@name Security
@sortOrder 525
@description
# Security
This document explains some of AngularJS's security features and best practices that you should
keep in mind as you build your application.
## Expression Sandboxing
AngularJS's expressions are sandboxed not for security reasons, but instead to maintain a proper
separation of application responsibilities. For example, access to `window` is disallowed
because it makes it easy to introduce brittle global state into your application.
However, this sandbox is not intended to stop attackers who can edit the template before it's
processed by Angular. It may be possible to run arbitrary JavaScript inside double-curly bindings
if an attacker can modify them.
But if an attacker can change arbitrary HTML templates, there's nothing stopping them from doing:
```html
<script>somethingEvil();</script>
```
It's better to design your application in such a way that users cannot change client-side templates.
For instance:
* Do not mix client and server templates
* Do not use user input to generate templates dynamically
* Do not run user input through `$scope.$eval`
* Consider using {@link ng.directive:ngCsp CSP} (but don't rely only on CSP)
## Mixing client-side and server-side templates
In general, we recommend against this because it can create unintended XSS vectors.
However, it's ok to mix server-side templating in the bootstrap template (`index.html`) as long
as user input cannot be used on the server to output html that would then be processed by Angular
in a way that would cause allow for arbitrary code execution.
For instance, you can use server-side templating to dynamically generate CSS, URLs, etc, but not
for generating templates that are bootstrapped/compiled by Angular.
## Reporting a security issue
Email us at [security@angularjs.org](mailto:security@angularjs.org) to report any potential
security issues in AngularJS.
Please keep in mind the above points about Angular's expression language.
## See also
* {@link ng.directive:ngCsp Content Security Policy}
* {@link ng.$sce Strict Contextual Escaping}
* {@link ngSanitize.$sanitize $sanitize}
+2 -2
View File
@@ -268,8 +268,8 @@ logic, further simplifying the application logic.
```js
myModule.filter('length', function() {
return function(text){
return (''+(text||'')).length;
return function(text) {
return ('' + (text || '')).length;
}
});
+1 -1
View File
@@ -31,7 +31,7 @@ development web server, run tests, and generate distributable files. Depending o
pre-packaged bundle.
* [Java](http://www.java.com): We minify JavaScript using our
[Closure Tools](https://developers.google.com/closure/) jar. Make sure you have Java (version 6 or higher) installed
[Closure Tools](https://developers.google.com/closure/) jar. Make sure you have Java (version 7 or higher) installed
and included in your [PATH](http://docs.oracle.com/javase/tutorial/essential/environment/paths.html) variable.
* [Grunt](http://gruntjs.com): We use Grunt as our build system. Install the grunt command-line tool globally with:
+3 -3
View File
@@ -22,7 +22,7 @@ So it's definitely not a plugin or some other native browser extension.
### Is AngularJS a templating system?
At the highest level, Angular does look like a just another templating system. But there is one
At the highest level, Angular does look like just another templating system. But there is one
important reason why the Angular templating system is different, that makes it very good fit for
application development: bidirectional data binding. The template is compiled in the browser and
the compilation step produces a live view. This means you, the developers, don't need to write
@@ -39,7 +39,7 @@ for server-side communication.
AngularJS was designed to be compatible with other security measures like Content Security Policy
(CSP), HTTPS (SSL/TLS) and server-side authentication and authorization that greatly reduce the
possible attack vectors and we highly recommended their use.
possible attack vectors and we highly recommend their use.
### Can I download the source, build, and host the AngularJS environment locally?
@@ -204,7 +204,7 @@ If you want to apply a directive to each inner piece of the repeat, put it on a
### `$rootScope` exists, but it can be used for evil
Scopes in Angular form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree.
Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Occasionally there are pieces of data that you want to make global to the whole app.
+2 -1
View File
@@ -110,8 +110,9 @@ suggested solution is to also install the `nodejs-legacy` apt package, which ren
`nodejs`.
```
apt-get install nodejs-legacy
apt-get install nodejs-legacy npm
nodejs --version
npm --version
```
+2 -2
View File
@@ -233,7 +233,7 @@ browser is limited, which results in your karma tests running extremely slow.
Refresh your browser and verify that it says "Hello, World!".
* Update the unit test for the controller in ./test/unit/controllersSpec.js to reflect the previous change. For example by adding:
* Update the unit test for the controller in `./test/unit/controllersSpec.js` to reflect the previous change. For example by adding:
expect(scope.name).toBe('World');
@@ -251,7 +251,7 @@ browser is limited, which results in your karma tests running extremely slow.
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
Extra points: try and make an 8x8 table using an additional ng-repeat.
Extra points: try and make an 8x8 table using an additional `ng-repeat`.
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
+3 -3
View File
@@ -95,7 +95,7 @@ describe('PhoneCat App', function() {
});
it('should filter the phone list as user types into the search box', function() {
it('should filter the phone list as a user types into the search box', function() {
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
@@ -120,7 +120,7 @@ really is that easy to set up any functional, readable, end-to-end test.
### Running End to End Tests with Protractor
Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of [Protractor](https://github.com/angular/protractor). Read
about the Protractor APIs at https://github.com/angular/protractor/blob/master/docs/api.md.
about the Protractor APIs at http://angular.github.io/protractor/#/api.
Much like Karma is the test runner for unit tests, we use Protractor to run end-to-end tests.
Try it with `npm run protractor`. End-to-end tests are slow, so unlike with unit tests, Protractor
@@ -159,7 +159,7 @@ Let's see how we can get the current value of the `query` model to appear in the
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
it('should filter the phone list as user types into the search box', function() {
it('should filter the phone list as a user types into the search box', function() {
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
+1 -1
View File
@@ -182,7 +182,7 @@ You can now rerun `npm run protractor` to see the tests run.
# Experiments
* In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and
you'll see that Angular will temporarily add a new "unknown" option to the drop-down list and the
you'll see that Angular will temporarily add a new blank ("unknown") option to the drop-down list and the
ordering will default to unordered/natural order.
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
+6 -4
View File
@@ -11,7 +11,7 @@ from our server using one of Angular's built-in {@link guide/services services}
ng.$http $http}. We will use Angular's {@link guide/di dependency
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
* There are now a list of 20 phones, loaded from the server.
* There is now a list of 20 phones, loaded from the server.
<div doc-tutorial-reset="5"></div>
@@ -236,7 +236,9 @@ the response is received:
```
* We flush the request queue in the browser by calling `$httpBackend.flush()`. This causes the
promise returned by the `$http` service to be resolved with the trained response.
promise returned by the `$http` service to be resolved with the trained response. See
'Flushing HTTP requests' in the {@link ngMock.$httpBackend mock $httpBackend} documentation for
a full explanation of why this is necessary.
* We make the assertions, verifying that the phone model now exists on the scope.
@@ -256,8 +258,8 @@ You should now see the following output in the Karma tab:
# Experiments
* At the bottom of `index.html`, add a `<pre>{{phones | json}}</pre>` binding to see the list of phones
displayed in json format.
* At the bottom of `index.html`, add a `<pre>{{phones | filter:query | orderBy:orderProp | json}}</pre>`
binding to see the list of phones displayed in json format.
* In the `PhoneListCtrl` controller, pre-process the http response by limiting the number of phones
to the first 5 in the list. Use the following code in the `$http` callback:
+1 -1
View File
@@ -9,7 +9,7 @@
In this step, you will learn how to create a layout template and how to build an app that has
multiple views by adding routing, using an Angular module called 'ngRoute'.
* When you now navigate to `app/index.html`, you are redirected to `app/index.html#/phones`
* When you now navigate to `app/index.html`, you are redirected to `app/index.html/#/phones`
and the phone list appears in the browser.
* When you click on a phone link the url changes to one specific to that phone and the stub of a
phone detail page is displayed.
+1 -1
View File
@@ -184,7 +184,7 @@ You can now rerun `npm run protractor` to see the tests run.
# Experiments
* Using the [Protractor API](https://github.com/angular/protractor/blob/master/docs/api.md),
* Using the [Protractor API](http://angular.github.io/protractor/#/api),
write a test that verifies that we display 4 thumbnail images on the Nexus S details page.
+1 -1
View File
@@ -171,7 +171,7 @@ we require, so in these cases, we can add a callback to process the server respo
## Test
Because we're now using the {@link ngResource ngResource} module, it's necessary to also need to
Because we're now using the {@link ngResource ngResource} module, it's necessary to
update the Karma config file with angular-resource so the new tests will pass.
__`test/karma.conf.js`:__
+11 -10
View File
@@ -9,8 +9,8 @@
In this final step, we will enhance our phonecat web application by attaching CSS and JavaScript
animations on top of the template code we created before.
* Used the `ngAnimate` to enable animations throughout the application.
* Common `ng` directives automatically trigger hooks for animations to tap into.
* We now use the `ngAnimate` module to enable animations throughout the application.
* We also use common `ng` directives to automatically trigger hooks for animations to tap into.
* When an animation is found then the animation will run in between the standard DOM operation that
is being issued on the element at the given time (e.g. inserting and removing nodes on
{@link api/ng.directive:ngRepeat `ngRepeat`} or adding and removing classes on
@@ -21,7 +21,7 @@ animations on top of the template code we created before.
## Dependencies
The animation functionality is provided by Angular in the `ngAnimate` module, which is distributed
separately from the core Angular framework. In addition we will use `JQuery` in this project to do
separately from the core Angular framework. In addition we will use `jQuery` in this project to do
extra JavaScript animations.
We are using [Bower][bower] to install client side dependencies. This step updates the
@@ -49,8 +49,8 @@ We are using [Bower][bower] to install client side dependencies. This step upda
* `"angular-animate": "~1.2.x"` tells bower to install a version of the
angular-animate component that is compatible with version 1.2.x.
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of JQuery. Note that this is not an
Angular library, it is the standard JQuery library. We can use bower to install a wide range of 3rd
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of jQuery. Note that this is not an
Angular library, it is the standard jQuery library. We can use bower to install a wide range of 3rd
party libraries.
We must ask bower to download and install this dependency. We can do this by running:
@@ -254,7 +254,7 @@ which are described in detail below.
Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
To start, let's add a new CSS class to our HTML like we did in the example above.
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
This time, instead of the `ng-repeat` element, let's add it to the element containing the `ng-view` directive.
In order to do this, we'll have to make some small changes to the HTML code so that we can have more control over our
animations between view changes.
@@ -339,13 +339,13 @@ a cross fade animation in between. So as the previous page is just about to be r
while the new page fades in right on top of it.
Once the leave animation is over then element is removed and once the enter animation is complete
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to
be position itself with its default CSS code (so no more absolute positioning once the animation is
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element, causing it to rerender and
reposition itself with its default CSS code (so no more absolute positioning once the animation is
over). This works fluidly so that pages flow naturally between route changes without anything
jumping around.
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time
a new page is loaded the ng-view directive will create a copy of itself, download the template and
a new page is loaded the `ng-view` directive will create a copy of itself, download the template and
append the contents. This ensures that all views are contained within a single HTML element which
allows for easy animation control.
@@ -368,7 +368,8 @@ occur whenever the CSS class itself changes.
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added
to the matching profile image and the animation plays.
Let's get started and tweak our HTML code on the `phone-detail.html` page first:
Let's get started and tweak our HTML code on the `phone-detail.html` page first. Notice that we
have changed the way we display our large image:
__`app/partials/phone-detail.html`.__
+37 -5
View File
@@ -8,7 +8,10 @@ var bower = require('bower');
var Dgeni = require('dgeni');
var merge = require('event-stream').merge;
var path = require('canonical-path');
var foreach = require('gulp-foreach');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var rename = require('gulp-rename');
// We indicate to gulp that tasks are async by returning the stream.
// Gulp can then wait for the stream to close before starting dependent tasks.
@@ -17,6 +20,9 @@ var path = require('canonical-path');
var outputFolder = '../build/docs';
var bowerFolder = 'bower_components';
var src = 'app/src/**/*.js';
var assets = 'app/assets/**/*';
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
pattern = pattern || '/**/*';
@@ -40,14 +46,37 @@ gulp.task('bower', function() {
});
gulp.task('build-app', function() {
gulp.src('app/src/**/*.js')
.pipe(concat('docs.js'))
.pipe(gulp.dest(outputFolder + '/js/'));
var file = 'docs.js';
var minFile = 'docs.min.js';
var folder = outputFolder + '/js/';
return gulp.src(src)
.pipe(sourcemaps.init())
.pipe(concat(file))
.pipe(gulp.dest(folder))
.pipe(rename(minFile))
.pipe(uglify())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(folder));
});
gulp.task('assets', ['bower'], function() {
var JS_EXT = /\.js$/;
return merge(
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
gulp.src([assets])
.pipe(gulp.dest(outputFolder)),
gulp.src([assets])
.pipe(foreach(function(stream, file) {
if (JS_EXT.test(file.relative)) {
var minFile = file.relative.replace(JS_EXT, '.min.js');
return stream
.pipe(sourcemaps.init())
.pipe(concat(minFile))
.pipe(uglify())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(outputFolder));
}
})),
copyComponent('bootstrap', '/dist/**/*'),
copyComponent('open-sans-fontface'),
copyComponent('lunr.js','/*.js'),
@@ -77,3 +106,6 @@ gulp.task('jshint', ['doc-gen'], function() {
// The default task that will be run if no task is supplied
gulp.task('default', ['assets', 'doc-gen', 'build-app', 'jshint']);
gulp.task('watch', function() {
gulp.watch([src, assets], ['assets', 'build-app']);
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

+1 -1
View File
@@ -12,7 +12,7 @@ set -e
# before_script:
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-4.3-linux.tar.gz"
CONNECT_URL="https://saucelabs.com/downloads/sc-4.3-linux.tar.gz"
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
CONNECT_DOWNLOAD="sc-4.3-linux.tar.gz"
+2 -11
View File
@@ -83,6 +83,7 @@ var getTaggedVersion = function() {
if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
version.codeName = getCodeName(tag);
version.full = version.version;
version.branch = 'v' + currentPackage.branchVersion.replace('*', 'x');
return version;
}
}
@@ -90,15 +91,6 @@ var getTaggedVersion = function() {
return null;
};
/**
* Stable versions have an even minor version and have no prerelease
* @param {SemVer} version The version to test
* @return {Boolean} True if the version is stable
*/
var isStable = function(version) {
return semver.satisfies(version, '1.0 || 1.2') && version.prerelease.length === 0;
};
/**
* Get a collection of all the previous versions sorted by semantic version
* @return {Array.<SemVer>} The collection of previous versions
@@ -118,8 +110,6 @@ var getPreviousVersions = function() {
})
.filter()
.map(function(version) {
version.isStable = isStable(version);
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
// Versions before 1.0.2 had a different docs folder name
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
@@ -197,6 +187,7 @@ var getSnapshotVersion = function() {
version.isSnapshot = true;
version.format();
version.full = version.version + '+' + version.build;
version.branch = 'master';
return version;
};
+832 -344
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -13,7 +13,6 @@
"canonical-path": "0.0.2",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.10.0",
"es6-shim": "^0.14.0",
"event-stream": "~3.1.0",
"grunt": "~0.4.2",
"grunt-bump": "~0.0.13",
@@ -29,8 +28,12 @@
"grunt-parallel": "~0.3.1",
"grunt-shell": "~0.4.0",
"gulp": "~3.8.0",
"gulp-concat": "~2.1.7",
"gulp-concat": "^2.4.1",
"gulp-foreach": "0.0.1",
"gulp-jshint": "~1.4.2",
"gulp-rename": "^1.2.0",
"gulp-sourcemaps": "^1.2.2",
"gulp-uglify": "^1.0.1",
"gulp-util": "^3.0.1",
"jasmine-node": "~1.11.0",
"jasmine-reporters": "~0.2.1",
@@ -49,7 +52,7 @@
"marked": "~0.3.0",
"node-html-encoder": "0.0.2",
"promises-aplus-tests": "~1.3.2",
"protractor": "1.2.0",
"protractor": "1.4.0",
"q": "~1.0.0",
"q-io": "^1.10.9",
"qq": "^0.3.5",
@@ -57,9 +60,7 @@
"semver": "~2.1.0",
"shelljs": "~0.2.6",
"sorted-object": "^1.0.0",
"stringmap": "^0.2.2",
"winston": "~0.7.2",
"yaml-js": "~0.0.8"
"stringmap": "^0.2.2"
},
"licenses": [
{
+2 -2
View File
@@ -21,9 +21,9 @@ exports.config = {
// Disable animations so e2e tests run more quickly
var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(function($animate) {
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
$animate.enabled(false);
});
}]);
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
+2 -2
View File
@@ -12,9 +12,9 @@ exports.config = {
// Disable animations so e2e tests run more quickly
var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(function($animate) {
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
$animate.enabled(false);
});
}]);
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
+20
View File
@@ -71,6 +71,8 @@ function prepare {
cd $TMP_DIR/bower-$repo
replaceJsonProp "bower.json" "version" ".*" "$NEW_VERSION"
replaceJsonProp "bower.json" "angular.*" ".*" "$NEW_VERSION"
replaceJsonProp "package.json" "version" ".*" "$NEW_VERSION"
replaceJsonProp "package.json" "angular.*" ".*" "$NEW_VERSION"
git add -A
@@ -88,6 +90,24 @@ function publish {
cd $TMP_DIR/bower-$repo
git push origin master
git push origin v$NEW_VERSION
# don't publish every build to npm
if [ "${NEW_VERSION/+sha}" = "$NEW_VERSION" ] ; then
if [ "${NEW_VERSION/-}" = "$NEW_VERSION" ] ; then
if [[ $NEW_VERSION =~ ^1\.2\.[0-9]+$ ]] ; then
# publish 1.2.x releases with the appropriate tag
# this ensures that `npm install` by default will not grab `1.2.x` releases
npm publish --tag=old
else
# publish releases as "latest"
npm publish
fi
else
# publish prerelease builds with the beta tag
npm publish --tag=beta
fi
fi
cd $SCRIPT_DIR
done
}
+3 -3
View File
@@ -155,8 +155,8 @@ if ('i' !== 'I'.toLowerCase()) {
}
var /** holds major version number for IE or NaN for real browsers */
msie,
var
msie, // holds major version number for IE, or NaN if UA is not IE.
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
slice = [].slice,
@@ -338,7 +338,7 @@ function setHashKey(obj, h) {
* @kind function
*
* @description
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
* to `dst`. You can specify multiple `src` objects.
*
* @param {Object} dst Destination object.
+5 -6
View File
@@ -7,13 +7,13 @@
* @kind function
*
* @description
* Creates an injector function that can be used for retrieving services as well as for
* Creates an injector object that can be used for retrieving services as well as for
* dependency injection (see {@link guide/di dependency injection}).
*
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See
* {@link angular.module}. The `ng` module must be explicitly added.
* @returns {function()} Injector function. See {@link auto.$injector $injector}.
* @returns {injector} Injector object. See {@link auto.$injector $injector}.
*
* @example
* Typical usage
@@ -101,7 +101,6 @@ function annotate(fn) {
/**
* @ngdoc service
* @name $injector
* @kind function
*
* @description
*
@@ -116,7 +115,7 @@ function annotate(fn) {
* expect($injector.get('$injector')).toBe($injector);
* expect($injector.invoke(function($injector){
* return $injector;
* }).toBe($injector);
* })).toBe($injector);
* ```
*
* # Injection Function Annotation
@@ -183,8 +182,8 @@ function annotate(fn) {
* @description
* Allows the user to query if the particular service exists.
*
* @param {string} Name of the service to query.
* @returns {boolean} returns true if injector has given service.
* @param {string} name Name of the service to query.
* @returns {boolean} `true` if injector has given service.
*/
/**
+1 -1
View File
@@ -43,7 +43,7 @@
* - [`children()`](http://api.jquery.com/children/) - Does not support selectors
* - [`clone()`](http://api.jquery.com/clone/)
* - [`contents()`](http://api.jquery.com/contents/)
* - [`css()`](http://api.jquery.com/css/)
* - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()`
* - [`data()`](http://api.jquery.com/data/)
* - [`empty()`](http://api.jquery.com/empty/)
* - [`eq()`](http://api.jquery.com/eq/)
+13
View File
@@ -53,6 +53,19 @@ function $AnchorScrollProvider() {
var autoScrollingEnabled = true;
/**
* @ngdoc method
* @name $anchorScrollProvider#disableAutoScrolling
*
* @description
* By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
* {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
* Use this method to disable automatic scrolling.
*
* If automatic scrolling is disabled, one must explicitly call
* {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
* current hash.
*/
this.disableAutoScrolling = function() {
autoScrollingEnabled = false;
};
+14 -7
View File
@@ -1,4 +1,5 @@
'use strict';
/* global stripHash: true */
/**
* ! This is a private undocumented service !
@@ -124,7 +125,7 @@ function Browser(window, document, $log, $sniffer) {
var lastBrowserUrl = location.href,
baseElement = document.find('base'),
newLocation = null;
reloadLocation = null;
/**
* @name $browser#url
@@ -153,8 +154,13 @@ function Browser(window, document, $log, $sniffer) {
// setter
if (url) {
if (lastBrowserUrl == url) return;
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
lastBrowserUrl = url;
if ($sniffer.history) {
// Don't use history API if only the hash changed
// due to a bug in IE10/IE11 which leads
// to not firing a `hashchange` nor `popstate` event
// in some cases (see #9143).
if (!sameBase && $sniffer.history) {
if (replace) history.replaceState(null, '', url);
else {
history.pushState(null, '', url);
@@ -162,7 +168,9 @@ function Browser(window, document, $log, $sniffer) {
baseElement.attr('href', baseElement.attr('href'));
}
} else {
newLocation = url;
if (!sameBase) {
reloadLocation = url;
}
if (replace) {
location.replace(url);
} else {
@@ -172,10 +180,10 @@ function Browser(window, document, $log, $sniffer) {
return self;
// getter
} else {
// - newLocation is a workaround for an IE7-9 issue with location.replace and location.href
// methods not updating location.href synchronously.
// - reloadLocation is needed as browsers don't allow to read out
// the new location.href if a reload happened.
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return newLocation || location.href.replace(/%27/g,"'");
return reloadLocation || location.href.replace(/%27/g,"'");
}
};
@@ -183,7 +191,6 @@ function Browser(window, document, $log, $sniffer) {
urlChangeInit = false;
function fireUrlChange() {
newLocation = null;
if (lastBrowserUrl == self.url()) return;
lastBrowserUrl = self.url();
+2 -1
View File
@@ -369,7 +369,8 @@ function $CacheFactoryProvider() {
* ```
*
* **Note:** the `script` tag containing the template does not need to be included in the `head` of
* the document, but it must be below the `ng-app` definition.
* the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
* element with ng-app attribute), otherwise the template will be ignored.
*
* Adding via the $templateCache service:
*
+23 -10
View File
@@ -255,8 +255,13 @@
* scope. This makes it possible for the widget to have private state, and the transclusion to
* be bound to the parent (pre-`isolate`) scope.
*
* * `true` - transclude the content of the directive.
* * `'element'` - transclude the whole element including any directives defined at lower priority.
* There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
* directive's element or the entire element:
*
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
* * `'element'` - transclude the whole of the directive's element including any directives on this
* element that defined at a lower priority than this directive. When used, the `template`
* property is ignored.
*
* <div class="alert alert-warning">
* **Note:** When testing an element transclude directive you must not place the directive at the root of the
@@ -392,7 +397,7 @@
* }
* ```
*
* Below is an example using `$compileProvider`.
* ## Example
*
* <div class="alert alert-warning">
* **Note**: Typically directives are registered with `module.directive`. The example below is
@@ -648,6 +653,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
};
Attributes.prototype = {
/**
* @ngdoc method
* @name $compile.directive.Attributes#$normalize
* @kind function
*
* @description
* Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
* `data-`) to its normalized, camelCase form.
*
* Also there is special case for Moz prefix starting with upper case letter.
*
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
*
* @param {string} name Name to normalize
*/
$normalize: directiveNormalize,
@@ -1981,13 +2001,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i;
/**
* Converts all accepted directives format into proper directive name.
* All of these will become 'myDirective':
* my:Directive
* my-directive
* x-my-directive
* data-my:directive
*
* Also there is special case for Moz prefix starting with upper case letter.
* @param name Name to normalize
*/
function directiveNormalize(name) {
+2 -3
View File
@@ -11,9 +11,8 @@
* make the link go to the wrong URL if the user clicks it before
* Angular has a chance to replace the `{{hash}}` markup with its
* value. Until Angular replaces the markup the link will be broken
* and will most likely return a 404 error.
*
* The `ngHref` directive solves this problem.
* and will most likely return a 404 error. The `ngHref` directive
* solves this problem.
*
* The wrong way to write it:
* ```html
+3 -3
View File
@@ -969,7 +969,7 @@ var VALID_CLASS = 'ng-valid',
*
* We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
* module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
* However, as we are using `$sce` the model can still decide to to provide unsafe content if it marks
* However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
* that content using the `$sce` service.
*
* <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
@@ -1001,7 +1001,7 @@ var VALID_CLASS = 'ng-valid',
// Listen for change events to enable binding
element.on('blur keyup change', function() {
scope.$apply(read);
scope.$evalAsync(read);
});
read(); // initialize
@@ -1284,7 +1284,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
*
* For best practices on using `ngModel`, see:
*
* - [https://github.com/angular/angular.js/wiki/Understanding-Scopes]
* - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
*
* For basic examples, how to use `ngModel`, see:
*
+4 -1
View File
@@ -139,7 +139,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
* element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
* ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
* is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
* core Angular.) You may also bypass sanitization for values you know are safe. To do so, bind to
* core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to
* include "angular-sanitize.js" in your application.
*
* You may also bypass sanitization for values you know are safe. To do so, bind to
* an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
* under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}.
*
+1
View File
@@ -23,6 +23,7 @@
*
* @element ANY
* @scope
* @priority 500
* @param {expression} ngController Name of a globally accessible constructor function or an
* {@link guide/expression expression} that on the current scope evaluates to a
* constructor function. The controller instance can be published into a scope property
+7 -5
View File
@@ -33,10 +33,8 @@
</example>
*/
/*
* A directive that allows creation of custom onclick handlers that are defined as angular
* expressions and are compiled and executed within the current scope.
*
* Events that are handled via these handler are always configured not to propagate further.
* A collection of directives that allows creation of custom event handlers that are defined as
* angular expressions and are compiled and executed within the current scope.
*/
var ngEventDirectives = {};
@@ -54,7 +52,11 @@ forEach(
ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
return {
compile: function($element, attr) {
var fn = $parse(attr[directiveName]);
// We expose the powerful $event object on the scope that provides access to the Window,
// etc. that isn't protected by the fast paths in $parse. We explicitly request better
// checks at the cost of speed since event handler expressions are not executed as
// frequently as regular change detection.
var fn = $parse(attr[directiveName], /* expensiveChecks */ true);
return function ngEventHandler(scope, element) {
element.on(eventName, function(event) {
var callback = function() {
+3 -3
View File
@@ -19,7 +19,7 @@
* Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
* is created when the element is restored. The scope created within `ngIf` inherits from
* its parent scope using
* [prototypal inheritance](https://github.com/angular/angular.js/wiki/The-Nuances-of-Scope-Prototypal-Inheritance).
* [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
* An important implication of this is if `ngModel` is used within `ngIf` to bind to
* a javascript primitive defined in the parent scope. In this case any modifications made to the
* variable within the child scope will override (hide) the value in the parent scope.
@@ -33,8 +33,8 @@
* and `leave` effects.
*
* @animations
* enter - happens just after the ngIf contents change and a new DOM element is created and injected into the ngIf container
* leave - happens just before the ngIf contents are removed from the DOM
* enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
* leave - happens just before the `ngIf` contents are removed from the DOM
*
* @element ANY
* @scope
-1
View File
@@ -40,7 +40,6 @@ var scriptDirective = ['$templateCache', function($templateCache) {
compile: function(element, attr) {
if (attr.type == 'text/ng-template') {
var templateUrl = attr.id,
// IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
text = element[0].text;
$templateCache.put(templateUrl, text);
+6 -1
View File
@@ -551,6 +551,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement = existingOption.element;
if (existingOption.label !== option.label) {
lastElement.text(existingOption.label = option.label);
lastElement.prop('label', existingOption.label);
}
if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id);
@@ -580,6 +581,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
.val(option.id)
.prop('selected', option.selected)
.attr('selected', option.selected)
.prop('label', option.label)
.text(option.label);
}
@@ -589,6 +591,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
id: option.id,
selected: option.selected
});
selectCtrl.addOption(option.label, element);
if (lastElement) {
lastElement.after(element);
} else {
@@ -600,7 +603,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
// remove any excessive OPTIONs in a group
index++; // increment since the existingOptions[0] is parent element not OPTION
while(existingOptions.length > index) {
existingOptions.pop().element.remove();
option = existingOptions.pop();
selectCtrl.removeOption(option.label);
option.element.remove();
}
}
// remove any excessive OPTGROUPs from select
+11 -11
View File
@@ -154,17 +154,17 @@ function filterFilter() {
}
var search = function(obj, text){
if (typeof text == 'string' && text.charAt(0) === '!') {
if (typeof text === 'string' && text.charAt(0) === '!') {
return !search(obj, text.substr(1));
}
switch (typeof obj) {
case "boolean":
case "number":
case "string":
case 'boolean':
case 'number':
case 'string':
return comparator(obj, text);
case "object":
case 'object':
switch (typeof text) {
case "object":
case 'object':
return comparator(obj, text);
default:
for ( var objKey in obj) {
@@ -175,7 +175,7 @@ function filterFilter() {
break;
}
return false;
case "array":
case 'array':
for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) {
return true;
@@ -187,13 +187,13 @@ function filterFilter() {
}
};
switch (typeof expression) {
case "boolean":
case "number":
case "string":
case 'boolean':
case 'number':
case 'string':
// Set up expression object and fall through
expression = {$:expression};
// jshint -W086
case "object":
case 'object':
// jshint +W086
for (var key in expression) {
(function(path) {
+11 -10
View File
@@ -31,9 +31,9 @@
}]);
</script>
<div ng-controller="ExampleController">
Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
<p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
<p>Output letters: {{ letters | limitTo:letterLimit }}</p>
</div>
</file>
@@ -50,14 +50,15 @@
expect(limitedLetters.getText()).toEqual('Output letters: abc');
});
it('should update the output when -3 is entered', function() {
numLimitInput.clear();
numLimitInput.sendKeys('-3');
letterLimitInput.clear();
letterLimitInput.sendKeys('-3');
expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
expect(limitedLetters.getText()).toEqual('Output letters: ghi');
});
// There is a bug in safari and protractor that doesn't like the minus key
// it('should update the output when -3 is entered', function() {
// numLimitInput.clear();
// numLimitInput.sendKeys('-3');
// letterLimitInput.clear();
// letterLimitInput.sendKeys('-3');
// expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
// expect(limitedLetters.getText()).toEqual('Output letters: ghi');
// });
it('should not exceed the maximum size of input array', function() {
numLimitInput.clear();
+13 -6
View File
@@ -11,7 +11,7 @@
* correctly, make sure they are actually being saved as numbers and not strings.
*
* @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
* used by the comparator to determine the order of elements.
*
* Can be one of:
@@ -24,10 +24,13 @@
* is interpreted as a property name to be used in comparisons (for example `"special name"`
* to sort object by the value of their `special name` property). An expression can be
* optionally prefixed with `+` or `-` to control ascending or descending sort order
* (for example, `+name` or `-name`).
* (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
* element itself is used to compare where sorting.
* - `Array`: An array of function or string predicates. The first predicate in the array
* is used for sorting, but when two items are equivalent, the next predicate is used.
*
* If the predicate is missing or empty then it defaults to `'+'`.
*
* @param {boolean=} reverse Reverse the order of the array.
* @returns {Array} Sorted copy of the source array.
*
@@ -116,8 +119,8 @@ orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
return function(array, sortPredicate, reverseOrder) {
if (!(isArrayLike(array))) return array;
if (!sortPredicate) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
sortPredicate = map(sortPredicate, function(predicate){
var descending = false, get = predicate || identity;
if (isString(predicate)) {
@@ -125,6 +128,12 @@ function orderByFilter($parse){
descending = predicate.charAt(0) == '-';
predicate = predicate.substring(1);
}
if ( predicate === '' ) {
// Effectively no predicate was passed so we compare identity
return reverseComparator(function(a,b) {
return compare(a, b);
}, descending);
}
get = $parse(predicate);
if (get.constant) {
var key = get();
@@ -137,9 +146,7 @@ function orderByFilter($parse){
return compare(get(a),get(b));
}, descending);
});
var arrayCopy = [];
for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
function comparator(o1, o2){
for ( var i = 0; i < sortPredicate.length; i++) {
+57 -19
View File
@@ -144,9 +144,18 @@ function $HttpProvider() {
};
/**
* Are ordered by request, i.e. they are applied in the same order as the
* @ngdoc property
* @name $httpProvider#interceptors
* @description
*
* Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
* pre-processing of request or postprocessing of responses.
*
* These service factories are ordered by request, i.e. they are applied in the same order as the
* array, on request, but reverse order, on response.
*/
*
* {@link ng.$http#interceptors Interceptors detailed info}
**/
var interceptorFactories = this.interceptors = [];
/**
@@ -308,6 +317,21 @@ function $HttpProvider() {
* In addition, you can supply a `headers` property in the config object passed when
* calling `$http(config)`, which overrides the defaults without changing them globally.
*
* To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
* Use the `headers` property, setting the desired header to `undefined`. For example:
*
* ```js
* var req = {
* method: 'POST',
* url: 'http://example.com',
* headers: {
* 'Content-Type': undefined
* },
* data: { test: 'test' },
* }
*
* $http(req).success(function(){...}).error(function(){...});
* ```
*
* # Transforming Requests and Responses
*
@@ -671,12 +695,13 @@ function $HttpProvider() {
expect(data.getText()).toMatch(/Hello, \$http!/);
});
it('should make a JSONP request to angularjs.org', function() {
sampleJsonpBtn.click();
fetchBtn.click();
expect(status.getText()).toMatch('200');
expect(data.getText()).toMatch(/Super Hero!/);
});
// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
// it('should make a JSONP request to angularjs.org', function() {
// sampleJsonpBtn.click();
// fetchBtn.click();
// expect(status.getText()).toMatch('200');
// expect(data.getText()).toMatch(/Super Hero!/);
// });
it('should make JSONP request to invalid URL and invoke the error handler',
function() {
@@ -886,18 +911,31 @@ function $HttpProvider() {
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
createShortMethodsWithData('post', 'put');
/**
* @ngdoc property
* @name $http#defaults
*
* @description
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
* default headers, withCredentials as well as request and response transformations.
*
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
*/
/**
* @ngdoc method
* @name $http#patch
*
* @description
* Shortcut method to perform `PATCH` request.
*
* @param {string} url Relative or absolute URL specifying the destination of the request
* @param {*} data Request content
* @param {Object=} config Optional configuration object
* @returns {HttpPromise} Future object
*/
createShortMethodsWithData('post', 'put', 'patch');
/**
* @ngdoc property
* @name $http#defaults
*
* @description
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
* default headers, withCredentials as well as request and response transformations.
*
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
*/
$http.defaults = defaults;
+24 -24
View File
@@ -57,33 +57,33 @@ function $IntervalProvider() {
* // 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();
* 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;
* }
* }, 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.resetFight = function() {
* $scope.blood_1 = 100;
* $scope.blood_2 = 120;
* };
*
* $scope.$on('$destroy', function() {
* // Make sure that the interval is destroyed too
* $scope.stopFight();
* });
* }])
* $scope.$on('$destroy', function() {
* // Make sure that the interval is destroyed too
* $scope.stopFight();
* });
* }])
* // Register the 'myCurrentTime' directive factory method.
* // We inject $interval and dateFilter service since the factory method is DI.
* .directive('myCurrentTime', ['$interval', 'dateFilter',
+40 -57
View File
@@ -127,21 +127,26 @@ function LocationHtml5Url(appBase, basePrefix) {
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
};
this.$$rewrite = function(url) {
this.$$parseLinkUrl = function(url, relHref) {
var appUrl, prevAppUrl;
var rewrittenUrl;
if ( (appUrl = beginsWith(appBase, url)) !== undefined ) {
prevAppUrl = appUrl;
if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) {
return appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
} else {
return appBase + prevAppUrl;
rewrittenUrl = appBase + prevAppUrl;
}
} else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) {
return appBaseNoFile + appUrl;
rewrittenUrl = appBaseNoFile + appUrl;
} else if (appBaseNoFile == url + '/') {
return appBaseNoFile;
rewrittenUrl = appBaseNoFile;
}
if (rewrittenUrl) {
this.$$parse(rewrittenUrl);
}
return !!rewrittenUrl;
};
}
@@ -231,10 +236,12 @@ function LocationHashbangUrl(appBase, hashPrefix) {
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
};
this.$$rewrite = function(url) {
this.$$parseLinkUrl = function(url, relHref) {
if(stripHash(appBase) == stripHash(url)) {
return url;
this.$$parse(url);
return true;
}
return false;
};
}
@@ -254,16 +261,21 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
var appBaseNoFile = stripFile(appBase);
this.$$rewrite = function(url) {
this.$$parseLinkUrl = function(url, relHref) {
var rewrittenUrl;
var appUrl;
if ( appBase == stripHash(url) ) {
return url;
rewrittenUrl = url;
} else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) {
return appBase + hashPrefix + appUrl;
rewrittenUrl = appBase + hashPrefix + appUrl;
} else if ( appBaseNoFile === url + '/') {
return appBaseNoFile;
rewrittenUrl = appBaseNoFile;
}
if (rewrittenUrl) {
this.$$parse(rewrittenUrl);
}
return !!rewrittenUrl;
};
this.$$compose = function() {
@@ -391,7 +403,7 @@ LocationHashbangInHtml5Url.prototype =
* @return {string} path
*/
path: locationGetterSetter('$$path', function(path) {
path = path ? path.toString() : '';
path = path !== null ? path.toString() : '';
return path.charAt(0) == '/' ? path : '/' + path;
}),
@@ -488,7 +500,7 @@ LocationHashbangInHtml5Url.prototype =
* @return {string} hash
*/
hash: locationGetterSetter('$$hash', function(hash) {
return hash ? hash.toString() : '';
return hash !== null ? hash.toString() : '';
}),
/**
@@ -636,7 +648,7 @@ function $LocationProvider(){
LocationMode = LocationHashbangUrl;
}
$location = new LocationMode(appBase, '#' + hashPrefix);
$location.$$parse($location.$$rewrite(initialUrl));
$location.$$parseLinkUrl(initialUrl, initialUrl);
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
@@ -655,6 +667,9 @@ function $LocationProvider(){
}
var absHref = elm.prop('href');
// get the actual href attribute - see
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
var relHref = elm.attr('href') || elm.attr('xlink:href');
if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
// SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
@@ -665,50 +680,18 @@ function $LocationProvider(){
// Ignore when url is started with javascript: or mailto:
if (IGNORE_URI_REGEXP.test(absHref)) return;
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
// somewhere#anchor or http://example.com/somewhere
if (LocationMode === LocationHashbangInHtml5Url) {
// get the actual href attribute - see
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
var href = elm.attr('href') || elm.attr('xlink:href');
if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
var prefix = '#' + hashPrefix;
if (href[0] == '/') {
// absolute path - replace old path
absHref = appBase + prefix + href;
} else if (href[0] == '#') {
// local anchor
absHref = appBase + prefix + ($location.path() || '/') + href;
} else {
// relative path - join with current path
var stack = $location.path().split("/"),
parts = href.split("/");
if (stack.length === 2 && !stack[1]) stack.length = 1;
for (var i=0; i<parts.length; i++) {
if (parts[i] == ".")
continue;
else if (parts[i] == "..")
stack.pop();
else if (parts[i].length)
stack.push(parts[i]);
}
absHref = appBase + prefix + stack.join('/');
}
}
}
var rewrittenUrl = $location.$$rewrite(absHref);
if (absHref && !elm.attr('target') && rewrittenUrl && !event.isDefaultPrevented()) {
event.preventDefault();
if (rewrittenUrl != $browser.url()) {
if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
if ($location.$$parseLinkUrl(absHref, relHref)) {
// We do a preventDefault for all urls that are part of the angular application,
// in html5mode and also without, so that we are able to abort navigation without
// getting double entries in the location history.
event.preventDefault();
// update location manually
$location.$$parse(rewrittenUrl);
$rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
if ($location.absUrl() != $browser.url()) {
$rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
window.angular['ff-684208-preventDefault'] = true;
}
}
}
});
+89 -42
View File
@@ -7,7 +7,7 @@ var promiseWarning;
// Sandboxing Angular Expressions
// ------------------------------
// Angular expressions are generally considered safe because these expressions only have direct
// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
// obtaining a reference to native JS functions such as the Function constructor.
//
// As an example, consider the following Angular expression:
@@ -16,7 +16,7 @@ var promiseWarning;
//
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
// practice and therefore we are not even trying to protect against interaction with an object
// explicitly exposed in this way.
//
@@ -24,6 +24,8 @@ var promiseWarning;
// window or some DOM object that has a reference to window is published onto a Scope.
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
// native objects.
//
// See https://docs.angularjs.org/guide/security
function ensureSafeMemberName(name, fullExpression) {
@@ -760,7 +762,7 @@ Parser.prototype = {
ensureSafeObject(context, parser.text);
ensureSafeFunction(fnPtr, parser.text);
// IE stupidity! (IE doesn't have apply for some native functions)
// IE doesn't have apply for some native functions
var v = fnPtr.apply
? fnPtr.apply(context, args)
: fnPtr(args[0], args[1], args[2], args[3], args[4]);
@@ -874,7 +876,12 @@ function setter(obj, path, setValue, fullExp, options) {
return setValue;
}
var getterFnCache = {};
var getterFnCacheDefault = {};
var getterFnCacheExpensive = {};
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
/**
* Implementation of the "Black Hole" variant from:
@@ -887,29 +894,38 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
ensureSafeMemberName(key2, fullExp);
ensureSafeMemberName(key3, fullExp);
ensureSafeMemberName(key4, fullExp);
var eso = function(o) {
return ensureSafeObject(o, fullExp);
};
var expensiveChecks = options.expensiveChecks;
var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
return !options.unwrapPromises
? function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
pathVal = eso0(pathVal[key0]);
if (!key1) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key1];
pathVal = eso1(pathVal[key1]);
if (!key2) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key2];
pathVal = eso2(pathVal[key2]);
if (!key3) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key3];
pathVal = eso3(pathVal[key3]);
if (!key4) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key4];
pathVal = eso4(pathVal[key4]);
return pathVal;
}
@@ -919,73 +935,81 @@ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) {
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
pathVal = eso0(pathVal[key0]);
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
promise.then(function(val) { promise.$$v = eso0(val); });
}
pathVal = pathVal.$$v;
pathVal = eso0(pathVal.$$v);
}
if (!key1) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key1];
pathVal = eso1(pathVal[key1]);
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
promise.then(function(val) { promise.$$v = eso1(val); });
}
pathVal = pathVal.$$v;
pathVal = eso1(pathVal.$$v);
}
if (!key2) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key2];
pathVal = eso2(pathVal[key2]);
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
promise.then(function(val) { promise.$$v = eso2(val); });
}
pathVal = pathVal.$$v;
pathVal = eso2(pathVal.$$v);
}
if (!key3) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key3];
pathVal = eso3(pathVal[key3]);
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
promise.then(function(val) { promise.$$v = eso3(val); });
}
pathVal = pathVal.$$v;
pathVal = eso3(pathVal.$$v);
}
if (!key4) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key4];
pathVal = eso4(pathVal[key4]);
if (pathVal && pathVal.then) {
promiseWarning(fullExp);
if (!("$$v" in pathVal)) {
promise = pathVal;
promise.$$v = undefined;
promise.then(function(val) { promise.$$v = val; });
promise.then(function(val) { promise.$$v = eso4(val); });
}
pathVal = pathVal.$$v;
pathVal = eso4(pathVal.$$v);
}
return pathVal;
};
}
function getterFnWithExtraArgs(fn, fullExpression) {
return function(s, l) {
return fn(s, l, promiseWarning, ensureSafeObject, fullExpression);
};
}
function getterFn(path, options, fullExp) {
var expensiveChecks = options.expensiveChecks;
var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
// Check whether the cache has this getter already.
// We can use hasOwnProperty directly on the cache because we ensure,
// see below, that the cache never stores a path called 'hasOwnProperty'
@@ -1017,35 +1041,48 @@ function getterFn(path, options, fullExp) {
}
} else {
var code = 'var p;\n';
if (expensiveChecks) {
code += 's = eso(s, fe);\nl = eso(l, fe);\n';
}
var needsEnsureSafeObject = expensiveChecks;
forEach(pathKeys, function(key, index) {
ensureSafeMemberName(key, fullExp);
code += 'if(s == null) return undefined;\n' +
's='+ (index
var lookupJs = (index
// we simply dereference 's' on any .dot notation
? 's'
// but if we are first then we check locals first, and if so read it first
: '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' +
(options.unwrapPromises
? 'if (s && s.then) {\n' +
: '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '["' + key + '"]';
var wrapWithEso = expensiveChecks || isPossiblyDangerousMemberName(key);
if (wrapWithEso) {
lookupJs = 'eso(' + lookupJs + ', fe)';
needsEnsureSafeObject = true;
}
code += 'if(s == null) return undefined;\n' +
's=' + lookupJs + ';\n';
if (options.unwrapPromises) {
code += 'if (s && s.then) {\n' +
' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' +
' if (!("$$v" in s)) {\n' +
' p=s;\n' +
' p.$$v = undefined;\n' +
' p.then(function(v) {p.$$v=v;});\n' +
' p.then(function(v) {p.$$v=' + (wrapWithEso ? 'eso(v)' : 'v') + ';});\n' +
'}\n' +
' s=s.$$v\n' +
'}\n'
: '');
' s=' + (wrapWithEso ? 'eso(s.$$v)' : 's.$$v') + '\n' +
'}\n';
}
});
code += 'return s;';
/* jshint -W054 */
var evaledFnGetter = new Function('s', 'k', 'pw', code); // s=scope, k=locals, pw=promiseWarning
// s=scope, l=locals, pw=promiseWarning, eso=ensureSafeObject, fe=fullExpression
var evaledFnGetter = new Function('s', 'l', 'pw', 'eso', 'fe', code);
/* jshint +W054 */
evaledFnGetter.toString = valueFn(code);
fn = options.unwrapPromises ? function(scope, locals) {
return evaledFnGetter(scope, locals, promiseWarning);
} : evaledFnGetter;
if (needsEnsureSafeObject || options.unwrapPromises) {
evaledFnGetter = getterFnWithExtraArgs(evaledFnGetter, fullExp);
}
fn = evaledFnGetter;
}
// Only cache the value if it's not going to mess up the cache object
@@ -1109,12 +1146,14 @@ function getterFn(path, options, fullExp) {
* service.
*/
function $ParseProvider() {
var cache = {};
var cacheDefault = {};
var cacheExpensive = {};
var $parseOptions = {
csp: false,
unwrapPromises: false,
logPromiseWarnings: true
logPromiseWarnings: true,
expensiveChecks: false
};
@@ -1201,6 +1240,12 @@ function $ParseProvider() {
this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
$parseOptions.csp = $sniffer.csp;
var $parseOptionsExpensive = {
csp: $parseOptions.csp,
unwrapPromises: $parseOptions.unwrapPromises,
logPromiseWarnings: $parseOptions.logPromiseWarnings,
expensiveChecks: true
};
promiseWarning = function promiseWarningFn(fullExp) {
if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
@@ -1209,18 +1254,20 @@ function $ParseProvider() {
'Automatic unwrapping of promises in Angular expressions is deprecated.');
};
return function(exp) {
return function(exp, expensiveChecks) {
var parsedExpression;
switch (typeof exp) {
case 'string':
var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
if (cache.hasOwnProperty(exp)) {
return cache[exp];
}
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
var lexer = new Lexer(parseOptions);
var parser = new Parser(lexer, $filter, parseOptions);
parsedExpression = parser.parse(exp);
if (exp !== 'hasOwnProperty') {
+9 -1
View File
@@ -6,7 +6,11 @@
* @requires $rootScope
*
* @description
* A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
* A service that helps you run functions asynchronously, and use their return values (or exceptions)
* when they are done processing.
*
* This is an implementation of promises/deferred objects inspired by
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
* interface for interacting with an object that represents the result of an action that is
@@ -100,6 +104,10 @@
*
* - `catch(errorCallback)` shorthand for `promise.then(null, errorCallback)`
*
* Because `catch` is a reserved word in JavaScript and reserved keywords are not supported as
* property names by ES3, you'll need to invoke the method like `promise['catch'](callback)` or
* `promise.then(null, errorCallback)` to make your code IE8 and Android 2.x compatible.
*
* - `finally(callback)` allows you to observe either the fulfillment or rejection of a promise,
* but to do so without modifying the final value. This is useful to release resources or do some
* clean-up that needs to be done whether the promise was rejected or resolved. See the [full
+5 -2
View File
@@ -979,8 +979,11 @@ function $RootScopeProvider(){
var self = this;
return function() {
namedListeners[indexOf(namedListeners, listener)] = null;
decrementListenerCount(self, 1, name);
var indexOfListener = indexOf(namedListeners, listener);
if (indexOfListener !== -1) {
namedListeners[indexOfListener] = null;
decrementListenerCount(self, 1, name);
}
};
},
+14 -1
View File
@@ -1156,6 +1156,16 @@ angular.module('ngAnimate', ['ng'])
var parentCounter = 0;
var animationReflowQueue = [];
var cancelAnimationReflow;
function clearCacheAfterReflow() {
if (!cancelAnimationReflow) {
cancelAnimationReflow = $$animateReflow(function() {
animationReflowQueue = [];
cancelAnimationReflow = null;
lookupCache = {};
});
}
}
function afterReflow(element, callback) {
if(cancelAnimationReflow) {
cancelAnimationReflow();
@@ -1524,7 +1534,8 @@ angular.module('ngAnimate', ['ng'])
//cancellation function then it means that there is no animation
//to perform at all
var preReflowCancellation = animateBefore(animationEvent, element, className);
if(!preReflowCancellation) {
if (!preReflowCancellation) {
clearCacheAfterReflow();
animationComplete();
return;
}
@@ -1599,6 +1610,7 @@ angular.module('ngAnimate', ['ng'])
});
return cancellationMethod;
}
clearCacheAfterReflow();
animationCompleted();
},
@@ -1623,6 +1635,7 @@ angular.module('ngAnimate', ['ng'])
});
return cancellationMethod;
}
clearCacheAfterReflow();
animationCompleted();
},
+33 -17
View File
@@ -1186,7 +1186,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* object and returns true if the headers match the current definition.
* @returns {requestHandler} Returns an object with `respond` method that controls how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*
* - respond
@@ -1222,7 +1222,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
*
* @param {string|RegExp} url HTTP url.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1234,7 +1234,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
*
* @param {string|RegExp} url HTTP url.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1246,7 +1246,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
*
* @param {string|RegExp} url HTTP url.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1260,7 +1260,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1274,7 +1274,21 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
/**
* @ngdoc method
* @name $httpBackend#whenPATCH
* @description
* Creates a new backend definition for PATCH requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @param {(string|RegExp|function(string))=} data HTTP request body or function that receives
* data string and returns true if the data is as expected.
* @param {(Object|function(Object))=} headers HTTP headers.
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1285,7 +1299,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* Creates a new backend definition for JSONP requests. For more info see `when()`.
*
* @param {string|RegExp} url HTTP url.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
createShortMethods('when');
@@ -1304,7 +1318,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* is in JSON format.
* @param {(Object|function(Object))=} headers HTTP headers or function that receives http header
* object and returns true if the headers match the current expectation.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*
* - respond
@@ -1333,7 +1347,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
*
* @param {string|RegExp} url HTTP url.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled. See #expect for more info.
*/
@@ -1345,7 +1359,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
*
* @param {string|RegExp} url HTTP url.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1357,7 +1371,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
*
* @param {string|RegExp} url HTTP url.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1372,7 +1386,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1387,7 +1401,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1402,7 +1416,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* receives data string and returns true if the data is as expected, or Object if request body
* is in JSON format.
* @param {Object=} headers HTTP headers.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
@@ -1413,7 +1427,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
* Creates a new request expectation for JSONP requests. For more info see `expect()`.
*
* @param {string|RegExp} url HTTP url.
* @returns {requestHandler} Returns an object with `respond` method that control how a matched
* @returns {requestHandler} Returns an object with a `respond` method that controls how a matched
* request is handled.
*/
createShortMethods('expect');
@@ -1506,7 +1520,7 @@ function createHttpBackendMock($rootScope, $delegate, $browser) {
function createShortMethods(prefix) {
angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) {
angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) {
$httpBackend[prefix + method] = function(url, headers) {
return $httpBackend[prefix](method, url, undefined, headers);
};
@@ -1549,7 +1563,9 @@ function MockHttpExpectation(method, url, data, headers) {
if (angular.isUndefined(data)) return true;
if (data && angular.isFunction(data.test)) return data.test(d);
if (data && angular.isFunction(data)) return data(d);
if (data && !angular.isString(data)) return angular.equals(data, angular.fromJson(d));
if (data && !angular.isString(data)) {
return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d));
}
return data == d;
};
+1 -4
View File
@@ -265,9 +265,6 @@ function $RouteProvider(){
* This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial.
*
* Note that this example is using {@link ng.directive:script inlined templates}
* to get it working on jsfiddle as well.
*
* <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true">
* <file name="index.html">
@@ -580,7 +577,7 @@ function $RouteProvider(){
if (i === 0) {
result.push(segment);
} else {
var segmentMatch = segment.match(/(\w+)(.*)/);
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
var key = segmentMatch[1];
result.push(params[key]);
result.push(segmentMatch[2] || '');
+3 -3
View File
@@ -141,9 +141,9 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
html.push(target);
html.push('" ');
}
html.push('href="');
html.push(url);
html.push('">');
html.push('href="',
url.replace('"', '&quot;'),
'">');
addText(text);
html.push('</a>');
}
+2 -1
View File
@@ -162,6 +162,7 @@
"provideLog": false,
"spyOnlyCallsWithArgs": false,
"createMockStyleSheet": false,
"browserTrigger": false
"browserTrigger": false,
"jqLiteCacheSize": false
}
}
+9
View File
@@ -118,6 +118,15 @@ function dealoc(obj) {
}
}
function jqLiteCacheSize() {
var size = 0;
forEach(jqLite.cache, function() { size++; });
return size - jqLiteCacheSize.initSize;
}
jqLiteCacheSize.initSize = 0;
/**
* @param {DOMElement} element
* @param {boolean=} showNgClass
+216 -18
View File
@@ -440,6 +440,17 @@ describe('browser', function() {
expect(locationReplace).not.toHaveBeenCalled();
});
it('should set location.href and not use pushState when the url only changed in the hash fragment to please IE10/11', function() {
sniffer.history = true;
browser.url('http://server/#123');
expect(fakeWindow.location.href).toEqual('http://server/#123');
expect(pushState).not.toHaveBeenCalled();
expect(replaceState).not.toHaveBeenCalled();
expect(locationReplace).not.toHaveBeenCalled();
});
it('should use location.replace when history.replaceState not available', function() {
sniffer.history = false;
browser.url('http://new.org', true);
@@ -451,6 +462,17 @@ describe('browser', function() {
expect(fakeWindow.location.href).toEqual('http://server/');
});
it('should use location.replace and not use replaceState when the url only changed in the hash fragment to please IE10/11', function() {
sniffer.history = true;
browser.url('http://server/#123', true);
expect(locationReplace).toHaveBeenCalledWith('http://server/#123');
expect(pushState).not.toHaveBeenCalled();
expect(replaceState).not.toHaveBeenCalled();
expect(fakeWindow.location.href).toEqual('http://server/');
});
it('should return $browser to allow chaining', function() {
expect(browser.url('http://any.com')).toBe(browser);
});
@@ -468,6 +490,55 @@ describe('browser', function() {
browser.url(current);
expect(fakeWindow.location.href).toBe('dontchange');
});
it('should not read out location.href if a reload was triggered but still allow to change the url', function() {
sniffer.history = false;
browser.url('http://server/someOtherUrlThatCausesReload');
expect(fakeWindow.location.href).toBe('http://server/someOtherUrlThatCausesReload');
fakeWindow.location.href = 'http://someNewUrl';
expect(browser.url()).toBe('http://server/someOtherUrlThatCausesReload');
browser.url('http://server/someOtherUrl');
expect(browser.url()).toBe('http://server/someOtherUrl');
expect(fakeWindow.location.href).toBe('http://server/someOtherUrl');
});
it('assumes that changes to location.hash occur in sync', function() {
// This is an asynchronous integration test that changes the
// hash in all possible ways and checks
// - whether the change to the hash can be read out in sync
// - whether the change to the hash can be read out in the hashchange event
var realWin = window,
$realWin = jqLite(realWin),
hashInHashChangeEvent = [];
runs(function() {
$realWin.on('hashchange', hashListener);
realWin.location.hash = '1';
realWin.location.href += '2';
realWin.location.replace(realWin.location.href + '3');
realWin.location.assign(realWin.location.href + '4');
expect(realWin.location.hash).toBe('#1234');
});
waitsFor(function() {
return hashInHashChangeEvent.length > 3;
});
runs(function() {
$realWin.off('hashchange', hashListener);
forEach(hashInHashChangeEvent, function(hash) {
expect(hash).toBe('#1234');
});
});
function hashListener() {
hashInHashChangeEvent.push(realWin.location.hash);
}
});
});
describe('urlChange', function() {
@@ -546,15 +617,15 @@ describe('browser', function() {
beforeEach(function() {
sniffer.history = false;
sniffer.hashchange = false;
browser.url("http://server.current");
browser.url("http://server/#current");
});
it('should fire callback with the correct URL on location change outside of angular', function() {
browser.onUrlChange(callback);
fakeWindow.location.href = 'http://server.new';
fakeWindow.location.href = 'http://server/#new';
fakeWindow.setTimeout.flush();
expect(callback).toHaveBeenCalledWith('http://server.new');
expect(callback).toHaveBeenCalledWith('http://server/#new');
fakeWindow.fire('popstate');
fakeWindow.fire('hashchange');
@@ -618,29 +689,156 @@ describe('browser', function() {
describe('integration tests with $location', function() {
beforeEach(module(function($provide, $locationProvider) {
spyOn(fakeWindow.history, 'pushState').andCallFake(function(stateObj, title, newUrl) {
fakeWindow.location.href = newUrl;
function setup(options) {
module(function($provide, $locationProvider) {
spyOn(fakeWindow.history, 'pushState').andCallFake(function(stateObj, title, newUrl) {
fakeWindow.location.href = newUrl;
});
spyOn(fakeWindow.location, 'replace').andCallFake(function(newUrl) {
fakeWindow.location.href = newUrl;
});
$provide.value('$browser', browser);
browser.pollFns = [];
sniffer.history = options.history;
$provide.value('$sniffer', sniffer);
$locationProvider.html5Mode(options.html5Mode);
});
$provide.value('$browser', browser);
browser.pollFns = [];
}
$locationProvider.html5Mode(true);
}));
it('should update $location when it was changed outside of Angular in sync '+
describe('update $location when it was changed outside of Angular in sync '+
'before $digest was called', function() {
inject(function($rootScope, $location) {
fakeWindow.history.pushState(null, '', 'http://server/someTestHash');
// Verify that infinite digest reported in #6976 no longer occurs
expect(function() {
it('should work with no history support, no html5Mode', function() {
setup({
history: false,
html5Mode: false
});
inject(function($rootScope, $location) {
$rootScope.$apply(function() {
$location.path('/initialPath');
});
expect(fakeWindow.location.href).toBe('http://server/#/initialPath');
fakeWindow.location.href = 'http://server/#/someTestHash';
$rootScope.$digest();
}).not.toThrow();
expect($location.path()).toBe('/someTestHash');
expect($location.path()).toBe('/someTestHash');
});
});
it('should work with history support, no html5Mode', function() {
setup({
history: true,
html5Mode: false
});
inject(function($rootScope, $location) {
$rootScope.$apply(function() {
$location.path('/initialPath');
});
expect(fakeWindow.location.href).toBe('http://server/#/initialPath');
fakeWindow.location.href = 'http://server/#/someTestHash';
$rootScope.$digest();
expect($location.path()).toBe('/someTestHash');
});
});
it('should work with no history support, with html5Mode', function() {
setup({
history: false,
html5Mode: true
});
inject(function($rootScope, $location) {
$rootScope.$apply(function() {
$location.path('/initialPath');
});
expect(fakeWindow.location.href).toBe('http://server/#/initialPath');
fakeWindow.location.href = 'http://server/#/someTestHash';
$rootScope.$digest();
expect($location.path()).toBe('/someTestHash');
});
});
it('should work with history support, with html5Mode', function() {
setup({
history: true,
html5Mode: true
});
inject(function($rootScope, $location) {
$rootScope.$apply(function() {
$location.path('/initialPath');
});
expect(fakeWindow.location.href).toBe('http://server/initialPath');
fakeWindow.location.href = 'http://server/someTestHash';
$rootScope.$digest();
expect($location.path()).toBe('/someTestHash');
});
});
});
it('should not reload the page on every $digest when the page will be reloaded due to url rewrite on load', function() {
setup({
history: false,
html5Mode: true
});
fakeWindow.location.href = 'http://server/some/deep/path';
var changeUrlCount = 0;
var _url = browser.url;
browser.url = function(newUrl, replace) {
if (newUrl) {
changeUrlCount++;
}
return _url.call(this, newUrl, replace);
};
spyOn(browser, 'url').andCallThrough();
inject(function($rootScope, $location) {
$rootScope.$digest();
$rootScope.$digest();
$rootScope.$digest();
$rootScope.$digest();
// from $location for rewriting the initial url into a hash url
expect(browser.url).toHaveBeenCalledWith('http://server/#/some/deep/path', true);
// from the initial call to the watch in $location for watching $location
expect(browser.url).toHaveBeenCalledWith('http://server/#/some/deep/path', false);
expect(changeUrlCount).toBe(2);
});
});
});
describe('integration test with $rootScope', function() {
beforeEach(module(function($provide, $locationProvider) {
$provide.value('$browser', browser);
browser.pollFns = [];
}));
it('should not interfere with legacy browser url replace behavior', function() {
inject(function($rootScope) {
var current = fakeWindow.location.href;
var newUrl = 'notyet';
sniffer.history = false;
browser.url(newUrl, true);
expect(browser.url()).toBe(newUrl);
$rootScope.$digest();
expect(browser.url()).toBe(newUrl);
expect(fakeWindow.location.href).toBe(current);
});
});
});
});
+19
View File
@@ -82,6 +82,25 @@ describe('event directives', function() {
});
describe('security', function() {
it('should allow access to the $event object', inject(function($rootScope, $compile) {
var scope = $rootScope.$new();
element = $compile('<button ng-click="e = $event">BTN</button>')(scope);
element.triggerHandler('click');
expect(scope.e.target).toBe(element[0]);
}));
it('should block access to DOM nodes (e.g. exposed via $event)', inject(function($rootScope, $compile) {
var scope = $rootScope.$new();
element = $compile('<button ng-click="e = $event.target">BTN</button>')(scope);
expect(function() {
element.triggerHandler('click');
}).toThrowMinErr(
'$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is disallowed! ' +
'Expression: e = $event.target');
}));
});
describe('blur', function() {
describe('call the listener asynchronously during $apply', function() {
+127 -24
View File
@@ -38,8 +38,28 @@ describe('select', function() {
};
return equals(expectedValues, actualValues);
},
toEqualOption: function(value, text, label) {
var errors = [];
if (this.actual.attr('value') !== value) {
errors.push('Expected option value "' + this.actual.attr('value') + '" to equal "' + value + '"');
}
if (text && this.actual.text() !== text) {
errors.push('Expected option value "' + this.actual.attr('value') + '" to equal "' + value + '"');
}
if (label && this.actual.attr('label') !== label) {
errors.push('Expected option value "' + this.actual.attr('value') + '" to equal "' + value + '"');
}
this.message = function() {
return errors.join('\n');
};
return errors.length === 0;
}
});
});
@@ -378,6 +398,39 @@ describe('select', function() {
expect(element).toEqualSelect(['? string:r2d2 ?']);
expect(scope.robot).toBe('r2d2');
});
describe('selectController.hasOption', function() {
it('should return true for options added via ngOptions', function() {
scope.robots = [
{key: 1, value: 'c3p0'},
{key: 2, value: 'r2d2'}
];
scope.robot = 'r2d2';
compile('<select ng-model="robot" ' +
'ng-options="item.key as item.value for item in robots">' +
'</select>');
var selectCtrl = element.data().$selectController;
expect(selectCtrl.hasOption('c3p0')).toBe(true);
expect(selectCtrl.hasOption('r2d2')).toBe(true);
scope.$apply(function() {
scope.robots.pop();
});
expect(selectCtrl.hasOption('c3p0')).toBe(true);
expect(selectCtrl.hasOption('r2d2')).toBe(false);
scope.$apply(function() {
scope.robots.push({key: 2, value: 'r2d2'});
});
expect(selectCtrl.hasOption('c3p0')).toBe(true);
expect(selectCtrl.hasOption('r2d2')).toBe(true);
});
});
});
});
});
@@ -454,6 +507,39 @@ describe('select', function() {
expect(element).toBeValid();
expect(element).toBeDirty();
});
describe('selectController.hasOption', function() {
it('should return true for options added via ngOptions', function() {
scope.robots = [
{key: 1, value: 'c3p0'},
{key: 2, value: 'r2d2'}
];
scope.robot = 'r2d2';
compile('<select ng-model="robot" multiple ' +
'ng-options="item.key as item.value for item in robots">' +
'</select>');
var selectCtrl = element.data().$selectController;
expect(selectCtrl.hasOption('c3p0')).toBe(true);
expect(selectCtrl.hasOption('r2d2')).toBe(true);
scope.$apply(function() {
scope.robots.pop();
});
expect(selectCtrl.hasOption('c3p0')).toBe(true);
expect(selectCtrl.hasOption('r2d2')).toBe(false);
scope.$apply(function() {
scope.robots.push({key: 2, value: 'r2d2'});
});
expect(selectCtrl.hasOption('c3p0')).toBe(true);
expect(selectCtrl.hasOption('r2d2')).toBe(true);
});
});
});
@@ -508,9 +594,9 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(3);
expect(sortedHtml(options[0])).toEqual('<option value="0">A</option>');
expect(sortedHtml(options[1])).toEqual('<option value="1">B</option>');
expect(sortedHtml(options[2])).toEqual('<option value="2">C</option>');
expect(options.eq(0)).toEqualOption('0', 'A');
expect(options.eq(1)).toEqualOption('1', 'B');
expect(options.eq(2)).toEqualOption('2', 'C');
});
it('should render zero as a valid display value', function() {
@@ -523,9 +609,9 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(3);
expect(sortedHtml(options[0])).toEqual('<option value="0">0</option>');
expect(sortedHtml(options[1])).toEqual('<option value="1">1</option>');
expect(sortedHtml(options[2])).toEqual('<option value="2">2</option>');
expect(options.eq(0)).toEqualOption('0', '0');
expect(options.eq(1)).toEqualOption('1', '1');
expect(options.eq(2)).toEqualOption('2', '2');
});
@@ -542,9 +628,9 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(3);
expect(sortedHtml(options[0])).toEqual('<option value="blue">blue</option>');
expect(sortedHtml(options[1])).toEqual('<option value="green">green</option>');
expect(sortedHtml(options[2])).toEqual('<option value="red">red</option>');
expect(options.eq(0)).toEqualOption('blue', 'blue');
expect(options.eq(1)).toEqualOption('green', 'green');
expect(options.eq(2)).toEqualOption('red', 'red');
expect(options[2].selected).toEqual(true);
scope.$apply(function() {
@@ -564,7 +650,7 @@ describe('select', function() {
});
expect(element.find('option').length).toEqual(1); // because we add special empty option
expect(sortedHtml(element.find('option')[0])).toEqual('<option value="?"></option>');
expect(element.find('option')).toEqualOption('?','');
scope.$apply(function() {
scope.values.push({name:'A'});
@@ -572,15 +658,15 @@ describe('select', function() {
});
expect(element.find('option').length).toEqual(1);
expect(sortedHtml(element.find('option')[0])).toEqual('<option value="0">A</option>');
expect(element.find('option')).toEqualOption('0', 'A');
scope.$apply(function() {
scope.values.push({name:'B'});
});
expect(element.find('option').length).toEqual(2);
expect(sortedHtml(element.find('option')[0])).toEqual('<option value="0">A</option>');
expect(sortedHtml(element.find('option')[1])).toEqual('<option value="1">B</option>');
expect(element.find('option').eq(0)).toEqualOption('0', 'A');
expect(element.find('option').eq(1)).toEqualOption('1', 'B');
});
@@ -599,15 +685,15 @@ describe('select', function() {
});
expect(element.find('option').length).toEqual(2);
expect(sortedHtml(element.find('option')[0])).toEqual('<option value="0">A</option>');
expect(sortedHtml(element.find('option')[1])).toEqual('<option value="1">B</option>');
expect(element.find('option').eq(0)).toEqualOption('0', 'A');
expect(element.find('option').eq(1)).toEqualOption('1', 'B');
scope.$apply(function() {
scope.values.pop();
});
expect(element.find('option').length).toEqual(1);
expect(sortedHtml(element.find('option')[0])).toEqual('<option value="0">A</option>');
expect(element.find('option')).toEqualOption('0', 'A');
scope.$apply(function() {
scope.values.pop();
@@ -659,9 +745,9 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(3);
expect(sortedHtml(options[0])).toEqual('<option value="0">B</option>');
expect(sortedHtml(options[1])).toEqual('<option value="1">C</option>');
expect(sortedHtml(options[2])).toEqual('<option value="2">D</option>');
expect(options.eq(0)).toEqualOption('0', 'B');
expect(options.eq(1)).toEqualOption('1', 'C');
expect(options.eq(2)).toEqualOption('2', 'D');
});
@@ -705,7 +791,7 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(1);
expect(sortedHtml(options[0])).toEqual('<option value="regularProperty">visible</option>');
expect(options.eq(0)).toEqualOption('regularProperty', 'visible');
});
it('should allow expressions over multiple lines', function() {
@@ -729,8 +815,8 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(3);
expect(sortedHtml(options[1])).toEqual('<option value="0">2</option>');
expect(sortedHtml(options[2])).toEqual('<option value="1">3</option>');
expect(options.eq(1)).toEqualOption('0', '2');
expect(options.eq(2)).toEqualOption('1', '3');
});
it('should not update selected property of an option element on digest with no change event',
@@ -761,6 +847,23 @@ describe('select', function() {
expect(scope.selected).toBe(scope.values[0]);
});
// bug fix #9621
it('should update the label property', function() {
// ng-options="value.name for value in values"
// ng-model="selected"
createSingleSelect();
scope.$apply(function() {
scope.values = [{name: 'A'}, {name: 'B'}, {name: 'C'}];
scope.selected = scope.values[0];
});
var options = element.find('option');
expect(options.eq(0).prop('label')).toEqual('A');
expect(options.eq(1).prop('label')).toEqual('B');
expect(options.eq(2).prop('label')).toEqual('C');
});
describe('binding', function() {
it('should bind to scope value', function() {
@@ -887,8 +990,8 @@ describe('select', function() {
var options = element.find('option');
expect(options.length).toEqual(2);
expect(sortedHtml(options[0])).toEqual('<option value="0">C</option>');
expect(sortedHtml(options[1])).toEqual('<option value="1">B</option>');
expect(options.eq(0)).toEqualOption('0', 'C');
expect(options.eq(1)).toEqualOption('1', 'B');
});
+191 -73
View File
@@ -1,92 +1,210 @@
'use strict';
describe('Filter: orderBy', function() {
var orderBy;
var orderBy, orderByFilter;
beforeEach(inject(function($filter) {
orderBy = $filter('orderBy');
orderBy = orderByFilter = $filter('orderBy');
}));
it('should return same array if predicate is falsy', function() {
var array = [1, 2, 3];
expect(orderBy(array)).toBe(array);
});
it('shouldSortArrayInReverse', function() {
expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
});
describe('(Arrays)', function() {
it('should return sorted array if predicate is not provided', function() {
expect(orderBy([2, 1, 3])).toEqual([1, 2, 3]);
it('should sort inherited from array', function(){
function BaseCollection(){}
BaseCollection.prototype = Array.prototype;
var child = new BaseCollection();
child.push({a:2});
child.push({a:15});
expect(orderBy([2, 1, 3], '')).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], [])).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], [''])).toEqual([1, 2, 3]);
expect(orderBy(child, 'a', true)).toEqualData([{a:15}, {a:2}]);
});
expect(orderBy([2, 1, 3], '+')).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], ['+'])).toEqual([1, 2, 3]);
it('should sort array by predicate', function() {
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['a', 'b'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['b', 'a'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['+b', '-a'])).toEqualData([{a:15, b:1}, {a:2, b:1}]);
expect(orderBy([2, 1, 3], '-')).toEqual([3, 2, 1]);
expect(orderBy([2, 1, 3], ['-'])).toEqual([3, 2, 1]);
});
it('shouldSortArrayInReverse', function() {
expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
});
it('should sort inherited from array', function() {
function BaseCollection(){}
BaseCollection.prototype = Array.prototype;
var child = new BaseCollection();
child.push({a:2});
child.push({a:15});
expect(orderBy(child, 'a', true)).toEqualData([{a:15}, {a:2}]);
});
it('should sort array by predicate', function() {
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['a', 'b'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['b', 'a'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['+b', '-a'])).toEqualData([{a:15, b:1}, {a:2, b:1}]);
});
it('should sort array by date predicate', function() {
// same dates
expect(orderBy([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2014'), b:4 },
{ a:new Date('01/01/2014'), b:2 }],
['a', 'b']))
.toEqualData([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:2 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2014'), b:4 }]);
// one different date
expect(orderBy([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2013'), b:4 },
{ a:new Date('01/01/2014'), b:2 }],
['a', 'b']))
.toEqualData([
{ a:new Date('01/01/2013'), b:4 },
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:2 },
{ a:new Date('01/01/2014'), b:3 }]);
});
it('should use function', function() {
expect(
orderBy(
[{a:15, b:1},{a:2, b:1}],
function(value) { return value.a; })).
toEqual([{a:2, b:1},{a:15, b:1}]);
});
it('should support string predicates with names containing non-identifier characters', function() {
/*jshint -W008 */
expect(orderBy([{"Tip %": .25}, {"Tip %": .15}, {"Tip %": .40}], '"Tip %"'))
.toEqualData([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}]);
expect(orderBy([{"원": 76000}, {"원": 31000}, {"원": 156000}], '"원"'))
.toEqualData([{"원": 31000}, {"원": 76000}, {"원": 156000}]);
});
it('should throw if quoted string predicate is quoted incorrectly', function() {
/*jshint -W008 */
expect(function() {
return orderBy([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}], '"Tip %\'');
}).toThrow();
});
});
it('should sort array by date predicate', function() {
// same dates
expect(orderBy([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2014'), b:4 },
{ a:new Date('01/01/2014'), b:2 }
],
['a', 'b']))
.toEqualData([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:2 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2014'), b:4 }
]);
// one different date
expect(orderBy([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2013'), b:4 },
{ a:new Date('01/01/2014'), b:2 }
],
['a', 'b']))
.toEqualData([
{ a:new Date('01/01/2013'), b:4 },
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:2 },
{ a:new Date('01/01/2014'), b:3 }
]);
});
describe('(Array-Like Objects)', function() {
function arrayLike(args) {
var result = {};
var i;
for (i = 0; i < args.length; ++i) {
result[i] = args[i];
}
result.length = i;
return result;
}
it('should use function', function() {
expect(
orderBy(
[{a:15, b:1},{a:2, b:1}],
function(value) { return value.a; })).
toEqual([{a:2, b:1},{a:15, b:1}]);
});
beforeEach(inject(function($filter) {
orderBy = function(collection) {
var args = Array.prototype.slice.call(arguments, 0);
args[0] = arrayLike(args[0]);
return orderByFilter.apply(null, args);
};
}));
it('should support string predicates with names containing non-identifier characters', function() {
/* jshint -W008 */
expect(orderBy([{"Tip %": .25}, {"Tip %": .15}, {"Tip %": .40}], '"Tip %"'))
.toEqualData([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}]);
expect(orderBy([{"원": 76000}, {"원": 31000}, {"원": 156000}], '"원"'))
.toEqualData([{"원": 31000}, {"원": 76000}, {"원": 156000}]);
});
it('should throw if quoted string predicate is quoted incorrectly', function() {
/* jshint -W008 */
expect(function() {
return orderBy([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}], '"Tip %\'');
}).toThrow();
it('should return sorted array if predicate is not provided', function() {
expect(orderBy([2, 1, 3])).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], '')).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], [])).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], [''])).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], '+')).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], ['+'])).toEqual([1, 2, 3]);
expect(orderBy([2, 1, 3], '-')).toEqual([3, 2, 1]);
expect(orderBy([2, 1, 3], ['-'])).toEqual([3, 2, 1]);
});
it('shouldSortArrayInReverse', function() {
expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
});
it('should sort array by predicate', function() {
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['a', 'b'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['b', 'a'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['+b', '-a'])).toEqualData([{a:15, b:1}, {a:2, b:1}]);
});
it('should sort array by date predicate', function() {
// same dates
expect(orderBy([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2014'), b:4 },
{ a:new Date('01/01/2014'), b:2 }],
['a', 'b']))
.toEqualData([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:2 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2014'), b:4 }]);
// one different date
expect(orderBy([
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:3 },
{ a:new Date('01/01/2013'), b:4 },
{ a:new Date('01/01/2014'), b:2 }],
['a', 'b']))
.toEqualData([
{ a:new Date('01/01/2013'), b:4 },
{ a:new Date('01/01/2014'), b:1 },
{ a:new Date('01/01/2014'), b:2 },
{ a:new Date('01/01/2014'), b:3 }]);
});
it('should use function', function() {
expect(
orderBy(
[{a:15, b:1},{a:2, b:1}],
function(value) { return value.a; })).
toEqual([{a:2, b:1},{a:15, b:1}]);
});
it('should support string predicates with names containing non-identifier characters', function() {
/*jshint -W008 */
expect(orderBy([{"Tip %": .25}, {"Tip %": .15}, {"Tip %": .40}], '"Tip %"'))
.toEqualData([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}]);
expect(orderBy([{"원": 76000}, {"원": 31000}, {"원": 156000}], '"원"'))
.toEqualData([{"원": 31000}, {"원": 76000}, {"원": 156000}]);
});
it('should throw if quoted string predicate is quoted incorrectly', function() {
/*jshint -W008 */
expect(function() {
return orderBy([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}], '"Tip %\'');
}).toThrow();
});
});
});

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