Compare commits

..

406 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
Brian Ford fc8d6d75ab docs(changelog): release notes for 1.2.23 superficial-malady 2014-08-22 15:57:26 -07:00
Brian Ford b93601e691 docs(changelog): release notes for 1.3.0-beta.19 rafter-ascension 2014-08-22 15:57:26 -07:00
Igor Minar 1a2caad922 chore(mocks): remove helper fn angular.mocks.clearData
we don't need this any more because Karma reloads the iframe after each test run

Related to #8532
Closes #8618
2014-08-22 15:30:34 -07:00
Tobias Bosch ffbd276d6d fix($compile): use the correct namespace for transcluded svg elements
Via transclusion, svg elements can occur outside an `<svg>` container in an
Angular template but are put into an `<svg>` container through compilation
and linking.

E.g.
Given that `svg-container` is a transcluding directive with
the following template:
```
<svg ng-transclude></svg>
```

The following markup creates a `<circle>` inside of an `<svg>` element
during runtime:
```
<svg-container>
  <circle></circle>
</svg-container>
```

However, this produces non working `<circle>` elements, as svg elements
need to be created inside of an `<svg>` element.

This change detects for most cases the correct namespace of transcluded content
and recreates that content in the correct `<svg>` container
when needed during compilation. For special cases it adds an addition argument
to `$transclude` that allows to specify the future parent node of elements
that will be cloned and attached using the `cloneAttachFn`.

Related to #8494
Closes #8716
2014-08-22 14:02:13 -07:00
Tobias Bosch 75c4cbf81f refactor($compile): rename directive.type to directive.templateNamespace
Also corrects the tests for MathML that use `directive.templateNamespace`.

BREAKING CHANGE (within 1.3.0-beta): `directive.type` was renamed to `directive.templateNamespace`

The property name `type` was too general.
2014-08-22 14:00:58 -07:00
Shahar Talmi 642af96c48 refactor(Angular): make NaN check in date equality cleaner
Closes #8718
2014-08-22 13:52:06 -07:00
Igor Minar 252e8b5733 revert: feat($compile): bind isolate scope properties to controller
This reverts commit 787c5a76dc.

This change causes a regression at Google. We'll take a better look at it
next week.

Reopens #7645
2014-08-22 11:48:29 -07:00
Igor Minar 0c4997f7d8 style: name anonymous fn that causes initial digest for better debugging/profiling 2014-08-22 11:46:45 -07:00
Caitlin Potter d713ad1b66 fix(ngRepeat): allow aliasAs identifiers which contain but do not match reserved words
Currently if a reserved word occurs anywhere within the aliasAs identifier, we throw. This CL fixes
this behaviour by allowing these identifiers, since they are technically perfectly valid.

Closes #8729
2014-08-22 09:50:22 -04:00
Jeff Cross 066c049957 fix(input): use lowercase method to account for undefined type 2014-08-22 00:35:35 -07:00
danrbergman 9352bdfdaf docs(guide/module): update tag in description
the reference to 'myApp' module changed in the example from <html> to a <div>. Updating description
to reflect the new <div> tag.

Closes #8720
2014-08-21 23:33:21 -04:00
danrbergman b8b8411b15 docs(guide/module): make the use of ng-app explicit in example
Helpful for people new to Angular to see the ng-app declaration in context with the expression
example. This will help illustrate the "Important thing to notice" point which follows: "The
reference to myApp module in <html ng-app="myApp">. This is what bootstraps the app using your
module."

Closes #8673
2014-08-21 22:28:15 -04:00
Sekib Omazic 693e846add fix(Angular): make Date comparison in equals() NaN-aware
Make angular.equals() Date comparison NaN-aware to prevent infinite digest errors when a dealy watched
date has an invalid value.

Closes #8650
Closes #8715
2014-08-21 21:17:21 -04:00
Jeff Cross fdeaa74c6c chore(shrinkwrap): add angular-benchpress to shrinkwrap 2014-08-21 16:49:01 -07:00
Caitlin Potter 09de7b5db4 feat($compile): use allOrNothing interpolation for ngAttr*
allOrNothing interpolation is now used for ng-attr-*, under all circumstances. This prevents
uninitialized attributes from being added to the DOM with invalid values which cause errors
to be shown.

BREAKING CHANGE:

Now, ng-attr-* will never add the attribute to the DOM if any of the interpolated expressions
evaluate to `undefined`.

To work around this, initialize values which are intended to be the empty string with the
empty string:

For example, given the following markup:

    <div ng-attr-style="border-radius: {{value}}{{units}}"></div>

If $scope.value is `4`, and $scope.units is undefined, the resulting markup is unchanged:

    <div ng-attr-style="border-radius: {{value}}{{units}}"></div>

However, if $scope.units is `""`, then the resulting markup is updated:

    <div ng-attr-style="border-radius: {{value}}{{units}}" style="border-radius: 4"></div>

Closes #8376
Closes #8399
2014-08-21 19:31:50 -04:00
Caitlin Potter a7fb357fa1 fix(input): by default, do not trim input[type=password] values
Do not trim input[type=password] values

BREAKING CHANGE:

Previously, input[type=password] would trim values by default, and would require an explicit ng-trim="false"
to disable the trimming behaviour. After this CL, ng-trim no longer effects input[type=password], and will
never trim the password value.

Closes #8250
Closes #8230
2014-08-21 19:11:57 -04:00
Caitlin Potter 09b298705f fix(ngRepeat): make allowed aliasAs expressions more strict
Ensure that aliasAs expressions are valid simple identifiers. These are still assigned to $scope in the same way
that they were previously, however now you won't accidentally create a property named "filtered.collection".

This change additionally restricts identifiers to prevent the use of certain ECMAScript reserved words ("null",
"undefined", "this" --- should probably add "super", "try", "catch" and "finally" there too), as well as certain
properties used by $scope or ngRepeat, including $parent, $index, $even, $odd, $first, $middle, or $last.

Closes #8438
Closes #8440
2014-08-21 18:58:54 -04:00
Caitlin Potter b674003f41 chore(protractor): enable testing ng-app-included examples
/cc @petebacondarwin / @juliemr please review :>

Blocks #8673
Closes #8677
2014-08-21 18:54:44 -04:00
Caitlin Potter 787c5a76dc 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-21 18:41:19 -04:00
Casey Flynn dfbe69c45a docs($interval): fix typo in example
It's not "nis", it's "is"! 〜( ̄▽ ̄)〜

Closes #8711
2014-08-21 15:29:02 -04:00
Lucas Galfaso 1339c11e36 refactor($q): make $q Promises A+ v1.1 compilant
The Promises A+ 1.1 spec introduces new constraints that would cause $q to fail,
particularly specs 2.3.1 and 2.3.3.

Newly satisfied requirements:

 * "then" functions that return the same fulfilled/rejected promise
	will fail with a TypeError
 * Support for edge cases where "then" is a value other than function

Full 1.1 spec: https://github.com/promises-aplus/promises-spec/tree/1.1.0

This commit also modifies the adapter to use "resolve" method instead of "fulfill"
2014-08-21 10:52:08 -07:00
Caitlin Potter a603e202cc fix(copy): clear array destinations correctly for non-array sources
Closes #8610
Closes #8702
2014-08-20 21:49:54 -04:00
Jeff Cross 0872388a1b fix(minErr): encode btstrpd error input to strip angle brackets
The $sanitize service was returning an empty string to the error page
because the input was usually a single html tag (sometimes it could be
`document`). This fix replaces angle brackets with html entities.

Closes #8683
2014-08-20 17:26:50 -07:00
Caitlin Potter 36230194be fix(forEach): match behaviour of Array.prototype.forEach (ignore missing properties)
Array.prototype.forEach will not invoke the callback function if the properety is not present in the
object. Because of this, we have the illusion of not iterating over non-added properties in a sparse
array.

From ECMAScript:

9. Repeat while k < len
     a. Let Pk be ToString(k).
     b. Let kPresent be HasProperty(O, Pk).
     c. ReturnIfAbrupt(kPresent).
     d. If kPresent is true, then
            i. Let kValue be Get(O, Pk)
            ... (steps for invoking the function and aborting if it throws)

Closes #8510
Closes #8522
Closes #8525
2014-08-20 19:27:49 -04:00
Henrik Nyh 14b3db369e docs(ngDisabled): clarify "don't do this" example
It's not clear until you read the whole thing that it's an explanation
of what *not* to do and why, so if you scan the page from the top, you
may use this bad solution.
2014-08-20 15:44:23 -07:00
Brian Ford a9d227120d fix(linky): handle quotes around email addresses
Closes #8520
2014-08-20 13:37:45 -07:00
mishoo78 05791c0133 docs(ngMock): note that inject/module helpers only defined for jasmine / mocha
Closes #8694
2014-08-20 13:43:14 -04:00
Ole Weitz 00d5fde49c docs($cacheFactory): prevent example breaking on key update
The example for $cacheFactory breaks when a user tries to update a value for a key.
Setting a new value for an existing key results in duplicate key entries in the key array, thus
breaking the ng-repeat directive. With this fix the key is only added if it isn't contained in the
cache.

Closes #8214
2014-08-20 10:48:29 -04:00
Izhaki 39f6e229c2 docs($compile): fix documentation for ?^ controller search
Fixed typo: 'parents parents' to 'parents'

Closes #8690
2014-08-20 09:31:17 -04:00
Igor Minar 8863b9d04c perf($parse): don't bind filters to a context
This change gives us ~10% boost in Chrome, less or nothing in other browsers.

BREAKING CHANGE:  `this` in filters is now undefined and no longer the scope

It's a bad practice for filters to have hidden dependencies, so pulling stuff from scope directly
is not a good idea. Scope being the filter context was never documented as public api, so we don't
expect that any significant code depends on this behavior.

If an existing filter has a dependency on the scope instance, the scope reference can
be passed into the filter as a filter argument (this is highly discouraged for new code):

Before: `{{ user.name | customFilter }}`
After: `{{ user.name | customFilter:this }}`
2014-08-19 20:56:57 -07:00
Caitlin Potter d18b281976 fix($location): rewrite relative URI correctly if path==='/' in legacy html5Mode
Currently, legacy browsers get to use a clever scheme for resolving relative URIs in html5Mode,
and resolve the URI relative to $location.path().

Currently, $location.path() can be '/' under certain circumstances, which means that when we
split $location.path() on '/' and later join by '/' after adding another path component,
we end up with '//pathComponent'. $$rewrite fails to deal with this correctly, and effectively
the $location is never changed from the root path.

This CL corrects this by ensuring that the duplicate '/' situation does not occur when resolving
relative URIs.

Closes #8684
2014-08-19 21:31:02 -04:00
Jason Bedard f02f7d9c15 fix($compile): update the jQuery .context when an element is replaced by replace:true directive
.context is a deprecated jQuery api still being used by at least live() queries, so
we need to keep it in up to date during replacement.

Because of the if check, we can be sure that we replace the context only when jQuery is being
used and the context property is set to the element being replaced.

Closes #8253
Closes #7900
2014-08-19 17:24:34 -07:00
Lucas Galfaso 7eae29e5ab perf($rootScope): do not use Function::call when not needed
When a `$watchGroup` delegates the call to a `$watch`, there is no
need to keep the context of `this`
2014-08-20 00:57:31 +01:00
Lucas Galfaso 94b5c9f00e perf($interpolate): do not keep empty separators
Do not keep empty separators and keep references to where
each expression goes
2014-08-20 00:57:31 +01:00
Caitlin Potter 5b77e30c1a fix($location): don't call indexOf() of undefined href attribute
Closes #7721
Closes #8681
2014-08-19 19:16:02 -04:00
Brian Ford b7e82a33ee fix($sanitize): sanitize javascript urls with comments
Closes #8274
2014-08-19 14:10:54 -07:00
Marty Kane 6fdaa3d5a6 docs(guide/di): correct a few awkward sentences
Closes #8678
2014-08-19 16:50:48 -04:00
Izhaki d250dd43a3 docs($compile): correct documentation for directive controller ^ notation
`^` searches the element and its parents, not exclusively the element's parents. This confuses
a lot of people :(

Closes #8622
2014-08-19 14:30:11 -04:00
Shahar Talmi b8e54ef572 docs(*): use @description instead of @returns for properties
Dgeni-packages was not actually rendering the `@returns` text.

Closes #8639
2014-08-19 14:24:48 -04:00
Igor Minar e3a2ecf0db revert: refactor($compile): automatically append end comment nodes to all element-transclusion templates
This reverts commit 0d608d041f.

The commits caused more breaking changes at Google than initially expected and since its
benefit is small, so it's not worth keeping.
2014-08-19 10:57:50 -07:00
Baptiste Fontaine 63249a0aaf docs(ngBind): irrelevant text removed from ngBindHtml’s example
The ngBindHtml’s example had a copied line from ngBindTemplate’s that’s irrelevant here.

Closes #8668
2014-08-19 12:38:31 -04:00
Igor Minar 28b54bbfbf test(jqLite): add test for #wrap() when mutliple elements are being wrapped 2014-08-18 21:50:54 -07:00
chrisrhoden 77d3e75446 fix(jqLite): clone wrapNode in jqlite/wrap
Change jqLite's implementation of wrap() to clone the wrapNode before
wrapping the target element in it.
Match jQuery's wrap() behavior and prevent accidentally attaching
target element to the DOM as a side effect.

Closes #3860
Closes #4194
2014-08-18 21:50:44 -07:00
Igor Minar 1bdca93d70 fix(jqLite): revert the #ready() optimization until jQuery does the same
The change unfortunatelly makes us incompatible with jQuery which always falls back to onLoad.

Not falling back to onLoad is a possible breaking change because if Angular was added to the document during DOMContentLoaded
document.readyState at this point is 'interactive' which we'd need to add to our check, but more importantly if more scripts
are added during DOMContentLoaded these won't be loaded before we bootstrap, which can cause angular modules not to be found
during bootstrap.

This load ordering issues is really just a cornercase that should be handled via manual bootstrap, but until jQuery has the same
behavior we shouldn't do something else.
2014-08-18 21:20:12 -07:00
Igor Minar bf1a57ad48 fix(Scope): don't clear the phase when an exception is thrown from asyncQueue or watch
If we clear it here, then any other watch or async task could start a new digest.
2014-08-18 21:20:12 -07:00
Igor Minar ece6ef479c perf($parse): optimize filter implementation
- generate less garbage
- optimize argument collection
- use call instead of apply for no arg case (2x boost)
2014-08-18 21:20:12 -07:00
Igor Minar f7fde04160 style($parse): small readability improvements 2014-08-18 21:20:12 -07:00
Igor Minar 472019fb32 chore($parse): name anonymous functions for better debugging 2014-08-18 21:20:11 -07:00
Igor Minar a17578ad3d perf($parse): speed up fn invocation for no args case 2014-08-18 21:20:11 -07:00
Igor Minar cc16340e88 refactor($parse): small readability improvements 2014-08-18 21:20:11 -07:00
Igor Minar fecfc5b09f perf($parse): speed up fn invocation by optimizing arg collection
8-15% improvement for depending on the number of args
2014-08-18 21:20:11 -07:00
Igor Minar 6f5fc557ad refactor(jqLite): simplify jqLiteAddNodes when multiple nodes are being added
This branch is not as hot as the single node branch and the slice that we need for old webkit/phantom makes for loop preferable.
2014-08-18 21:20:11 -07:00
Igor Minar 0d608d041f refactor($compile): automatically append end comment nodes to all element-transclusion templates
Previously we would do it manually in all of our structural directives.

BREAKING CHANGE: element-transcluded directives now have an extra comment automatically appended to their cloned DOM

This comment is usually needed to keep track the end boundary in the event child directives modify the root node(s).
If not used for this purpose it can be safely ignored.
2014-08-18 21:20:11 -07:00
Igor Minar b5714ce1df refactor(ngRepeat): simplify previousNode boundary calculation
the previousNode was almost always correct except when we added a new block in which case incorrectly
assigned the cloned collection to the variable instead of the end comment node.
2014-08-18 21:20:11 -07:00
Igor Minar 27d4a4f0ca chore(ngRepeat): improve inline comments 2014-08-18 21:20:10 -07:00
Igor Minar 5cec4612c4 chore(ngRepeat): fix typo in a comment 2014-08-18 21:20:10 -07:00
Igor Minar 08eb05583b perf(ngRepeat): simplify code and remove duplicate array.length access
minimal perf gain (~2ms)
2014-08-18 21:20:10 -07:00
Igor Minar 2ef25887ef perF($compile): don't create jq wrapper for linkNode needlesly
when compileNode and linkNode are the same, we can reuse the existing jqLite wrapper.
2014-08-18 21:20:10 -07:00
Igor Minar f8f7a1df34 perf(jqLite): simplify jqLiteDealoc
while querySelectorAll is much more expensive than getElementsByTagName on elements with
both many and few children, cloning the live node list returned by getElementsByTagName
makes it as expensive as querySelectorAll (we need to clone because we need the node list
not to change while we iterate over it).

the childNodes and childNodes.length check is as expensive as querySelectorAll on a node
without any children, so it only makes the whole lookup 2x as slow, so I'm removing it.
2014-08-18 21:20:10 -07:00
Igor Minar b5f7970be5 perf($compile): don't register $destroy callbacks on element-transcluded nodes
This is a major perf win in the large table benchmark (~100ms or 9).

This cleanup is needed only for regular transclusion because only then the DOM hierarchy doesn't match scope hierarchy
(transcluded scope is a child of the parent scope and not a child of the isolate scope)

We should consider refactoring this further for the case of regular transclusion
and consider using scope events instead.
2014-08-18 21:20:10 -07:00
Igor Minar d05f27e274 perf(jqLite): optimize event handler 2014-08-18 21:20:09 -07:00
Igor Minar b0571517c5 refactor(isArray): use Array.isArray exclusively
IE9 supports Array.isArray so we don't need a polyfill any more.
2014-08-18 21:20:09 -07:00
Pawel Kozlowski c093c43b1f docs(orderBy): clarify expression usage in a predicate
Closes #8592
2014-08-18 13:32:09 -07:00
Tom Kadwill e1c85d1f98 docs(tutorial/index): improve wording
Removed repetition of 'machine' and 'local machine'.
I think this change makes the sentence more concise
2014-08-18 13:23:09 -07:00
Ciro Nunes 5feb9ab0f8 docs($compile): change restrict's defaults to EA
restrict defaults are EA let's update the docs
2014-08-18 13:12:11 -07:00
Jeff Cross 22bece7311 test(benchmark): add jQuery no-op script to large table benchmark 2014-08-18 11:00:23 -07:00
Jeff Sheets 65ec0ab70a docs($http): correct link to "salt (cryptography)" wikipedia article
Closes #8654
2014-08-18 13:34:07 -04:00
Caitlin Potter 6133c63214 docs(misc/contribute): fix syntax highlighting of URLS
Closes #8168
Closes #8169
2014-08-17 21:13:30 -04:00
Caitlin Potter 187b1b8ef4 perf(jqLite): only take str.split() path when needed
bda673f8e7 changed code to only use `str.split()` when necessary,
but the result was that `str.split()` would always be taken unless ' ' was the first character
in the string, negating the effectiveness of the perf fix.

Closes #8648
2014-08-17 17:49:42 -04:00
Igor Minar b24e381427 style(shallowCopy): use common for loop style 2014-08-16 22:32:35 -07:00
Igor Minar e3f26b5a6b style(jqLite): use the common for loop code style 2014-08-16 22:29:00 -07:00
Igor Minar c1cb341c87 refactor($compile): simplify controllersBoundTransclude check
no measurable perf difference, but the code more simple and minifies better
2014-08-16 22:10:04 -07:00
Igor Minar 0605e612e1 chore(angular.suffix): fix typo 2014-08-16 22:09:56 -07:00
Igor Minar 645625cf34 perf($compile): refactor publicLinkFn to simplify the code and use 'for in' loop 2014-08-16 22:09:48 -07:00
Igor Minar e822e9061c perf(Scope): optimize $watchCollection when used for watching objects
Since we control the oldValue, we don't need to worry about proto-inhereted properties which means we can use
'for in' and skip hasOwnProperty checks.

http://jsperf.com/for-in-vs-object-keys2
2014-08-16 22:09:38 -07:00
Igor Minar 301463a2e2 perf(Scope): don't use forEach in
doesn't make any significant impact on our current benchmarks because we don't have benchmarks with
many scope events, but this is a straightforward change worth doing
2014-08-16 22:09:29 -07:00
Igor Minar 7d96ab0d13 perf(Scope): watchCollection optimization
tiny ~4ms improvement and code that minifies better
2014-08-16 22:09:21 -07:00
Igor Minar 0a738ce176 perf(ngBind): bypass jquery/jqlite when setting text 2014-08-16 22:08:59 -07:00
Igor Minar d208ba2544 perf(isObject): use strict comparison
this is a micro-optimization based on http://jsperf.com/isobject4

no significant improvement in macro-benchmarks, but since it makes the code better it makes
sense making this change.
2014-08-16 22:06:14 -07:00
Igor Minar de3f238764 chore(test): rename getBlockElements to getBlockNodes in .jshintrc for tests 2014-08-16 22:00:10 -07:00
Igor Minar d302ea0cfa perf($parse): use no-proto maps as caches and avoid hasOwnProperty checks 2014-08-16 21:59:38 -07:00
Igor Minar a09fa35641 perf(Scope): exit $broadcast early if nobody is listening for the given event 2014-08-16 21:52:10 -07:00
Igor Minar 36e35b2cb1 perf(ngRepeat): optimize marking of nodes that are being removed via an animation 2014-08-16 21:52:04 -07:00
Igor Minar 13d113c522 perf(ngRepeat): use no-proto objects for blockMaps 2014-08-16 21:51:56 -07:00
Igor Minar 215d9545dc style(ngRepeat): ws and indentation fixes 2014-08-16 21:51:49 -07:00
Igor Minar bdd853cb83 perf(ngRepeat): move work to compile fn
this has impact only on nested repeaters where the number of columns is significant
2014-08-16 21:51:40 -07:00
Igor Minar e58d65a520 perf(ngRepeat): move updateScope fn to factory and reuse it for all repeaters 2014-08-16 21:51:33 -07:00
Igor Minar fbd48845e0 perf(ngRepeat): clone boundary comment nodes
http://jsperf.com/clone-vs-createcomment

most of the improvement comes from not reconcatinating the strings
2014-08-16 21:51:27 -07:00
Igor Minar abb17cce8b perf(jqLite): optimize off()
'for in' is much faster than Object.keys() and since the events object is ours, we know
that we don't need to worry about prototypically inherited properties so we can skip
expensive hasOwnProperty check.

http://jsperf.com/for-in-vs-object-keys2
2014-08-16 21:51:17 -07:00
Igor Minar 3e0a2e1f33 perf($compile): clone the nodeList during linking only if necessary 2014-08-16 21:51:06 -07:00
Igor Minar 1e8698b33e perf(jqLite): refactor jqLiteExpandoStore to minimize access to expensive element.ng339 expando property 2014-08-16 21:50:27 -07:00
Igor Minar 503dcb0281 style(jqLite): rename onFn to jqLiteOn 2014-08-16 21:31:41 -07:00
Igor Minar bf2e238418 refactor(jqLite): clean up jqLiteRemoveData fn 2014-08-16 21:31:35 -07:00
Igor Minar 8b77161702 refactor(Scope): remove useless compileToFn helper fn 2014-08-16 21:31:20 -07:00
Igor Minar 50e16a67bb refactor(Scope): rename $$childScopeClass to ChildClass to simplify debugging/profiling 2014-08-16 21:31:17 -07:00
Igor Minar b9b1aa7797 refactor($parse): simplify addInterceptor fn 2014-08-16 21:30:49 -07:00
Igor Minar a1341223c0 perf($parse): trim expression only if string 2014-08-16 21:30:44 -07:00
Igor Minar 99d70af11c style($parse): rename variables in generated code so that code is more readable 2014-08-16 21:30:38 -07:00
Igor Minar 6acea1152f fix($parse): remove unused variable declaration in generated getters 2014-08-16 21:30:32 -07:00
Igor Minar 33ab57948c style(ngRepeat): fix indentation 2014-08-16 21:30:27 -07:00
Igor Minar 31ed0af74b perf($compile): delay object initialization in nodeLinkFn 2014-08-16 21:30:20 -07:00
Igor Minar 13112710e6 refactor: remove unused variables 2014-08-16 21:30:07 -07:00
Igor Minar 54fa16e45d perf: speed up shallowCopy and special case Attributes cloning
`for in` is much faster than `Object.keys()` but `for in` includes properties from the prototype.

http://jsperf.com/for-in-vs-object-keys2

All the uses of shallowCopy don't deal with objects with heavy prototypes, except for Attributes instances
in $compile.

For this reason it's better to special-case Attributes constructor and make it do it's own shallow copy.
This cleans up the Attribute/$compile code as well.
2014-08-16 21:29:51 -07:00
Igor Minar fb00210cb6 refactor(shallowCopy): microoptimization 2014-08-16 21:01:13 -07:00
Igor Minar fafbd49490 perf(jqLite): microoptimization in chaining fn
note: no siginificant difference observed in macrobenchmarks, so this is just to make
me feel better :)
2014-08-16 21:01:13 -07:00
Igor Minar bda673f8e7 perf(jqLite): don't use String#split in on() unless we need it 2014-08-16 21:01:13 -07:00
Igor Minar b678f314f1 refactor(jqLite): don't look up the entry needlessly 2014-08-16 21:01:13 -07:00
Igor Minar 550965f111 style(jqLite): remove MiskoCode(tm) 2014-08-16 21:01:13 -07:00
Igor Minar 443b521e22 perf(jqLite): don't check isString many times in constructor
Note: no significant perf gain in Chrome
2014-08-16 21:01:12 -07:00
Igor Minar e6a9f9e130 refactor: simplify trim fn now that IE9 has String#trim 2014-08-16 21:01:12 -07:00
Igor Minar 441ab5235c refactor: rename getBlockElements to more correct getBlockNodes 2014-08-16 21:01:11 -07:00
Igor Minar 6b7b40af74 style(ngBind): name anonymous link fn to ease debugging/profiling 2014-08-16 15:38:50 -07:00
Igor Minar edc586f911 refactor(ngBindHtml): improve readability of the code 2014-08-16 15:36:32 -07:00
Igor Minar c7393093ff refactor(ngRepeat): name anonymous transclude callback for better debugging 2014-08-16 15:32:43 -07:00
Igor Minar b493c62f6b perf(jqLite): optimize jqLiteAcceptsData method
This doesn't show up too high in profiles, but this code is cleaner anyway
2014-08-16 15:32:32 -07:00
Igor Minar 35134a0e23 perf($compile): optimize nodeLinkFn
Functions with try/catch block can't be optimized, so we can
move the try/catch block into a tiny fn and make it possible for the
complex nodeLinkFn to get optimized.
2014-08-16 15:32:24 -07:00
Igor Minar 8d933bf995 perf(jqLite): optimize append() and after() 2014-08-16 15:31:10 -07:00
Igor Minar 6251751ad7 perf(jqLite): don't register DOM listener for $destroy event
This even is fired purely within jqLite/jQuery so it doesn't make sense to register DOM listener here.

6% improvement in large table benchmark for both creation and destruction
2014-08-16 15:05:22 -07:00
Igor Minar 8f10dca300 refactor(jqLite): remove legacy code to support IE8 and older 2014-08-16 15:05:22 -07:00
Igor Minar 566f1015d2 perf(jqLite): optimize event listener registration 2014-08-16 14:31:29 -07:00
Igor Minar 274e9c4ddf perf($compile): optimize publicLinkFn 2014-08-16 14:31:29 -07:00
Igor Minar e9cd6dc055 perf(jqLite): improve createEventHandler method by switching from forEach to for loop 2014-08-16 14:31:29 -07:00
Igor Minar 960a841051 perf(jqLite): don't use forEach in #off
off() is called on each element removal, so we want to make it as fast as possible
2014-08-16 14:31:28 -07:00
Igor Minar 01b10d7a24 refactor(jqLite): remove code duplication 2014-08-16 14:31:28 -07:00
Igor Minar 2a5dbbdc52 refactor(jqLite): don't recreate mouse event map 2014-08-16 14:31:28 -07:00
Igor Minar 6c4d601ff6 refactor(jqLite): drop Node.contains polyfill
Node.contains is supported on IE5+ and all the other browsers we care about.
2014-08-16 14:31:24 -07:00
Igor Minar d1536e7c8b perf(jqLite): don't recreate the Node.contains polyfill 2014-08-16 14:22:56 -07:00
Ken Sheedlo a19131494c style($rootScope): add semicolon for jshint 2014-08-15 17:17:55 -07:00
Tobias Bosch a353ed834c refactor(perf): migration event delegation benchmark to benchpress 2014-08-15 16:03:59 -07:00
rodyhaddad bf0e83732a fix($watchGroup): call listener once when the watchExpressions array is empty 2014-08-15 15:53:51 -07:00
Igor Minar 0554c1aae4 chore(Scope): remove deregisterNotifier feature for $watch
We no longer have a need for this feature that was added to primarily support
$watchGroup (see previous commit).

BREAKING CHANGE: deregisterNotifier callback for $watch is no longer available

This api was available only in the last few 1.3 beta versions and is not
very useful for applications, so we don't expect that anyone will be affected
by this change.
2014-08-15 15:53:51 -07:00
Igor Minar 3f0e642eef perf(Scope): use remove the need for the extra watch in $watchGroup
Instead of using a counter and an extra watch, just schedule the reaction function via .

This gives us the same/similar ordering and coalecsing of updates as counter without the extra
overhead. Also the code is easier to read.

Since interpolation uses watchGroup, this change additionally improves performance of interpolation.

In large table benchmark digest cost went down by 15-20% for interpolation.

Closes #8396
2014-08-15 15:53:51 -07:00
J. Bruni 1a05daf5dc feat(jqLite): implement the detach method
Closes #5461
2014-08-14 21:19:44 +02:00
Michał Gołębiowski b9389b26ba fix(jQuery): cooperate with other libraries monkey-patching jQuery.cleanData
Some libraries (like jQuery UI) patch jQuery.cleanData as well. This commit
makes Angular work correctly even if such external patching was done after
the Angular one.

Fixes #8471
2014-08-14 21:09:13 +02:00
Jeff Cross 6bdaa4bc21 feat(benchpress): configure benchpress grunt task 2014-08-13 23:28:13 -07:00
Jeff Cross bfd311174d docs(benchpress): add readme with instructions on using benchpress 2014-08-13 23:28:13 -07:00
Jeff Cross 1229334fbd perf(benchpress): add benchpress node module and port over large table test 2014-08-13 23:28:13 -07:00
Jack Wearden 77a1acc7fc feat(ngRoute): add method for changing url params
Add a $route#updateParams method for changing the current route
parameters without having to build a URL and call $location#path.
Useful for apps with a structure involving programmatically moving
between pages on the current route, but with different :param
values.

Properties in the object passed to $route.updateParams() will be
added to the location as queryParams if not contained within the
route's path definition.
2014-08-13 14:11:58 -07:00
Namolovan Nicolae 56bd0378bf docs(guide/e2e-testing): correct link to protractor's getting started
angular/protractor@fcd973b#diff-f3b56000093113bd3bfb6c9c05e7e945 splits the overview doc into
multiple files, and removes overview.md from the repository entirely, making the current link a 404.

This CL points the link to the new getting-started document rather than the overview, and avoids
linking to a missing document.

Closes #8595
2014-08-13 13:20:27 -04:00
Peter Bacon Darwin 76e57a764f chore(doc-gen): move e2e tests into docs folder
These tests didn't really fit in the test folder as the docs app is mostly
a separate entity from the AngularJS codebase.
2014-08-13 11:24:19 +01:00
Caitlin Potter f30e2d093b docs(CHANGELOG.md): fix typo, it's 1.2.22 not 1.2.2!
Whoops!
2014-08-12 16:51:40 -04:00
Caitlin Potter b33716f42a docs(CHANGELOG.md): add changelog for v1.3.0-beta.18 and v1.2.22
Closes #8581
2014-08-12 13:16:32 -04:00
947 changed files with 59913 additions and 6594 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"directory": "bower_components",
"json": "bower.json"
}
+2 -2
View File
@@ -1,4 +1,5 @@
/build/
/benchpress-build/
.DS_Store
gen_docs.disable
test.disable
@@ -9,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
+1157
View File
File diff suppressed because it is too large Load Diff
+16 -2
View File
@@ -10,6 +10,7 @@ module.exports = function(grunt) {
require('load-grunt-tasks')(grunt);
grunt.loadTasks('lib/grunt');
grunt.loadNpmTasks('angular-benchpress');
var NG_VERSION = versionInfo.currentVersion;
NG_VERSION.cdn = versionInfo.cdnVersion;
@@ -22,7 +23,12 @@ module.exports = function(grunt) {
//config
grunt.initConfig({
NG_VERSION: NG_VERSION,
bp_build: {
options: {
buildPath: 'build/benchmarks',
benchmarksPath: 'benchmarks'
}
},
parallel: {
travis: {
tasks: [
@@ -147,6 +153,9 @@ module.exports = function(grunt) {
},
ngTouch: {
files: { src: 'src/ngTouch/**/*.js' },
},
ngAria: {
files: {src: 'src/ngAria/**/*.js'},
}
},
@@ -214,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']
@@ -230,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
@@ -0,0 +1,6 @@
Instructions for using benchpress (how to create benchmarks, how to run, how to configure) can be
found at: https://github.com/angular/benchpress/blob/master/README.md.
In this project, there is a configured grunt task for building the benchmarks,
`grunt bp_build`, which places the runnable benchmarks in "/build/benchmarks/".
The existing `grunt webserver` task can be used to serve the built benchmarks at `localhost:8000/build/benchmarks/&lt;benchmark-name&gt;`
+57
View File
@@ -0,0 +1,57 @@
var app = angular.module('eventDelegationBenchmark', []);
app.directive('noopDir', function() {
return {
compile: function($element, $attrs) {
return function($scope, $element) {
return 1;
}
}
};
});
app.directive('nativeClick', ['$parse', function($parse) {
return {
compile: function($element, $attrs) {
var expr = $parse($attrs.tstEvent);
return function($scope, $element) {
$element[0].addEventListener('click', function() {
console.log('clicked');
}, false);
}
}
};
}]);
app.directive('dlgtClick', function() {
return {
compile: function($element, $attrs) {
var evt = $attrs.dlgtClick;
// We don't setup the global event listeners as the costs are small and one time only...
}
};
});
app.controller('DataController', function($rootScope) {
this.ngRepeatCount = 1000;
this.rows = [];
var self = this;
benchmarkSteps.push({
name: '$apply',
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('row'+i);
}
}
$rootScope.$apply();
}
});
});
+10
View File
@@ -0,0 +1,10 @@
module.exports = function(config) {
config.set({
scripts: [{
id: 'angular',
src: '/build/angular.js'
},{
src: 'app.js',
}]
});
};
+139
View File
@@ -0,0 +1,139 @@
<div ng-app="eventDelegationBenchmark">
<div ng-controller="DataController as ctrl">
<div class="container-fluid">
<p>
Impact of event delegation.
</p>
<p>
<label>
Number of ngRepeats:
<input type="number" ng-model="ctrl.ngRepeatCount">
</label>
</p>
<p>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="ngClick">ngClick</label></div>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="ngClickNoJqLite">ngClick without jqLite</label></div>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="ngShow">baseline: ng-show</label></div>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="textInterpolation">baseline: text interpolation</label></div>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="dlgtClick">delegate event directive (only compile)</label></div>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="noopDir">baseline: noop directive (compile and link)</label></div>
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="noop">baseline: no directive</label></div>
</p>
<p>
How to read the results:
<ul>
<li>The benchmark measures how long it takes to instantiate a given number of directives</li>
<li>ngClick is compared against ngShow and text interpolation as baseline. The results show
how expensive ngClick is compared to other very simple directives that touch the DOM.
</li>
<li>To measure the impact of jqLite.on vs element.addEventListener there is also a benchmark
that as a modified version of ngClick that uses element.addEventListener.
</li>
<li>The delegate event directive is compared against a noop directive with a compile and link function and the case with no directives.
The result shows how expensive it is to add a link function to a directive, as the delegate event directive has none.
</li>
</ul>
</p>
<p>
Results as of 7/31/2014:
<ul>
<li>ngClick is very close to ngShow and text interpolation, especially when looking at a version of ngClick that does not use jqLite.on but element.addEventListener instead.</li>
<li>A delegate event directive that has no link function has the same speed as a directive with link function. I.e. ngClick is slower compared to the delegate event directive only because ngClick touches
the DOM for every element</li>
<li>A delegate event directive could be about 50% faster than ngClick. However, the overall performance
benefit depends on how many (and which) other directives are used on the same element
and what other things are part of the measures use case.
E.g. rows of a table with ngRepeat that use ngClick will probably also contain text interpolation.
</li>
</ul>
</p>
Debug output:
<ng-switch on="benchmarkType">
<div ng-switch-when="ngClick">
<div>
<span ng-repeat="row in ctrl.rows">
<span ng-click="a()">1</span>
<span ng-click="a()">1</span>
<span ng-click="a()">1</span>
<span ng-click="a()">1</span>
</span>
</div>
</div>
<div ng-switch-when="ngClickNoJqLite">
<div>
<span ng-repeat="row in ctrl.rows">
<span native-click="a()">1</span>
<span native-click="a()">1</span>
<span native-click="a()">1</span>
<span native-click="a()">1</span>
<span native-click="a()">1</span>
</span>
</div>
</div>
<div ng-switch-when="ngShow">
<div>
<span ng-repeat="row in ctrl.rows">
<span ng-show="true">1</span>
<span ng-show="true">1</span>
<span ng-show="true">1</span>
<span ng-show="true">1</span>
<span ng-show="true">1</span>
</span>
</div>
</div>
<div ng-switch-when="textInterpolation">
<div>
<span ng-repeat="row in ctrl.rows">
<span>{{row}}</span>
<span>{{row}}</span>
<span>{{row}}</span>
<span>{{row}}</span>
<span>{{row}}</span>
</span>
</div>
</div>
<div ng-switch-when="dlgtClick">
<div>
<span ng-repeat="row in ctrl.rows">
<span dlgt-click="a()">1</span>
<span dlgt-click="a()">1</span>
<span dlgt-click="a()">1</span>
<span dlgt-click="a()">1</span>
<span dlgt-click="a()">1</span>
</span>
</div>
</div>
<div ng-switch-when="noopDir">
<div>
<span ng-repeat="row in ctrl.rows">
<span noop-dir>1</span>
<span noop-dir>1</span>
<span noop-dir>1</span>
<span noop-dir>1</span>
<span noop-dir>1</span>
</span>
</div>
</div>
<div ng-switch-when="noop">
<div>
<span ng-repeat="row in ctrl.rows">
<span>1</span>
<span>1</span>
<span>1</span>
<span>1</span>
<span>1</span>
</span>
</div>
</div>
</ng-switch>
</div>
</div>
</div>
+183
View File
@@ -0,0 +1,183 @@
var app = angular.module('largetableBenchmark', []);
app.config(function($compileProvider) {
if ($compileProvider.debugInfoEnabled) {
$compileProvider.debugInfoEnabled(false);
}
});
app.filter('noop', function() {
return function(input) {
return input;
};
});
app.controller('DataController', function($scope, $rootScope) {
var totalRows = 1000;
var totalColumns = 20;
var data = $scope.data = [];
$scope.digestDuration = '?';
$scope.numberOfBindings = totalRows*totalColumns*2 + totalRows + 1;
$scope.numberOfWatches = '?';
function iGetter() { return this.i; }
function jGetter() { return this.j; }
for (var i=0; i<totalRows; i++) {
data[i] = [];
for (var j=0; j<totalColumns; j++) {
data[i][j] = {
i: i, j: j,
iFn: iGetter,
jFn: jGetter
};
}
}
var previousType;
benchmarkSteps.push({
name: 'destroy',
fn: function() {
$scope.$apply(function() {
previousType = $scope.benchmarkType;
$scope.benchmarkType = 'none';
});
}
});
benchmarkSteps.push({
name: 'create',
fn: function() {
$scope.$apply(function() {
$scope.benchmarkType = previousType;
});
}
});
benchmarkSteps.push({
name: '$apply',
fn: function() {
$rootScope.$apply();
}
});
});
var fn = function() { return 'x'};
app.directive('baselineBindingTable', function() {
return {
restrict: 'E',
link: function ($scope, $element) {
var i, j, row, cell, comment;
var template = document.createElement('span');
template.setAttribute('ng-repeat', 'foo in foos');
template.classList.add('ng-scope');
template.appendChild(document.createElement('span'));
template.appendChild(document.createTextNode(':'));
template.appendChild(document.createElement('span'));
template.appendChild(document.createTextNode('|'));
for (i = 0; i < 1000; i++) {
row = document.createElement('div');
$element[0].appendChild(row);
for (j = 0; j < 20; j++) {
cell = template.cloneNode(true);
row.appendChild(cell);
cell.childNodes[0].textContent = i;
cell.childNodes[2].textContent = j;
cell.ng3992 = 'xxx';
comment = document.createComment('ngRepeat end: bar in foo');
row.appendChild(comment);
}
comment = document.createComment('ngRepeat end: foo in foos');
$element[0].appendChild(comment);
}
}
};
});
app.directive('baselineInterpolationTable', function() {
return {
restrict: 'E',
link: function ($scope, $element) {
var i, j, row, cell, comment;
var template = document.createElement('span');
template.setAttribute('ng-repeat', 'foo in foos');
template.classList.add('ng-scope');
for (i = 0; i < 1000; i++) {
row = document.createElement('div');
$element[0].appendChild(row);
for (j = 0; j < 20; j++) {
cell = template.cloneNode(true);
row.appendChild(cell);
cell.textContent = '' + i + ':' + j + '|';
cell.ng3992 = 'xxx';
comment = document.createComment('ngRepeat end: bar in foo');
row.appendChild(comment);
}
comment = document.createComment('ngRepeat end: foo in foos');
$element[0].appendChild(comment);
}
}
};
});
/*
the fastest
240/44
app.directive('baselineTable', function() {
return function($scope, $element) {
var i, j, row, cell;
for (i = 0; i < 1000; i++) {
row = document.createElement('div');
for (j = 0; j < 20; j++) {
cell = document.createElement('span');
cell.textContent = '' + i + ':' + j;
row.appendChild(cell);
}
$element[0].appendChild(row);
}
};
});
*/
/*
with comments and expando
232/90
app.directive('baselineTable', function() {
return function($scope, $element) {
var i, j, row, cell, comment;
for (i = 0; i < 1000; i++) {
row = document.createElement('div');
$element[0].appendChild(row);
for (j = 0; j < 20; j++) {
cell = document.createElement('span');
row.appendChild(cell);
cell.textContent = '' + i + ':' + j;
cell.ng3992 = 'xxx';
comment = document.createComment('ngRepeat end: bar in foo');
row.appendChild(comment);
}
comment = document.createComment('ngRepeat end: foo in foos');
$element[0].appendChild(comment);
}
};
});
*/
+15
View File
@@ -0,0 +1,15 @@
module.exports = function(config) {
config.set({
scripts: [{
id: 'jquery',
src: 'jquery-noop.js'
},
{
id: 'angular',
src: '/build/angular.js'
},
{
src: 'app.js',
}]
});
};
+1
View File
@@ -0,0 +1 @@
//Override me with ?jquery=/bower_components/jquery/dist/jquery.js
+65
View File
@@ -0,0 +1,65 @@
<style>
[ng-cloak] { display: none; }
</style>
<div ng-app="largetableBenchmark" ng-cloak>
<div ng-controller="DataController">
<div class="container-fluid">
<p>
Large table rendered with AngularJS
</p>
<div>none: <input type=radio ng-model="benchmarkType" value="none"></div>
<div>baseline binding: <input type=radio ng-model="benchmarkType" value="baselineBinding"></div>
<div>baseline interpolation: <input type=radio ng-model="benchmarkType" value="baselineInterpolation"></div>
<div>ngBind: <input type=radio ng-model="benchmarkType" value="ngBind"></div>
<div>interpolation: <input type=radio ng-model="benchmarkType" value="interpolation"></div>
<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>interpolation + filter: <input type=radio ng-model="benchmarkType" value="interpolationFilter"></div>
<ng-switch on="benchmarkType">
<baseline-binding-table ng-switch-when="baselineBinding">
</baseline-binding-table>
<baseline-interpolation-table ng-switch-when="baselineInterpolation">
</baseline-interpolation-table>
<div ng-switch-when="ngBind">
<h2>baseline binding</h2>
<div ng-repeat="row in data">
<span ng-repeat="column in row"><span ng-bind="column.i"></span>:<span ng-bind="column.j"></span>|</span>
</div>
</div>
<div ng-switch-when="interpolation">
<h2>baseline interpolation</h2>
<div ng-repeat="row in data">
<span ng-repeat="column in row">{{column.i}}:{{column.j}}|</span>
</div>
</div>
<div ng-switch-when="ngBindFn">
<h2>bindings with functions</h2>
<div ng-repeat="row in data">
<span ng-repeat="column in row"><span ng-bind="column.iFn()"></span>:<span ng-bind="column.jFn()"></span>|</span>
</div>
</div>
<div ng-switch-when="interpolationFn">
<h2>interpolation with functions</h2>
<div ng-repeat="row in data">
<span ng-repeat="column in row">{{column.iFn()}}:{{column.jFn()}}|</span>
</div>
</div>
<div ng-switch-when="ngBindFilter">
<h2>bindings with filter</h2>
<div ng-repeat="row in data">
<span ng-repeat="column in row"><span ng-bind="column.i | noop"></span>:<span ng-bind="column.j | noop"></span>|</span>
</div>
</div>
<div ng-switch-when="interpolationFilter">
<h2>interpolation with filter</h2>
<div ng-repeat="row in data">
<span ng-repeat="column in row">{{column.i | noop}}:{{column.j | noop}}|</span>
</div>
</div>
</ng-switch>
</div>
</div>
</div>
+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;
+4
View File
@@ -6,6 +6,10 @@
line-height: 1.5;
}
.lang-text * {
color: #333333!important;
}
.pln {
color: #333333;
}
+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,12 +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('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,49 +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
};
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,7 +0,0 @@
describe("{$ doc.description $}", function() {
beforeEach(function() {
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
});
{$ 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.
@@ -0,0 +1,39 @@
@ngdoc error
@name ngRepeat:badident
@fullName Invalid identifier expression
@description
Occurs when an invalid identifier is specified in an {@link ng.directive:ngRepeat ngRepeat} expression.
The {@link ng.directive:ngRepeat ngRepeat} directive's `alias as` syntax is used to assign an alias for the processed collection in scope.
If the expression is not a simple identifier (such that you could declare it with `var {name}`, or if the expression is a reserved name,
this error is thrown.
Reserved names include:
- `null`
- `this`
- `undefined`
- `$parent`
- `$even`
- `$odd`
- `$first`
- `$last`
- `$middle`
Invalid expressions might look like this:
```html
<li ng-repeat="item in items | filter:searchString as this">{{item}}</li>
<li ng-repeat="item in items | filter:searchString as some.objects["property"]">{{item}}</li>
<li ng-repeat="item in items | filter:searchString as resultOfSomeMethod()">{{item}}</li>
<li ng-repeat="item in items | filter:searchString as foo=6">{{item}}</li>
```
Valid expressions might look like this:
```html
<li ng-repeat="item in items | filter:searchString as collections">{{item}}</li>
<li ng-repeat="item in items | filter:searchString as filteredCollection">{{item}}</li>
```
+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
+7 -6
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.
@@ -135,7 +136,7 @@ These can be used interchangeably as you see fit and are equivalent.
### Implicit Dependencies
The simplest way to get hold of the dependencies, is to assume that the function parameter names
The simplest way to get hold of the dependencies is to assume that the function parameter names
are the names of the dependencies.
```js
@@ -144,7 +145,7 @@ function MyController($scope, greeter) {
}
```
Given a function the injector can infer the names of the service to inject by examining the
Given a function the injector can infer the names of the services to inject by examining the
function declaration and extracting the parameter names. In the above example `$scope`, and
`greeter` are two services which need to be injected into the function.
@@ -154,7 +155,7 @@ rename the method parameter names. This makes this way of annotating only useful
### `$inject` Property Annotation
To allow the minifiers to rename the function parameters and still be able to inject right services,
To allow the minifiers to rename the function parameters and still be able to inject the right services,
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
of service names to inject.
@@ -166,7 +167,7 @@ MyController['$inject'] = ['$scope', 'greeter'];
```
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
arguments to inject. Using the above code snippet as an example, `$scope` will be injected into
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
annotation is kept in sync with the actual arguments in the function declaration.
@@ -206,7 +207,7 @@ someModule.factory('greeter', ['$window', function(renamed$window) {
}]);
```
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
Here, instead of simply providing the factory function, we pass an array whose elements consist of
a list of strings (the names of the dependencies) followed by the function itself.
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
+8 -8
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,9 +160,11 @@ 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).
(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
results in `undefined`, the attribute is removed and not added to the element.
For example, we could fix the example above by instead writing:
@@ -279,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:
+2 -2
View File
@@ -1,6 +1,6 @@
@workInProgress
@ngdoc overview
@name E2E Testing
@sortOrder 420
@description
# E2E Testing
@@ -27,7 +27,7 @@ Protractor is a [Node.js](http://nodejs.org) program, and runs end to end tests
written in JavaScript and run with node. Protractor uses [WebDriver](https://code.google.com/p/selenium/wiki/GettingStarted)
to control browsers and simulate user actions.
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/overview.md)
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/getting-started.md)
or the [api docs](https://github.com/angular/protractor/blob/master/docs/api.md).
Protractor uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) for its test syntax.
+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),
+9 -6
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Modules
@sortOrder 320
@description
# What is a Module?
@@ -26,10 +27,12 @@ should be bootstrapped. There are several advantages to this approach:
I'm in a hurry. How do I get a Hello World module working?
<example module='myApp'>
<example ng-app-included="true">
<file name="index.html">
<div>
{{ 'World' | greet }}
<div ng-app="myApp">
<div>
{{ 'World' | greet }}
</div>
</div>
</file>
@@ -48,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>
@@ -56,7 +59,7 @@ I'm in a hurry. How do I get a Hello World module working?
Important things to notice:
* The {@link angular.Module Module} API
* The reference to `myApp` module in `<html ng-app="myApp">`.
* The reference to `myApp` module in `<div ng-app="myApp">`.
This is what bootstraps the app using your module.
* The empty array in `angular.module('myApp', [])`.
This array is the list of modules `myApp` depends on.
@@ -126,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
+5 -5
View File
@@ -62,13 +62,13 @@ minified AngularJS files:
```shell
# Clone your Github repository:
git clone git@github.com:<github username>/angular.js.git
git clone "git@github.com:<github username>/angular.js.git"
# Go to the AngularJS directory:
cd angular.js
# Add the main AngularJS repository as an upstream remote to your repository:
git remote add upstream https://github.com/angular/angular.js.git
git remote add upstream "https://github.com/angular/angular.js.git"
# Install node.js dependencies:
npm install
@@ -126,13 +126,13 @@ made available a local web server based on Node.js.
```
2. To access the local server, enter the following URL into your web browser:
```
```text
http://localhost:8000/
```
By default, it serves the contents of the AngularJS project directory.
3. To access the locally served docs, visit this URL:
```
```text
http://localhost:8000/build/docs/
```
@@ -165,7 +165,7 @@ change. To execute tests in this mode run:
2. To capture more browsers, open this URL in the desired browser (URL might be different if you have multiple instance
of Karma running, read Karma's console output for the correct URL):
```shell
```text
http://localhost:9876/
```
+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.
+2 -2
View File
@@ -44,8 +44,8 @@ really digging into it. If you're looking for a shorter introduction to AngularJ
# Get Started
The rest of this page explains how you can set up your machine to work with the code on your local
machine. If you just want to read the tutorial then you can just go straight to the first step:
The rest of this page explains how you can set up your local machine for development.
If you just want to read the tutorial then you can just go straight to the first step:
[Step 0 - Bootstrapping](tutorial/step_00).
# Working with the code
+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)`.

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