Compare commits

...

264 Commits

Author SHA1 Message Date
Jeff Cross de38899f74 docs(changelog): add release notes for 1.3.0-rc.3 2014-09-23 18:47:24 -07:00
Caitlin Potter 729c238e19 feat(input): support dynamic element validation
Interpolates the form and form control attribute name, so that dynamic form controls (such as those
rendered in an ngRepeat) will always have their expected interpolated name.

The control will be present in its parent form controller with the interpolated property name, and
this name can change when the interpolated value changes.

Closes #4791
Closes #1404
2014-09-23 16:03:53 -04:00
Jeff Cross dc3de7fb7a feat($location): add ability to opt-out of <base/> tag requirement in html5Mode
This feature allows disabling Angular's requirement of using a <base/> tag
when using location in html5Mode, for applications that do not require
using $location in html5Mode in IE9. To accomplish this, the $locationProvider.html5Mode 
method has been changed to accept a definition object which can optionally set a 
requireBase property to false, removing the requirement of a <base> tag being present
when html5Mode is enabled.

BREAKING CHANGE: 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
2014-09-23 11:34:24 -07:00
Peter Bacon Darwin ace40d5526 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 18:58:45 +01:00
Shahar Talmi fd8997551f feat(formController): add $setUntouched to propagate untouched state
Closes #9050
2014-09-23 13:48:39 -04:00
Brian Ford d8c8b2ebb7 chore(bower): add ngAria module to script 2014-09-22 15:27:00 -07:00
Andrew Delikat f5bb34ab4a docs(tutorial/step_05): fix typo 2014-09-22 14:52:20 -07:00
Shahar Talmi 4b83f6ca2c fix(ngModel): support milliseconds in time and datetime
Closes #8874
2014-09-22 14:50:08 -07:00
Rouven Weßling a591e8b8d3 perf(map): use Array.prototype.map
Replace helper functions with the native ES5 method
2014-09-22 14:09:48 -07:00
William Chen 6b05105c08 docs(triaging): fix formatting 2014-09-22 13:14:49 -07:00
Bocharsky Victor 728832ec85 docs(guide/$location): fix broken link 2014-09-22 13:12:27 -07:00
Christopher Rains f1a75a445c docs(tutorial/step_02): fix formatting 2014-09-22 13:07:19 -07:00
James Ferguson c59bee5d21 docs(readme): improve readability 2014-09-22 11:40:30 -07:00
Maarten Stolte 17ecf84b90 docs(ngAria): fix wording 2014-09-22 11:35:10 -07:00
Ariel Mashraki df8d9507aa docs(route): remove irrelevant note
Closes #9196
Closes #9200
2014-09-22 13:27:43 -04:00
krusty 6e7fbe77c9 docs(guide/directive): remove note about default restrict value
The text said a directive wouldn't work out of the box as an element, but the note immediatelly
below says that by default the directives restrict to elements or attributes.

11f5aee made the removed comments invalid.

Closes #9205
2014-09-21 20:59:20 -04:00
Jeff Cross 3686f45398 chore($http): disable flaky JSONP test
See #9185
2014-09-19 17:19:57 -07:00
Brian Ford ad28baaa6c refactor(ngAria): bind to ngModel rather than form types 2014-09-19 15:31:48 -07:00
Peter Bacon Darwin 8f9c4daca5 docs(limitTo): restore the missing * to make comment a jsdoc block 2014-09-19 22:53:48 +01:00
Peter Bacon Darwin f0c94ea292 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:45:14 +01:00
Peter Bacon Darwin 729129b461 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:42:48 +01:00
Peter Bacon Darwin bf2c55ea29 docs($aria): add basic missing docs for the $aria service
The individual service methods should be documented too.

cc: @arbus
2014-09-19 22:40:05 +01:00
Peter Bacon Darwin deafb5e545 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:35:51 +01:00
Ciro Nunes 0702aef7ee docs(guide/unit-testing): clarify the use of underscore notation
Closes #9024
2014-09-19 22:24:48 +01:00
Ciro Nunes f0ee335311 test(injector): allow service names with a single underscore
Closes #9024
2014-09-19 22:24:48 +01:00
Sekib Omazic 02169d4957 docs(angular.extend) actually only copies own enumerable properties
Closes #9007
2014-09-19 21:55:31 +01:00
Zahid Mahir 25d0eff3e6 docs(tutorial/step-3): correct slight grammar issue
Closes #8996
2014-09-19 21:36:08 +01:00
jimmywarting bd41bd594c docs(limitTo): fix input type in examples
Closes #8989
2014-09-19 21:24:04 +01:00
Georgios Kalpakas 373d7c95d9 docs(ngResource): fix error in one of the code examples
Closes #8948
Closes #9069
2014-09-19 19:36:43 +01:00
Sercan Eraslan 4c5c762378 docs(navigation): side navigation footer overlap problem fix
Closes #8923
2014-09-19 19:26:06 +01:00
Subra d1434c999a feat(ngAria): add an ngAria module to make a11y easier
Conditionally adds various aria attributes to the built in directives.
This module currently hooks into ng-show/hide, input, textarea and
button as a basic level of support for a11y.

Closes #5486 and #1600
2014-09-18 16:17:14 -07:00
Luke Schoen 8b8f6f5124 docs(guide/directive): fix grammar 2014-09-18 16:12:00 -07:00
Matt Kim 25082b3439 docs(misc/faq): fix typo 2014-09-18 16:09:56 -07:00
Greg Fedirko 27b3ea4d32 docs(guide/$location): improve readability 2014-09-18 16:08:27 -07:00
DeK ffc32b4e42 docs(guide/migration): fix typo 2014-09-18 16:02:26 -07:00
Luke Schoen 80b0909927 docs(tutorial): improve readability 2014-09-18 16:00:25 -07:00
Christopher Rains efbb365533 docs(tutorial): fix formatting
- proper case "jQuery" vs "JQuery"
- wrap ng-view in markdown code `ng-view`
2014-09-18 15:55:51 -07:00
Brian 38e0ab9bd8 docs(guide/filter): fix label in example 2014-09-18 15:53:56 -07:00
jeffavis 3c53b28cc2 docs(guide/bootstrap): fix missing ngController in example 2014-09-18 15:51:07 -07:00
Rahul Doshi 0ba864184b docs(guide): add angular-localization module to internationalization section
Closes #9158
2014-09-18 17:59:54 -04:00
Sebastian Müller 4f9dc44f88 refactor(ngMessages): remove unused $scope
Closes #9150
2014-09-18 16:04:47 -04:00
Georgios Kalpakas f3884df0a9 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-18 11:43:17 -04:00
Peter Bacon Darwin f7a4a70c28 chore(npm-shrinkwrap): update to dgeni-packages v0.10.0-rc.6
Closes https://github.com/angular/dgeni-packages/pull/70
2014-09-17 19:11:24 +01:00
Jose Martinez 8428a0adef 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 10:56:21 -07:00
Brian Ford 14a92a5982 docs(changelog): release notes for 1.2.25 2014-09-16 14:52:25 -07:00
Brian Ford 798ed3be21 docs(changelog): release notes for 1.3.0-rc.2 2014-09-16 14:31:04 -07:00
Brian Ford 4627410245 fix(select): update option labels when model changes
Closes #9025
2014-09-16 12:36:05 -07:00
Igor Minar 7c6026437a docs(guide/filters): add a note about $stateful flag and stateful filters 2014-09-16 14:12:59 +02:00
Igor Minar 76741a9393 refactor($parse): merge ternary and ternaryFn methods
splitting the code into two chunks doesn't buy us anything and only makes the code harder to follow
2014-09-16 14:12:59 +02:00
Igor Minar 8900390add refactor($parse): name anonymous fns for easier debugging and profiling 2014-09-16 14:12:59 +02:00
Jason Bedard fca6be7127 perf($parse): execute watched expressions only when the inputs change
With this change, expressions like "firstName + ' ' + lastName | uppercase"
will be analyzed and only the inputs for the expression will be watched
(in this case "firstName" and "lastName"). Only when at least one of the inputs
change, the expression will be evaluated.

This change speeds up simple expressions like `firstName | noop` by ~15%
and more complex expressions like `startDate | date` by ~2500%.

BREAKING CHANGE: all filters are assumed to be stateless functions

Previously it was a good practice to make all filters stateless, but now
it's a requirement in order for the model change-observation to pick up
all changes.

If an existing filter is statefull, it can be flagged as such but keep in
mind that this will result in a significant performance-penalty (or rather
lost opportunity to benefit from a major perf improvement) that will
affect the $digest duration.

To flag a filter as stateful do the following:

myApp.filter('myFilter', function() {
  function myFilter(input) { ... };
  myFilter.$stateful = true;
  return myFilter;
});

Closes #9006
Closes #9082
2014-09-16 14:12:58 +02:00
Igor Minar ec9c0d76fe chore(largetable-bp): fix typo in main.html 2014-09-16 14:12:58 +02:00
Stephen Bunch fd2d6c02f9 feat(ngInclude): add template url parameter to events
The 'src` (i.e. the url of the template to load) is now provided to the
`$includeContentRequested`, `$includeContentLoaded` and `$includeContentError`
events.

Closes #8453
Closes #8454
2014-09-16 12:25:33 +01:00
Georgii cfdd16157e docs(ngModelOptions): mention that it's inherited
Closes #9096
Closes #7212
2014-09-15 20:27:31 +01:00
Peter Bacon Darwin 06280a14b4 docs(ngView): remove obsolete comment from code sample
Closes #9086
2014-09-15 17:09:37 +01:00
Peter Bacon Darwin 8173382c4b chore(docs): remove excess indentation from code blocks
dgeni-packages 0.10.0-rc.5 has a fix for this problem, so this commit updates
to that version.
Adds a new e2e test to prove this is fixed.

Closes #8963
2014-09-15 14:57:18 +01:00
Peter Bacon Darwin 55244390c8 test(docs): fix url matching on api e2e tests 2014-09-15 14:57:18 +01:00
Peter Bacon Darwin fb7e05cc38 chore(docs): ensure all docs e2e tests are run 2014-09-15 14:57:18 +01:00
Melissa Ip ea94e63e35 docs(ngShowHide): use backticks to denote CSS classes and directive names
add backticks around directive names to improve documentation consistency, as it's used in some
parts of the docs already.

Closes #9081
2014-09-14 20:27:39 -04:00
Caitlin Potter e7ac08a061 fix($compile): update '@'-bindings in controller when bindToController is true
'@'-bindings were previously updating the scope when they ought to have been
updating the controller (requested via `bindToController: true` + controllerAs).

It's a one-line fix + test case.

Closes #9052
Closes #9077
2014-09-14 12:12:15 -04:00
Michael Benford 7ffe524171 test(jqLite): Refactor test for isDefaultPrevent
Refactor the spec for isDefaultPrevent method so it fails if the
existing expectations aren't executed.
2014-09-13 15:11:54 -05:00
Michael Benford 30354c58fe fix(jqLite): fix event.stopImmediatePropagation() so it works as expected
jqLite doesn't override the default implementation of event.stopImmediatePropagation()
and so it doesn't work as expected, i.e, it doesn't prevent the rest of the event
handlers from being executed.

Closes #4833
2014-09-13 15:11:49 -05:00
Peter Bacon Darwin 19871d28cf test(docs): add extra docs e2e tests 2014-09-12 23:05:37 +01:00
Peter Bacon Darwin 63761fdac1 test(docs): improve docs e2e tests 2014-09-12 23:05:37 +01:00
Peter Bacon Darwin 3d367eb7b9 chore(docs): update to dgeni-0.4.0
* update package with new services and computeId config
* generateIndexPagesProcessor was not using log
* use StringMap not ES6-shim Map in errorNamespaceMap
* remove unused dependencies from generateErrorDocsProcessor
* ensure generatePagesDataProcessor adds its doc to the collection
* debugDumpProcessor was moved to dgeni-packages
2014-09-12 23:05:37 +01:00
Caitlin Potter d13b4bd1f5 fix($parse): ensure CSP assignable expressions have assign()
Fixes regression where the `assign()` method was not added to chains of identifiers in CSP mode,
introduced originally in b3b476d.

Also fixes the $parse test suite to ensure that CSP code paths are taken when they're expected to be
taken.

Closes #9048
2014-09-12 14:47:58 -04:00
Igor Minar c1f2c3ea83 refactor($parse): remove commented out code 2014-09-12 16:08:24 +02:00
Igor Minar 6af2ff2c7d refactor($parse): clean up object literal fn
No measurable impact on performance
2014-09-12 16:06:12 +02:00
Jason Bedard 67919c8087 perf($parse): removing binaryFn and valueFn wrappers from filter expressions
Improves parsed-expressions/filters benchmark by 15%.
2014-09-12 15:17:26 +02:00
Georgii 5cf1d89692 refactor(ngEventDirs): remove a useless call
The event names are already lower-case.
2014-09-11 12:57:16 -07:00
Georgios Kalpakas 58adaa6634 docs(CHANGELOG.md): fix typos for ng-switch-changed workaround
Fix the JavaScript errors in the work-around proposed in 0f806d9 in order to emulate the behaviour
of the removed `change` attribute of ngSwitch.

Closes #9034
2014-09-11 15:38:49 -04:00
Justin Walsh 0eadee5c24 docs(guide/forms): correct grammar 2014-09-11 12:10:24 -07:00
Caitlin Potter 6a96a8200a fix(ngLocale): Regenerate Locale Files
Fixes number formatting and symbols for many locales.
Adds support for additional locales.

Closes #8931
Closes #8583
Closes #7799
2014-09-10 20:15:22 -04:00
Michael Gallagher 871f321f50 chore(ngLocale): Include numberformatsymbolext.js in Closure Slurper Script
Adds missing number format and symbol rules to Closure slurper script.

Closes #6179
Closes #9013
2014-09-10 20:15:22 -04:00
SirTophamHatt bd8a912774 docs(guide/providers): note that services can create functions
The conclusion table incorrectly states that services can not create functions.
New table row added to separate "can create functions" and "can create primitives".
2014-09-10 16:04:46 -07:00
Caitlin Potter a3962f0df3 fix(ngResource): make badcfg error message more helpful
The error message should help identify the problem. To do so, more details need to be provided.

Closes #9005
Closes #9010
2014-09-10 18:31:08 -04:00
Lucas Galfaso eb4afd45f7 fix(i18n): fix typo at i18n generation code
Fix typo at i18n generation code. This would remove the
property `macFrac` that has no meaning from all the
generated locales
2014-09-10 15:29:56 -07:00
Reuben Doetsch 1c8a7459c9 feat(limitTo): support numeric input to limitTo
Closes #8926
2014-09-10 15:19:30 -07:00
Peter Bacon Darwin 9cd272ac60 docs(tutorial/steps-11&12): add warning about bower conflict error
See: https://github.com/angular/angular-phonecat/issues/163#issuecomment-55181854
2014-09-10 22:52:39 +01:00
Brian Ford 1a1ef62903 fix(ngModel): do not reset bound date objects
Previously, if you bound a `Date` object to `<input type="time">`,
whenever you changed the time, the day, month, and year fields of
the new resulting bound `Date` object would be reset. Now fields
not modified by bound time input elements are copied to the new
resulting object.

Same for input types of `month`, `week`, etc.

Closes #6666
2014-09-10 14:19:28 -07:00
Tobias Bosch 3e51b84bc1 fix(input): always pass in the model value to ctrl.$isEmpty
Fixes #5164
Closes #9017
2014-09-10 11:45:36 -07:00
Michael Silver a0d6e64889 docs(tutorial): update step 7 to reference angular-phonecat
This changes the example JSON to be the actual bower.json for angular-phonecat,
with name set to angular-phonecat.
2014-09-10 11:44:08 -07:00
standup75 1d36cff22e docs(ngResource): document steps to skip default json serialization/deserialization 2014-09-10 11:39:57 -07:00
Jason Bedard 56f09f0b44 perf($compile): move $$isolateBinding creation to directive factory instead of on each link 2014-09-10 11:29:17 -07:00
Tobias Bosch 9314719d1e fix(ngModel): don’t clear the model when an external validator failed
Calling `ctrl.$setValidity()` with a an error key that
does not belong to a validator in `ctrl.$validator` should
not result in setting the model to `undefined` on the next
input change. This bug was introduced in 1.3.0-beta.12.

Closes #8357
Fixes #8080
2014-09-10 11:16:42 -07:00
Peter Bacon Darwin 14183833f9 docs(angular.getTestability): unpublish the function as it is not public 2014-09-10 18:53:15 +01:00
Peter Bacon Darwin d307e77ff3 docs(angular.testability): add jsdoc marker to ensure it is included in docs 2014-09-10 15:02:38 +01:00
Peter Bacon Darwin f0d3722e07 docs(api/index): fix strange uses of the word namespace 2014-09-10 14:43:05 +01:00
Jeff Cross fb39e322ea docs(CHANGELOG.md): add upcoming breaking change 2014-09-09 16:21:52 -07:00
Jeff Cross d1318f7dc7 docs(CHANGELOG.md): fix wording and remove reverted change 2014-09-09 16:21:44 -07:00
Jeff Cross c54990ca6e docs(CHANGELOG.md): add changes for 1.3.0-rc.1 and 1.2.24 2014-09-09 15:45:51 -07:00
Shahar Talmi 3c538c1d21 feat(ngModelOptions): add allowInvalid option
This option allows to write invalid values to the model instead of having them become undefined.
Use this together with calling `ctrl.$setValidity` directly for displaying errors
from serverside validation.

Closes #8290
Closes #8313
2014-09-09 13:48:17 -07:00
Tobias Bosch 64c3b745fb fix(ngModel): update model value with async validators correctly
If the view value changed in the first digest and there are async validators,
the view value was never applied to the model after the validators were
resolved. Only important for tests.
2014-09-09 13:46:28 -07:00
Tobias Bosch f94d551529 fix(ngModel): render immediately also with async validators 2014-09-09 13:46:28 -07:00
Tobias Bosch 9ad7d745ab refactor(ngModel): remove $$invalidModelValue and refactor methods
- define `ngModelGet` and `ngModelSet` to already use
  the getter/setter semantics, so the rest of the code does
  not need to care about it.
- remove `ctrl.$$invalidModelValue` to simplify the internal logic
2014-09-09 13:46:24 -07:00
Igor Minar 90cd1e0a1b docs: add perf todo notes from #8515 2014-09-09 22:36:35 +02:00
Jason Bedard b3b476db7d perf($parse): remove getterFn wrapper for internal use
Closes #8901
2014-09-09 22:15:23 +02:00
Jason Bedard 456dcb020b test($parse): adding benchmark for execution of $parse()ed expressions
Part of #8901
2014-09-09 22:15:23 +02:00
Tobias Bosch 7235329ff8 docs(ngModel): update breaking change from 1.3.0-beta.11 to 1.3.0-beta.12
Closes #8357
2014-09-09 12:38:43 -07:00
Jeff Cross 06fa2868de revert: "fix($compile): render nested transclusion at the root of a template"
This reverts commit 6d1e7cdc51.

This commit was causing breakages because of its assumption that transcluded
content would be handled predictably, i.e. with ngTransclude, whereas many
use cases involve manipulating transcluded content in linking functions.
2014-09-09 11:05:34 -07:00
Chirayu Krishnappa bd8ad0fbe8 fix($parse): disallow passing Function to Array.sort
Fix the following exploit:

    hasOwnProperty.constructor.prototype.valueOf = valueOf.call;
    ["a", "alert(1)"].sort(hasOwnProperty.constructor);

The exploit:
• 1. Array.sort takes a comparison function and passes it 2 parameters to compare.
  2. It then calls .valueOf() if the result is not a primitive.
• The Function object conveniently accepts two string arguments so we can use this
  to construct a function.  However, this doesn't do much unless we can execute it.
• We set the valueOf function on Function.prototype to Function.prototype.call.
  This causes the function that we constructed to be executed when sort calls
  .valueOf() on the result of the comparison.

The fix is in two parts.
• Disallow passing unsafe objects to function calls as parameters.
• Do not traverse the Function object when setting a path.
2014-09-09 10:39:39 -07:00
Caitlin Potter 6639ca9d6b fix(ngInclude): correctly add svg-namespaced template content
It is now possible for ngInclude to correctly load SVG content in non-blink browsers, which do not
sort out the namespace when parsing HTML.

Closes #7538
Closes #8981
Closes #8997
2014-09-09 12:45:15 -04:00
Caitlin Potter 719f42e949 chore(.jshintrc): add jqLiteParseHTML to globals 2014-09-09 11:43:49 -04:00
Caitlin Potter 73916108f8 chore(.jshintrc): add jqLiteBuildFragment to globals 2014-09-09 11:43:49 -04:00
Igor Minar ed63733000 fix(ngRepeat): preserve original position of elements that are being animated away
During the recent refactoring a typo was made that broke code that detects if we are
already removed from the DOM (animation has completed).

Closes #8918
Closes #8994
2014-09-09 16:19:47 +02:00
Igor Minar f33a938545 chore(testabilityPatch): fix the dump fn so that it works with karma's debug.html 2014-09-09 16:19:47 +02:00
Igor Minar 01ef12884b test(animateSpec): remove unused variables 2014-09-09 16:19:46 +02:00
Igor Minar 732d72d991 test(ngRepeat): move an existing test into the right describe block 2014-09-09 16:19:46 +02:00
Peter Bacon Darwin 66103c8277 chore(npm-shrinkwrap): safely update karma to 0.12.23 2014-09-09 10:21:11 +01:00
Peter Bacon Darwin fee8f1c07b chore(npm-shrinkwrap): fix karma dependencies
Updating to karma 0.12.13 (in commit 408508ad29)
caused `iit` and `ddescribe` to crash and disconnect the browser stopping the
test run.

It appears that the problem is with one of the dependencies of karma rather
than karma itself. At least one of the karma dependencies updated in line
with karma's dependencies' semver specifications but subtly changed their
behaviour to break karma.  Possibly this is related to chokidar, glob,
minimatch or fsevents.
2014-09-09 10:14:15 +01:00
Tobias Bosch 6046e14bd2 refactor(ngModelController,formController): centralize and simplify logic
The previous logic for async validation in
`ngModelController` and `formController` was not maintainable:
- control logic is in multiple parts, e.g. `ctrl.$setValidity`
  waits for end of promises and continuous the control flow
  for async validation
- logic for updating the flags `ctrl.$error`, `ctrl.$pending`, `ctrl.$valid`
  is super complicated, especially in `formController`

This refactoring makes the following changes:
- simplify async validation: centralize control logic
  into one method in `ngModelController`:
  * remove counters `invalidCount` and `pendingCount`
  * use a flag `currentValidationRunId` to separate
    async validator runs from each other
  * use `$q.all` to determine when all async validators are done
- centralize way how `ctrl.$modelValue` and `ctrl.$invalidModelValue`
  is updated
- simplify `ngModelController/formCtrl.$setValidity` and merge
  `$$setPending/$$clearControlValidity/$$clearValidity/$$clearPending`
  into one method, that is used by `ngModelController` AND
  `formController`
  * remove diff calculation, always calculate the correct state anew,
    only cache the css classes that have been set to not
    trigger too many css animations.
  * remove fields from `ctrl.$error` that are valid and add private `ctrl.$$success`:
    allows to correctly separate states for valid, invalid, skipped and pending,
    especially transitively across parent forms.
- fix bug in `ngModelController`:
  * only read out `input.validity.badInput`, but not
    `input.validity.typeMismatch`,
    to determine parser error: We still want our `email`
    validator to run event when the model is validated.
- fix bugs in tests that were found as the logic is now consistent between
  `ngModelController` and `formController`

BREAKING CHANGE:
- `ctrl.$error` does no more contain entries for validators that were
  successful.
- `ctrl.$setValidity` now differentiates between `true`, `false`,
  `undefined` and `null`, instead of previously only truthy vs falsy.

Closes #8941
2014-09-08 15:10:02 -07:00
Tobias Bosch 2a5af502c5 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-09-08 10:40:20 -07:00
Jason Bedard 9bedeb3353 perf(extend): remove use of forEach to remove calls/closures/passing arguments
Closes #8898
2014-09-08 17:15:02 +02:00
Jason Bedard df9e60c8e7 feat(angular.forEach): add the array/object as the 3rd param like the native array forEach
Closes #7902
2014-09-08 17:06:16 +02:00
Igor Minar 41cec4d680 refactor(select): remove code duplication 2014-09-08 16:58:47 +02:00
Peter Bacon Darwin 0f3ea45d73 docs($filter): remove duplicate documentation of register method 2014-09-08 12:05:11 +01:00
Peter Bacon Darwin cddd48fe20 docs(form): move param tag outside of main description 2014-09-08 12:05:11 +01:00
Peter Bacon Darwin be3b62cd09 chore(bower): refactor bower usage
The gulp bower task in the docs app was never actually running since it couldn't
find the bower.json file and was silently failing. Updating to a newer bower
highlighted this issue.

This commit moves the docs app specific bower components into the docs folder.
There are only jquery and closure compiler related components in the project
folder now.

It also improves the gulp bower task to provide better feedback of progress
and errors.
2014-09-08 12:05:11 +01:00
Peter Bacon Darwin 40308e5935 chore(package.json): tidy up dependencies
Sorted dependencies into alphabetic order. If we can keep them like this
it will be much easier to keep track of version changes.

Updated bower and gulp to newer versions.
2014-09-08 12:05:11 +01:00
Jason Bedard f6aa1c5561 perf(jQuery): only trigger $destroy if a handler exists
Speeds up largetable destruction by 30% (500ms) when jQuery 2.1.1 is present.

Closes #8859
2014-09-07 23:56:11 +02:00
Jason Bedard 43c67ccd16 perf($parse): removing references to Parser/Lexer from parsed expressions
This allows the parser and lexer objects to get GC-ed once the expression
is parsed.

Part of #8901
2014-09-07 23:02:31 +02:00
Jason Bedard 1cfd49ddf0 refactor($parse): simplifying some while(true) loops
Part of #8901
2014-09-07 22:55:33 +02:00
Jason Bedard 907b8c1675 perf($parse): calculate array lengths once at start of loop
Part of #8901
2014-09-07 22:53:16 +02:00
Jason Bedard 432aa9e4e4 refactor($parse): adding function names for easier debugging
Part of #8901
2014-09-07 22:52:00 +02:00
Jason Bedard b95dabb881 refactor($parse): simplifying multi-statement execution
Part of #8901
2014-09-07 22:42:05 +02:00
Pawel Kozlowski 0f3adece3b refactor($http): simplify buildUrl function
Closes #8955
2014-09-05 20:21:00 -04:00
Shahar Talmi e322cd9b3b fix(ngModelOptions): do not trigger digest on setViewValue if debouncing
Note that this change means that anyone watching `$viewValue` will have to
wait for a new digest before they are aware that it has been updated.

Closes #8814
Closes #8850
Closes #8911
2014-09-05 20:15:34 +01:00
Caitlin Potter b3b672130d fix(ngAnimate): support removing classes from SVG elements when using jQuery
Fixes a regression in ngAnimate introduced in 2f4437b3, whereby SVG elements would not be able to
have classes removed by ngAnimate methods when jQuery was loaded (without also including libraries
which patch jQuery to support SVG elements, such as jquery-svgdom.js).

This fix exports jqLiteHasClass as a private method `$$hasClass` on the `angular` global object,
which enables ngAnimate to use this SVG-safe method for testing if the class is available.

Closes #8872
Closes #8893
2014-09-05 13:51:17 -04:00
Julie Ralph 8b5d33dab8 chore(tests): update Protractor to v1.2.0 2014-09-05 10:49:18 -07:00
Vitali Tsevan cbdaabfb59 fix(orderBy): allow arrayLike objects to be ordered
Closes #8944
2014-09-05 11:56:09 +01:00
Rouven Weßling 3471fedfbc refactor(indexOf) Remove a now unused copy of the indexOf function
This was missed in 300bffc4fe

Closes #8939
2014-09-05 10:23:52 +01:00
Jeff Cross c3a58a9f34 fix($location): don't call toString on null values 2014-09-04 17:16:11 -07:00
thorn0 99d95f1639 fix($location): remove an unused parameter of $location.url 2014-09-04 15:09:18 -07:00
John Reilly 48e66cff40 docs($location): update search description to include number param 2014-09-04 14:49:52 -07:00
Pawel Kozlowski adb5c6d6cc fix($location): allow numeric location setter arguments
Fixes #7054
2014-09-04 09:55:03 -07:00
Peter Bacon Darwin b01a03c1b9 docs(guide): add sortOrder to each page
Finally we can control the order of the guide pages
2014-09-04 16:49:25 +01:00
Peter Bacon Darwin 43e0dc2dfe chore(docs): enable page ordering by @sortOrder tag 2014-09-04 16:49:25 +01:00
Matias Niemelä 976da56d50 test(ngModel): add missing tests for ngMin/ngMax for date inputs 2014-09-04 11:46:05 -04:00
Matias Niemelä 088545c185 fix(ngModel): properly parse min/max date values as strings for date inputs
Due to the nature of how date objects are rendered when JSON.stringify
is called, the resulting string contains two sets of quotes surrounding
it. This commit fixes that issue.

Closes #6755
2014-09-04 11:45:59 -04:00
Matias Niemelä b350283503 fix(ngModel): revalidate the model when min/max expression values change for date inputs
Closes #6755
2014-09-04 11:45:53 -04:00
Matias Niemelä 25541c1f87 fix(ngModel): consider ngMin/ngMax values when validating number input types
With this fix ngModel will treat ngMin as a min error and ngMax as a max error.
This also means that when either of these two values is changed then ngModel will
revaliate itself.
2014-09-04 11:45:46 -04:00
Matias Niemelä 7b273a2c97 fix(ngModel): revalidate the model when min/max expression values change for number inputs
As of this fix if the max or min value is changed via scope or by another ngModel
then it will trigger the model containing the min/max attributes to revalidate itself.

Closes #2404
2014-09-04 11:45:36 -04:00
Peter Bacon Darwin 3952408cf1 chore(docs): improve searching by member
The keywords processor now also extracts the members (i.e. method, properties
and events) into its own search term property. These are then used in the lunr
search index with higher weighting that normal keywords to push services that
contain the query term as a member higher up the search results.

Closes #7661
2014-09-04 14:23:04 +01:00
Peter Bacon Darwin 02bada130e refact(ngSwitch): don't create extra function in for loop
Closes #8927
2014-09-04 13:17:21 +01:00
The Big Red Geek 712299c2a2 fix(ngSwitch): ensure correct iterator is passed to async function
Closes #8833
2014-09-04 12:49:14 +01:00
Peter Bacon Darwin 6d1e7cdc51 fix($compile): render nested transclusion at the root of a template
Closes #8914
Closes #8925
2014-09-04 12:30:09 +01:00
Peter Bacon Darwin 86c6be82e5 docs($http): clarify overriding of default transformations
Closes #8590
2014-09-04 12:28:18 +01:00
Smitha Milli ae952fbf0b fix(numberFilter): format numbers that round to zero as nonnegative
Previously when a negative number was rounded to 0 by the number filter
it would be formated as a negative number.  This means something like
{{ -0.01 | number: 1 }} would output -0.0.  Now it will ouput 0.0
instead.

Closes #8489
2014-09-03 14:59:53 -07:00
thorn0 73157b0a0b docs(docs.css): improve comma spacing in TOC on mobile 2014-09-03 13:57:49 -07:00
Juampy 0ee0ce18c1 docs($sce:unsafe): fix link to $sce docs
The second link to Strict Contextual Escaping (SCE) points to a 404.

Closes #8514
2014-09-03 16:31:06 -04:00
thorn0 7f87988e41 docs(README): fix 'Contribution guidelines' link
The current link leads to a page 'Building and Testing AngularJS'.
This same link is also included in the 'Building AngularJS' section
of the README where it's more relevant.
2014-09-03 13:24:56 -07:00
TLChan 9a09f1d96d docs(tutorial/step_05): improve formatting of code identifier
Closes #8557
2014-09-03 16:24:06 -04:00
Zach Pomerantz e96be5bfb8 docs(interpolate): fix link text 2014-09-03 13:17:46 -07:00
Vic Metcalfe 095627ad17 docs(ngBlur): explain blur events 2014-09-03 13:04:50 -07:00
thammin 1b4a2c8fee docs(ngSwitch): fix priority 2014-09-03 12:00:35 -07:00
Caitlin Potter b474c36e41 docs(CHANGELOG.md): add breaking change for a9fcb0d0 (v1.2.13)
Closes #8909
2014-09-03 13:13:25 -04:00
Jeff Cross 007e320835 chore(shrink-wrap): update shrinkwrap to latest benchpress 2014-09-03 09:22:13 -07:00
Tim Whitbeck f211419be6 docs(ngModelController): fix $asyncValidators example
Also fixes a typo (◜௰◝)

Closes #8906
2014-09-03 12:21:07 -04:00
Igor Minar 5dd9f138c7 chore(orderby-bp): fix the benchmark code and add jquery support 2014-09-03 16:37:46 +02:00
Peter Bacon Darwin 821da26e18 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-09-03 14:56:49 +01:00
Rouven Weßling 300bffc4fe refactor(indexOf) use Array.prototype.indexOf exclusively
Replace helper functions with the native ES5 method

Closes #8847
2014-09-03 13:37:03 +01:00
thorn0 5b2a386b1f docs(jsdoc): remove @kind function for providers
This gets rid of the bizarre "Usage: fooProvider();" which does not really communicate anything useful.

Closes #8809
2014-09-03 08:32:15 -04:00
Shahar Talmi 953ee22f76 fix(ngForm): don't clear validity of whole form when removing control
Calling `$$clearControlValidity` on the parent of a nested form caused the parent form
to look like there are no more errors on the nested form even if it still had some
inputs with errors. there is no need to call this method recursively since `$setValidity`
will propagate the new validity state well enough.

Closes #8863
2014-09-03 13:30:33 +01:00
Shahar Talmi c3064f728c refactor(ngModel): get rid of revalidate
Since the validation was refactored we can now work out inside
`$commitViewValue()` whether to ignore validation by looking at whether
the input has native validators.

Closes #8856
2014-09-03 13:20:16 +01:00
The Big Red Geek 0f806d9659 refactor(ngSwitch): remove undocumented change attribute from ngSwitch
BREAKING CHANGE:

Ever since 0df93fd, tagged in v1.0.0rc1, the ngSwitch directive has had an undocumented `change`
attribute, used for evaluating a scope expression when the switch value changes.

While it's unlikely, applications which may be using this feature should work around the removal
by adding a custom directive which will perform the eval instead. Directive controllers are
re-instantiated when being transcluded, so by putting the attribute on each item that you want
to be notified of a change to, you can more or less emulate the old behaviour.

Example:

```js
angular.module("switchChangeWorkaround", []).
  directive("onSwitchChanged", function() {
    return {
      linke: function($scope, $attrs) {
        $scope.$parent.$eval($attrs.change);
      }
    };
  });
```

```html
<div ng-switch="switcher">
  <div ng-switch-when="a" on-switch-changed="doSomethingInParentScope()"></div>
  <div ng-switch-when="b" on-switch-changed="doSomethingInParentScope()"></div>
</div>
```

Closes #8858
Closes #8822
2014-09-03 07:38:11 -04:00
Peter Bacon Darwin ac535549c9 chore(package.json): update to dgeni-packages v0.9.8
Closes #8860
2014-09-03 11:38:44 +01:00
Leonardo Zizzamia d9cdb105fc test(orderBy): implement benchmark for ngRepeat with orderBy 2014-09-02 15:21:36 -07:00
Brian Ford 44cf2e1904 style(ngModel): fix indentation 2014-09-02 14:51:16 -07:00
Nicolai Skogheim ce25ac83b4 docs(ngRepeat): update step_02.ngdoc with challenge
Add a simple task for the user to better understand ng-repeat.

Close #8757
2014-09-02 16:46:01 -04:00
Shahar Talmi d2d8117edd docs($rootScope): document scope properties 2014-09-02 13:35:17 -07:00
Tiago Ribeiro 63fb60de86 docs(changelog): fix inline formatting 2014-09-02 13:01:41 -07:00
Nima Mehanian 29714a34ab docs(guide/providers): fix grammar and punctuation 2014-09-02 12:43:28 -07:00
Brian Ford d5686ffc48 style(ngModel): fix spacing 2014-09-02 12:10:23 -07:00
Shahar Talmi 203ea10f9e fix(ngEventDirs): check scope.$$phase only on $rootScope
Closes #8891, #8849
2014-09-02 10:42:01 -07:00
Shahar Talmi bf59d7274f fix(input): check scope.$$phase only on $rootScope 2014-09-02 10:41:32 -07:00
Tobias Bosch 995a8d39af docs(errors): fix base href in $location:nobase error page 2014-09-02 10:21:41 -07:00
Tobias Bosch fc706d13d8 fix($location): set baseHref in mock browser to /
Set the default value for the base tag in the mock browser to `/`,
as we now always require a base tag to be present for html5 mode.

Fixes #8866
Closes #8889
2014-09-02 09:54:46 -07:00
Peter Bacon Darwin 9525d0ad1d docs($compile): clarify linking when there are templateUrl directives
Closes #8877
Closes #8631
2014-09-02 15:37:29 +01:00
Caitlin Potter e0d49a3109 refactor(ngRepeat): specify explicit false for cloneNode deepClone parameter
This should provide a slight compat improvement for old versions of Opera, which did not treat the
`false` as the default value.

There is no test for this fix as Opera 11 is not a browser which runs on the CI servers.

Closes #8883
Closes #8885
2014-09-02 03:04:00 -04:00
Caitlin Potter c5b32f14d5 docs(CHANGELOG.md): add breaking change from 5f3f25a1
5f3f25a1 included a breaking change which was not documented, which is that the return value of directive
constructors is ignored. The reason they are ignored is to ensure that the correct object is bound to when
binding properties to the controller. It may be possible to come up with a better solution which informs
the developer that what they are doing is wrong, rather than just breaking instead.

Closes #8876
Closes #8882
2014-09-01 18:15:57 -04:00
Igor Minar 2e0982c1ec docs(errors): fix link in the $location:nobase error page 2014-08-31 11:03:59 -07:00
Igor Minar b2902446eb chore(travis): move docse2e tests into the unit tests vm
we spend more time making getting the build ready than running the docs e2e tests.

by piggy-backing on unit tests we'll finish the build faster
2014-08-29 21:54:32 -07:00
Matias Niemelä 202aed7770 docs(changelog): release notes for 1.3.0-RC.0 sonic-boltification 2014-08-29 21:22:46 -04:00
Tobias Bosch 271572c20e fix(docs): only check for SEVERE logs in tests 2014-08-29 17:57:23 -07:00
Igor Minar 550ba01b32 fix(docs): don't throw exception on the 404 page
Closes #8518
2014-08-29 16:13:11 -07:00
Tobias Bosch 22948807e3 fix($location): always resolve relative links in html5mode to <base> url
BREAKING CHANGE (since 1.2.0 and 1.3.0-beta.1):

Angular now requires a `<base>` tag when html5 mode of `$location` is enabled. Reasoning:
Using html5 mode without a `<base href="...">` tag makes relative links for images, links, ...
relative to the current url if the browser supports
the history API. However, if the browser does not support the history API Angular falls back to using the `#`,
and then all those relative links would be broken.

The `<base>` tag is also needed when a deep url is loaded from the server, e.g. `http://server/some/page/url`.
In that case, Angular needs to decide which part of the url is the base of the application, and which part
is path inside of the application.

To summarize: Now all relative links are always relative to the `<base>` tag.

Exception (also a breaking change):
Link tags whose `href` attribute starts with a `#` will only change the hash of the url, but nothing else
(e.g. `<a href="#someAnchor">`). This is to make it easy to scroll to anchors inside a document.

Related to #6162
Closes #8492

BREAKING CHANGE (since 1.2.17 and 1.3.0-beta.10):

In html5 mode without a `<base>` tag on older browser that don't support the history API
relative paths were adding up. E.g. clicking on `<a href="page1">` and then on `<a href="page2">`
would produce `$location.path()==='/page1/page2'. The code that introduced this behavior was removed
and Angular now also requires a `<base>` tag to be present when using html5 mode.

Closes #8172, #8233
2014-08-29 15:19:51 -07:00
Tobias Bosch b9007df590 refactor(locationSpec): make helper functions take an object
Makes tests more readable
2014-08-29 15:05:50 -07:00
Smitha Milli 0604bb7b7a fix(ngRepeat): improve errors for duplicate items
-Log the value that had the duplicate key, as well as the key
The error that is thrown when items have duplicate track by keys can be
confusing because only the duplicate key is logged.  If the user didn't
provide that key themselves, they may not know what it is or what item
it corresponds to.
2014-08-29 13:46:37 -07:00
Matias Niemelä 92576743ee fix($animate): wait two until two digests are over until enabling animations
Even when no remote templates are to be downloaded, wait until the end of the
post digest queue before enabling animations since all $animate-triggered
animation events perform a post digest before running animations.

Closes #8844
2014-08-29 16:17:32 -04:00
Matias Niemelä c9b0bfecc9 fix(ngSwitch): avoid removing DOM nodes twice within watch operation
Closes #8662
2014-08-29 15:58:13 -04:00
Igor Minar 2ae10f67fc fix(numberFilter): pass through null and undefined values
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the number filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes #8605
Closes #8842
2014-08-29 12:33:43 -07:00
Igor Minar c2aaddbe4b fix(currencyFilter): pass through null and undefined values
When these special values are passed through one-time binding will work correctly.

BREAKING CHANGE: previously the currency filter would convert null and undefined values into empty string, after this change
these values will be passed through.

Only cases when the currency filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.

This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.

Closes #8605
2014-08-29 12:32:51 -07:00
Wesley Cho 6f7018d52f perf(select): execute render after $digest cycle
This is an optimization to defer execution of the render function in the
select directive after the $digest cycle completes inside the
$watchCollection expressions.  This does a check to see if the render
function is already registered in the $$postDigestQueue before it passes
it into $$postDigest, guaranteeing that the DOM manipulation happens
only in one execution after the model settles.

Closes #8825
2014-08-29 12:24:49 -07:00
Michael Barton 4f9ac078d5 docs($rootScope): remove duplicate $digest()
Closes #8840
2014-08-29 14:38:08 -04:00
zahragh 9057ed21ac docs(FormController): document $submitted property
Closes #8732
2014-08-29 14:35:22 -04:00
danrbergman c09f619318 docs($q): fixed spelling, removed extra characters
Closes #8779
2014-08-29 14:00:50 -04:00
Matias Niemelä 1eda18365a fix(ngModel): always format the viewValue as a string for text, url and email types
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.
2014-08-29 13:29:47 -04:00
Caitlin Potter 77ce5b89f9 fix(input): validate minlength/maxlength for non-string values
Use the viewValue rather than modelValue when validating. The viewValue should always be a string, and
should reflect what the user has entered, or the formatted model value.

BREAKING CHANGE:

Always uses the viewValue when validating minlength and maxlength.

Closes #7967
Closes #8811
2014-08-29 13:20:03 -04:00
Caitlin Potter c5f1ca3d91 chore(compare-master-to-stable): make checks for bugfixes better
Prevent the script from alerting you if a docs fix has something like "Fixes typo in foo", which is
not how bugfixes are worded.

Closes #8801
2014-08-29 11:59:07 -04:00
Sekib Omazic 7a36d49533 docs($filter): Date filter am/pm case
Use uppercase for AM/PM in date filter

fixes #8763
2014-08-29 13:04:05 +02:00
rodyhaddad cd21602d5b fix(ngBindHtml): throw error if interpolation is used in expression
Closes #8824
2014-08-29 01:24:46 -04:00
Caitlin Potter 5f3f25a1a6 feat($compile): bind isolate scope properties to controller
It is now possible to ask the $compiler's isolate scope property machinery to bind isolate
scope properties to a controller rather than scope itself. This feature requires the use of
controllerAs, so that the controller-bound properties may still be referenced from binding
expressions in views.

The current syntax is to prefix the scope name with a '@', like so:

    scope: {
        "myData": "=someData",
        "myString": "@someInterpolation",
        "myExpr": "&someExpr"
    },
    controllerAs: "someCtrl",
    bindtoController: true

The putting of properties within the context of the controller will only occur if
controllerAs is used for an isolate scope with the `bindToController` property of the
directive definition object set to `true`.

Closes #7635
Closes #7645
2014-08-28 20:46:52 -07:00
Tobias Bosch cb73a37c7c fix($compile): use the correct namespace for transcluded svg elements
This fixes the case when a directive that uses `templateUrl`
is used inside of a transcluding directive like `ng-repeat`.

Fixes #8808
Closes #8816
2014-08-28 17:34:11 -07:00
Guilbert 49455a75dc docs(filterFilter): add note on negation 2014-08-28 14:55:53 -07:00
Julie 85880a6490 feat(testability): add $$testability service
The $$testability service is a collection of methods for use when debugging
or by automated testing tools. It is available globally through the function
`angular.getTestability`.
For reference, see the Angular.Dart version at
https://github.com/angular/angular.dart/pull/1191
2014-08-28 14:25:50 -07:00
Colin Casey 46343c603d feat(filterFilter): pass index to function predicate
Closes #654
2014-08-28 12:34:42 -07:00
Erin Altenhof-Long 3b5d75c021 feat(ngRoute): alias string as redirectTo property in .otherwise()
Allow `.otherwise()` to interpret a string parameter
as the `redirectTo` property

Closes #7794
2014-08-28 11:58:31 -07:00
Tobias Bosch 719c747cd8 fix(ngEventDirs): execute blur and focus expression using scope.$evalAsync
BREAKING CHANGE:
The `blur` and `focus` event fire synchronously, also during DOM operations
that remove elements. This lead to errors as the Angular model was not
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.

This change executes the expression of those events using
`scope.$evalAsync` if an `$apply` is in progress, otherwise
keeps the old behavior.

Fixes #4979
Fixes #5945
Closes #8803
Closes #6910
Closes #5402
2014-08-28 11:49:31 -07:00
Tim Kindberg 2137542e09 docs(ngModelOptions): fix example 2014-08-28 11:36:22 -07:00
Shahar Talmi ab878a6c03 fix(ngModel): allow non-assignable binding when getterSetter is used
Closes #8704
2014-08-28 11:25:03 -07:00
Igor Minar 474a0337bd chore(benchmarks): disable debugInfo in largetable benchmark 2014-08-28 09:31:02 -07:00
Matias Niemelä 97a1b399b7 test($animate): add tests for noop enaled and cancel methods 2014-08-28 11:31:21 -04:00
Brian Ford c6bde52006 docs(debugInfo): add docs for $compileProvider.debugInfoEnabled() 2014-08-27 20:45:59 -07:00
Vojta Jina 563be7e879 refactor(ngBind): name link and compile functions
For easier debugging.
2014-08-27 20:45:59 -07:00
Vojta Jina e0489abd8d perf($compile): add debug classes in compile phase
In a93f03d and d37f103 we changed the compiler and ngBind to add debugging CSS classes (i.e. ng-scope, ng-binding) in linking function. This simplified the code and made sense under the original assumptions that the debug info will be disabled by default. That is however not the case - debug info is enabled by default.

When debug info is enabled, this change improves the largetable-bp
benchmark by ~580ms, that is 30% faster.
Measuring the “create” phase, 25 loops, meantime ~1920ms -> ~1340ms.

This change does not affect performance when debug info is disabled.
2014-08-27 20:45:59 -07:00
Vojta Jina 2218e6f8cd refactor($compile): rename element -> $element
To follow our convention (at least in this file): if it’s
a jqLite/jQuery wrapper than the variable name starts with `$`.
2014-08-27 20:45:59 -07:00
Vojta Jina cec9ecf951 refactor($compile): $$addScopeInfo always expects jq wrapper
`$$addScopeInfo` used to accept either DOM Node or jqLite/jQuery
wrapper. This commit simplifies the method to always require
jqLite/jQuery wrapper and thus remove the `element.data` condition which
was wrong. If `element` was a raw comment element, the `data` property
was a string (the value of the comment) and an exception was thrown.
2014-08-27 20:45:59 -07:00
Vojta Jina 36a547b852 refactor: remove doReload arg used only for testing
We run unit tests in “strict” mode and thus can’t monkey-patch `window.location` nor `window.location.reload`. In order to avoid full page reload, we could pass location as argument, or another level of indirection, something like this:
```js
var ourGlobalFunkyLocation = window.location;
function reloadWithDebugInfo() {
  window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
  ourGlobalFunkyLocation.reload();
}

// in the test
ourGlobalFunkyLocation = {
  reload: function() {}
};
reloadWithDebugInfo();
ourGlobalFunkyLocation = window.location;
```

I don’t think any of these make sense, just so that we can test setting `window.name`. If the `reloadWithDebugInfo` function was more complicated, I would do it.

I don’t think it’s worthy to confuse production code with extra logic which purpose was only to make testing possible.
2014-08-27 20:45:59 -07:00
Vojta Jina b3ec730c42 refactor($compile): $$addBindingInfo accepts single expression or an array
Instead of knowing about `.expressions` property, it just accepts a single expression or an array of expressions.
2014-08-27 20:45:58 -07:00
Peter Bacon Darwin 2ab0d5d370 test(e2e): fix by.binding() locators
After upgrading, Protractor requires exact string that is used in the binding.
2014-08-27 20:45:58 -07:00
Vojta Jina ac68ee49c1 chore(deps): update protractor to 1.1.1 2014-08-27 20:45:58 -07:00
Peter Bacon Darwin be8ef25a5a chore(clean-shrinkwrap): chokidar is fixed since 0.8.2 2014-08-27 20:45:58 -07:00
Peter Bacon Darwin 41c1b8858f feat: add angular.reloadWithDebugInfo() 2014-08-27 20:45:58 -07:00
Vojta Jina fce8915f39 test(ngClass): dealoc elements 2014-08-27 20:45:58 -07:00
Vojta Jina d4dd5dfa18 test(input): dealoc elements 2014-08-27 20:45:58 -07:00
Peter Bacon Darwin a1e5cd5fe3 feat($compile): allow disabling scope info
The compiler adds scope information (`ng-scope` CSS class and `$scope` data property) to elements
when the are bound to the scope. This is mostly to aid debugging tools such as Batarang. In
production this should be unnecesary and adds a performance penalty.

In the bench/apps/largetable-bp this change caused an improvement of ~100ms (7%).

This can be now disabled by calling `$compileProvider.debugInfoEnabled(false)`
in a module `config` block:
```
someModule.config(['$compileProvider', function($compileProvider) {
  $compileProvider.debugInfoEnabled(false);
}]);
```

In the bench/apps/largetable-bp benchmark this change, with debug info disabled,
improved by ~120ms, that is ~10%.
Measuring the "create" phase, 25 loops, mean time ~1200ms -> ~1080ms.
2014-08-27 20:45:58 -07:00
Peter Bacon Darwin 3660fd0912 feat($compile/ngBind): allow disabling binding info
The compiler and ngBind directives add binding information (`ng-binding`
CSS class and `$binding` data property) to elements when they are bound to
the scope. This is only to aid testing and debugging for tools such as
Protractor and Batarang. In production this is unnecessary and add a
performance penalty.

This can be now disabled by calling `$compileProvider.debugInfoEnabled(false)`
in a module `config` block:
```
someModule.config(['$compileProvider', function($compileProvider) {
  $compileProvider.debugInfoEnabled(false);
}]);
```

In the bench/apps/largetable-bp benchmark this change, with debug info disabled,
improved by ~140ms, that is 10%.
Measuring the "create" phase, 25 loops, mean time ~1340ms -> ~1200ms.

We were storing the whole `interpolationFn` in the `$binding` data on
elements but this function was bringing a lot of closure variables with it
and so was consuming unwanted amounts of memory.

Now we are only storing the parsed interpolation expressions from the
binding (i.e. the values of `interpolationFn.expressions`).

BREAKING CHANGE:
The value of `$binding` data property on an element is always an array now
and the expressions do not include the curly braces `{{ ... }}`.
2014-08-27 20:45:58 -07:00
Matias Niemelä 4bca4c44b9 fix($animate): ensure guarded animations consider AJAX requests upon bootstrap
Prior to this fix when an Angular application is bootstrapped it would only
place an animation guard to prevent animations from running when the application
starts for the first two digest cycles. However, if any controllers or directives,
that are executed upon boostrap, trigger any remote code to be downloaded (via $http)
then the guard does not put that into consideration. This fix now properly addresses
that circumstance and removes the guard once all outbound HTTP requests are complete
when an Angular application is bootstrapped.

Closes #8275
Closes #5262
2014-08-27 23:19:29 -04:00
Matias Niemelä a70e2833ea feat($templateRequest): introduce the $templateRequest service
This handy service is designed to download and cache template contents
and to throw an error when a template request fails.

BREAKING CHANGE

Angular will now throw a $compile minErr each a template fails to download
for ngView, directives and ngMessage template requests. This changes the former
behavior of silently ignoring failed HTTP requests--or when the template itself
is empty. Please ensure that all directive, ngView and ngMessage code now properly
addresses this scenario. NgInclude is uneffected from this change.
2014-08-27 23:19:19 -04:00
Tobias Bosch 3be00df495 fix($browser): detect changes to the browser url that happened in sync
Closes #6976.
2014-08-27 16:36:53 -07:00
Zach Pomerantz 2efe1c2e7b docs(compile): translcuded -> transcluded
Oops.

Closes #8799
2014-08-27 17:14:44 -04:00
Igor Minar d8a02f9987 chore(build): uprade closure-compiler to v20140814
no significant change in code size
2014-08-27 13:57:36 -07:00
Igor Minar a4520a745d fix(Angular): remove duplicate nodeName_ references
I forgot to remove the variable declaration in previous nodeName_ commit.
2014-08-27 13:56:13 -07:00
Richard Harrington fe697527b4 docs(shallowCopy): add missing word 'are' and period.
Closes #8794
2014-08-27 08:38:35 -04:00
Caitlin Potter ea6fc6e69c feat($http): implement mechanism for coalescing calls to $apply in $http
When multiple responses are received within a short window from each other, it can be wasteful to
perform full dirty-checking cycles for each individual response. In order to prevent this, it is
now possible to coalesce calls to $apply for responses which occur close together.

This behaviour is opt-in, and the default is disabled, in order to avoid breaking tests or
applications.

In order to activate coalesced apply in tests or in an application, simply perform the following
steps during configuration.

   angular.module('myFancyApp', []).
     config(function($httpProvider) {
       $httpProvider.useApplyAsync(true);
     });

OR:

   angular.mock.module(function($httpProvider) {
     $httpProvider.useApplyAsync(true);
   });

Closes #8736
Closes #7634
Closes #5297
2014-08-26 21:42:47 -04:00
Caitlin Potter e94d454b84 feat($rootScope): implement $applyAsync to support combining calls to $apply into a single digest.
It is now possible to queue up multiple expressions to be evaluated in a single digest using
$applyAsync. The asynchronous expressions will be evaluated either 1) the next time $apply or
$rootScope.$digest is called, or 2) after after the queue flushing scheduled for the next turn
occurs (roughly ~10ms depending on browser and application).
2014-08-26 21:33:44 -04:00
Matias Niemelä 2ae4f40be1 feat(ngModel): provide validation API functions for sync and async validations
This commit introduces a 2nd validation queue called `$asyncValidators`. Each time a value
is processed by the validation pipeline, if all synchronous `$validators` succeed, the value
is then passed through the `$asyncValidators` validation queue. These validators should return
a promise. Rejection of a validation promise indicates a failed validation.
2014-08-26 18:31:01 -04:00
Matias Niemelä db044c408a fix(ngModel): treat undefined parse responses as parse errors
With this commit, ngModel will now handle parsing first and then validation
afterwards once the parsing is successful. If any parser along the way returns
`undefined` then ngModel will break the chain of parsing and register a
a parser error represented by the type of input that is being collected
(e.g. number, date, datetime, url, etc...). If a parser fails for a standard
text input field then an error of `parse` will be placed on `model.$error`.

BREAKING CHANGE

Any parser code from before that returned an `undefined` value
(or nothing at all) will now cause a parser failure. When this occurs
none of the validators present in `$validators` will run until the parser
error is gone.
2014-08-26 18:30:53 -04:00
Igor Minar 0e44ac2de0 refactor(hashKey): don't generate memory garbage
we now store both the object type and the id as the hashkey and return it for all objects.

for primitives we still have to do string concatination because we can't use expandos on them to
store the hashkey
2014-08-26 15:00:35 -07:00
Igor Minar 5a1a0c9622 perf(nodeName_): simplify the code and reduce the number of DOM calls 2014-08-26 15:00:35 -07:00
Igor Minar 4ebbd7e210 refactor(nodeName_): remove IE8 specific branch 2014-08-26 15:00:34 -07:00
Tobias Bosch 5f90340abb fix(input): allow to use seconds in input[time] and input[datetime-local]
The HTML5 spec allows to use seconds for `input[time]` and `input[datetime-local]`,
even though they are not displayed by all browsers.

Related to #8447.
2014-08-26 14:21:05 -07:00
Tobias Bosch cc6fc199f5 feat(input): allow to define the timezone for parsing dates
Angular used to always use the browser timezone when parsing
`input[date]`, `input[time]`, … The timezone can now be changed
to `UTC` via `ngModelOptions`.

Closes #8447.
2014-08-26 14:21:02 -07:00
Tobias Bosch 29f0b568de fix(input): use year 1970 instead of 1900 for input[time]
BREAKING CHANGE:

According to the HTML5 spec `input[time]` should create dates
based on the year 1970 (used to be based on the year 1900).

Related to #8447.
2014-08-26 14:20:55 -07:00
Tobias Bosch 4739b1d9da feat(filter): allow to define the timezone for formatting dates
Angular used to always use the browser timezone for
`dateFilter`. An additional parameter was added to allow to use
`UTC` timezone instead.

Related to #8447.
2014-08-26 14:20:49 -07:00
Tobias Bosch feed7d6944 chore(tests): use jquery again in e2e tests
jQuery was not included in e2e tests, but we did not notice it
as Angular fell back to jqlite…
2014-08-26 14:19:25 -07:00
Caitlin Potter 9da8d63ef4 docs($q): correct @ngdoc annotations for methods of $q
Closes #8782
Closes #8784
2014-08-26 17:11:37 -04:00
Jeff Cross 0462b688f9 chore($q): replace plain TypeError with minErr+TypeError in cyclical resolve check 2014-08-26 12:33:51 -07:00
Jeff Cross 1b331f3729 chore($q): convert thrown Error to $minErr when calling $q constructor without resolver 2014-08-26 12:09:26 -07:00
Jeff Cross a6bd4bc866 feat(minErr): allow specifying ErrorConstructor in minErr constructor
In some cases, the type of Error thrown by minErr is meaningful, such as in $q where a TypeError
is sometimes required. This fix allows providing an error constructor as the second argument to
minErr, which will be used to construct the error that gets returned by the factory function.
2014-08-26 12:09:26 -07:00
Matias Niemelä 23da614043 fix($animate): use $timeout to handle the delay within staggering animations
When transition-delay and animation-delay were used to drive the staggering
animation the result was unpredictable at times due to the browser not being
able to register the generated delay styles in time. This caused a hard to
track down bug that didn't have a solid solution when styles were being used.

This fix ensures that stagger delays are handled by the $timeout service.

Closes #7228
Closes #7547
Closes #8297
Closes #8547

BREAKING CHANGE

If any stagger code consisted of having BOTH transition staggers and delay staggers
together then that will not work the same way. Angular will now instead choose
the highest stagger delay value and set the timeout to wait for that before
applying the active CSS class.
2014-08-26 11:45:00 -04:00
Matias Niemelä bf0f5502b1 feat($animate): use promises instead of callbacks for animations
The $animate service (both the service inside of ng and ngAnimate) now
makes use of promises instead of callback functions.

BREAKING CHANGE

Both the API for the cancallation method and the done callback for
$animate animations is different. Instead of using a callback function
for each of the $animate animation methods, a promise is used instead.

```js
//before
$animate.enter(element, container, null, callbackFn);

//after
$animate.enter(element, container).then(callbackFn);
```

The animation can now be cancelled via `$animate.cancel(promise)`.

```js
//before
var cancelFn = $animate.enter(element, container);
cancelFn(); //cancels the animation

//after
var promise = $animate.enter(element, container);
$animate.cancel(promise); //cancels the animation
```
2014-08-26 11:44:25 -04:00
Matias Niemelä 2f4437b3a1 feat($animate): coalesce concurrent class-based animations within a digest loop
All class-based animation methods (addClass, removeClass and setClass) on $animate
are now processed after the next digest occurs. This fix prevents any sequencing
errors from occuring from excessive calls to $animate.addClass, $animate.remoteClass
or $animate.setClass.

BREAKING CHANGE

$animate.addClass, $animate.removeClass and $animate.setClass will no longer start the animation
right after being called in the directive code. The animation will only commence once a digest
has passed. This means that all animation-related testing code requires an extra digest to kick
off the animation.

```js
//before this fix
$animate.addClass(element, 'super');
expect(element).toHaveClass('super');

//now
$animate.addClass(element, 'super');
$rootScope.$digest();
expect(element).toHaveClass('super');
```

$animate will also tally the amount of times classes are added and removed and only animate
the left over classes once the digest kicks in. This means that for any directive code that
adds and removes the same CSS class on the same element then this may result in no animation
being triggered at all.

```js
$animate.addClass(element, 'klass');
$animate.removeClass(element, 'klass');

$rootScope.$digest();

//nothing happens...
```
2014-08-26 11:43:18 -04:00
Matias Niemelä d0b41890bf chore(ngAnimate): fix if statement whitespacing 2014-08-26 10:29:47 -04:00
grsmvg 7ef2921cae docs(form): add dollar sign back to setSubmitted()
Dollar sign was missing for setSubmitted() due to fc73256464, just
adding it back in ヽ(^。^)ノ

Closes #8772
2014-08-26 03:58:54 -04:00
Richard Harrington 5e15b11509 refactor($injector): remove unused strictDi argument from createInternalInjector
createInternalInjector does not specify the formal parameter `strictDi`, and instead uses the binding
from the parent function's formal parameters, making this parameter unnecessary.

Closes #8771
2014-08-26 01:37:44 -04:00
dennishall1 42f28751e0 docs(dateFilter): add example of string literals in format string
Also changes the wording to include the word "escaped" and "escape", which may help users find the
information they're looking for via searching. (ノ◕ヮ◕)ノ*:・゚✧

Closes #8770
2014-08-25 23:16:21 -04:00
Akhlesh b728030be0 docs(guide/directive): update since 'EA' is now the default restrict value
11f5aeeee9 changed the compiler to use 'EA' as a 'restrict'
value if not specified in the directive object, and the directive guide needed some slight
changes to address this.

Closes #8769
2014-08-25 22:53:08 -04:00
vdyckn 2e3a7fd3e9 docs(guide/directive) example79 ptor missing attr
Closes #8257
2014-08-25 12:28:33 -07:00
Jason Bedard fdf9989f7c perf($compile): only iterate over elements with link functions
Closes #8741
2014-08-25 11:02:48 -07:00
Richard Harrington addfc567fd refactor($injector): remove unused invoke queue variable
Closes #8755
2014-08-24 19:14:20 +02:00
Smith a9371a1875 docs(guide/di): correct spelling behinds > behind
Closes #8749
2014-08-23 16:37:07 -04:00
Pawel Kozlowski 8ac90357a6 fix($parse): properly handle dots at the end of identifiers
Fixes #4613
Fixes #4912
Closes #8559
2014-08-23 11:38:24 +02:00
Peter Bacon Darwin 525a8f851e docs($compile): fix grammar and specify version for deprecation 2014-08-23 07:07:38 +01:00
922 changed files with 57311 additions and 5666 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"directory": "bower_components",
"json": "bower.json"
}
+1 -2
View File
@@ -10,8 +10,7 @@ performance/temp*.html
*.swp
angular.js.tmproj
/node_modules/
/components/
/bower_components/
bower_components/
angular.xcodeproj
.idea
.agignore
-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
+734
View File
@@ -1,3 +1,729 @@
<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)
## Bug Fixes
- **i18n:** fix typo at i18n generation code
([1b6d74cc](https://github.com/angular/angular.js/commit/1b6d74cc9f7f7b7bd529abe6ce612de3ae661601))
- **ngLocale:** Regenerate Locale Files
([06c76694](https://github.com/angular/angular.js/commit/06c76694ac9b2280594712e6a4b46a1d5987d098))
- **select:** update option labels when model changes
([d89d59f4](https://github.com/angular/angular.js/commit/d89d59f453d4e28be4f595fea7e2c4ff2338351f),
[#9025](https://github.com/angular/angular.js/issues/9025))
<a name="1.3.0-rc.2"></a>
# 1.3.0-rc.2 tactile-perception (2014-09-16)
## Bug Fixes
- **$compile:** update `'@'`-bindings in controller when `bindToController` is `true`
([e7ac08a0](https://github.com/angular/angular.js/commit/e7ac08a0619d2bdc91c125d341772b4fbc0d5a78),
[#9052](https://github.com/angular/angular.js/issues/9052), [#9077](https://github.com/angular/angular.js/issues/9077))
- **$parse:** ensure CSP assignable expressions have `assign()`
([d13b4bd1](https://github.com/angular/angular.js/commit/d13b4bd1f5f2abaad00f5d1bf81f79549a8d0e46),
[#9048](https://github.com/angular/angular.js/issues/9048))
- **i18n:** fix typo at i18n generation code
([eb4afd45](https://github.com/angular/angular.js/commit/eb4afd45f77d7d67744e01ce63a831c13c2b22e8))
- **input:** always pass in the model value to `ctrl.$isEmpty`
([3e51b84b](https://github.com/angular/angular.js/commit/3e51b84bc19f7e6acc61cb536ddcdbfed307c831),
[#5164](https://github.com/angular/angular.js/issues/5164), [#9017](https://github.com/angular/angular.js/issues/9017))
- **jqLite:** fix `event.stopImmediatePropagation()` so it works as expected
([30354c58](https://github.com/angular/angular.js/commit/30354c58fe2bd371df364f7a3f55b270692a4051),
[#4833](https://github.com/angular/angular.js/issues/4833))
- **ngLocale:** Regenerate Locale Files
([6a96a820](https://github.com/angular/angular.js/commit/6a96a8200aff4749bc84c44a1e8018b09d9ebdb4),
[#8931](https://github.com/angular/angular.js/issues/8931), [#8583](https://github.com/angular/angular.js/issues/8583), [#7799](https://github.com/angular/angular.js/issues/7799))
- **ngModel:**
- do not reset bound date objects
([1a1ef629](https://github.com/angular/angular.js/commit/1a1ef62903c8fdf4ceb81277d966a8eff67f0a96),
[#6666](https://github.com/angular/angular.js/issues/6666))
- dont clear the model when an external validator failed
([9314719d](https://github.com/angular/angular.js/commit/9314719d1eb5f480b877f5513f6e0e474edcb67d),
[#8357](https://github.com/angular/angular.js/issues/8357), [#8080](https://github.com/angular/angular.js/issues/8080))
- **ngResource:** make badcfg error message more helpful
([a3962f0d](https://github.com/angular/angular.js/commit/a3962f0df3f9b8382b47952f9e4fcb48a4cc098b),
[#9005](https://github.com/angular/angular.js/issues/9005), [#9010](https://github.com/angular/angular.js/issues/9010))
- **select:** update option labels when model changes
([46274102](https://github.com/angular/angular.js/commit/46274102454038ee7fd4543a32166e9bbbc98904),
[#9025](https://github.com/angular/angular.js/issues/9025))
## Features
- **limitTo:** support numeric input to limitTo
([1c8a7459](https://github.com/angular/angular.js/commit/1c8a7459c90efc77b1a0987f976e3bddab4565fe),
[#8926](https://github.com/angular/angular.js/issues/8926))
- **ngInclude:** add template url parameter to events
([fd2d6c02](https://github.com/angular/angular.js/commit/fd2d6c02f9654e753d3655a3377a9534f7a54de3),
[#8453](https://github.com/angular/angular.js/issues/8453), [#8454](https://github.com/angular/angular.js/issues/8454))
## Performance Improvements
- **$compile:** move `$$isolateBinding` creation to directive factory instead of on each link
([56f09f0b](https://github.com/angular/angular.js/commit/56f09f0b44048b62f964d29db4d3d2630662f6ea))
- **$parse:**
- execute watched expressions only when the inputs change
([fca6be71](https://github.com/angular/angular.js/commit/fca6be71274e537c7df86ae9e27a3bd1597e9ffa),
[#9006](https://github.com/angular/angular.js/issues/9006), [#9082](https://github.com/angular/angular.js/issues/9082))
- remove `binaryFn` and `valueFn` wrappers from filter expressions
([67919c80](https://github.com/angular/angular.js/commit/67919c808771a9b185a9d552cd32a90748d36666))
## Breaking Changes
- **$parse:** due to [fca6be71](https://github.com/angular/angular.js/commit/fca6be71274e537c7df86ae9e27a3bd1597e9ffa),
all filters are assumed to be stateless functions
Previously it was just a good practice to make all filters stateless. Now
it's a requirement in order for the model change-observation to pick up
all changes.
If an existing filter is statefull, it can be flagged as such but keep in
mind that this will result in a significant performance-penalty (or rather
lost opportunity to benefit from a major perf improvement) that will
affect the `$digest` duration.
To flag a filter as stateful do the following:
```javascript
myApp.filter('myFilter', function() {
function myFilter(input) { ... };
myFilter.$stateful = true;
return myFilter;
});
```
<a name="1.3.0-rc.1"></a>
# 1.3.0-rc.1 backyard-atomicity (2014-09-09)
## Bug Fixes
- **$location:**
- don't call toString on null values
([c3a58a9f](https://github.com/angular/angular.js/commit/c3a58a9f34919f121587540e03ecbd51b25198d4))
- remove an unused parameter of $location.url
([99d95f16](https://github.com/angular/angular.js/commit/99d95f1639b64c39231448d77209676b54e6f0be))
- allow numeric location setter arguments
([adb5c6d6](https://github.com/angular/angular.js/commit/adb5c6d6cc76b928436743707727ab0974d6810b),
[#7054](https://github.com/angular/angular.js/issues/7054))
- set `baseHref` in mock browser to `/`
([fc706d13](https://github.com/angular/angular.js/commit/fc706d13d80bb40eb3dade58ea4b92dca33ce4e7),
[#8866](https://github.com/angular/angular.js/issues/8866), [#8889](https://github.com/angular/angular.js/issues/8889))
- **$parse:** disallow passing Function to Array.sort
([bd8ad0fb](https://github.com/angular/angular.js/commit/bd8ad0fbe81f6c280baa26a596d78e58fc7842e6))
- **input:** check `scope.$$phase` only on `$rootScope`
([bf59d727](https://github.com/angular/angular.js/commit/bf59d7274f4a667c5b19e6d4ba5ed2730ca2fe42))
- **ngAnimate:** support removing classes from SVG elements when using jQuery
([b3b67213](https://github.com/angular/angular.js/commit/b3b672130d4d1c6f13bdf7e58be76b2aafea2497),
[#8872](https://github.com/angular/angular.js/issues/8872), [#8893](https://github.com/angular/angular.js/issues/8893))
- **ngEventDirs:** check `scope.$$phase` only on `$rootScope`
([203ea10f](https://github.com/angular/angular.js/commit/203ea10f9ea49d7e29569a4232d3b2a666307cd8),
[#8891](https://github.com/angular/angular.js/issues/8891))
- **ngForm:** don't clear validity of whole form when removing control
([953ee22f](https://github.com/angular/angular.js/commit/953ee22f76f8c1137949ed07f36fafc5bbfeb7fe),
[#8863](https://github.com/angular/angular.js/issues/8863))
- **ngInclude:** correctly add svg-namespaced template content
([6639ca9d](https://github.com/angular/angular.js/commit/6639ca9d6bc00a6e3a31e54c50474361ae3561c6),
[#7538](https://github.com/angular/angular.js/issues/7538), [#8981](https://github.com/angular/angular.js/issues/8981), [#8997](https://github.com/angular/angular.js/issues/8997))
- **ngModel:**
- update model value with async validators correctly
([64c3b745](https://github.com/angular/angular.js/commit/64c3b745fba0792166f30e057f9251f263d80dac))
- render immediately also with async validators
([f94d5515](https://github.com/angular/angular.js/commit/f94d551529b7c970c38b29e3073cec4e7f6b0e00))
- properly parse min/max date values as strings for date inputs
([088545c1](https://github.com/angular/angular.js/commit/088545c1856ce1c3ec3416965dff65077a6e0523),
[#6755](https://github.com/angular/angular.js/issues/6755))
- revalidate the model when min/max expression values change for date inputs
([b3502835](https://github.com/angular/angular.js/commit/b3502835039178296b730b7526e5666b66ba9156),
[#6755](https://github.com/angular/angular.js/issues/6755))
- consider ngMin/ngMax values when validating number input types
([25541c1f](https://github.com/angular/angular.js/commit/25541c1f876a16c892d71faae11727bec7bba98c))
- revalidate the model when min/max expression values change for number inputs
([7b273a2c](https://github.com/angular/angular.js/commit/7b273a2c978d5f5ef374f5335afab0ca7d8cfd4d),
[#2404](https://github.com/angular/angular.js/issues/2404))
- **ngModelOptions:** do not trigger digest on `setViewValue` if debouncing
([e322cd9b](https://github.com/angular/angular.js/commit/e322cd9b3b8b47b95c9de3edf631bb46f919c492),
[#8814](https://github.com/angular/angular.js/issues/8814), [#8850](https://github.com/angular/angular.js/issues/8850), [#8911](https://github.com/angular/angular.js/issues/8911))
- **ngRepeat:** preserve original position of elements that are being animated away
([ed637330](https://github.com/angular/angular.js/commit/ed6373300028deda9a0878b3975699d183c1f75c),
[#8918](https://github.com/angular/angular.js/issues/8918), [#8994](https://github.com/angular/angular.js/issues/8994))
- **ngSwitch:** ensure correct iterator is passed to async function
([712299c2](https://github.com/angular/angular.js/commit/712299c2a24390e74cd5c20f51cb1d78f0233b6f),
[#8833](https://github.com/angular/angular.js/issues/8833))
- **numberFilter:** format numbers that round to zero as nonnegative
([ae952fbf](https://github.com/angular/angular.js/commit/ae952fbf0be925a48743d1c925ffe4e31a42c280),
[#8489](https://github.com/angular/angular.js/issues/8489))
- **orderBy:** allow arrayLike objects to be ordered
([cbdaabfb](https://github.com/angular/angular.js/commit/cbdaabfb59bf3348588d5b581f2754e0f9f034a4),
[#8944](https://github.com/angular/angular.js/issues/8944))
## Features
- **angular.forEach:** add the array/object as the 3rd param like the native array forEach
([df9e60c8](https://github.com/angular/angular.js/commit/df9e60c8e7453cdca2cb5a4fa48f3981ecc23a7d),
[#7902](https://github.com/angular/angular.js/issues/7902))
- **ngModelOptions:** add allowInvalid option
([3c538c1d](https://github.com/angular/angular.js/commit/3c538c1d21c43422c7b4cd9b69cb67981bce2b87),
[#8290](https://github.com/angular/angular.js/issues/8290), [#8313](https://github.com/angular/angular.js/issues/8313))
## Performance Improvements
- **$parse:**
- remove getterFn wrapper for internal use
([b3b476db](https://github.com/angular/angular.js/commit/b3b476db7d34bc2f8b099ab5b993b1e899b9cffd),
[#8901](https://github.com/angular/angular.js/issues/8901))
- removing references to Parser/Lexer from parsed expressions
([43c67ccd](https://github.com/angular/angular.js/commit/43c67ccd167aecc3549e1b7f7d100956204e3ed4))
- calculate array lengths once at start of loop
([907b8c16](https://github.com/angular/angular.js/commit/907b8c1675865ac38dd055f3f304272e68b233d0))
- **extend:** remove use of forEach to remove calls/closures/passing arguments
([9bedeb33](https://github.com/angular/angular.js/commit/9bedeb3353969fba631ad9164edea3c38059fbda),
[#8898](https://github.com/angular/angular.js/issues/8898))
- **jQuery:** only trigger $destroy if a handler exists
([f6aa1c55](https://github.com/angular/angular.js/commit/f6aa1c55616b34215f562e0445e436210860ef04),
[#8859](https://github.com/angular/angular.js/issues/8859))
## Breaking Changes
- **ngModelController,formController:** due to [6046e14b](https://github.com/angular/angular.js/commit/6046e14bd22491168116e61ffdf5fd3fed5f135c),
- `ctrl.$error` no longer contains entries for validators that were
successful.
- `ctrl.$setValidity` now differentiates between `true`, `false`,
`undefined` and `null`, instead of previously only truthy vs falsy.
Closes #8941- **ngSwitch:** due to [0f806d96](https://github.com/angular/angular.js/commit/0f806d9659b5b89a4bd9493364bc36398677e939),
Ever since 0df93fd, tagged in v1.0.0rc1, the ngSwitch directive has had an undocumented `change`
attribute, used for evaluating a scope expression when the switch value changes.
While it's unlikely, applications which may be using this feature should work around the removal
by adding a custom directive which will perform the eval instead. Directive controllers are
re-instantiated when being transcluded, so by putting the attribute on each item that you want
to be notified of a change to, you can more or less emulate the old behaviour.
Example:
```js
angular.module("switchChangeWorkaround", []).
directive("onSwitchChanged", function() {
return {
link: function($scope, $element, $attrs) {
$scope.$parent.$eval($attrs.onSwitchChanged);
}
};
});
```
```html
<div ng-switch="switcher">
<div ng-switch-when="a" on-switch-changed="doSomethingInParentScope()"></div>
<div ng-switch-when="b" on-switch-changed="doSomethingInParentScope()"></div>
</div>
```
Closes #8858
Closes #8822
<a name="1.2.24"></a>
# 1.2.24 static-levitation (2014-09-09)
## Bug Fixes
- **$browser:** detect changes to the browser url that happened in sync
([2ece4d03](https://github.com/angular/angular.js/commit/2ece4d0347a8a18d4d35993bb882ed6b5b24266c),
[#6976](https://github.com/angular/angular.js/issues/6976))
- **$compile:**
- render nested transclusion at the root of a template
([9d9cdfb5](https://github.com/angular/angular.js/commit/9d9cdfb575b89e96ae957c986734a49995e2b511),
[#8914](https://github.com/angular/angular.js/issues/8914), [#8925](https://github.com/angular/angular.js/issues/8925))
- render nested transclusion at the root of a template
([466320f6](https://github.com/angular/angular.js/commit/466320f6911698048bae5406e341d25af7efafa0),
[#8914](https://github.com/angular/angular.js/issues/8914), [#8925](https://github.com/angular/angular.js/issues/8925))
- **$location:**
- don't call toString on null values
([c12e8d46](https://github.com/angular/angular.js/commit/c12e8d4665b635ba6b09d12802efb88d38b7ad5c))
- remove an unused parameter of $location.url
([c65796d4](https://github.com/angular/angular.js/commit/c65796d496038554861e70da8012f9d0e2521e6d))
- allow numeric location setter arguments
([68a09ba7](https://github.com/angular/angular.js/commit/68a09ba74d10a1490feca1d248f85b0023aa399b),
[#7054](https://github.com/angular/angular.js/issues/7054))
- **$parse:** disallow passing Function to Array.sort
([b39e1d47](https://github.com/angular/angular.js/commit/b39e1d47b9a1b39a9fe34c847a81f589fba522f8))
- **form:** ensure concurrent animations use setClass
([d7548fdf](https://github.com/angular/angular.js/commit/d7548fdf1ce6f543bf55d330985a83ef09d0cb83),
[#8166](https://github.com/angular/angular.js/issues/8166))
- **input:** check `scope.$$phase` only on `$rootScope`
([36e6de1d](https://github.com/angular/angular.js/commit/36e6de1d91937d73e900ac115ae366fbefcdf6da))
- **ngEventDirs:**
- check `scope.$$phase` only on `$rootScope`
([2712c2f1](https://github.com/angular/angular.js/commit/2712c2f1979db23eeb53be8a519b9f79bd75e217),
[#8891](https://github.com/angular/angular.js/issues/8891))
- execute `blur` and `focus` expression using `scope.$evalAsync`
([54f0bc0f](https://github.com/angular/angular.js/commit/54f0bc0fe0c6b6d974d23f2c5ef07359dd93eb99),
[#4979](https://github.com/angular/angular.js/issues/4979), [#5945](https://github.com/angular/angular.js/issues/5945), [#8803](https://github.com/angular/angular.js/issues/8803), [#6910](https://github.com/angular/angular.js/issues/6910), [#5402](https://github.com/angular/angular.js/issues/5402))
- **ngRepeat:** improve errors for duplicate items
([1812af58](https://github.com/angular/angular.js/commit/1812af58c2d470d586c2a543c9a7db3f0baca04f))
- **numberFilter:** format numbers that round to zero as nonnegative
([7e02fa07](https://github.com/angular/angular.js/commit/7e02fa07eb5b02e75b1db0058d638af3d1074942),
[#8489](https://github.com/angular/angular.js/issues/8489))
- **orderBy:** allow arrayLike objects to be ordered
([94b0f2d3](https://github.com/angular/angular.js/commit/94b0f2d35de601ded3d93ea4fa78a4d9b139c0a0),
[#8944](https://github.com/angular/angular.js/issues/8944))
## Breaking Changes
- **ngEventDirs:** due to [54f0bc0f](https://github.com/angular/angular.js/commit/54f0bc0fe0c6b6d974d23f2c5ef07359dd93eb99),
The `blur` and `focus` event fire synchronously, also during DOM operations
that remove elements. This lead to errors as the Angular model was not
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.
This change executes the expression of those events using
`scope.$evalAsync` if an `$apply` is in progress, otherwise
keeps the old behavior.
Fixes #4979
Fixes #5945
Closes #8803
Closes #6910
Closes #5402
<a name="1.3.0-RC.0"></a>
# 1.3.0-RC.0 sonic-boltification (2014-08-29)
## Bug Fixes
- **$animate:**
- wait two until two digests are over until enabling animations
([92576743](https://github.com/angular/angular.js/commit/92576743eec0cef5ffdd701b83f72a61e6489c3b),
[#8844](https://github.com/angular/angular.js/issues/8844))
- ensure guarded animations consider AJAX requests upon bootstrap
([4bca4c44](https://github.com/angular/angular.js/commit/4bca4c44b95a7435722605a750804043f2960160),
[#8275](https://github.com/angular/angular.js/issues/8275), [#5262](https://github.com/angular/angular.js/issues/5262))
- use $timeout to handle the delay within staggering animations
([23da6140](https://github.com/angular/angular.js/commit/23da614043fe5dcf0be132b86466eecb11c766a2),
[#7228](https://github.com/angular/angular.js/issues/7228), [#7547](https://github.com/angular/angular.js/issues/7547), [#8297](https://github.com/angular/angular.js/issues/8297), [#8547](https://github.com/angular/angular.js/issues/8547))
- **$browser:** detect changes to the browser url that happened in sync
([3be00df4](https://github.com/angular/angular.js/commit/3be00df495f6eed3b3d9587ebab1fdd633e94e08),
[#6976](https://github.com/angular/angular.js/issues/6976))
- **$compile:** use the correct namespace for transcluded svg elements
([cb73a37c](https://github.com/angular/angular.js/commit/cb73a37c7cae5cdebadf7b3ddd44c5a452495e4e),
[#8808](https://github.com/angular/angular.js/issues/8808), [#8816](https://github.com/angular/angular.js/issues/8816))
- **$location:** always resolve relative links in html5mode to `<base>` url
([22948807](https://github.com/angular/angular.js/commit/22948807e324eb0b182b15b31045dc306a9f3231),
[#8492](https://github.com/angular/angular.js/issues/8492), [#8172](https://github.com/angular/angular.js/issues/8172))
- **$parse:** properly handle dots at the end of identifiers
([8ac90357](https://github.com/angular/angular.js/commit/8ac90357a66ae0c62dbfe6db2c6eaf1d600ecc65),
[#4613](https://github.com/angular/angular.js/issues/4613), [#4912](https://github.com/angular/angular.js/issues/4912), [#8559](https://github.com/angular/angular.js/issues/8559))
- **Angular:** remove duplicate nodeName_ references
([a4520a74](https://github.com/angular/angular.js/commit/a4520a745d917c77f1d12cdbce48272c643f7255))
- **currencyFilter:** pass through null and undefined values
([c2aaddbe](https://github.com/angular/angular.js/commit/c2aaddbe4b21348aab8c13a78cdd6aaee846ae4e),
[#8605](https://github.com/angular/angular.js/issues/8605))
- **docs:** don't throw exception on the 404 page
([550ba01b](https://github.com/angular/angular.js/commit/550ba01b325fc29460030fc9c24fa00269dec2a9),
[#8518](https://github.com/angular/angular.js/issues/8518))
- **input:**
- validate minlength/maxlength for non-string values
([77ce5b89](https://github.com/angular/angular.js/commit/77ce5b89f97aa83c3eb1fe2e19375ef00a822015),
[#7967](https://github.com/angular/angular.js/issues/7967), [#8811](https://github.com/angular/angular.js/issues/8811))
- allow to use seconds in `input[time]` and `input[datetime-local]`
([5f90340a](https://github.com/angular/angular.js/commit/5f90340abb78aa08dde4876328bcc00e46232e46))
- use year 1970 instead of 1900 for `input[time]`
([29f0b568](https://github.com/angular/angular.js/commit/29f0b568debab7810752969d363d337099e96cdc))
- **ngBindHtml:** throw error if interpolation is used in expression
([cd21602d](https://github.com/angular/angular.js/commit/cd21602d5b1650d8be373618cb7320d697e32c4d),
[#8824](https://github.com/angular/angular.js/issues/8824))
- **ngEventDirs:** execute `blur` and `focus` expression using `scope.$evalAsync`
([719c747c](https://github.com/angular/angular.js/commit/719c747cd892ee933e7e414a7dc97e657b88317d),
[#4979](https://github.com/angular/angular.js/issues/4979), [#5945](https://github.com/angular/angular.js/issues/5945), [#8803](https://github.com/angular/angular.js/issues/8803), [#6910](https://github.com/angular/angular.js/issues/6910), [#5402](https://github.com/angular/angular.js/issues/5402))
- **ngModel:**
- always format the viewValue as a string for text, url and email types
([1eda1836](https://github.com/angular/angular.js/commit/1eda18365a348c9597aafba9d195d345e4f13d1e))
- allow non-assignable binding when getterSetter is used
([ab878a6c](https://github.com/angular/angular.js/commit/ab878a6c038f47b95f3a7e85a4fdb599e0c73e63),
[#8704](https://github.com/angular/angular.js/issues/8704))
- treat undefined parse responses as parse errors
([db044c40](https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a))
- **ngRepeat:** improve errors for duplicate items
([0604bb7b](https://github.com/angular/angular.js/commit/0604bb7b7a6156e33679396e805e327662d9a178))
- **ngSwitch:** avoid removing DOM nodes twice within watch operation
([c9b0bfec](https://github.com/angular/angular.js/commit/c9b0bfecc99837af1c97792b3ca3408ba182b0bb),
[#8662](https://github.com/angular/angular.js/issues/8662))
- **numberFilter:** pass through null and undefined values
([2ae10f67](https://github.com/angular/angular.js/commit/2ae10f67fcde3e172f695956301ef796b68a50c2),
[#8605](https://github.com/angular/angular.js/issues/8605), [#8842](https://github.com/angular/angular.js/issues/8842))
## Features
- **core:**
- add angular.reloadWithDebugInfo()
([41c1b88](https://github.com/angular/angular.js/commit/41c1b8858f02c7310bfabdd545ebb28e90eb4258))
- **$animate:**
- use promises instead of callbacks for animations
([bf0f5502](https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9))
- coalesce concurrent class-based animations within a digest loop
([2f4437b3](https://github.com/angular/angular.js/commit/2f4437b3a149eafb899f25933bd6c713b167d10e))
- **$compile:**
- bind isolate scope properties to controller
([5f3f25a1](https://github.com/angular/angular.js/commit/5f3f25a1a6f9d4f2a66e2700df3b9c5606f1c255),
[#7635](https://github.com/angular/angular.js/issues/7635), [#7645](https://github.com/angular/angular.js/issues/7645))
- allow disabling scope info
([a1e5cd5f](https://github.com/angular/angular.js/commit/a1e5cd5fe3906ebee8c400247a1f793d3e2239fb))
- **$compile/ngBind:** allow disabling binding info
([3660fd09](https://github.com/angular/angular.js/commit/3660fd0912d3ccf6def8c9f02d8d4c0621c8d91f))
- **$http:** implement mechanism for coalescing calls to $apply in $http
([ea6fc6e6](https://github.com/angular/angular.js/commit/ea6fc6e69c2a2aa213c71ed4e917a0d54d064e4c),
[#8736](https://github.com/angular/angular.js/issues/8736), [#7634](https://github.com/angular/angular.js/issues/7634), [#5297](https://github.com/angular/angular.js/issues/5297))
- **$rootScope:** implement $applyAsync to support combining calls to $apply into a single digest.
([e94d454b](https://github.com/angular/angular.js/commit/e94d454b840f6cc55a440741382b407836ad245b))
- **$templateRequest:** introduce the $templateRequest service
([a70e2833](https://github.com/angular/angular.js/commit/a70e2833ea276107b11aafea96ef4a6724ad4d83))
- **filter:** allow to define the timezone for formatting dates
([4739b1d9](https://github.com/angular/angular.js/commit/4739b1d9daebfd094b6181c5f2cb52ff71e31c61))
- **filterFilter:** pass index to function predicate
([46343c60](https://github.com/angular/angular.js/commit/46343c603db6192daf5303b92eb664749326c7e6),
[#654](https://github.com/angular/angular.js/issues/654))
- **input:** allow to define the timezone for parsing dates
([cc6fc199](https://github.com/angular/angular.js/commit/cc6fc199f5abaacdf781aa03634337d776eb0fc9),
[#8447](https://github.com/angular/angular.js/issues/8447))
- **minErr:** allow specifying ErrorConstructor in minErr constructor
([a6bd4bc8](https://github.com/angular/angular.js/commit/a6bd4bc866a18f860c7548fa1b3f6d4c2a953416))
- **ngModel:** provide validation API functions for sync and async validations
([2ae4f40b](https://github.com/angular/angular.js/commit/2ae4f40be1803d999ca2a8cc30ec17ff19ea6d86))
- **ngRoute:** alias string as redirectTo property in .otherwise()
([3b5d75c0](https://github.com/angular/angular.js/commit/3b5d75c021e21fa6ec4dc6c47b8eafa55680ea63),
[#7794](https://github.com/angular/angular.js/issues/7794))
- **testability:** add $$testability service
([85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5))
## Performance Improvements
- **$compile:**
- add debug classes in compile phase
([e0489abd](https://github.com/angular/angular.js/commit/e0489abd8d9e4971ae23cc38805a92d227d1f3a1))
- only iterate over elements with link functions
([fdf9989f](https://github.com/angular/angular.js/commit/fdf9989f7cf1ed81982a788b75a338ac33334571),
[#8741](https://github.com/angular/angular.js/issues/8741))
- **nodeName_:** simplify the code and reduce the number of DOM calls
([5a1a0c96](https://github.com/angular/angular.js/commit/5a1a0c96220101b5e040f0755e5eb401e2c73f65))
- **select:** execute render after $digest cycle
([6f7018d5](https://github.com/angular/angular.js/commit/6f7018d52fa4f9f9c7fa8e3035317d1239efb20f),
[#8825](https://github.com/angular/angular.js/issues/8825))
## Breaking Changes
- **$location**: due to [22948807](https://github.com/angular/angular.js/commit/22948807e324eb0b182b15b31045dc306a9f3231)
#### since 1.2.0 and 1.3.0-beta.1
Angular now requires a `<base>` tag when html5 mode of `$location` is enabled. Reasoning:
Using html5 mode without a `<base href="...">` tag makes relative links for images, links, ...
relative to the current url if the browser supports
the history API. However, if the browser does not support the history API Angular falls back to using the `#`,
and then all those relative links would be broken.
The `<base>` tag is also needed when a deep url is loaded from the server, e.g. `http://server/some/page/url`.
In that case, Angular needs to decide which part of the url is the base of the application, and which part
is path inside of the application.
To summarize: Now all relative links are always relative to the `<base>` tag.
Exception (also a breaking change):
Link tags whose `href` attribute starts with a `#` will only change the hash of the url, but nothing else
(e.g. `<a href="#someAnchor">`). This is to make it easy to scroll to anchors inside a document.
Related to #6162
Closes #8492
#### since 1.2.17 and 1.3.0-beta.10
In html5 mode without a `<base>` tag on older browser that don't support the history API
relative paths were adding up. E.g. clicking on `<a href="page1">` and then on `<a href="page2">`
would produce `$location.path()==='/page1/page2'`. The code that introduced this behavior was removed
and Angular now also requires a `<base>` tag to be present when using html5 mode.
Closes #8172, #8233
- **ngInclude, ngMessage, ngView and directives that load templates**: due to [a70e2833](https://github.com/angular/angular.js/commit/a70e2833ea276107b11aafea96ef4a6724ad4d83)
Angular will now throw a $compile minErr each a template fails to download
for ngView, directives and ngMessage template requests. This changes the former
behavior of silently ignoring failed HTTP requests--or when the template itself
is empty. Please ensure that all directive, ngView and ngMessage code now properly
addresses this scenario. NgInclude is uneffected from this change.
- **$animate**: due to [23da6140](https://github.com/angular/angular.js/commit/23da614043fe5dcf0be132b86466eecb11c766a2)
If any stagger code consisted of having BOTH transition staggers and delay staggers
together then that will not work the same way. Angular will now instead choose
the highest stagger delay value and set the timeout to wait for that before
applying the active CSS class.
- **$animate**: due to [bf0f5502](https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9)
Both the API for the cancallation method and the done callback for
$animate animations is different. Instead of using a callback function
for each of the $animate animation methods, a promise is used instead.
```js
//before
$animate.enter(element, container, null, callbackFn);
//after
$animate.enter(element, container).then(callbackFn);
```
The animation can now be cancelled via `$animate.cancel(promise)`.
```js
//before
var cancelFn = $animate.enter(element, container);
cancelFn(); //cancels the animation
//after
var promise = $animate.enter(element, container);
$animate.cancel(promise); //cancels the animation
```
keep in mind that you will still need to run $scope.$apply inside of the `then` callback
to trigger a digest.
- **$animate**: due to [2f4437b3](https://github.com/angular/angular.js/commit/2f4437b3a149eafb899f25933bd6c713b167d10e)
$animate.addClass, $animate.removeClass and $animate.setClass will no longer start the animation
right after being called in the directive code. The animation will only commence once a digest
has passed. This means that all animation-related testing code requires an extra digest to kick
off the animation.
```js
//before this fix
$animate.addClass(element, 'super');
expect(element).toHaveClass('super');
//now
$animate.addClass(element, 'super');
$rootScope.$digest();
expect(element).toHaveClass('super');
```
$animate will also tally the amount of times classes are added and removed and only animate
the left over classes once the digest kicks in. This means that for any directive code that
adds and removes the same CSS class on the same element then this may result in no animation
being triggered at all.
```js
$animate.addClass(element, 'klass');
$animate.removeClass(element, 'klass');
$rootScope.$digest();
//nothing happens...
```
- **$compile/ngBind:** due to [3660fd09](https://github.com/angular/angular.js/commit/3660fd0912d3ccf6def8c9f02d8d4c0621c8d91f),
The value of `$binding` data property on an element is always an array now
and the expressions do not include the curly braces `{{ ... }}`.
- **currencyFilter:** due to [c2aaddbe](https://github.com/angular/angular.js/commit/c2aaddbe4b21348aab8c13a78cdd6aaee846ae4e),
previously the currency filter would convert null and undefined values into empty string, after this change
these values will be passed through.
Only cases when the currency filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.
This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.
Closes #8605
- **numberFilter:** due to [2ae10f67](https://github.com/angular/angular.js/commit/2ae10f67fcde3e172f695956301ef796b68a50c2),
previously the number filter would convert null and undefined values into empty string, after this change
these values will be passed through.
Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.
This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.
Closes #8605
Closes #8842
- **input:**
- due to [77ce5b89](https://github.com/angular/angular.js/commit/77ce5b89f97aa83c3eb1fe2e19375ef00a822015),
NgModel.viewValue will always be used when rendering validations for `minlength` and `maxlength`.
Closes #7967
Closes #8811
- **input:**
- due to [29f0b568](https://github.com/angular/angular.js/commit/29f0b568debab7810752969d363d337099e96cdc),
According to the HTML5 spec `input[time]` should create dates
based on the year 1970 (used to be based on the year 1900).
Related to #8447.
- **ngModel**: due to [db044c40](https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a)
Any parser code from before that returned an `undefined` value
(or nothing at all) will now cause a parser failure. When this occurs
none of the validators present in `$validators` will run until the parser
error is gone. The error will be stored on `ngModel.$error`.
- **ngEventDirs:** due to [719c747c](https://github.com/angular/angular.js/commit/719c747cd892ee933e7e414a7dc97e657b88317d),
The `blur` and `focus` event fire synchronously, also during DOM operations
that remove elements. This lead to errors as the Angular model was not
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.
This change executes the expression of those events using
`scope.$evalAsync` if an `$apply` is in progress, otherwise
keeps the old behavior.
Fixes #4979
Fixes #5945
Closes #8803
Closes #6910
Closes #5402
- **$compile:** due to [5f3f25a1](https://github.com/angular/angular.js/commit/5f3f25a1a6f9d4f2a66e2700df3b9c5606f1c255),
The returned value from directive controller constructors are now ignored, and only the constructed
instance itself will be attached to the node's expando. This change is necessary in order to ensure
that it's possible to bind properties to the controller's instance before the actual constructor is
invoked, as a convenience to developers.
In the past, the following would have worked:
```js
angular.module("myApp", []).
directive("myDirective", function() {
return {
controller: function($scope) {
return {
doAThing: function() { $scope.thingDone = true; },
undoAThing: function() { $scope.thingDone = false; }
};
},
link: function(scope, element, attrs, ctrl) {
ctrl.doAThing();
}
};
});
```
However now, the reference to `doAThing()` will be undefined, because the return value of the controller's constructor is ignored. In order to work around this, one can opt for several strategies, including the use of `_.extend()` or `merge()` like routines, like so:
```js
angular.module("myApp", []).
directive("myDirective", function() {
return {
controller: function($scope) {
_.extend(this, {
doAThing: function() { $scope.thingDone = true; },
undoAThing: function() { $scope.thingDone = false; }
});
},
link: function(scope, element, attrs, ctrl) {
ctrl.doAThing();
}
};
});
```
<a name="1.2.23"></a>
# 1.2.23 superficial-malady (2014-08-22)
@@ -1281,6 +2007,7 @@ this limitation, use a regular expression object as the value for the expression
//after
$scope.exp = /abc/i;
- **Scope:** due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
Scope#$id is now of time number rather than string. Since the
id is primarily being used for debugging purposes this change should not affect
@@ -2381,6 +3108,13 @@ For more info: http://blog.angularjs.org/2013/12/angularjs-13-new-release-approa
application if 100s of elements are being inserted into the page. Therefore after this
change callbacks are only fired if registered on the element being animated.
- **input:**
- due to [a9fcb0d0](https://github.com/angular/angular.js/commit/a9fcb0d0fc6456f80501b8820d02b04d7c15b6d6),
input[type=file] will no longer support ngModel. Due to browser support being spotty among target browsers,
file inputs cannot be cleanly supported, and even features which technically do work (such as ng-change)
work in an inconsistent way depending on the attributes of the form control.
As a workaround, one can manually listen for change events on file inputs and handle them manually.
<a name="1.2.12"></a>
# 1.2.12 cauliflower-eradication (2014-02-07)
+9 -1
View File
@@ -153,6 +153,9 @@ module.exports = function(grunt) {
},
ngTouch: {
files: { src: 'src/ngTouch/**/*.js' },
},
ngAria: {
files: {src: 'src/ngAria/**/*.js'},
}
},
@@ -220,6 +223,10 @@ module.exports = function(grunt) {
dest: 'build/angular-cookies.js',
src: util.wrap(files['angularModules']['ngCookies'], 'module')
},
aria: {
dest: 'build/angular-aria.js',
src: util.wrap(files['angularModules']['ngAria'], 'module')
},
"promises-aplus-adapter": {
dest:'tmp/promises-aplus-adapter++.js',
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
@@ -236,7 +243,8 @@ module.exports = function(grunt) {
touch: 'build/angular-touch.js',
resource: 'build/angular-resource.js',
route: 'build/angular-route.js',
sanitize: 'build/angular-sanitize.js'
sanitize: 'build/angular-sanitize.js',
aria: 'build/angular-aria.js'
},
+7 -6
View File
@@ -6,16 +6,17 @@ 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
* API Docs: http://docs.angularjs.org/api
* Developer Guide: http://docs.angularjs.org/guide
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
* Dashboard: http://dashboard.angularjs.org
Building AngularJS
@@ -37,7 +38,7 @@ To execute end-to-end (e2e) tests, use:
grunt test:e2e
To learn more about the grunt tasks, run `grunt --help` and also read our
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
[contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/README.md?pixel)](https://github.com/igrigorik/ga-beacon)
-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
+11 -3
View File
@@ -34,6 +34,8 @@ var angularFiles = {
'src/ng/sanitizeUri.js',
'src/ng/sce.js',
'src/ng/sniffer.js',
'src/ng/templateRequest.js',
'src/ng/testability.js',
'src/ng/timeout.js',
'src/ng/urlUtils.js',
'src/ng/window.js',
@@ -106,6 +108,9 @@ var angularFiles = {
'src/ngTouch/directive/ngClick.js',
'src/ngTouch/directive/ngSwipe.js'
],
'ngAria': [
'src/ngAria/aria.js'
]
},
'angularScenario': [
@@ -139,7 +144,8 @@ var angularFiles = {
'test/ngRoute/**/*.js',
'test/ngSanitize/**/*.js',
'test/ngMock/*.js',
'test/ngTouch/**/*.js'
'test/ngTouch/**/*.js',
'test/ngAria/*.js'
],
'karma': [
@@ -173,7 +179,8 @@ var angularFiles = {
'test/ngRoute/**/*.js',
'test/ngResource/*.js',
'test/ngSanitize/**/*.js',
'test/ngTouch/**/*.js'
'test/ngTouch/**/*.js',
'test/ngAria/*.js'
],
'karmaJquery': [
@@ -201,7 +208,8 @@ angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngRoute'],
angularFiles['angularModules']['ngSanitize'],
angularFiles['angularModules']['ngMock'],
angularFiles['angularModules']['ngTouch']
angularFiles['angularModules']['ngTouch'],
angularFiles['angularModules']['ngAria']
);
if (exports) {
+6
View File
@@ -1,5 +1,11 @@
var app = angular.module('largetableBenchmark', []);
app.config(function($compileProvider) {
if ($compileProvider.debugInfoEnabled) {
$compileProvider.debugInfoEnabled(false);
}
});
app.filter('noop', function() {
return function(input) {
return input;
+1 -1
View File
@@ -16,7 +16,7 @@
<div>ngBind + fnInvocation: <input type=radio ng-model="benchmarkType" value="ngBindFn"></div>
<div>interpolation + fnInvocation: <input type=radio ng-model="benchmarkType" value="interpolationFn"></div>
<div>ngBind + filter: <input type=radio ng-model="benchmarkType" value="ngBindFilter"></div>
<div>ngBind + filter: <input type=radio ng-model="benchmarkType" value="interpolationFilter"></div>
<div>interpolation + filter: <input type=radio ng-model="benchmarkType" value="interpolationFilter"></div>
<ng-switch on="benchmarkType">
<baseline-binding-table ng-switch-when="baselineBinding">
+48
View File
@@ -0,0 +1,48 @@
var app = angular.module('orderByBenchmark', []);
app.controller('DataController', function($rootScope, $scope) {
this.ngRepeatCount = 5000;
this.rows = [];
var self = this;
$scope.benchmarkType = 'basic';
$scope.rawProperty = function(key) {
return function(item) {
return item[key];
};
};
// Returns a random integer between min (included) and max (excluded)
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
benchmarkSteps.push({
name: 'setup',
description: 'Set rows to empty array and apply, then push new rows to be applied in next step',
fn: function() {
var oldRows = self.rows;
$rootScope.$apply(function() {
self.rows = [];
});
self.rows = oldRows;
if (self.rows.length !== self.ngRepeatCount) {
self.rows = [];
for (var i = 0; i < self.ngRepeatCount; i++) {
self.rows.push({
'name': getRandomInt(i, (i + 40)),
'index': i
});
}
}
}
})
benchmarkSteps.push({
name: '$apply',
fn: function() {
$rootScope.$apply();
}
});
});
+14
View File
@@ -0,0 +1,14 @@
module.exports = function(config) {
config.set({
scripts: [
{
"id": "jquery",
"src": "jquery-noop.js"
},{
id: 'angular',
src: '/build/angular.js'
},{
src: 'app.js',
}]
});
};
+82
View File
@@ -0,0 +1,82 @@
<div class="container-fluid" ng-app="orderByBenchmark">
<div class="row" ng-controller="DataController as ctrl">
<div class="col-lg-8">
<p>Filters</p>
<p>
<label>Number of ngRepeats:</label>
<input type="number" ng-model="ctrl.ngRepeatCount">
</p>
<p>
<div class="radio">
<label>
<input type="radio" ng-model="benchmarkType" value="baseline">baseline
</label>
</div>
<pre><code>ng-repeat="row in ctrl.rows"</code></pre>
<br />
<div class="radio">
<label>
<input type="radio" ng-model="benchmarkType" value="orderBy">orderBy
</label>
</div>
<pre><code>ng-repeat="row in ctrl.rows | orderBy:'name'"</code></pre>
<br />
<div class="radio">
<label>
<input type="radio" ng-model="benchmarkType" value="orderByArray">orderBy array expression
</label>
</div>
<pre><code>ng-repeat="row in ctrl.rows | orderBy:['name', 'index']"</code></pre>
<br />
<div class="radio">
<label>
<input type="radio" ng-model="benchmarkType"
value="orderByFunction">orderBy function expression
</label>
</div>
<pre><code>ng-repeat="row in ctrl.rows | orderBy:rawProperty('name')"</code></pre>
<br />
<div class="radio">
<label>
<input type="radio" ng-model="benchmarkType"
value="orderByArrayFunction">orderBy array function expression
</label>
</div>
<pre><code>ng-repeat="row in ctrl.rows | orderBy:[rawProperty('name'), rawProperty('index')]"</code></pre>
</p>
Debug output:
<ng-switch on="benchmarkType">
<div ng-switch-when="baseline">
<span ng-repeat="row in ctrl.rows">
<span ng-bind="row.name"></span>,
</span>
</div>
<div ng-switch-when="orderBy">
<span ng-repeat="row in ctrl.rows | orderBy:'name'">
<span ng-bind="row.name"></span>,
</span>
</div>
<div ng-switch-when="orderByArray">
<span ng-repeat="row in ctrl.rows | orderBy:['name', 'index']">
<span ng-bind="row.name"></span>,
</span>
</div>
<div ng-switch-when="orderByFunction">
<span ng-repeat="row in ctrl.rows | orderBy:rawProperty('name')">
<span ng-bind="row.name"></span>,
</span>
</div>
<div ng-switch-when="orderByArrayFunction">
<span ng-repeat="row in ctrl.rows | orderBy:[rawProperty('name'), rawProperty('index')]">
<span ng-bind="row.name"></span>,
</span>
</div>
</ng-switch>
</div>
</div>
</div>
+86
View File
@@ -0,0 +1,86 @@
var app = angular.module('parsedExpressionBenchmark', []);
app.config(function($compileProvider) {
if ($compileProvider.debugInfoEnabled) {
$compileProvider.debugInfoEnabled(false);
}
});
app.filter('noop', function() {
return function(input) {
return input;
};
});
//Executes the specified expression as a watcher
app.directive('bmPeWatch', function() {
return {
restrict: 'A',
compile: function($element, $attrs) {
$element.text( $attrs.bmPeWatch );
return function($scope, $element, $attrs) {
$scope.$watch($attrs.bmPeWatch, function(val) {
$element.text(val);
});
};
}
};
});
//Executes the specified expression as a watcher
//Adds a simple wrapper method to allow use of $watch instead of $watchCollection
app.directive('bmPeWatchLiteral', function($parse) {
function retZero() {
return 0;
}
return {
restrict: 'A',
compile: function($element, $attrs) {
$element.text( $attrs.bmPeWatchLiteral );
return function($scope, $element, $attrs) {
$scope.$watch( $parse($attrs.bmPeWatchLiteral, retZero) );
};
}
};
});
app.controller('DataController', function($scope, $rootScope) {
var totalRows = 10000;
var data = $scope.data = [];
var star = '*';
$scope.func = function() { return star;};
for (var i=0; i<totalRows; i++) {
data.push({
index: i,
odd: i%2 === 0,
even: i%2 === 1,
str0: "foo-" + Math.random()*Date.now(),
str1: "bar-" + Math.random()*Date.now(),
str2: "baz-" + Math.random()*Date.now(),
num0: Math.random()*Date.now(),
num1: Math.random()*Date.now(),
num2: Math.random()*Date.now(),
date0: new Date(Math.random()*Date.now()),
date1: new Date(Math.random()*Date.now()),
date2: new Date(Math.random()*Date.now()),
func: function(){ return star; },
obj: data[i-1],
keys: data[i-1] && (data[i-1].keys || Object.keys(data[i-1]))
});
}
benchmarkSteps.push({
name: '$apply',
fn: function() {
for (var i=0; i<50; i++) {
$rootScope.$digest();
}
}
});
});
+11
View File
@@ -0,0 +1,11 @@
module.exports = function(config) {
config.set({
scripts: [ {
id: 'angular',
src: '/build/angular.js'
},
{
src: 'app.js',
}]
});
};
+209
View File
@@ -0,0 +1,209 @@
<div ng-app="parsedExpressionBenchmark" ng-cloak>
<div ng-controller="DataController">
<div class="container-fluid">
<p>
Tests the execution of $parse()ed expressions. Each test tries to isolate specific expression types. Expressions should (probably) not be constant so they get evaluated per digest.
</p>
<ul style="list-style:none">
<li>
<input type="radio" ng-model="expressionType" value="simplePath" id="simplePath">
<label for="simplePath">Simple Paths</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="complexPath" id="complexPath">
<label for="complexPath">Complex Paths</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="fieldAccess" id="fieldAccess">
<label for="fieldAccess">Field Accessors</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="fieldIndex" id="fieldIndex">
<label for="fieldIndex">Field Indexes</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="operators" id="operators">
<label for="operators">Binary/Unary operators</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="shortCircuitingOperators" id="shortCircuitingOperators">
<label for="shortCircuitingOperators">AND/OR short-circuiting operators</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="filters" id="filters">
<label for="filters">Filters</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="functionCalls" id="functionCalls">
<label for="functionCalls">Function calls</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="objectLiterals" id="objectLiterals">
<label for="objectLiterals">Object Literals</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="arrayLiterals" id="arrayLiterals">
<label for="arrayLiterals">Array Literals</label>
</li>
</ul>
<!--
NOTES:
- ensure each tested expression has at least one variable in it to avoid constant expressions
-->
<ul ng-switch="expressionType">
<li ng-switch-when="simplePath" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="rowIdx"></span>
<span bm-pe-watch="row.index"></span>
<span bm-pe-watch="row.num0"></span>
<span bm-pe-watch="row.num1"></span>
<span bm-pe-watch="row.num2"></span>
<span bm-pe-watch="row.str0"></span>
<span bm-pe-watch="row.str1"></span>
<span bm-pe-watch="row.str2"></span>
<span bm-pe-watch="row.date0"></span>
<span bm-pe-watch="row.obj"></span>
<span bm-pe-watch="row.keys"></span>
</li>
<li ng-switch-when="complexPath" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="row.index"></span>
<span bm-pe-watch="row.num0"></span>
<span bm-pe-watch="row.num1"></span>
<span bm-pe-watch="row.str0"></span>
<span bm-pe-watch="row.str1"></span>
<span bm-pe-watch="row.obj.index"></span>
<span bm-pe-watch="row.obj.index"></span>
<span bm-pe-watch="row.obj.index"></span>
<span bm-pe-watch="row.obj.obj.index"></span>
<span bm-pe-watch="row.obj.obj.index"></span>
<span bm-pe-watch="row.obj.obj.obj.index"></span>
<span bm-pe-watch="row.obj.obj.obj.index"></span>
</li>
<li ng-switch-when="fieldAccess" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="data[rowIdx].index"></span>
<span bm-pe-watch="data[rowIdx].num0"></span>
<span bm-pe-watch="data[rowIdx].num1"></span>
<span bm-pe-watch="data[rowIdx].str0"></span>
<span bm-pe-watch="data[rowIdx].str1"></span>
<span bm-pe-watch="data[rowIdx].obj.index"></span>
<span bm-pe-watch="data[rowIdx].obj.index"></span>
<span bm-pe-watch="data[rowIdx].obj.index"></span>
<span bm-pe-watch="data[rowIdx].obj.obj.index"></span>
<span bm-pe-watch="data[rowIdx].obj.obj.index"></span>
<span bm-pe-watch="data[rowIdx].obj.obj.obj.index"></span>
<span bm-pe-watch="data[rowIdx].obj.obj.obj.index"></span>
</li>
<li ng-switch-when="fieldIndex" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="data[rowIdx]"></span>
<span bm-pe-watch="row['str0']"></span>
<span bm-pe-watch="row['str1']"></span>
<span bm-pe-watch="data[row['index']]['index']"></span>
<span bm-pe-watch="data[rowIdx]['obj']"></span>
<span bm-pe-watch="data[rowIdx]['obj']['obj']"></span>
<span bm-pe-watch="row[row['keys'][0]]"></span>
<span bm-pe-watch="row[row['keys'][1]]"></span>
<span bm-pe-watch="row[row['keys'][2]]"></span>
<span bm-pe-watch="row[row['keys'][3]]"></span>
<span bm-pe-watch="row[row['keys'][4]]"></span>
<span bm-pe-watch="row[row['keys'][5]]"></span>
</li>
<li ng-switch-when="operators" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="+rowIdx"></span>
<span bm-pe-watch="-rowIdx"></span>
<span bm-pe-watch="rowIdx + 1"></span>
<span bm-pe-watch="rowIdx - 1"></span>
<span bm-pe-watch="rowIdx * 2"></span>
<span bm-pe-watch="rowIdx + -1"></span>
<span bm-pe-watch="rowIdx - -1"></span>
<span bm-pe-watch="-rowIdx * 2 + 1"></span>
<span bm-pe-watch="rowIdx % 2"></span>
<span bm-pe-watch="rowIdx % 2 === 1"></span>
<span bm-pe-watch="rowIdx % 2 === 0"></span>
<span bm-pe-watch="rowIdx / 1"></span>
<span bm-pe-watch="-rowIdx * 2 * rowIdx + rowIdx / rowIdx + 1"></span>
</li>
<li ng-switch-when="shortCircuitingOperators" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="rowIdx && row.odd"></span>
<span bm-pe-watch="row.odd && row.even"></span>
<span bm-pe-watch="row.odd && !row.even"></span>
<span bm-pe-watch="row.odd || row.even"></span>
<span bm-pe-watch="row.odd || row.even || row.index"></span>
<span bm-pe-watch="row.index === 1 || row.index === 2"></span>
<span bm-pe-watch="row.num0 < row.num1 && row.num1 < row.num2"></span>
<span bm-pe-watch="row.num0 < row.num1 || row.num1 < row.num2"></span>
</li>
<li ng-switch-when="filters" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="rowIdx | noop"></span>
<span bm-pe-watch="rowIdx | noop"></span>
<span bm-pe-watch="rowIdx | noop"></span>
<span bm-pe-watch="rowIdx | noop:1"></span>
<span bm-pe-watch="rowIdx | noop:rowIdx"></span>
<span bm-pe-watch="rowIdx | noop:1:2:3:4:5"></span>
<span bm-pe-watch="rowIdx | noop:rowIdx:rowIdx:rowIdx"></span>
<span bm-pe-watch="rowIdx | noop | noop"></span>
<span bm-pe-watch="rowIdx | noop:1 | noop"></span>
<span bm-pe-watch="rowIdx | noop | noop:null:undefined:0"></span>
<span bm-pe-watch="rowIdx | noop | noop | noop"></span>
<span bm-pe-watch="rowIdx | noop:1 | noop:2 | noop:3"></span>
</li>
<li ng-switch-when="functionCalls" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="func()"></span>
<span bm-pe-watch="func(1)"></span>
<span bm-pe-watch="func(1, 2)"></span>
<span bm-pe-watch="func(1, 2, 3)"></span>
<span bm-pe-watch="row.func()"></span>
<span bm-pe-watch="row.func(1)"></span>
<span bm-pe-watch="row.func(1, 2)"></span>
<span bm-pe-watch="row.func(1, 2, 3)"></span>
<span bm-pe-watch="func(func())"></span>
<span bm-pe-watch="func(func(), func())"></span>
<span bm-pe-watch="row.func(row.func())"></span>
<span bm-pe-watch="row.func(row.func(), row.func())"></span>
</li>
<li ng-switch-when="objectLiterals" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch-literal="{foo: rowIdx}"></span>
<span bm-pe-watch-literal="{foo: row, bar: rowIdx}"></span>
<span bm-pe-watch-literal="{0: row, 1: rowIdx, 2: 3}"></span>
<span bm-pe-watch-literal="{str: 'foo', num: rowIdx, b: true}"></span>
<span bm-pe-watch-literal="{a: {b: {c: {d: {e: {f: rowIdx}}}}}}"></span>
<span bm-pe-watch-literal="{a: rowIdx, b: 1, c: 2, d: 3, e: 4, f: 5, g: rowIdx, h: 6, i: 7, j: 8, k: rowIdx}"></span>
</li>
<li ng-switch-when="arrayLiterals" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch-literal="[rowIdx]"></span>
<span bm-pe-watch-literal="[rowIdx, 0]"></span>
<span bm-pe-watch-literal="[rowIdx, 0, 1]"></span>
<span bm-pe-watch-literal="[rowIdx, 0, 1, 2]"></span>
<span bm-pe-watch-literal="[rowIdx, 0, 1, 2, 3]"></span>
<span bm-pe-watch-literal="[[], [rowIdx], [], [], [3], [[[]]]]"></span>
<span bm-pe-watch-literal="[rowIdx, undefined, null, true, false]"></span>
<span bm-pe-watch-literal="[[][0], [0][0], [][rowIdx]]"></span>
<span bm-pe-watch-literal="[0, rowIdx]"></span>
<span bm-pe-watch-literal="[0, 1, rowIdx]"></span>
<span bm-pe-watch-literal="[0, 1, 2, rowIdx]"></span>
<span bm-pe-watch-literal="[0, 1, 2, 3, rowIdx]"></span>
</li>
</ul>
</div>
</div>
</div>
+2 -6
View File
@@ -2,11 +2,7 @@
"name": "AngularJS",
"devDependencies": {
"jquery": "2.1.1",
"lunr.js": "0.4.3",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip",
"bootstrap": "3.1.1"
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
}
}
+1 -1
View File
@@ -145,7 +145,7 @@ then(allInSeries(function (branch) {
line = line.split(' ');
var sha = line.shift();
var msg = line.join(' ');
return sha + (msg.toLowerCase().indexOf('fix') === -1 ? ' ' : ' * ') + msg;
return sha + ((/fix\([^\)]+\):/i.test(msg)) ? ' * ' : ' ') + msg;
});
branch.log = log.map(function (line) {
return line.substr(41);
+10 -8
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 {
@@ -635,12 +635,14 @@ ul.events > li {
display:inline-block;
padding:3px 0;
}
.nav-index-group .nav-index-listing:not(.nav-index-section) + .nav-index-listing:not(.nav-index-section):after {
padding-right:5px;
content:", ";
.nav-index-group .nav-index-listing:not(.nav-index-section):after {
padding-right:5px;
margin-left:-3px;
content:", ";
}
.nav-index-group .nav-index-listing:last-child {
.nav-index-group .nav-index-listing:last-child:after {
content:"";
display:inline-block;
}
.nav-index-group .nav-index-section {
display:block;
+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 });
};
+42
View File
@@ -0,0 +1,42 @@
{
"extends": "../../../.jshintrc-base",
"globals": {
/* jasmine / karma */
"it": false,
"iit": false,
"describe": false,
"ddescribe": false,
"beforeEach": false,
"afterEach": false,
"expect": false,
"jasmine": false,
"spyOn": false,
"waits": false,
"waitsFor": false,
"runs": false,
"dump": false,
/* e2e */
"browser": false,
"element": false,
"by": false,
/* testabilityPatch / matchers */
"inject": false,
"module": false,
"dealoc": false,
"_jQuery": false,
"_jqLiteMode": false,
"sortedHtml": false,
"childrenTagsOf": false,
"assertHidden": false,
"assertVisible": false,
"provideLog": false,
"spyOnlyCallsWithArgs": false,
"createMockStyleSheet": false,
"browserTrigger": false,
"jqLiteCacheSize": false
}
}
@@ -0,0 +1,51 @@
'use strict';
describe("doc.angularjs.org", function() {
describe("API pages", function() {
it("should display links to code on GitHub", function() {
browser.get('index-debug.html#!/api/ng/service/$http');
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
browser.get('index-debug.html#!/api/ng/service/$http');
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
});
it('should change the page content when clicking a link to a service', function () {
browser.get('');
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
ngBindLink.click();
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('ngClick');
});
it('should show the functioning input directive example', function () {
browser.get('index-debug.html#!/api/ng/directive/input');
// Ensure that the page is loaded before trying to switch frames.
browser.waitForAngular();
browser.switchTo().frame('example-input-directive');
var nameInput = element(by.model('user.name'));
nameInput.sendKeys('!!!');
var code = element.all(by.css('tt')).first();
expect(code.getText()).toContain('guest!!!');
});
it("should trim indentation from code blocks", function() {
browser.get('index-debug.html#!/api/ng/type/$rootScope.Scope');
var codeBlocks = element.all(by.css('pre > code.lang-js'));
codeBlocks.each(function(codeBlock) {
var firstSpan = codeBlock.all(by.css('span')).first();
expect(firstSpan.getText()).not.toMatch(/^\W+$/);
});
});
});
});
@@ -0,0 +1,12 @@
'use strict';
describe("provider pages", function() {
it("should show the related service", function() {
browser.get('index-debug.html#!/api/ng/provider/$compileProvider');
var serviceLink = element.all(by.css('ol.api-profile-header-structure li a')).first();
expect(serviceLink.getText()).toEqual('- $compile');
expect(serviceLink.getAttribute('href')).toMatch(/api\/ng\/service\/\$compile/);
});
});
@@ -0,0 +1,22 @@
'use strict';
describe("service pages", function() {
it("should show the related provider if there is one", function() {
browser.get('index-debug.html#!/api/ng/service/$compile');
var providerLink = element.all(by.css('ol.api-profile-header-structure li a')).first();
expect(providerLink.getText()).toEqual('- $compileProvider');
expect(providerLink.getAttribute('href')).toMatch(/api\/ng\/provider\/\$compileProvider/);
browser.get('index-debug.html#!/api/ng/service/$q');
providerLink = element.all(by.css('ol.api-profile-header-structure li a')).first();
expect(providerLink.getText()).not.toEqual('- $qProvider');
expect(providerLink.getAttribute('href')).not.toMatch(/api\/ng\/provider\/\$compileProvider/);
});
it("should show parameter defaults", function() {
browser.get('index-debug.html#!/api/ng/service/$timeout');
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
});
});
@@ -1,6 +1,29 @@
'use strict';
var webdriver = require('protractor/node_modules/selenium-webdriver');
describe('docs.angularjs.org', function () {
beforeEach(function() {
// read and clear logs from previous tests
browser.manage().logs().get('browser');
});
afterEach(function() {
// verify that there were no console errors in the browser
browser.manage().logs().get('browser').then(function(browserLog) {
var filteredLog = browserLog.filter(function(logEntry) {
return logEntry.level.value > webdriver.logging.Level.WARNING.value;
});
expect(filteredLog.length).toEqual(0);
if (filteredLog.length) {
console.log('browser console errors: ' + require('util').inspect(filteredLog));
}
});
});
describe('App', function () {
// it('should filter the module list when searching', function () {
// browser.get();
@@ -26,21 +49,6 @@ describe('docs.angularjs.org', function () {
});
it('should show the functioning input directive example', function () {
browser.get('index-debug.html#!/api/ng/directive/input');
// Ensure that the page is loaded before trying to switch frames.
browser.waitForAngular();
browser.switchTo().frame('example-input-directive');
var nameInput = element(by.model('user.name'));
nameInput.sendKeys('!!!');
var code = element.all(by.css('tt')).first();
expect(code.getText()).toContain('guest!!!');
});
it('should be resilient to trailing slashes', function() {
browser.get('index-debug.html#!/api/ng/function/angular.noop/');
@@ -67,22 +75,15 @@ describe('docs.angularjs.org', function () {
browser.get('index-debug.html#!error/ng/areq?p0=Missing&p1=not%20a%20function,%20got%20undefined');
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
});
});
describe("templates", function() {
it("should show parameter defaults", function() {
browser.get('index-debug.html#!/api/ng/service/$timeout');
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
});
});
describe("API pages", function() {
it("should display links to code on GitHub", function() {
browser.get('index-debug.html#!/api/ng/service/$http');
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
browser.get('index-debug.html#!/api/ng/service/$http');
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
});
});
});
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!');
});
});
+1
View File
@@ -6,6 +6,7 @@ angular.module('docsApp', [
'DocsController',
'versionsData',
'pagesData',
'navData',
'directives',
'errors',
'examples',
+12 -72
View File
@@ -6,87 +6,39 @@ 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,
active: navItem.href && this.currentPage && this.currentPage.path,
'nav-index-section': navItem.type === 'section'
};
};
$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.$on('$includeContentError', function() {
$scope.partialPath = 'Error404.html';
});
$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);
}
path = path.replace(/^\/?(.+?)(\/index)?\/?$/, '$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);
}
$scope.partialPath = 'partials/' + path + '.html';
currentPage = $scope.currentPage = NG_PAGES[path];
if ( currentPage ) {
$scope.currentArea = currentPage && NG_NAVIGATION[currentPage.area];
$scope.currentArea = NG_NAVIGATION[currentPage.area];
var pathParts = currentPage.path.split('/');
var breadcrumb = $scope.breadcrumb = [];
var breadcrumbPath = '';
@@ -107,24 +59,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 -62
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,76 +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('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
// 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) {
+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']);
}));
});
+10
View File
@@ -0,0 +1,10 @@
{
"name": "AngularJS-docs-app",
"dependencies": {
"jquery": "2.1.1",
"lunr.js": "0.4.3",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"bootstrap": "3.1.1"
}
}
+148 -31
View File
@@ -1,43 +1,160 @@
var _ = require('lodash');
"use strict";
var path = require('canonical-path');
var packagePath = __dirname;
var basePackage = require('dgeni-packages/ngdoc');
var examplesPackage = require('dgeni-packages/examples');
var Package = require('dgeni').Package;
module.exports = function(config) {
// 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.
module.exports = new Package('angularjs', [
require('dgeni-packages/ngdoc'),
require('dgeni-packages/nunjucks'),
require('dgeni-packages/examples')
])
config = basePackage(config);
config = examplesPackage(config);
config.append('processing.processors', [
require('./processors/git-data'),
require('./processors/error-docs'),
require('./processors/keywords'),
require('./processors/versions-data'),
require('./processors/pages-data'),
require('./processors/protractor-generate'),
require('./processors/index-page'),
require('./processors/debug-dump')
]);
.factory(require('./services/errorNamespaceMap'))
.factory(require('./services/getMinerrInfo'))
.factory(require('./services/getVersion'))
.factory(require('./services/gitData'))
config.append('processing.tagDefinitions', [
require('./tag-defs/tutorial-step')
]);
.factory(require('./services/deployments/debug'))
.factory(require('./services/deployments/default'))
.factory(require('./services/deployments/jquery'))
.factory(require('./services/deployments/production'))
config.append('processing.defaultTagTransforms', [
require('dgeni-packages/jsdoc/tag-defs/transforms/trim-whitespace')
]);
.factory(require('./inline-tag-defs/type'))
config.append('processing.inlineTagDefinitions', [
require('./inline-tag-defs/type')
]);
config.set('processing.search.ignoreWordsFile', path.resolve(packagePath, 'ignore.words'));
.processor(require('./processors/error-docs'))
.processor(require('./processors/index-page'))
.processor(require('./processors/keywords'))
.processor(require('./processors/pages-data'))
.processor(require('./processors/versions-data'))
config.prepend('rendering.templateFolders', [
path.resolve(packagePath, 'templates')
]);
return config;
};
.config(function(dgeni, log, readFilesProcessor, writeFilesProcessor) {
dgeni.stopOnValidationError = true;
dgeni.stopOnProcessingError = true;
log.level = 'info';
readFilesProcessor.basePath = path.resolve(__dirname,'../..');
readFilesProcessor.sourceFiles = [
{ include: 'src/**/*.js', basePath: 'src' },
{ include: 'docs/content/**/*.ngdoc', basePath: 'docs/content' }
];
writeFilesProcessor.outputFolder = 'build/docs';
})
.config(function(parseTagsProcessor) {
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/tutorial-step'));
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/sortOrder'));
})
.config(function(inlineTagProcessor, typeInlineTagDef) {
inlineTagProcessor.inlineTagDefinitions.push(typeInlineTagDef);
})
.config(function(templateFinder, renderDocsProcessor, gitData) {
templateFinder.templateFolders.unshift(path.resolve(packagePath, 'templates'));
renderDocsProcessor.extraData.git = gitData;
})
.config(function(computePathsProcessor, computeIdsProcessor) {
computePathsProcessor.pathTemplates.push({
docTypes: ['error'],
pathTemplate: 'error/${namespace}/${name}',
outputPathTemplate: 'partials/error/${namespace}/${name}.html'
});
computePathsProcessor.pathTemplates.push({
docTypes: ['errorNamespace'],
pathTemplate: 'error/${name}',
outputPathTemplate: 'partials/error/${name}.html'
});
computePathsProcessor.pathTemplates.push({
docTypes: ['overview', 'tutorial'],
getPath: function(doc) {
var docPath = path.dirname(doc.fileInfo.relativePath);
if ( doc.fileInfo.baseName !== 'index' ) {
docPath = path.join(docPath, doc.fileInfo.baseName);
}
return docPath;
},
outputPathTemplate: 'partials/${path}.html'
});
computePathsProcessor.pathTemplates.push({
docTypes: ['e2e-test'],
getPath: function() {},
outputPathTemplate: 'ptore2e/${example.id}/${deployment.name}_test.js'
});
computePathsProcessor.pathTemplates.push({
docTypes: ['indexPage'],
getPath: function() {},
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'],
getId: function(doc) { return doc.fileInfo.baseName; },
getAliases: function(doc) { return [doc.id]; }
});
computeIdsProcessor.idTemplates.push({
docTypes: ['error', 'errorNamespace'],
getId: function(doc) { return 'error:' + doc.name; },
getAliases: function(doc) { return [doc.id]; }
});
})
.config(function(
generateIndexPagesProcessor,
generateProtractorTestsProcessor,
generateExamplesProcessor,
debugDeployment, defaultDeployment,
jqueryDeployment, productionDeployment) {
generateIndexPagesProcessor.deployments = [
debugDeployment,
defaultDeployment,
jqueryDeployment,
productionDeployment
];
generateProtractorTestsProcessor.deployments = [
defaultDeployment,
jqueryDeployment
];
generateExamplesProcessor.deployments = [
debugDeployment,
defaultDeployment,
jqueryDeployment,
productionDeployment
];
});
+15 -10
View File
@@ -1,12 +1,17 @@
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
"use strict";
var encoder = new require('node-html-encoder').Encoder();
module.exports = {
name: 'type',
description: 'Replace with markup that displays a nice type',
handlerFactory: function() {
return function(doc, tagName, tagDescription) {
return '<a href="" class="' + typeClassFilter.process(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
};
}
};
/**
* @dgService typeInlineTagDef
* @description
* Replace with markup that displays a nice type
*/
module.exports = function typeInlineTagDef(getTypeClass) {
return {
name: 'type',
handler: function(doc, tagName, tagDescription) {
return '<a href="" class="' + getTypeClass(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
}
};
};
-33
View File
@@ -1,33 +0,0 @@
var fs = require('q-io/fs');
var log = require('winston');
var util = require("util");
module.exports = {
name: 'debug-dump',
runBefore: ['write-files'],
description: 'This processor dumps docs that match a filter to a file',
process: function(docs, config) {
var filter, outputPath, depth;
filter = config.get('processing.debug-dump.filter');
outputPath = config.get('processing.debug-dump.outputPath');
depth = config.get('processing.debug-dump.depth', 2);
if ( filter && outputPath ) {
log.info('Dumping docs:', filter, outputPath);
var filteredDocs = filter(docs);
var dumpedDocs = util.inspect(filteredDocs, depth);
return writeFile(outputPath, dumpedDocs).then(function() {
return docs;
});
}
}
};
function writeFile(file, content) {
return fs.makeTree(fs.directory(file)).then(function() {
return fs.write(file, content, 'wb');
});
}
+43 -50
View File
@@ -1,59 +1,52 @@
"use strict";
var _ = require('lodash');
var log = require('winston');
var path = require('canonical-path');
module.exports = {
name: 'error-docs',
description: 'Compute the various fields for docs in the Error area',
runAfter: ['tags-extracted', 'compute-path'],
runBefore: ['extra-docs-added'],
exports: {
errorNamespaces: ['factory', function() { return {}; }],
minerrInfo: ['factory', function(config) {
var minerrInfoPath = config.get('processing.errors.minerrInfoPath');
if ( !minerrInfoPath ) {
throw new Error('Error in configuration: Please provide a path to the minerr info file (errors.json) ' +
'in the `config.processing.errors.minerrInfoPath` property');
}
return require(minerrInfoPath);
}]
},
process: function(docs, partialNames, errorNamespaces, minerrInfo) {
/**
* @dgProcessor errorDocsProcessor
* @description
* Process "error" docType docs and generate errorNamespace docs
*/
module.exports = function errorDocsProcessor(errorNamespaceMap, getMinerrInfo) {
return {
$runAfter: ['tags-extracted'],
$runBefore: ['extra-docs-added'],
$process: function(docs) {
// Create error namespace docs and attach error docs to each
_.forEach(docs, function(doc) {
if ( doc.docType === 'error' ) {
// Create error namespace docs and attach error docs to each
docs.forEach(function(doc) {
var parts, namespaceDoc;
// Parse out the error info from the id
parts = doc.name.split(':');
doc.namespace = parts[0];
doc.name = parts[1];
if ( doc.docType === 'error' ) {
// Parse out the error info from the id
parts = doc.name.split(':');
doc.namespace = parts[0];
doc.name = parts[1];
var namespaceDoc = errorNamespaces[doc.namespace];
if ( !namespaceDoc ) {
// First time we came across this namespace, so create a new one
namespaceDoc = errorNamespaces[doc.namespace] = {
area: doc.area,
name: doc.namespace,
errors: [],
path: path.dirname(doc.path),
outputPath: path.dirname(doc.outputPath) + '.html',
docType: 'errorNamespace'
};
// Get or create the relevant errorNamespace doc
namespaceDoc = errorNamespaceMap.get(doc.namespace);
if ( !namespaceDoc ) {
namespaceDoc = {
area: 'error',
name: doc.namespace,
errors: [],
docType: 'errorNamespace'
};
errorNamespaceMap.set(doc.namespace, namespaceDoc);
}
// Link this error doc to its namespace doc
namespaceDoc.errors.push(doc);
doc.namespaceDoc = namespaceDoc;
doc.formattedErrorMessage = getMinerrInfo().errors[doc.namespace][doc.name];
}
});
// Add this error to the namespace
namespaceDoc.errors.push(doc);
doc.namespace = namespaceDoc;
doc.formattedErrorMessage = minerrInfo.errors[doc.namespace.name][doc.name];
}
});
return docs.concat(_.values(errorNamespaces));
}
};
errorNamespaceMap.forEach(function(errorNamespace) {
docs.push(errorNamespace);
});
}
};
};
-20
View File
@@ -1,20 +0,0 @@
var gruntUtils = require('../../../lib/grunt/utils');
var versionInfo = require('../../../lib/versions/version-info');
module.exports = {
name: 'git-data',
runBefore: ['reading-files'],
description: 'This processor adds information from the local git repository to the extraData injectable',
exports: {
gitData: ['factory', function() {
return {
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
};
}]
},
process: function(extraData, gitData) {
extraData.git = gitData;
}
};
+38 -35
View File
@@ -1,40 +1,43 @@
"use strict";
var _ = require('lodash');
var log = require('winston');
var path = require('canonical-path');
module.exports = {
name: 'index-page',
runAfter: ['adding-extra-docs'],
runBefore: ['extra-docs-added'],
description: 'This processor creates docs that will be rendered as the index page for the app',
process: function(docs, config) {
/**
* @dgProcessor generateIndexPagesProcessor
* @description
* This processor creates docs that will be rendered as the index page for the app
*/
module.exports = function generateIndexPagesProcessor() {
return {
deployments: [],
$validate: {
deployments: { presence: true }
},
$runAfter: ['adding-extra-docs'],
$runBefore: ['extra-docs-added'],
$process: function(docs) {
var deployment = config.deployment;
if ( !deployment || !deployment.environments ) {
throw new Error('No deployment environments found in the config.');
// Collect up all the areas in the docs
var areas = {};
docs.forEach(function(doc) {
if ( doc.area ) {
areas[doc.area] = doc.area;
}
});
areas = _.keys(areas);
this.deployments.forEach(function(deployment) {
var indexDoc = _.defaults({
docType: 'indexPage',
areas: areas
}, deployment);
indexDoc.id = 'index' + (deployment.name === 'default' ? '' : '-' + deployment.name);
docs.push(indexDoc);
});
}
// Collect up all the areas in the docs
var areas = {};
_.forEach(docs, function(doc) {
if ( doc.area ) {
areas[doc.area] = doc.area;
}
});
areas = _.keys(areas);
_.forEach(deployment.environments, function(environment) {
var indexDoc = _.defaults({
docType: 'indexPage',
areas: areas
}, environment);
indexDoc.id = 'index' + (environment.name === 'default' ? '' : '-' + environment.name);
// Use .. to put it at the root of the build
indexDoc.outputPath = indexDoc.id + '.html';
docs.push(indexDoc);
});
}
};
};
};
+72 -46
View File
@@ -1,50 +1,64 @@
"use strict";
var _ = require('lodash');
var log = require('winston');
var fs = require('fs');
var path = require('canonical-path');
module.exports = {
name: 'keywords',
runAfter: ['docs-processed'],
runBefore: ['adding-extra-docs'],
description: 'This processor extracts all the keywords from the document',
process: function(docs, config) {
/**
* @dgProcessor generateKeywordsProcessor
* @description
* This processor extracts all the keywords from each document and creates
* a new document that will be rendered as a JavaScript file containing all
* this data.
*/
module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
return {
ignoreWordsFile: undefined,
areasToSearch: ['api', 'guide', 'misc', 'error', 'tutorial'],
propertiesToIgnore: [],
$validate: {
ignoreWordsFile: { },
areasToSearch: { presence: true },
propertiesToIgnore: { }
},
$runAfter: ['memberDocsProcessor'],
$runBefore: ['rendering-docs'],
$process: function(docs) {
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var areasToSearch;
// Keywords to ignore
var wordsToIgnore = [];
var propertiesToIgnore;
var areasToSearch;
// Keywords start with "ng:" or one of $, _ or a letter
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
// Keywords start with "ng:" or one of $, _ or a letter
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
// Load up the keywords to ignore, if specified in the config
if ( config.processing.search && config.processing.search.ignoreWordsFile ) {
// Load up the keywords to ignore, if specified in the config
if ( this.ignoreWordsFile ) {
var ignoreWordsPath = path.resolve(config.basePath, config.processing.search.ignoreWordsFile);
wordsToIgnore = fs.readFileSync(ignoreWordsPath, 'utf8').toString().split(/[,\s\n\r]+/gm);
var ignoreWordsPath = path.resolve(readFilesProcessor.basePath, this.ignoreWordsFile);
wordsToIgnore = fs.readFileSync(ignoreWordsPath, 'utf8').toString().split(/[,\s\n\r]+/gm);
log.debug('Loaded ignore words from "' + ignoreWordsPath + '"');
log.silly(wordsToIgnore);
log.debug('Loaded ignore words from "' + ignoreWordsPath + '"');
log.silly(wordsToIgnore);
}
areasToSearch = _.indexBy(config.get('processing.search.areasToSearch', ['api', 'guide', 'misc', 'error', 'tutorial']));
propertiesToIgnore = _.indexBy(config.get('processing.search.propertiesToIgnore', []));
log.debug('Properties to ignore', propertiesToIgnore);
var ignoreWordsMap = _.indexBy(wordsToIgnore);
// If the title contains a name starting with ng, e.g. "ngController", then add the module name
// without the ng to the title text, e.g. "controller".
function extractTitleWords(title) {
var match = /ng([A-Z]\w*)/.exec(title);
if ( match ) {
title = title + ' ' + match[1].toLowerCase();
}
return title;
}
areasToSearch = _.indexBy(this.areasToSearch);
propertiesToIgnore = _.indexBy(this.propertiesToIgnore);
log.debug('Properties to ignore', propertiesToIgnore);
var ignoreWordsMap = _.indexBy(wordsToIgnore);
// If the title contains a name starting with ng, e.g. "ngController", then add the module name
// without the ng to the title text, e.g. "controller".
function extractTitleWords(title) {
var match = /ng([A-Z]\w*)/.exec(title);
if ( match ) {
title = title + ' ' + match[1].toLowerCase();
}
return title;
}
function extractWords(text, words, keywordMap) {
@@ -52,37 +66,49 @@ module.exports = {
_.forEach(tokens, function(token){
var match = token.match(KEYWORD_REGEX);
if (match){
key = match[1];
var key = match[1];
if ( !keywordMap[key]) {
keywordMap[key] = true;
words.push(key);
}
}
});
}
}
});
}
// We are only interested in docs that live in the right area
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
// We are only interested in docs that live in the right area
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
_.forEach(docs, function(doc) {
_.forEach(docs, function(doc) {
var words = [];
var keywordMap = _.clone(ignoreWordsMap);
var members = [];
var membersMap = {};
// Search each top level property of the document for search terms
_.forEach(doc, function(value, key) {
if ( _.isString(value) && !propertiesToIgnore[key] ) {
extractWords(value, words, keywordMap);
}
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
_.forEach(value, function(member) {
extractWords(member.name, members, membersMap);
});
}
});
doc.searchTerms = {
titleWords: extractTitleWords(doc.name),
keywords: _.sortBy(words).join(' ')
keywords: _.sortBy(words).join(' '),
members: _.sortBy(members).join(' ')
};
});
});
}
}
};
};
+181 -156
View File
@@ -1,6 +1,7 @@
"use strict";
var _ = require('lodash');
var path = require('canonical-path');
var log = require('winston');
var AREA_NAMES = {
api: 'API',
@@ -33,182 +34,206 @@ function getNavGroup(pages, area, pageSorter, pageMapper) {
}
var navGroupMappers = {
api: function(areaPages, area) {
var navGroups = _(areaPages)
.filter('module') // We are not interested in docs that are not in a module
/**
* @dgProcessor generatePagesDataProcessor
* @description
* This processor will create a new doc that will be rendered as a JavaScript file
* containing meta information about the pages and navigation
*/
module.exports = function generatePagesDataProcessor(log) {
.groupBy('module')
.map(function(modulePages, moduleName) {
log.debug('moduleName: ' + moduleName);
var navItems = [];
var modulePage;
var navGroupMappers = {
api: function(areaPages, area) {
var navGroups = _(areaPages)
.filter('module') // We are not interested in docs that are not in a module
_(modulePages)
.groupBy('module')
.groupBy('docType')
.map(function(modulePages, moduleName) {
log.debug('moduleName: ' + moduleName);
var navItems = [];
var modulePage;
.tap(function(docTypes) {
log.debug(_.keys(docTypes));
// Extract the module page from the collection
modulePage = docTypes.module[0];
delete docTypes.module;
})
_(modulePages)
.tap(function(docTypes) {
if ( docTypes.input ) {
docTypes.directive = docTypes.directive || [];
// Combine input docTypes into directive docTypes
docTypes.directive = docTypes.directive.concat(docTypes.input);
delete docTypes.input;
}
})
.groupBy('docType')
.forEach(function(sectionPages, sectionName) {
.tap(function(docTypes) {
log.debug(moduleName, _.keys(docTypes));
// Extract the module page from the collection
modulePage = docTypes.module[0];
delete docTypes.module;
})
sectionPages = _.sortBy(sectionPages, 'name');
.tap(function(docTypes) {
if ( docTypes.input ) {
docTypes.directive = docTypes.directive || [];
// Combine input docTypes into directive docTypes
docTypes.directive = docTypes.directive.concat(docTypes.input);
delete docTypes.input;
}
})
if ( sectionPages.length > 0 ) {
// Push a navItem for this section
navItems.push({
name: sectionName,
type: 'section',
href: path.dirname(sectionPages[0].path)
});
.forEach(function(sectionPages, sectionName) {
// Push the rest of the sectionPages for this section
_.forEach(sectionPages, function(sectionPage) {
sectionPages = _.sortBy(sectionPages, 'name');
if ( sectionPages.length > 0 ) {
// Push a navItem for this section
navItems.push({
name: sectionPage.name,
href: sectionPage.path,
type: sectionPage.docType
name: sectionName,
type: 'section',
href: path.dirname(sectionPages[0].path)
});
});
}
});
// Push the rest of the sectionPages for this section
_.forEach(sectionPages, function(sectionPage) {
navItems.push({
name: sectionPage.name,
href: sectionPage.path,
type: sectionPage.docType
});
});
}
});
return {
name: moduleName,
href: modulePage.path,
type: 'group',
navItems: navItems
};
})
.value();
return navGroups;
},
tutorial: function(pages, area) {
return [getNavGroup(pages, area, 'step', function(page) {
return {
name: moduleName,
href: modulePage.path,
type: 'group',
navItems: navItems
name: page.name,
step: page.step,
href: page.path,
type: 'tutorial'
};
})
.value();
return navGroups;
},
tutorial: function(pages, area) {
return [getNavGroup(pages, area, 'step', function(page) {
return {
name: page.name,
step: page.step,
href: page.path,
type: 'tutorial'
};
})];
},
error: function(pages, area) {
return [getNavGroup(pages, area, 'path', function(page) {
return {
name: page.name,
href: page.path,
type: page.docType === 'errorNamespace' ? 'section' : 'error'
};
})];
},
pages: function(pages, area) {
return [getNavGroup(pages, area, 'path', function(page) {
return {
name: page.name,
href: page.path,
type: 'page'
};
})];
}
};
module.exports = {
name: 'pages-data',
description: 'This plugin will create a new doc that will be rendered as an angularjs module ' +
'which will contain meta information about the pages and navigation',
runAfter: ['adding-extra-docs', 'component-groups-generate', 'compute-path'],
runBefore: ['extra-docs-added'],
process: function(docs, config) {
var outputFolder = config.rendering.outputFolder;
_(docs)
.filter(function(doc) { return doc.area === 'api'; })
.filter(function(doc) { return 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 a area and not landing pages
var navPages = _.filter(docs, function(page) {
return page.area &&
page.docType != 'componentGroup';
});
// Generate an object collection of pages that is grouped by area e.g.
// - area "api"
// - group "ng"
// - section "directive"
// - ngApp
// - ngBind
// - section "global"
// - angular.element
// - angular.bootstrap
// - section "service"
// - $compile
// - group "ngRoute"
// - section "directive"
// - ngView
// - section "service"
// - $route
//
var areas = {};
_(navPages)
.groupBy('area')
.forEach(function(pages, areaId) {
var area = {
id: areaId,
name: AREA_NAMES[areaId]
})];
},
error: function(pages, area) {
return [getNavGroup(pages, area, 'path', function(page) {
return {
name: page.name,
href: page.path,
type: page.docType === 'errorNamespace' ? 'section' : 'error'
};
areas[areaId] = area;
})];
},
pages: function(pages, area) {
return [getNavGroup(
pages,
area,
function(page) {
return page.sortOrder || page.path;
},
function(page) {
return {
name: page.name,
href: page.path,
type: 'page'
};
}
)];
}
};
var navGroupMapper = navGroupMappers[area.id] || navGroupMappers['pages'];
area.navGroups = navGroupMapper(pages, area);
return {
$runAfter: ['paths-computed', 'generateKeywordsProcessor'],
$runBefore: ['rendering-docs'],
$process: function(docs) {
// We are only interested in docs that are in an area
var pages = _.filter(docs, function(doc) {
return doc.area;
});
// Extract a list of basic page information for mapping paths to partials and for client side searching
var pages = _(docs)
.map(function(doc) {
var page = _.pick(doc, [
'docType', 'id', 'name', 'area', 'outputPath', 'path', 'searchTerms'
]);
return page;
})
.indexBy('path')
.value();
// 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.
// - area "api"
// - group "ng"
// - section "directive"
// - ngApp
// - ngBind
// - section "global"
// - angular.element
// - angular.bootstrap
// - section "service"
// - $compile
// - group "ngRoute"
// - section "directive"
// - ngView
// - section "service"
// - $route
//
var areas = {};
_(navPages)
.groupBy('area')
.forEach(function(pages, areaId) {
var area = {
id: areaId,
name: AREA_NAMES[areaId]
};
areas[areaId] = area;
var navGroupMapper = navGroupMappers[area.id] || navGroupMappers['pages'];
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 docData = {
docType: 'pages-data',
id: 'pages-data',
template: 'pages-data.template.js',
outputPath: 'js/pages-data.js',
areas: areas,
pages: pages
};
docs.push(docData);
}
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 pageData = _(docs)
.map(function(doc) {
return _.pick(doc, ['name', 'area', 'path']);
})
.indexBy('path')
.value();
docs.push({
docType: 'pages-data',
id: 'pages-data',
template: 'pages-data.template.js',
outputPath: 'js/pages-data.js',
pages: pageData
});
}
};
};
@@ -1,50 +0,0 @@
var _ = require('lodash');
var path = require('canonical-path');
module.exports = {
name: 'protractor-generate',
description: 'Generate a protractor test file from the e2e tests in the examples',
runAfter: ['adding-extra-docs'],
runBefore: ['extra-docs-added'],
process: function(docs, examples, config) {
var protractorFolder = config.get('rendering.protractor.outputFolder', 'ptore2e');
_.forEach(examples, function(example) {
_.forEach(example.files, function(file) {
// Check if it's a Protractor test.
if (file.type !== 'protractor') {
return;
}
// Create new files for the tests.
docs.push(createProtractorDoc(example, file, 'jquery'));
docs.push(createProtractorDoc(example, file, 'jqlite'));
});
});
function createProtractorDoc(example, file, env) {
var protractorDoc = {
docType: 'e2e-test',
id: 'protractorTest' + '-' + example.id,
template: 'protractorTests.template.js',
outputPath: path.join(protractorFolder, example.id, env + '_test.js'),
innerTest: file.fileContents,
pathPrefix: '.', // Hold for if we test with full jQuery
exampleId: example.id,
description: example.doc.id,
'ng-app-included': example['ng-app-included']
};
if (env === 'jquery') {
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html';
} else {
protractorDoc.examplePath = example.outputFolder + '/index.html';
}
return protractorDoc;
}
}
};
+29 -33
View File
@@ -1,38 +1,34 @@
"use strict";
var _ = require('lodash');
module.exports = {
name: 'versions-data',
description: 'This plugin will create a new doc that will be rendered as an angularjs module ' +
'which will contain meta information about the versions of angular',
runAfter: ['adding-extra-docs', 'pages-data'],
runBefore: ['extra-docs-added'],
process: function(docs, gitData) {
/**
* @dgProcessor generateVersionDocProcessor
* @description
* This processor will create a new doc that will be rendered as a JavaScript file
* containing meta information about the current versions of AngularJS
*/
module.exports = function generateVersionDocProcessor(gitData) {
return {
$runAfter: ['generatePagesDataProcessor'],
$runBefore: ['rendering-docs'],
$process: function(docs) {
var version = gitData.version;
var versions = gitData.versions;
var versionDoc = {
docType: 'versions-data',
id: 'versions-data',
template: 'versions-data.template.js',
outputPath: 'js/versions-data.js',
currentVersion: gitData.version
};
if ( !version ) {
throw new Error('Invalid configuration. Please provide a valid `source.currentVersion` property');
versionDoc.versions = _(gitData.versions)
.filter(function(version) { return version.major > 0; })
.push(gitData.version)
.reverse()
.value();
docs.push(versionDoc);
}
if ( !versions ) {
throw new Error('Invalid configuration. Please provide a valid `source.previousVersions` property');
}
var versionDoc = {
docType: 'versions-data',
id: 'versions-data',
template: 'versions-data.template.js',
outputPath: 'js/versions-data.js',
};
versionDoc.currentVersion = version;
versionDoc.versions = _(versions)
.filter(function(version) { return version.major > 0; })
.push(version)
.reverse()
.value();
docs.push(versionDoc);
}
};
};
};
+40
View File
@@ -0,0 +1,40 @@
"use strict";
module.exports = function debugDeployment(getVersion) {
return {
name: 'debug',
examples: {
commonFiles: {
scripts: [ '../../../angular.js' ]
},
dependencyPath: '../../../'
},
scripts: [
'../angular.js',
'../angular-resource.js',
'../angular-route.js',
'../angular-cookies.js',
'../angular-sanitize.js',
'../angular-touch.js',
'../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: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
};
};
@@ -0,0 +1,40 @@
"use strict";
module.exports = function defaultDeployment(getVersion) {
return {
name: 'default',
examples: {
commonFiles: {
scripts: [ '../../../angular.min.js' ]
},
dependencyPath: '../../../'
},
scripts: [
'../angular.min.js',
'../angular-resource.min.js',
'../angular-route.min.js',
'../angular-cookies.min.js',
'../angular-sanitize.min.js',
'../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',
'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/nav-data.js',
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
};
};
+44
View File
@@ -0,0 +1,44 @@
"use strict";
module.exports = function jqueryDeployment(getVersion) {
return {
name: 'jquery',
examples: {
commonFiles: {
scripts: [
'../../components/jquery-' + getVersion('jquery') + '/jquery.js',
'../../../angular.js'
]
},
dependencyPath: '../../../'
},
scripts: [
'components/jquery-' + getVersion('jquery') + '/jquery.js',
'../angular.min.js',
'../angular-resource.min.js',
'../angular-route.min.js',
'../angular-cookies.min.js',
'../angular-sanitize.min.js',
'../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',
'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/nav-data.js',
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
};
};
@@ -0,0 +1,43 @@
"use strict";
var versionInfo = require('../../../../lib/versions/version-info');
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
module.exports = function productionDeployment(getVersion) {
return {
name: 'production',
examples: {
commonFiles: {
scripts: [ cdnUrl + '/angular.min.js' ]
},
dependencyPath: cdnUrl + '/'
},
scripts: [
cdnUrl + '/angular.min.js',
cdnUrl + '/angular-resource.min.js',
cdnUrl + '/angular-route.min.js',
cdnUrl + '/angular-cookies.min.js',
cdnUrl + '/angular-sanitize.min.js',
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',
'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/nav-data.js',
'js/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
};
};
+10
View File
@@ -0,0 +1,10 @@
"use strict";
var StringMap = require('stringmap');
/**
* @dgService errorNamespaceMap
* A map of error namespaces by name.
*/
module.exports = function errorNamespaceMap() {
return new StringMap();
};
+15
View File
@@ -0,0 +1,15 @@
"use strict";
var path = require('canonical-path');
/**
* @dgService minErrInfo
* @description
* Load the error information that was generated during the AngularJS build.
*/
module.exports = function getMinerrInfo(readFilesProcessor) {
return function() {
var minerrInfoPath = path.resolve(readFilesProcessor.basePath, 'build/errors.json');
return require(minerrInfoPath);
};
};
+17
View File
@@ -0,0 +1,17 @@
"use strict";
var path = require('canonical-path');
/**
* dgService getVersion
* @description
* Find the current version of the bower component (or npm module)
*/
module.exports = function getVersion(readFilesProcessor) {
var basePath = readFilesProcessor.basePath;
return function(component, sourceFolder, packageFile) {
sourceFolder = path.resolve(basePath, sourceFolder || 'docs/bower_components');
packageFile = packageFile || 'bower.json';
return require(path.join(sourceFolder,component,packageFile)).version;
};
};
+17
View File
@@ -0,0 +1,17 @@
"use strict";
var gruntUtils = require('../../../lib/grunt/utils');
var versionInfo = require('../../../lib/versions/version-info');
/**
* @dgService gitData
* @description
* Information from the local git repository
*/
module.exports = function gitData() {
return {
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
};
};
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
name: 'sortOrder',
transforms: function(doc, tag, value) {
return parseInt(value, 10);
}
};
+1 -10
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']);
@@ -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 $});
@@ -1,10 +0,0 @@
describe("{$ doc.description $}", function() {
var rootEl;
beforeEach(function() {
rootEl = browser.rootEl;{% if doc['ng-app-included'] %}
browser.rootEl = '[ng-app]';{% endif %}
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
});
{% if doc['ng-app-included'] %}afterEach(function() { browser.rootEl = rootEl; });{% endif %}
{$ doc.innerTest $}
});
@@ -2,15 +2,16 @@
is HTML and wrap each line in a <p> - thus breaking the HTML #}
<div>
<a ng-click="openPlunkr('{$ doc.example.outputFolder $}')" class="btn pull-right">
<a ng-click="openPlunkr('{$ doc.path $}')" class="btn pull-right">
<i class="glyphicon glyphicon-edit">&nbsp;</i>
Edit in Plunker</a>
<div class="runnable-example"
path="{$ doc.example.outputFolder $}"
path="{$ doc.example.deployments.default.path $}"
{%- for attrName, attrValue in doc.example.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% for fileName, file in doc.example.files %}
{% for fileName, file in doc.example.files %}
<div class="runnable-example-file" {% for attrName, attrValue in file.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% code -%}
@@ -19,7 +20,7 @@
</div>
{% endfor %}
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
<iframe class="runnable-example-frame" src="{$ doc.example.deployments.default.outputPath $}" name="{$ doc.example.id $}"></iframe>
</div>
</div>
+2 -2
View File
@@ -9,14 +9,14 @@ The documentation is organized into **{@link guide/module modules}** which conta
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs, and testing mocks.
<div class="alert alert-info">
**Angular Namespaces `$` and `$$`**
**Angular Prefixes `$` and `$$`**:
To prevent accidental name collisions with your code,
Angular prefixes names of public objects with `$` and names of private objects with `$$`.
Please do not use the `$` or `$$` prefix in your code.
</div>
## Angular Namespace
## Angular Modules
## {@link ng ng (core module)}
+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.
+63
View File
@@ -0,0 +1,63 @@
@ngdoc error
@name $location:nobase
@fullName $location in HTML5 mode requires a <base> tag to be present!
@description
If you configure {@link ng.$location `$location`} to use
{@link api/ng.provider.$locationProvider `html5Mode`} (`history.pushState`), you need to specify the base URL for the application with a [`<base href="">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag or configure
`$locationProvider` to not require a base tag by passing a definition object with
`requireBase:false` to `$locationProvider.html5Mode()`:
```javascript
$locationProvider.html5Mode({
enabled: true,
requireBase: false
});
```
Note that removing the requirement for a <base> tag will have adverse side effects when resolving
relative paths with `$location` in IE9.
The base URL is then used to resolve all relative URLs throughout the application regardless of the
entry point into the app.
If you are deploying your app into the root context (e.g. `https://myapp.com/`), set the base URL to `/`:
```html
<head>
<base href="/">
...
</head>
```
If you are deploying your app into a sub-context (e.g. `https://myapp.com/subapp/`), set the base URL to the
URL of the subcontext:
```html
<head>
<base href="/subapp">
...
</head>
```
Before Angular 1.3 we didn't have this hard requirement and it was easy to write apps that worked
when deployed in the root context but were broken when moved to a sub-context because in the
sub-context all absolute urls would resolve to the root context of the app. To prevent this,
use relative URLs throughout your app:
```html
<!-- wrong: -->
<a href="/userProfile">User Profile</a>
<!-- correct: -->
<a href="userProfile">User Profile</a>
```
Additionally, if you want to support [browsers that don't have the `history.pushState`
API](http://caniuse.com/#feat=history), the fallback mechanism provided by `$location`
won't work well without specifying the base url of the application.
In order to make it easier to migrate from hashbang mode to html5 mode, we require that the base
URL is always specified when `$location`'s `html5mode` is enabled.
+17
View File
@@ -0,0 +1,17 @@
@ngdoc error
@name $q:norslvr
@fullName No resolver function passed to $Q
@description
Occurs when calling creating a promise using {@link $q} as a constructor, without providing the
required `resolver` function.
```
//bad
var promise = $q().then(doSomething);
//good
var promise = $q(function(resolve, reject) {
waitForSomethingAsync.then(resolve);
}).then(doSomething);
```
+23
View File
@@ -0,0 +1,23 @@
@ngdoc error
@name $q:qcycle
@fullName Cannot resolve a promise with itself
@description
Occurs when resolving a promise with itself as the value, including returning the promise in a
function passed to `then`. The A+ 1.1 spec mandates that this behavior throw a TypeError.
https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
```
var promise = $q.defer().promise;
//bad
promise.then(function (val) {
//Cannot return self
return promise;
});
//good
promise.then(function (val) {
return 'some other value';
});
```
+2 -2
View File
@@ -10,7 +10,7 @@ Angular's {@link ng.$sce Strict Contextual Escaping (SCE)} mode
contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an
Angular template from a URL requires that the URL is one considered safe for loading resources.)
This helps prevent XSS and other security issues. Read more at {@link
api/ng.$sce Strict Contextual Escaping (SCE)}
This helps prevent XSS and other security issues. Read more at
{@link ng.$sce Strict Contextual Escaping (SCE)}
You may want to include the ngSanitize module to use the automatic sanitizing.
+68 -62
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Using $location
@sortOrder 500
@description
# What does it do?
@@ -90,10 +91,11 @@ To configure the `$location` service, retrieve the
{@link ng.$locationProvider $locationProvider} and set the parameters as follows:
- **html5Mode(mode)**: {boolean}<br />
`true` - see HTML5 mode<br />
`false` - see Hashbang mode<br />
default: `false`
- **html5Mode(mode)**: {boolean|Object}<br />
`true` or `enabled:true` - see HTML5 mode<br />
`false` or `enabled:false` - see Hashbang mode<br />
`requireBase:true` - see Relative links<br />
default: `enabled:false`
- **hashPrefix(prefix)**: {string}<br />
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
@@ -163,7 +165,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.
@@ -210,6 +212,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
@@ -240,7 +246,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
@@ -249,6 +255,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
@@ -297,8 +307,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,
@@ -313,32 +323,28 @@ 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`.
### Relative links
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url
base in the head of your main html file (`<base href="/my-base">`) unless `html5Mode.requireBase` is
set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
that, relative urls will always be resolved to this base url, event if the initial url of the
document was different.
There is one exception: Links that only contain a hash fragment (e.g. `<a href="#target">`)
will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling
to anchors on the same page without needing to know on which page the user currently is.
### Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
to entry point of your application (e.g. index.html)
### Relative links
Be sure to check all relative links, images, scripts etc. You must either specify the url base in
the head of your main html file (`<base href="/my-base">`) or you must use absolute urls
(starting with `/`) everywhere because relative urls will be resolved to absolute urls using the
initial absolute url of the document, which is often different from the root of the application.
Running Angular apps with the History API enabled from document root is strongly encouraged as it
takes care of all relative link issues.
to entry point of your application (e.g. index.html). Requiring a `<base>` tag is also important for
this case, as it allows Angular to differentiate between the part of the url that is the application
base and the path that should be handeled by the application.
### Sending links among different browsers
@@ -469,12 +475,12 @@ In these examples we use `<base href="/base/index.html" />`
it("should show fake browser info on load", function(){
expect(addressBar.getAttribute('value')).toBe(url);
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port')).getText()).toBe('80');
expect(element(by.binding('$location.path')).getText()).toBe('/path');
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash')).getText()).toBe('h');
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port()')).getText()).toBe('80');
expect(element(by.binding('$location.path()')).getText()).toBe('/path');
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash()')).getText()).toBe('h');
});
@@ -485,24 +491,24 @@ In these examples we use `<base href="/base/index.html" />`
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/first?a=b");
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port')).getText()).toBe('80');
expect(element(by.binding('$location.path')).getText()).toBe('/first');
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash')).getText()).toBe('');
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port()')).getText()).toBe('80');
expect(element(by.binding('$location.path()')).getText()).toBe('/first');
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash()')).getText()).toBe('');
navigation.get(1).click();
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/sec/ond?flag#hash");
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port')).getText()).toBe('80');
expect(element(by.binding('$location.path')).getText()).toBe('/sec/ond');
expect(element(by.binding('$location.search')).getText()).toBe('{"flag":true}');
expect(element(by.binding('$location.hash')).getText()).toBe('hash');
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port()')).getText()).toBe('80');
expect(element(by.binding('$location.path()')).getText()).toBe('/sec/ond');
expect(element(by.binding('$location.search()')).getText()).toBe('{"flag":true}');
expect(element(by.binding('$location.hash()')).getText()).toBe('hash');
});
</file>
@@ -621,12 +627,12 @@ In these examples we use `<base href="/base/index.html" />`
it("should show fake browser info on load", function(){
expect(addressBar.getAttribute('value')).toBe(url);
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port')).getText()).toBe('80');
expect(element(by.binding('$location.path')).getText()).toBe('/path');
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash')).getText()).toBe('h');
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port()')).getText()).toBe('80');
expect(element(by.binding('$location.path()')).getText()).toBe('/path');
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash()')).getText()).toBe('h');
});
@@ -637,24 +643,24 @@ In these examples we use `<base href="/base/index.html" />`
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/first?a=b");
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port')).getText()).toBe('80');
expect(element(by.binding('$location.path')).getText()).toBe('/first');
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash')).getText()).toBe('');
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port()')).getText()).toBe('80');
expect(element(by.binding('$location.path()')).getText()).toBe('/first');
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
expect(element(by.binding('$location.hash()')).getText()).toBe('');
navigation.get(1).click();
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/sec/ond?flag#hash");
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port')).getText()).toBe('80');
expect(element(by.binding('$location.path')).getText()).toBe('/sec/ond');
expect(element(by.binding('$location.search')).getText()).toBe('{"flag":true}');
expect(element(by.binding('$location.hash')).getText()).toBe('hash');
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
expect(element(by.binding('$location.port()')).getText()).toBe('80');
expect(element(by.binding('$location.path()')).getText()).toBe('/sec/ond');
expect(element(by.binding('$location.search()')).getText()).toBe('{"flag":true}');
expect(element(by.binding('$location.hash()')).getText()).toBe('hash');
});
</file>
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Animations
@sortOrder 310
@description
+4 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Bootstrap
@sortOrder 350
@description
# Bootstrap
@@ -86,7 +87,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>
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name HTML Compiler
@sortOrder 330
@description
<div class="alert alert-warning">
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Conceptual Overview
@sortOrder 200
@description
# Conceptual Overview
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Controllers
@sortOrder 220
@description
# Understanding Controllers
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Working With CSS
@sortOrder 510
@description
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Data Binding
@sortOrder 210
@description
Data-binding in Angular apps is the automatic synchronization of data between the model and view
+2 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Dependency Injection
@sortOrder 250
@description
# Dependency Injection
@@ -109,7 +110,7 @@ asks the injector to create an instance of the controller and its dependencies.
injector.instantiate(MyController);
```
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
This is all done behind the scenes. Notice that by having the `ng-controller` ask the injector to
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
controller ever knowing about the injector.
+5 -7
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Directives
@sortOrder 300
@description
# Creating Custom Directives
@@ -84,7 +85,7 @@ Here are some equivalent examples of elements that match `ngBind`:
<span x-ng-bind="name"></span> <br/>
</div>
</file>
<file name="protractorTest.js">
<file name="protractor.js" type="protractor">
it('should show off bindings', function() {
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)');
@@ -159,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). When using `ngAttr`, the `allOrNothing` flag of
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
@@ -281,12 +282,9 @@ using `templateUrl` instead:
</file>
</example>
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
<div class="alert alert-warning">
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
create directives that are triggered by element or class name, you need to use the `restrict` option.
**Note:** When you create a directive, it is restricted to attribute and elements only by default. In order to
create directives that are triggered by class name, you need to use the `restrict` option.
</div>
The `restrict` option is typically set to:
+1 -1
View File
@@ -1,6 +1,6 @@
@workInProgress
@ngdoc overview
@name E2E Testing
@sortOrder 420
@description
# E2E Testing
+4 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Expressions
@sortOrder 270
@description
# Angular Expressions
@@ -38,7 +39,9 @@ the method from your view. If you want to `eval()` an Angular expression yoursel
## Example
<example>
<file name="index.html">
1+2={{1+2}}
<span>
1+2={{1+2}}
</span>
</file>
<file name="protractor.js" type="protractor">
+50 -4
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Filters
@sortOrder 280
@description
A filter formats the value of an expression for display to the user. They can be used in view templates,
@@ -86,12 +87,16 @@ This factory function should return a new filter function which takes the input
as the first argument. Any filter arguments are passed in as additional arguments to the filter
function.
The filter function should be a [pure function](http://en.wikipedia.org/wiki/Pure_function), which
means that it should be stateless and idempotent. Angular relies on these properties and executes
the filter only when the inputs to the function change.
The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case.
<example module="myReverseModule">
<example module="myReverseFilterApp">
<file name="index.html">
<div ng-controller="Controller">
<div ng-controller="MyController">
<input ng-model="greeting" type="text"><br>
No filter: {{greeting}}<br>
Reverse: {{greeting|reverse}}<br>
@@ -100,7 +105,7 @@ text upper-case.
</file>
<file name="script.js">
angular.module('myReverseModule', [])
angular.module('myReverseFilterApp', [])
.filter('reverse', function() {
return function(input, uppercase) {
input = input || '';
@@ -115,12 +120,53 @@ text upper-case.
return out;
};
})
.controller('Controller', ['$scope', function($scope) {
.controller('MyController', ['$scope', function($scope) {
$scope.greeting = 'hello';
}]);
</file>
</example>
## Stateful filters
It is strongly discouraged to write filters that are stateful, because the execution of those can't
be optimized by Angular, which often leads to performance issues. Many stateful filters can be
converted into stateless filters just by exposing the hidden state as a model and turning it into an
argument for the filter.
If you however do need to write a stateful filter, you have to mark the filter as `$stateful`, which
means that it will be executed one or more times during the each `$digest` cycle.
<example module="myStatefulFilterApp">
<file name="index.html">
<div ng-controller="MyController">
Input: <input ng-model="greeting" type="text"><br>
Decoration: <input ng-model="decoration.symbol" type="text"><br>
No filter: {{greeting}}<br>
Decorated: {{greeting | decorate}}<br>
</div>
</file>
<file name="script.js">
angular.module('myStatefulFilterApp', [])
.filter('decorate', ['decoration', function(decoration) {
function decorateFilter(input) {
return decoration.symbol + input + decoration.symbol;
}
decorateFilter.$stateful = true;
return decorateFilter;
}])
.controller('MyController', ['$scope', 'decoration', function($scope, decoration) {
$scope.greeting = 'hello';
$scope.decoration = decoration;
}])
.value('decoration', {symbol: '*'});
</file>
</example>
## Testing custom filters
See the [phonecat tutorial](http://docs.angularjs.org/tutorial/step_09#test) for an example.
+2 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Forms
@sortOrder 290
@description
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
@@ -285,7 +286,7 @@ In the following example we create two directives.
* The first one is `integer` and it validates whether the input is a valid integer.
For example `1.23` is an invalid value, since it contains a fraction.
Note that we unshift the array instead of pushing.
This is because we want to be first parser and consume the control string value, as we need to execute the validation function before a conversion to number occurs.
This is because we want it to be the first parser and consume the control string value, as we need to execute the validation function before a conversion to number occurs.
* The second directive is a `smart-float`.
It parses both `1.2` and `1,2` into a valid float number `1.2`.
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name i18n and l10n
@sortOrder 520
@description
# i18n and l10n
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Internet Explorer Compatibility
@sortOrder 530
@description
# Internet Explorer Compatibility
+2 -1
View File
@@ -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)
@@ -81,6 +81,7 @@ This is a short list of libraries with specific support and documentation for wo
### General
* **Docs Page:** {@link guide/production Running an AngularJS App in Production}
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ng-annotate automation tool](https://github.com/olov/ng-annotate)
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Introduction
@sortOrder 100
@description
+2 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Migrating from Previous Versions
@sortOrder 550
@description
Minor version releases in AngularJS introduce several breaking changes that may require changes to your
@@ -95,7 +96,7 @@ this limitation, use a regular expression object as the value for the expression
//after
$scope.exp = /abc/i;
- **Scope:** due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
Scope#$id is now of time number rather than string. Since the
Scope#$id is now of type number rather than string. Since the
id is primarily being used for debugging purposes this change should not affect
anyone.
- **forEach:** due to [55991e33](https://github.com/angular/angular.js/commit/55991e33af6fece07ea347a059da061b76fc95f5),
+3 -2
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Modules
@sortOrder 320
@description
# What is a Module?
@@ -50,7 +51,7 @@ I'm in a hurry. How do I get a Hello World module working?
<file name="protractor.js" type="protractor">
it('should add Hello to the name', function() {
expect(element(by.binding("{{ 'World' | greet }}")).getText()).toEqual('Hello, World!');
expect(element(by.binding("'World' | greet")).getText()).toEqual('Hello, World!');
});
</file>
</example>
@@ -128,7 +129,7 @@ The above is a suggestion. Tailor it to your needs.
<file name="protractor.js" type="protractor">
it('should add Hello to the name', function() {
expect(element(by.binding("{{ greeting }}")).getText()).toEqual('Bonjour World!');
expect(element(by.binding("greeting")).getText()).toEqual('Bonjour World!');
});
</file>
+43
View File
@@ -0,0 +1,43 @@
@ngdoc overview
@name Running in Production
@sortOrder 540
@description
# Running an AngularJS App in Production
There are a few things you might consider when running your AngularJS application in production.
## Disabling Debug Data
By default AngularJS attaches information about scopes to DOM nodes, and adds CSS classes
to data-bound elements. The information that is not included is:
As a result of `ngBind`, `ngBindHtml` or `{{...}}` interpolations, binding data and CSS class
`ng-class` is attached to the corresponding element.
Where the compiler has created a new scope, the scope and either `ng-scope` or `ng-isolated-scope`
CSS class are attached to the corresponding element. These scope references can then be accessed via
`element.scope()` and `element.isolateScope()`.
Tools like [Protractor](github.com/angular/protractor) and
[Batarang](https://github.com/angular/angularjs-batarang) need this information to run,
but you can disable this in production for a significant performance boost with:
```js
myApp.config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false);
}]);
```
If you wish to debug an application with this information then you should open up a debug
console in the browser then call this method directly in this console:
```js
angular.reloadWithDebugInfo();
```
The page should reload and the debug information should now be available.
For more see the docs pages on {@link ng.$compileProvider#debugInfoEnabled `$compileProvider`}
and {@link ng/function/angular.reloadWithDebugInfo `angular.reloadWithDebugInfo`}.
+27 -19
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Providers
@sortOrder 340
@description
# Providers
@@ -97,9 +98,8 @@ created by this recipe.
Note: All services in Angular are singletons. That means that the injector uses each recipe at most
once to create the object. The injector then caches the reference for all future needs.
Since Factory is more a powerful version of the Value recipe, you can construct the same service with it.
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
this:
Since a Factory is a more powerful version of the Value recipe, the same service can be constructed with it.
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like this:
```javascript
myApp.factory('clientId', function clientIdFactory() {
@@ -134,11 +134,11 @@ token.
<div class="alert alert-success">
**Best Practice:** name the factory functions as `<serviceId>Factory`
(e.g. apiTokenFactory). While this naming convention is not required, it helps when navigating the code base
(e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase
or looking at stack traces in the debugger.
</div>
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
Just like with the Value recipe, the Factory recipe can create a service of any type, whether it be a
primitive, object literal, function, or even an instance of a custom type.
@@ -153,7 +153,7 @@ function UnicornLauncher(apiToken) {
this.launchedCount = 0;
this.launch = function() {
// make a request to the remote api and include the apiToken
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
@@ -170,7 +170,7 @@ myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
```
This is, however, exactly the use-case that Service recipe is the most suitable for.
This is, however, exactly the use-case that the Service recipe is the most suitable for.
The Service recipe produces a service just like the Value or Factory recipes, but it does so by
*invoking a constructor with the `new` operator*. The constructor can take zero or more arguments,
@@ -189,7 +189,7 @@ myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Much simpler!
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
be somehow punished for our mis-deed. It's like we named one of our offspring 'Child'. Boy,
be somehow punished for our misdeed. It's like we named one of our offspring 'Child'. Boy,
that would mess with the teachers.
@@ -199,8 +199,8 @@ As already mentioned in the intro, the Provider recipe is the core recipe type a
all the other recipe types are just syntactic sugar on top of it. It is the most verbose recipe
with the most abilities, but for most services it's overkill.
Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
method is a factory function just like the one we use in Factory recipe. In fact, if you define
The Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
method is a factory function just like the one we use in the Factory recipe. In fact, if you define
a Factory recipe, an empty Provider type with the `$get` method set to your factory function is
automatically created under the hood.
@@ -248,7 +248,7 @@ and wires (injects) all provider instances only.
During application bootstrap, before Angular goes off creating all services, it configures and
instantiates all providers. We call this the configuration phase of the application life-cycle.
During this phase services aren't accessible because they haven't been created yet.
During this phase, services aren't accessible because they haven't been created yet.
Once the configuration phase is over, interaction with providers is disallowed and the process of
creating services starts. We call this part of the application life-cycle the run phase.
@@ -259,9 +259,9 @@ creating services starts. We call this part of the application life-cycle the ru
We've just learned how Angular splits the life-cycle into configuration phase and run phase and how
you can provide configuration to your application via the config function. Since the config
function runs in the configuration phase when no services are available, it doesn't have access
even to simple value objects created via Value recipe.
even to simple value objects created via the Value recipe.
Since simple values, like url prefix, don't have dependencies or configuration, it is often handy
Since simple values, like URL prefixes, don't have dependencies or configuration, it's often handy
to make them available in both the configuration and run phases. This is what the Constant recipe
is for.
@@ -317,7 +317,7 @@ Let's take a look at how we would create a very simple component via the directi
on the `planetName` constant we've just defined and displays the planet name, in our case:
"Planet Name: Greasy Giant".
Since the directives are registered via Factory recipe, we can use the same syntax as with factories.
Since the directives are registered via the Factory recipe, we can use the same syntax as with factories.
```javascript
myApp.directive('myPlanet', ['planetName', function myPlanetDirectiveFactory(planetName) {
@@ -340,7 +340,7 @@ We can then use the component like this:
</html>
```
Using Factory recipes you can also define Angular's filters and animations, but the controllers
Using Factory recipes, you can also define Angular's filters and animations, but the controllers
are a bit special. You create a controller as a custom type that declares its dependencies as
arguments for its constructor function. This constructor is then registered with a module. Let's
take a look at the `DemoController`, created in one of the early examples:
@@ -351,7 +351,7 @@ myApp.controller('DemoController', ['clientId', function DemoController(clientId
}]);
```
The DemoController is instantiated via its constructor every time the app needs an instance of
The DemoController is instantiated via its constructor, every time the app needs an instance of
DemoController (in our simple app it's just once). So unlike services, controllers are not
singletons. The constructor is called with all the requested services, in our case the `clientId`
service.
@@ -365,12 +365,12 @@ To wrap it up, let's summarize the most important points:
- There are five recipe types that define how to create objects: Value, Factory, Service, Provider
and Constant.
- Factory and Service are the most commonly used recipes. The only difference between them is that
Service recipe works better for objects of custom type, while Factory can produce JavaScript
the Service recipe works better for objects of a custom type, while the Factory can produce JavaScript
primitives and functions.
- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
- Provider is the most complex recipe type. You don't need it unless you are building a reusable
piece of code that needs global configuration.
- All special purpose objects except for Controller are defined via Factory recipes.
- All special purpose objects except for the Controller are defined via Factory recipes.
<table class="table table-bordered code-table">
<thead>
@@ -409,7 +409,15 @@ To wrap it up, let's summarize the most important points:
<td class="success">yes\*\*</td>
</tr>
<tr>
<td>can create functions/primitives</td>
<td>can create functions</td>
<td class="success">yes</td>
<td class="success">yes</td>
<td class="success">yes</td>
<td class="success">yes</td>
<td class="success">yes</td>
</tr>
<tr>
<td>can create primitives</td>
<td class="success">yes</td>
<td class="error">no</td>
<td class="success">yes</td>
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Scopes
@sortOrder 240
@description
# What are Scopes?
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Services
@sortOrder 230
@description
# Services
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Templates
@sortOrder 260
@description
In Angular, templates are written with HTML that contains Angular-specific elements and attributes.
+11
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Unit Testing
@sortOrder 410
@description
JavaScript is a dynamically typed language which comes with great power of expression, but it also
@@ -337,6 +338,16 @@ We inject the $compile service and $rootScope before each jasmine test. The $com
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
replaced the content and "lidless, wreathed in flame, 2 times" is present.
<div class="alert alert-info">
**Underscore notation**:
The use of the underscore notation (e.g.: `_$rootScope_`) is a convention wide spread in AngularJS
community to keep the variable names clean in your tests. That's why the
{@link $injector} strips out the leading and the trailing underscores when
matching the parameters. The underscore rule applies ***only*** if the name starts **and** ends with
exactly one underscore, otherwise no replacing happens.
</div>
### Testing Transclusion Directives
Directives that use transclusion are treated specially by the compiler. Before their compile
+1 -1
View File
@@ -205,7 +205,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.
+3 -1
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');
@@ -250,6 +250,8 @@ browser is limited, which results in your karma tests running extremely slow.
<tr><th>row number</th></tr>
<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`.
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
+2 -2
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'));
@@ -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');
+2 -2
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>
@@ -166,7 +166,7 @@ Because we started using dependency injection and our controller has dependencie
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
constructor with some kind of fake `$http` implementation. However, Angular provides a mock `$http`
service that we can use in unit tests. We configure "fake" responses to server requests by calling
methods on a service called $httpBackend:
methods on a service called `$httpBackend`:
```js
describe('PhoneCat controllers', function() {
+3 -2
View File
@@ -26,15 +26,16 @@ We are using [Bower][bower] to install client side dependencies. This step upda
```json
{
"name": "angular-seed",
"name": "angular-phonecat",
"description": "A starter project for AngularJS",
"version": "0.0.0",
"homepage": "https://github.com/angular/angular-seed",
"homepage": "https://github.com/angular/angular-phonecat",
"license": "MIT",
"private": true,
"dependencies": {
"angular": "1.2.x",
"angular-mocks": "~1.2.x",
"jquery": "1.10.2",
"bootstrap": "~3.1.1",
"angular-route": "~1.2.x"
}
+8 -1
View File
@@ -49,8 +49,15 @@ and install this dependency. We can do this by running:
npm install
```
<div class="alert alert-warning">
**Warning:** If a new version of Angular has been released since you last ran `npm install`, then you may have a
problem with the `bower install` due to a conflict between the versions of angular.js that need to
be installed. If you get this then simply delete your `app/bower_components` folder before running
`npm install`.
</div>
<div class="alert alert-info">
If you have bower installed globally then you can run `bower install` but for this project we have
**Note:** If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured `npm install` to run bower for us.
</div>
+15 -8
View File
@@ -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:
@@ -59,8 +59,15 @@ We must ask bower to download and install this dependency. We can do this by run
npm install
```
<div class="alert alert-warning">
**Warning:** If a new version of Angular has been released since you last ran `npm install`, then you may have a
problem with the `bower install` due to a conflict between the versions of angular.js that need to
be installed. If you get this then simply delete your `app/bower_components` folder before running
`npm install`.
</div>
<div class="alert alert-info">
If you have bower installed globally then you can run `bower install` but for this project we have
**Note:** If you have bower installed globally then you can run `bower install` but for this project we have
preconfigured `npm install` to run bower for us.
</div>
@@ -248,7 +255,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.
@@ -333,13 +340,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.
-185
View File
@@ -1,185 +0,0 @@
var path = require('canonical-path');
var versionInfo = require('../lib/versions/version-info');
var basePath = __dirname;
var basePackage = require('./config');
module.exports = function(config) {
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
var getVersion = function(component, sourceFolder, packageFile) {
sourceFolder = sourceFolder || '../bower_components';
packageFile = packageFile || 'bower.json';
return require(path.join(sourceFolder,component,packageFile)).version;
};
config = basePackage(config);
config.set('source.projectPath', path.resolve(basePath, '..'));
config.set('source.files', [
{ pattern: 'src/**/*.js', basePath: path.resolve(basePath,'..') },
{ pattern: '**/*.ngdoc', basePath: path.resolve(basePath, 'content') }
]);
config.set('processing.stopOnError', true);
config.set('processing.errors.minerrInfoPath', path.resolve(basePath, '../build/errors.json'));
config.set('rendering.outputFolder', '../build/docs');
config.set('rendering.contentsFolder', 'partials');
config.set('logging.level', 'info');
config.merge('deployment', {
environments: [{
name: 'debug',
examples: {
commonFiles: {
scripts: [ '../../../angular.js' ]
},
dependencyPath: '../../../'
},
scripts: [
'../angular.js',
'../angular-resource.js',
'../angular-route.js',
'../angular-cookies.js',
'../angular-sanitize.js',
'../angular-touch.js',
'../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/docs.js'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
},
{
name: 'default',
examples: {
commonFiles: {
scripts: [ '../../../angular.min.js' ]
},
dependencyPath: '../../../'
},
scripts: [
'../angular.min.js',
'../angular-resource.min.js',
'../angular-route.min.js',
'../angular-cookies.min.js',
'../angular-sanitize.min.js',
'../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',
'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'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
},
{
name: 'jquery',
examples: {
commonFiles: {
scripts: [
'../../components/jquery-' + getVersion('jquery') + '/jquery.js',
'../../../angular.js'
]
},
dependencyPath: '../../../'
},
scripts: [
'components/jquery-' + getVersion('jquery') + '/jquery.js',
'../angular.min.js',
'../angular-resource.min.js',
'../angular-route.min.js',
'../angular-cookies.min.js',
'../angular-sanitize.min.js',
'../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',
'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'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
},
{
name: 'production',
examples: {
commonFiles: {
scripts: [ cdnUrl + '/angular.min.js' ]
},
dependencyPath: cdnUrl + '/'
},
scripts: [
cdnUrl + '/angular.min.js',
cdnUrl + '/angular-resource.min.js',
cdnUrl + '/angular-route.min.js',
cdnUrl + '/angular-cookies.min.js',
cdnUrl + '/angular-sanitize.min.js',
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',
'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'
],
stylesheets: [
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/docs.css',
'css/animations.css'
]
}
]
});
return config;
};
+26 -12
View File
@@ -1,8 +1,11 @@
"use strict";
var gulp = require('gulp');
var log = require('gulp-util').log;
var concat = require('gulp-concat');
var jshint = require('gulp-jshint');
var bower = require('bower');
var dgeni = require('dgeni');
var Dgeni = require('dgeni');
var merge = require('event-stream').merge;
var path = require('canonical-path');
@@ -12,8 +15,10 @@ var path = require('canonical-path');
// See clean and bower for async tasks, and see assets and doc-gen for dependent tasks below
var outputFolder = '../build/docs';
var bowerFolder = '../bower_components';
var bowerFolder = 'bower_components';
var src = 'app/src/**/*.js';
var assets = 'app/assets/**/*';
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
pattern = pattern || '/**/*';
@@ -26,34 +31,40 @@ var copyComponent = function(component, pattern, sourceFolder, packageFile) {
};
gulp.task('bower', function() {
return bower.commands.install();
var bowerTask = bower.commands.install();
bowerTask.on('log', function (result) {
log('bower:', result.id, result.data.endpoint.name);
});
bowerTask.on('error', function(error) {
log(error);
});
return bowerTask;
});
gulp.task('build-app', function() {
gulp.src('app/src/**/*.js')
gulp.src(src)
.pipe(concat('docs.js'))
.pipe(gulp.dest(outputFolder + '/js/'));
});
gulp.task('assets', ['bower'], function() {
return merge(
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
gulp.src([assets]).pipe(gulp.dest(outputFolder)),
copyComponent('bootstrap', '/dist/**/*'),
copyComponent('open-sans-fontface'),
copyComponent('lunr.js','/*.js'),
copyComponent('google-code-prettify'),
copyComponent('jquery', '/jquery.*'),
copyComponent('jquery', '/dist/*.js'),
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
);
});
gulp.task('doc-gen', function() {
var generateDocs = dgeni.generator('docs.config.js');
return generateDocs()
.catch(function(error) {
process.exit(1);
});
gulp.task('doc-gen', ['bower'], function() {
var dgeni = new Dgeni([require('./config')]);
return dgeni.generate().catch(function(error) {
process.exit(1);
});
});
// JSHint the example and protractor test files
@@ -68,3 +79,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']);
});
+13
View File
@@ -0,0 +1,13 @@
'use strict';
var config = require('../protractor-shared-conf').config;
config.specs = [
'app/e2e/**/*.scenario.js'
];
config.capabilities = {
browserName: 'chrome',
};
exports.config = config;
+3 -14
View File
@@ -319,7 +319,6 @@ goog.provide('goog.i18n.DateTimeSymbols_fur');
goog.provide('goog.i18n.DateTimeSymbols_fur_IT');
goog.provide('goog.i18n.DateTimeSymbols_fy');
goog.provide('goog.i18n.DateTimeSymbols_fy_NL');
goog.provide('goog.i18n.DateTimeSymbols_ga');
goog.provide('goog.i18n.DateTimeSymbols_ga_IE');
goog.provide('goog.i18n.DateTimeSymbols_gd');
goog.provide('goog.i18n.DateTimeSymbols_gd_GB');
@@ -10770,9 +10769,9 @@ goog.i18n.DateTimeSymbols_fy_NL = goog.i18n.DateTimeSymbols_fy;
/**
* Date/time formatting symbols for locale ga.
* Date/time formatting symbols for locale ga_IE.
*/
goog.i18n.DateTimeSymbols_ga = {
goog.i18n.DateTimeSymbols_ga_IE = {
ERAS: ['RC', 'AD'],
ERANAMES: ['Roimh Chríost', 'Anno Domini'],
NARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D', 'S', 'N'],
@@ -10809,12 +10808,6 @@ goog.i18n.DateTimeSymbols_ga = {
};
/**
* Date/time formatting symbols for locale ga_IE.
*/
goog.i18n.DateTimeSymbols_ga_IE = goog.i18n.DateTimeSymbols_ga;
/**
* Date/time formatting symbols for locale gd.
*/
@@ -21329,12 +21322,8 @@ if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fy;
}
if (goog.LOCALE == 'ga') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
}
if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga_IE;
}
if (goog.LOCALE == 'gd') {
+43
View File
@@ -67,6 +67,7 @@ goog.provide('goog.i18n.DateTimeSymbols_fi');
goog.provide('goog.i18n.DateTimeSymbols_fil');
goog.provide('goog.i18n.DateTimeSymbols_fr');
goog.provide('goog.i18n.DateTimeSymbols_fr_CA');
goog.provide('goog.i18n.DateTimeSymbols_ga');
goog.provide('goog.i18n.DateTimeSymbols_gl');
goog.provide('goog.i18n.DateTimeSymbols_gsw');
goog.provide('goog.i18n.DateTimeSymbols_gu');
@@ -1409,6 +1410,46 @@ goog.i18n.DateTimeSymbols_fr_CA = {
};
/**
* Date/time formatting symbols for locale ga.
*/
goog.i18n.DateTimeSymbols_ga = {
ERAS: ['RC', 'AD'],
ERANAMES: ['Roimh Chríost', 'Anno Domini'],
NARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D', 'S', 'N'],
STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D',
'S', 'N'],
MONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine', 'Meitheamh',
'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair', 'Samhain',
'Nollaig'],
STANDALONEMONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine',
'Meitheamh', 'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair',
'Samhain', 'Nollaig'],
SHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith', 'Iúil',
'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
STANDALONESHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith',
'Iúil', 'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
WEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin',
'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
STANDALONEWEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt',
'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
SHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine', 'Sath'],
STANDALONESHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine',
'Sath'],
NARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
QUARTERS: ['1ú ráithe', '2ú ráithe', '3ú ráithe', '4ú ráithe'],
AMPMS: ['a.m.', 'p.m.'],
DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
FIRSTDAYOFWEEK: 6,
WEEKENDRANGE: [5, 6],
FIRSTWEEKCUTOFFDAY: 2
};
/**
* Date/time formatting symbols for locale gl.
*/
@@ -4307,6 +4348,8 @@ if (goog.LOCALE == 'af') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
} else if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CA;
} else if (goog.LOCALE == 'ga') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
} else if (goog.LOCALE == 'gl') {
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gl;
} else if (goog.LOCALE == 'gsw') {

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