Compare commits

..

263 Commits

Author SHA1 Message Date
Igor Minar 652feebd86 chore(release): cut the 1.0.4 bewildering-hair release 2013-01-23 10:57:51 -08:00
Igor Minar 16ccbc4f61 docs(changelog): release notes for 1.1.2 and 1.0.4 2013-01-23 10:57:51 -08:00
Igor Minar 072d45fd01 chore(changelog.js): improve the changelog script 2013-01-23 10:57:51 -08:00
pavelgj 4439e39319 fix(ngResource): correct leading slash removal.
Fixed an issues with ngResource param substitution where it was incorrectly removing leading slash when param was followed by a non-slash character.
Ex:
'/:foo/:bar.baz/:aux'

params = {
  foo: 'aaa',
  bar: undefined,
  aux: undefined
}

The above params were incorrectly producing '/aaa.baz' but now it results in '/aaa/.baz'.
2013-01-22 11:32:27 -08:00
Miško Hevery b49c3a1a1e docs(q): added testing information 2013-01-22 11:32:27 -08:00
Igor Minar c7addd4886 fix(angular.equals): relax the comparison for undefined properties
in 5ae63fd3 the comparison was made consistent but strict, so that

angular.equals({}, {foo: undefined}) // always returns false

this turns out to cause issues for data that is being roundtripped via network
and serialized via JSON because JSON.stringify serializes {foo: undefined} as {}.

Since angular.equals() behaved like this before the 5ae63fd3 in 50% of the cases,
changing the behavior in this way should not introduce any significant issues.

Closes #1648
2013-01-22 07:35:06 -08:00
Igor Minar 6df60aff52 chore(Rakefile): skip build parallelization on Travis
Due to a infrastructure change on Travis starting JVMs in forked
processes doesn't currently work. Since we don't really care that
much about the build speed on Travis, I'm going to disable it there.

Related issue: https://github.com/travis-ci/travis-ci/issues/845
2013-01-21 07:50:24 -08:00
sergiopantoja e7a080d6e6 docs(jqLite): fix typo 2013-01-20 18:59:43 +01:00
Will Moore 661a728764 docs(contribute): adding npm install to step-by-step
npm install is listed in the dependencies section of the contribute guide but
is missing from the step-by-step. This adds it as step 4.
2013-01-18 21:33:58 -08:00
danilsomsikov 89303fd2dc fix(ngSwitch): don't leak when destroyed while not attached
The leak can occur when ngSwich is used inside ngRepeat or any other
directive which is destroyed while its transcluded content (which
includes ngSwitch) is not attached to the DOM.

Refactor ngSwitch to use controller instead of storing data on compile
node. This means that we don't need to clean up the jq data cache.
Controller reference is released when the linking fn is released.

Closes #1621
2013-01-18 00:03:42 -08:00
Fred Sauer 1f8be1ca66 docs(exceptionHandler): document testing
Update src/ng/exceptionHandler.js

Here's an iniitla attempt at documenting how one might write a
test using $exceptionHandlerProvider. The key take-away is the use
of this pattern:

it(...

 module(...
   $exceptionHandlerProvider.mode('log');
 });

 inject(...
 );

});
2013-01-17 23:11:02 -08:00
Matthew Browne cbc2024092 docs(rootScope): correct code examples 2013-01-17 23:11:02 -08:00
Amir H. Hajizamani f0ff7023c3 docs(cookbook): change prototype methods to scope methods in Buzz
As explained in 'Understanding the Controller Component', Controllers
written for new (post 1.0 RC) versions of Angular need to add methods to
the scope directly, not the function's prototype. Correcting this
example should remove any ambiguity, especially for beginners.
2013-01-18 00:56:52 -05:00
Amir H. Hajizamani 6e8728a364 docs(guide): change prototype methods to scope methods in DI examples
As explained in 'Understanding the Controller Component', Controllers
written for new (post 1.0 RC) versions of Angular need to add methods to
the scope directly, not the function's prototype. Correcting this
example should remove any ambiguity, especially for beginners.
2013-01-18 00:56:52 -05:00
Fred Sauer f179a63dd4 docs(ngMock.$httpBackend): fix variable declaration 2013-01-18 00:15:20 -05:00
Shai Reznik 3a71c1e595 doc(guide): Fix examples of $location.html5mode 2013-01-17 23:46:19 -05:00
Shai Reznik ef7a61a67e doc(guide): Fixed typos at the unit tests guide 2013-01-17 23:46:19 -05:00
Igor Minar a6b2ee785b fix(angular.equals): consistently compare undefined object props
previously:

a = {};
b = {x:undefined};
angular.equals(a, b) == angular.equals(b, a) // returns false.

both should return false because these objects are not equal.

unlike in implemented in #1695 the comparison should be stricter
and return false not true. if we need to relax this in the future
we can always do so.

Closes #1648
2013-01-17 17:49:07 -08:00
Daniel Demmel d5a1336e6b docs: recommend using Google CDN 2013-01-17 16:53:32 -08:00
Matt Rohrer d4312b731a docs(guide): minor grammar fixes 2013-01-17 19:36:14 -05:00
Martin Probst 66f051386e feat($compile): support modifying the DOM structure in postlink fn
Support modifying the DOM structure in the post link function of a directive
by creating a defensive copy of the node list, as opposed to a live DOM list.
This is useful for directives to actually replace their entire DOM fragment,
e.g. with the HTML fragment generated by a 3rd party component (Closure, Bootstrap ...).
Fix the indentation of the compileNodes function (was one too little).
2013-01-17 12:57:36 -08:00
Igor Minar 8b3f9684e0 style($compile): fix indentation 2013-01-17 12:55:21 -08:00
Gergely Imreh 331f32deac chore(Rakefile): force 32bit JVM mode only when java supports it
Some Java installs don't have '-d32' flag (e.g. OpenJDK which is standard
for some Linux systems), and the closure_compile fails because of forcing
that flag. Test, and only run in faster 32bit mode if supported, or
else just run with no flag (default mode).
2013-01-17 10:44:39 -08:00
Igor Minar bb80c96754 chore(docs): use done() instead of end() in gen-docs.js 2013-01-17 00:52:19 -08:00
Pete Bacon Darwin 0d8e19c26f fix($compile): do not wrap empty root text nodes in spans
Closes #1059
2013-01-17 00:28:56 -08:00
Pete Bacon Darwin ed2fd2d0ca fix(ngRepeat): correctly apply $last if repeating over object
If the $last property is calculated from the original collectionLength
on an object and properties starting with $ were filtered out, then $last
is never applied and $middle is applied erroniously.

Closes #1789
2013-01-17 00:25:07 -08:00
James deBoer 7c60151cb8 chore(Rakefile): remove a duplicate file in angularFiles.js 2013-01-16 23:45:13 -08:00
James deBoer 965308a32c chore(Rakefile): generate version.json
Changes 'rake version' to output a version.json file which
contains the structured version info which can be used in other tools.
2013-01-16 23:41:51 -08:00
Igor Minar 92c612a9de fix(scenario): don't trigger input events on IE9
input.enter() should trigger 'change' rather than 'input' event on IE9 because
input events on IE9 are broken and angular doesn't rely on them
2013-01-16 23:30:16 -08:00
Kanwei Li a7b53abcad docs(CHANGELOG): fix typo 2013-01-16 23:29:11 -08:00
Martin Probst d575e1f613 fix($route): support route params not separated with slashes.
Commit 773ac4a broke support for route parameters that are not seperated
from other route parts by slashes, which this change fixes. It also adds
some documentation about path parameters to the when() method and
escapes all regular expression special characters in the URL, not just
some.
2013-01-16 09:42:35 -08:00
Igor Minar 2ba458387d fix($compile): safely create transclude comment nodes
Closes #1740
2013-01-14 21:59:23 -08:00
Lucas Galfasó 98489a1d0c doc(directive): Fix typos in dialog widget
Fixes #1799
2013-01-13 10:09:22 +00:00
Igor Minar 51d501aab2 chore(*): remove obsolete files 2013-01-09 21:29:13 -08:00
naomiblack 841bdf1c07 Update docs/content/misc/faq.ngdoc
Updated the canonical video to a recent one. Fixed a typo.
2013-01-09 09:50:27 +00:00
Pete Bacon Darwin 86cac55c7c fix(jqLite): children() should only return elements
The jQuery implementation of children only returns child nodes of the given element that are elements themselves. The previous jqLite implementation was returning all nodes except those that are text nodes. Use jQLite.contents() to get all the child nodes.

The jQuery implementation of contents returns [] if the object has no child nodes.  The previous jqLite implementation was returning undefined, causing a stack overflow in test/testabilityPatch.js when it tried to `cleanup()` a window object.

The testabilityPatch was incorrectly using children() rather than contents() inside cleanup() to iterate down through all the child nodes of the element to clean up.
2013-01-09 09:42:05 +00:00
Keyamoon c0995399d4 fix(jqLite): make next() ignore non-element nodes
next() is supposed to return the next sibling *element* so it
should ignore text nodes. To achieve this, nextElementSibling()
should be used instead of nextSibling().
2013-01-08 14:54:56 -08:00
Igor Minar de6cc287e5 fix($injector): remove bogus fn arg
getService fn takes only one argument, removing the second one.

Closes #1711
2013-01-08 14:36:36 -08:00
Igor Minar afd6771163 refactor($browser): remove faulty 20+ cookies warning
the warning is defunct (and the test is incorrect) so obviously nobody is using
it and it just takes up space.

also the browser behavior varies (ff and chrome allow up to 150 cookies, safari
even more), so it's not very useful.

Closes #1712
2013-01-08 14:27:51 -08:00
Igor Minar 4cda028609 revert: fix(a): prevent Opera from incorrectly navigating on link click
This reverts commit c81d8176cc.

This commit causes several issues (#1651, #1674, #1662) and doesn't even
contain a test that proves that anything on Opera got actually fixed.

If the original Opera resurfaces, we'll fix it properly.
2013-01-08 11:51:17 -08:00
kim lokoy 8ecce7642b docs(guide): fix typos in unit test guide 2013-01-07 21:00:13 +01:00
Pawel Kozlowski d1d5761232 docs(forms): fix code example for a custom form control
Closes #1021
2013-01-05 23:04:33 +01:00
naomiblack 759cba1a8d docs(faq): add info on logo reuse and how to get t-shirts and stickers 2013-01-04 19:22:00 +01:00
Jonathan Card 23cd40a8ec docs(form): minor form doc and example fixes
Form documentation fixes:
- Fix broken form example in docs
- A few small other corrections in form docs.
2013-01-04 17:25:24 +01:00
Per Rovegård f3188c1d09 docs($http): clarify documentation on error status codes
Modify the documentation for $http to correspond to what Angular
considers a success status code.

Closes #1693
2013-01-03 20:47:55 +01:00
Matt Hardy 2f4967f100 docs(guide): change example controller to properly call greet method on greeter 2012-12-31 13:21:29 +01:00
Murilo da Silva 4fe4e7457c docs(anchorScroll): correct word "location" 2012-12-19 21:14:09 +01:00
John Fletcher d4e7274d4b docs(guide): minor English corrections to the Directive guide 2012-12-19 20:55:22 +01:00
Miško Hevery cffa015554 docs(directive): old syntax 2012-12-18 20:39:17 -08:00
Pawel Kozlowski 1104c7d75b docs(ngView): fix code example (change template to templateUrl)
Closes #1715
2012-12-18 17:56:04 +01:00
Gonzalo Ruiz de Villa 4c6b4447db fix($route): correctly extract $routeParams from urls
Routes like '/bar/foovalue/barvalue' matching '/bar/:foo/:bar'
now are well mapped in $routeParams to:
{bar:'barvalue', foo:'foovalue'}

Closes: #1501
Signed-off-by: Gonzalo Ruiz de Villa <gonzaloruizdevilla@gmail.com>
2012-12-14 01:16:07 +01:00
ggoodman 741a37b338 feat(docs): Add angularjs tag to plunks and make private
This is a minor edit to allow Plunks created by way of the docs.angularjs.org site to be appropriately tagged as `angularjs`.
Also, make these generated Plunks private by default.
2012-12-12 21:00:41 +00:00
Peter Evjan 1157c5d341 docs(README.md): add missing 'you' and a comma 2012-12-11 19:33:28 +01:00
Romain Neutron e48adebfb7 docs(guide): fix injector service code example
Fix syntax and update code to the latest API
2012-12-10 23:50:12 +01:00
Juha Syrjälä 0a61dcb486 docs($resource): document port number escaping and fix typo 2012-12-09 17:49:42 +01:00
Eric Case 4b51eaadf8 docs(tutorial): typo fix commandx -> command 2012-12-08 11:39:56 +01:00
Eric Case e2457ca16d docs($q): typo fix - programing -> programming 2012-12-07 21:00:25 +01:00
János Rusiczki bd62790080 doc(concepts): Fix typo in $render() function 2012-12-07 10:19:08 +00:00
Jeremy Tymes 53fdcafa44 docs($http): fix link typo in $http doc
Should be $httpBackend instead of $httpBacked

Closes #1516
2012-12-06 21:50:21 +01:00
Fred Sauer af6f2483be docs(mocks): update src/ngMock/angular-mocks.js documentation
Clarify how to use `$exceptionHandlerProvider.mode('log')` in tests
2012-12-06 21:50:21 +01:00
Fred Sauer d8522aa349 docs(mocks): fix documentation bug: angular.mock.debug 2012-12-06 21:50:20 +01:00
Igor Minar bae3121683 chore(bootstrap-prettify): update urls to code.angularjs.org
Closes #1599
2012-12-05 02:55:19 +01:00
_pants 54c0d464b0 fix(select): support optgroup + select[multiple] combo
Closes #1553
2012-12-05 02:21:31 +01:00
Sudhir Jonathan cf89e8653c fix($injector): provider can now be defined in the array format
`injector.instantiate` is now called for arrays too, instead of only for functions.

Closes #1452
2012-12-01 19:09:36 +01:00
Sudhir Jonathan 0c3500f532 fix($resource): HTTP method should be case-insensitive
Perform call `angular.uppercase` on all given action methods.

Closes #1403
2012-11-30 23:23:34 +01:00
Cezar Berea c12f525df4 refactor($resource): fix indentation and move a method definition
Moved Resource.bind out of the actions forEach
2012-11-30 22:47:25 +01:00
Igor Minar e7ba830691 fix(Scope): ensure that a scope is destroyed only once
Due to bd524fc4 calling $destroy() on a scope mupltiple times cases NPE.

Closes #1627
2012-11-30 13:09:50 +01:00
Daniel Luz 4eb0716711 docs(directive): correct expression, fix typo and re-wrap lines 2012-11-29 20:22:41 +01:00
Johannes Hansen ed90f3b7ea fix(docs): add missing </div> tag to sourceEdit directive template 2012-11-29 20:22:40 +01:00
Igor Minar 14b19ecf5e docs(menu): fix the navbar drop down links 2012-11-28 23:56:21 +01:00
Igor Minar 644432a14c chore(release): start 1.0.4 bewildering-hair iteration 2012-11-28 15:51:23 +01:00
Igor Minar a03e370a09 chore(release): cut the 1.0.3 bouncy-thunder release 2012-11-27 01:44:46 +01:00
Igor Minar 23677d3ddb docs(CHANGELOG): release notes for 1.0.3 and 1.1.1 releases 2012-11-27 01:44:46 +01:00
Rado Kirov fc781560a3 fix($location): reset $location.$$replace with every watch call
Closes #1111
2012-11-26 23:24:39 +01:00
Vojta Jina c9199ee663 docs: load angular from CDN only on production
So that when running the docs locally, eg. during e2e testing, we use the latest build version of angular, rather than the stable one from CDN.

This fixes e2e tests running with Testacular.
2012-11-26 21:33:45 +01:00
Igor Minar 6f18adedef fix(ngClassOdd/ngClassEven): support shrinking/reordering in repeaters
We need to watch $index in addition to cssClasses because only then
we can correctly respond to shrinking or reordered repeaters.

Closes #1076
2012-11-26 21:33:45 +01:00
Igor Minar 6ad894cd58 style(jqLite): better variable names
selector => cssClasses
2012-11-26 21:33:45 +01:00
Igor Minar cde2f1a868 fix(ngRepeat): support mostly-stable repeating for primitives
I'm reverting changes that were originally done to ngRepeat to fix #933,
because these are now not necessary after the previous changes to keep
ngModel always synced with the DOM.
2012-11-26 21:33:45 +01:00
Igor Minar 6a831495de fix(ngModel): sync ngModel state with scope state
In cases when we reuse elements in a repeater but associate
them with a new scope (see #933 - repeating over array of
primitives) it's possible for the internal ngModel state and
the scope state to get out of sync. This change ensure that
the two are always sync-ed up even in cases where we
reassociate an element with a different (but similar) scope.

In the case of repeating over array of primitives it's still
possible to run into issue if we iterate over primitives and
use form controls or similar widgets without ngModel - oh well,
we'd likely need a special repeater for primitives to deal
with this properly, even then there might be cornercases.

Closes #933
2012-11-26 21:33:45 +01:00
Igor Minar 6e2c38f54d test(ngRepeat): clean up and improve tests 2012-11-26 20:39:10 +01:00
Igor Minar 00e7e31418 fix(ngRepeat): attempt to simplify and improve existing fix for #933
I'm keeping this in for future reference. The issue with this solution
is that if we shift() the first item in the array, the whole repeater
DOM will be rebuilt from scratch, we need to do better than that.
2012-11-26 20:39:00 +01:00
Igor Minar ff4b3e20c1 test(ngRepeat): add test for issue #1076 2012-11-26 20:38:51 +01:00
Igor Minar 269fb43b36 fix(jqLite): fire $destroy event via triggerHandler
in jQuery 1.8.x the data() data structure is changed and events are
not accessible via data().events. Since all we need is to trigger
all event handlers, we can do so via triggerHandler() api instead of
mocking with the internal jQuery data structures.

This fix was originally proposed by PeteAppleton via PR #1512.

Closes #1512
2012-11-26 16:03:43 +01:00
Igor Minar 7530654328 feat(jqLite): add triggerHandler()
we need triggerHandler() to become jQuery 1.8.x compatible.

this is not fully featured triggerHandler() implementation - it doesn't
bother creating new DOM events and passing them into the event handlers.

this is intentional, we don't need access to the native DOM event for our
own purposes and creating these event objects is really tricky.
2012-11-26 16:03:39 +01:00
Iristyle c7bd464384 docs(CONTRIBUTING.md): add contrib info file for GitHub 2012-11-25 21:00:56 +01:00
Vojta Jina ef1874d1f3 fix(Scope): allow removing a listener during event 2012-11-25 11:41:32 +01:00
Kevin Western c6d8205fdd docs(README.md): fix "API Docs" link
use direct link to api docs
2012-11-25 01:24:03 +01:00
Dean Sofer 55150a669a docs(api): add ngRequired to input/select/textarea directives
Closes #1202
2012-11-25 01:19:47 +01:00
Jeremy Tymes 1eb9e22d45 fix($cacheFactory): return undefined when removing non-existent entry
Instead of throwning an exception, remove should return undefined when
cache entry to be removed doesn't exist.

Closes #1497
2012-11-24 21:58:17 +01:00
Adrian Gheorghe c0de8fb737 fix($resource): prevent default params to be shared between actions
Having a $resource defined as:

var R = $resource('/Path', {}, {
  get: {method: 'GET', params: {objId: '1'}},
  perform: {method: 'GET'}
});

was causing both actions to call the same URI (if called in this order):

R.get({}); // => /Path?objId=1
R.perform({}); // => /Path?objId=1
2012-11-24 21:29:16 +01:00
Kris Jenkins 557e3894d7 docs(): Fix a couple of typos in the documentation 2012-11-22 08:28:49 +01:00
Dave Clayton 38a9695413 docs(guide/concepts): some typo/grammar fixes 2012-11-22 08:28:49 +01:00
John Hume 293e0336b0 docs(guide/directive): fix typo 2012-11-22 08:28:49 +01:00
Uri Goldshtein 1f5bc0a1cd docs($q): fix missing bracket in the example 2012-11-22 08:28:49 +01:00
Igor Minar 1fe666192b fix($rootScope): workaround for Chrome's memleak
Under certain circumstances chrome fails to GC scopes
because of buggy optimizations and caching. Nulling out
references to (not from!) other scopes helps Chrome to
realize that this object should be GC-ed.

This is really just a workaround as the real problem needs
to be fixed in Chrome.

See discusstion at:
https://github.com/angular/angular.js/issues/1313#issuecomment-10378451

And chrome bug at:
https://code.google.com/p/v8/issues/detail?id=2073

Closes #1313
2012-11-14 19:53:29 +01:00
Igor Minar 29541e735d revert($resource): support custom headers per action
This reverts commit b936e52874.

This commit introduces a feature and should haven't been merged
into the stable branch.
2012-11-11 12:07:26 +01:00
Igor Minar f5b567d44b chore(validate-commit-msg): recognize 'revert' as valid commit type 2012-11-11 12:06:05 +01:00
Haralan Dobrev 5ee3bbee90 docs(angular.module): improve angular.Module#run docs 2012-11-11 11:40:52 +01:00
Jamison Dance 80927c5811 docs(guide): fix run-on sentence in modules guide 2012-11-11 11:35:20 +01:00
Jamison Dance ca8b344e20 docs(tutorial): change module name in step-7 2012-11-11 11:35:15 +01:00
Wes Alvaro 3dab93874d docs($timeout): set return type to Promise instead of *.
The cancel function accepts a Promise, but the timeout function
fails to specify returning a Promise.
2012-11-11 11:32:00 +01:00
Josh Adams 7550f90a57 docs(ngList): fix typo 2012-11-11 11:25:40 +01:00
Josh Adams d78fea87d1 docs(encodeUriSegment): fix typo 2012-11-11 11:23:46 +01:00
Tim Macfarlane 27cee7db0a docs(guide/directive): fix names in scope '='; easier to grok 2012-11-11 11:20:57 +01:00
Christian Vuerings 60acba3840 docs(ngHide): Fix typo and make it more in line with ngShow 2012-11-11 10:37:59 +01:00
Igor Minar 51bed36370 chore(docs): fix docs-scenario.html 2012-11-08 22:18:34 +01:00
Igor Minar 6d940213ac chore(docs): remove obsolete gae files 2012-11-08 22:18:34 +01:00
Miško Hevery 494b527fa7 docs(directive): fix typo 2012-11-05 19:35:31 -08:00
Sudhir Jonathan 8ce84cb2ea chore(testacular): use local testacular version
Making testacular a dependency to avoid having to install it globally.
(Causes npm issues on some machines)
2012-10-31 17:00:00 -07:00
Sudhir Jonathan d981c2a3ec fix(select): select option with a label of 0 is not shown
Bug caused by the use of the `||` operator to replace all non-truthy
values with an empty string. Changed to replace only `undefined` values.

Closes #1401
2012-10-31 15:03:48 -07:00
Igor Minar 537e20065a chore(validate-commit-msg): allow '/' in scope 2012-10-31 14:48:24 -07:00
Fred Sauer 97578b4dae docs(guide/location): fix table formatting
Fix table formatting so headings are bold, rows are separated by lines, and rows have :hover style
2012-10-31 14:48:18 -07:00
Tim Macfarlane fa12564607 docs(module): fix typo in example
fixed example app, `simpleAppModule` should have been `myAppModule`.
2012-10-31 14:22:12 -07:00
sqwishy trick 54bcb9ae25 chore(injector): fix typo in injector documentation 2012-10-31 14:19:52 -07:00
Adam Macejak ad7ce0d402 fix(scenario-runner): support data-ng and x-ng based attributes
Prefixed attributes like data-ng-model and x-ng-model were not being
found by the Selector. It was only looking at ng: and ng- prefixed
attributes.
Added a few tests as well to ensure the aforementioned prefixed
attributes are being matched properly.

Closes #1020
2012-10-31 14:10:26 -07:00
Daniel Luz 085e0ea8ef docs(contribute): fix task name for continuous testing 2012-10-31 13:13:33 -07:00
Igor Minar bb52c4e8d3 fix(docs): correctly generate filenames for plunkr/fiddle
previously examples like $http where broken because we would strip part of the
filename (http-hello.html -> http)

we really want to strip only the id suffix that we append to disambiguate
common filenames (like index.html) which appear in many examples.
2012-10-31 13:06:22 -07:00
Shyam Seshadri 295af335c1 feat(docs): add plunkr support
Add option to edit source in Angular Docs in Plunkr in addition to JsFiddle
2012-10-31 13:06:16 -07:00
Daniel Luz 2c2e18c37a fix(doc): typo on FAQ
Closes #1493
2012-10-31 10:26:00 -07:00
Igor Minar adfb75e3c6 fix($compile): don't look for class directives in empty string
if className is undefined or empty string, don't bother looking for directives in there
2012-10-29 19:39:34 -07:00
Igor Minar 9bff5c60df fix($compile): compilation should not recurse into empty nodes
if a node doesn't have children then don't try to compile these non-existent children
2012-10-29 19:39:21 -07:00
Igor Minar 3ba008d4b2 style($compile): better fn names for debugging 2012-10-29 19:38:57 -07:00
Igor Minar 4e45a2f8e2 refactor($compile): simplify nodeLinkFn 2012-10-29 19:38:29 -07:00
Igor Minar 4dbd8452eb fix($compile): prevent double attr interpolation w/ templateUrl
This fixes the issue that caused two attr interpolation observers
to be registered for the same attribute as a result of isolate
scope definition with attr (@) property for this attribute.

Duplicate observers would then fight with each other updating the
model.

The issue occured only when this directive was used in a repeater
because that's when we clone the template node which caused the
two observers to point to two different sets of $attr instances.

Closes #1166, #836
2012-10-29 19:38:03 -07:00
Braden Shepherdson 45a8db9c08 fix(currency): Handle not-quite-zero values
IEEE 754 floating point sometimes results in values that are very small,
rather than zero. One example is 1.0 + 1.07 - 2.07, which returns
4.440892098500626e-16 instead of 0.

This change tweaks the number formatting logic so that an exponential
value with a negative exponent that is larger than the precision+1
returns 0 instead. For example: with precision 2, anything with an
exponent of -4, -5 or more would become 0. 9e-3 = 0.009 = 0.01, but 9e-4
= 0.0009 = 0.001 = 0.00. This detail is unlikely to matter since this
quirk is usually only triggered with values very close to zero.

Closes #1469
2012-10-29 19:37:52 -07:00
Braden Shepherdson d930a410fb doc(faq): Add Common Pitfalls section
Describes several common pitfalls new users of Angular fall into that
I've observed in #angularjs.
2012-10-29 19:37:38 -07:00
Braden Shepherdson 66505ffc40 doc(faq): Fix minor spelling and wording errors 2012-10-29 19:37:29 -07:00
Igor Minar 045de959b9 chore(check-size.sh): fix rake target 2012-10-29 19:37:21 -07:00
Igor Minar 3ca11d5235 docs(contribute): add CLA note to code submission section 2012-10-19 09:15:34 -07:00
Igor Minar d5d8ac01e3 docs(contribute): add visible link to github project 2012-10-19 09:15:34 -07:00
Igor Minar ace81c053c chore(validate-commit-msg): allow * and - in scope string 2012-10-18 03:29:12 -07:00
Igor Minar 1e95c419b8 chore(jasmine): remove Jasmine from our repo
it's bundled with Testacular, so we don't need it here
2012-10-18 03:29:12 -07:00
Igor Minar 49ed63d26a chore(jstd): remove JsTestDriver from our repo
Testacular FTW!
2012-10-18 03:29:12 -07:00
Igor Minar 6ff2685668 docs(tutorial): replace JsTD with Testacular + drop snapshots
JsTD references have been replaced with Testacular stuff.

snapshots are PITA to maintain so I'm dropping them, everyone loves the Git
version anyway.
2012-10-18 02:34:27 -07:00
Igor Minar c4573c04aa chore(Rakefile): remove test_out dir when cleaning 2012-10-17 20:20:54 -07:00
Igor Minar d57abdb3f7 chore(Rakefile): tune JVM for closure compiler
Using the client VM and forcing 32bit mode gives us huge perf boost.

before:

reali   0m8.173s
user    0m39.984s
sys     0m1.408s

after:

real    0m3.000s
user    0m12.687s
sys     0m0.852s
2012-10-17 20:20:53 -07:00
Igor Minar 4050e89446 chore(Rakefile): paralelize closure compilation
this speeds up the build by paralelizing closure compilation (the slowest
piece of the build process)

before:

real  0m14.372s
user  0m31.649s
sys   0m1.006s

after:

real  0m8.191s
user  0m40.473s
sys   0m1.378s
2012-10-17 20:16:36 -07:00
Vojta Jina b6620c737f chore(test): add junit config for testacular 2012-10-17 20:16:36 -07:00
Igor Minar 0c8e908841 chore(Rakefile): misc_options should support + -> , conversion 2012-10-17 20:16:35 -07:00
Igor Minar 5595e196a8 chore(Rakefile): use exec for webserver
exec unlike system replaces the current process. this way when we kill
the webserver process we don't get scary looking 'rake aborted' error
2012-10-17 12:44:38 -07:00
Misko Hevery f92e4146d1 fix(doc): disable directory listing in docs.angularjs.org 2012-10-08 15:09:06 -07:00
Vojta Jina 8b7108e3c9 chore: add travis config 2012-10-05 10:20:02 -07:00
Igor Minar caf702cc88 docs(downloading): update the downloading docs 2012-10-05 03:15:11 -07:00
Vojta Jina cf2c49ed7f fix($compile): reference local in isolate scope
This was really corner case:
Watcher needs to return changed value, to notify that model might have changed and one more $digest cycle needs to be performed.

The watcher, that takes care of reference binding into an isolate scope ("="), did not return changed value, if the change was from the isolate scope to the parent.

If any other watcher returned change, it worked fine, as this change caused re-digest.

Closes #1272
2012-09-22 12:00:16 -07:00
Igor Minar ccd52abf5d docs(README): update README.md with new rake tasks 2012-09-17 14:52:06 -07:00
Igor Minar 74c574015d chore(Rakefile): fix test:jquery task 2012-09-17 14:52:06 -07:00
Igor Minar 1f1a6fb6d2 docs(contribute): update contribute docs 2012-09-17 09:46:34 -07:00
Igor Minar 8632e893b0 chore(): remove unused files 2012-09-17 09:46:34 -07:00
Igor Minar c2b6e127fa docs(contribute): update misc/contribute docs with Testacular info 2012-09-17 09:46:34 -07:00
Igor Minar 06eceeb09f chore(testing): Testacular config files + rake tasks
- adds testacular config files for jqlite, jquery, modules and e2e tests
- replaces obsolete JsTD Rake tasks with Testacular onces
- rake tasks are parameterazied so that they can be used locally as well as on CI server

usage:

rake test  # run all tests on Chrome
rake test[Safari+Chrome+Opera]  # run all tests on Safari, Chrome and Opera
rake test[Safari]  # run all tests on Safari
rake test:jqlite # run unit tests using jqlite on Chrome
rake test:jqlite[Safari,"--reporter=dots"]  # run jqlite-based unit tests on Safari with dots reporter
rake autotest:jquery  # start testacular with jquery-based config and watch fs for changes
rake test:e2e # run end to end tests
2012-09-17 09:46:13 -07:00
Miško Hevery 8133d468b9 docs(directive): remove reference to old isolation syntax 2012-09-13 11:32:13 -07:00
Misko Hevery 074a354fa9 fix($route): support inline annotation on .resolve 2012-09-11 23:16:41 -07:00
Vojta Jina e191582a02 chore(scripts): add init-repo script 2012-09-11 23:16:38 -07:00
Jay Zeng 6fbe926cda docs(ngResource): Spelling typo (agressive => aggressive) 2012-09-11 16:39:46 -07:00
Igor Minar ebbc224e09 fix($resource): fix isDefined -> angular.isDefined 2012-09-11 16:39:46 -07:00
Shyam Seshadri 2c6aa4c300 fix(*): name all anonymous watch functions in Angular
This will allow us to see function names in Batarang and debugger.

Closes #1119
2012-09-11 16:39:46 -07:00
Zhenbo Zhang f7a8f17fc7 fix(ng-repeat) to work with primitive types 2012-09-11 16:38:42 -07:00
Brian Ford 191efbb558 docs(guide): fix directive interpolation example code
Closes #1339
2012-09-11 16:19:45 -07:00
Shyam Seshadri bf873d6f02 fix(scenario): emit RunnerBegin event 2012-09-11 16:19:44 -07:00
Vojta Jina e741107c55 chore(scripts): add commit-msg hook (validation) 2012-09-11 16:19:44 -07:00
Jimmy Zhuo 82f4b99d99 fix(scenario): NPE when no angular loaded in test page 2012-09-11 16:19:44 -07:00
Daniel Luz 7210b7ae1d docs($rootScope): fix iteration limit described by $watch, it's actually 10 as of now 2012-09-11 16:19:44 -07:00
Daniel Luz afed23c001 docs($rootScope): fix typos and minor wording tweaks on $watch 2012-09-11 16:19:44 -07:00
Daniel Luz 1f69cc2989 docs($rootScope): fix quoting on expression 2012-09-11 16:19:43 -07:00
Daniel Luz 3401833c83 docs($rootScope): standardize on present, third-person actions for descriptions 2012-09-11 16:19:43 -07:00
Daniel Luz 06606e2816 docs($rootScope): backquote attribute types too on $on 2012-09-11 16:19:43 -07:00
Daniel Luz eba64e1f31 docs($cacheFactory): fix backquotes on method descriptions 2012-09-11 16:19:43 -07:00
Daniel Luz 81dd1df1b1 docs($rootScope): fix typos on $new 2012-09-11 16:19:43 -07:00
Daniel Luz dbafbb0de5 docs($rootScope): fix typo on $eval 2012-09-11 16:19:42 -07:00
Daniel Luz 1d0aa7b7c6 docs($rootScope): fix typos on $watch 2012-09-11 16:19:42 -07:00
Igor Minar afd02ca48c fix(docs): update docs top menu links 2012-09-06 15:56:11 -07:00
sgtpep 67db7616ad fix(a): prevent Opera from incorrectly navigating on link click
we handle the navigation by ourselves, so we need to prevent the default action.

Opera ignores event.preventDefault() call so we must return false.
2012-09-06 15:54:20 -07:00
Kai Groner 3d7c752e27 fix(FormController): propagate dirty state to parent forms 2012-09-06 15:54:19 -07:00
Jonathan Zacsh f02833d634 chore(docs): get correct location for jasmine-node 2012-09-06 15:54:19 -07:00
Misko Hevery 0eb373e0e6 fix($injector): more conservative annotation parsing 2012-09-06 15:49:50 -07:00
Xiangru Chen fd3071843c fix(ngSrc): don't set src if value is empty string
Current implementation of ngSrc may lead to empty src attribute when page is loading.

For example:

<img ng-src="{{image.url}}">
can be temporarily rendered as

<img src="">
before the image resource is loaded.

Some browser emits a request to the current page when seeing <img src=""> (Firefox13 and IE8 will, Chromium20 won't), which leads to performance problems.
2012-09-06 15:49:49 -07:00
Iwein Fuld a631ceb223 fix(dateFilter): make timezone optional
Makes the time zone optional in the date filter

Problem with the current R_ISO8601_STR regex was that the time was optional, but the zone was not.
This results in the filter not formatting local date times, which it could easily do.

For example:
2012-08-30 -> formatted
2012-08-30T06:06:06.123Z -> formatted
2012-08-30T06:06:06.123 -> NOT formatted

A simple change in the regex fixes this. Arguably this is closer to the ISO8601 spec which specifies
local dates being in the "current time zone" and not requiring a Z. In any case it behaves more like
a user would expect.
2012-09-06 15:49:49 -07:00
Misko Hevery a713928210 docs(concept): correct example for creating injector 2012-09-06 15:49:49 -07:00
Godmar Back 05fa20df81 docs(module): fixed module example and corrected typos 2012-09-06 15:49:49 -07:00
Benjamín Eidelman 125573602f fix($resource): allow falsy values in URL parameters
Close #1212

when a param value was 0 (or false) it was ignored and removed from url.
after this fix that only happens if the value is undefined or null.
2012-09-06 15:49:49 -07:00
Jay Zeng ed5dfbcd66 docs(module): myAppModule -> simpleAppModule 2012-09-06 15:49:49 -07:00
petrovalex d2e52b2376 fix($resource): ignore undefined parameters
- $resource should handle multiple params with same name
- ignore slashes of undefined parameters
- fix default parameters issue, mentioned in #875

Closes #875
Closes #782
2012-09-06 15:49:49 -07:00
petrovalex a56aaa9877 fix(ngClassEven/Odd): filtering/ordering and repeater
Closes #1076
2012-09-06 15:49:48 -07:00
Max Martinsson 79bb7b1f0b fix(ngClass): works with class interpolation
Closes #1016
2012-09-06 15:49:48 -07:00
Max Martinsson b936e52874 feat($resource): support custom headers per action
Closes #736
2012-09-06 15:49:48 -07:00
petrovalex 0d52ff0f10 fix($parser): string concatination with undefined model
Closes #988
2012-09-06 15:49:48 -07:00
Stein Jakob Nordbø baf52e902d fix(dateFilter): support sub-second precision on dateFilter 2012-09-06 15:49:48 -07:00
Igor Minar dffea9e2b7 docs($route): rename leftover $afterRouteChange to $routeChangeSuccess 2012-09-06 15:03:58 -07:00
Igor Minar 4a411b8f82 chore(release): prepare 1.0.3 bouncy-thunder iteration 2012-09-06 08:42:51 -07:00
Misko Hevery 278bfc4bb2 fix(docs): broken url to angular-bootstrap 2012-09-04 18:16:52 -07:00
Igor Minar 18731173f9 chore(Rakefile): add 'version' rake task to generate version.txt 2012-09-04 16:38:29 -07:00
Igor Minar d43fb404d7 chore(docs): bump up the stable version 2012-09-04 16:30:33 -07:00
Igor Minar da984ad187 chore(release): cut the 1.0.2 debilitating-awesomeness release 2012-09-04 11:08:40 -07:00
Igor Minar 4015357ce5 chore(docs): don't rewrite colons in doc filenames 2012-09-04 11:08:40 -07:00
Igor Minar dc3d11ad19 chore(Rakefile): zip only the build dir 2012-09-04 11:08:40 -07:00
Igor Minar 0e1545eb04 revert: fix(ng-repeat) to work with primitive types
this commit was accidentaly merged. it needs more work and we don't
have CLA signature

This reverts commit 98d489712e.
2012-08-31 13:44:36 -07:00
Igor Minar ec7cabf5c9 docs(changelog): release notes for 1.0.2 and 1.1.0 releases 2012-08-31 13:23:23 -07:00
Jonathan Zacsh 3051beba2f fix(docs): Making sure gen_docs.sh looks for a globally installed copy of jasmine-node as well as local. 2012-08-30 22:38:54 -07:00
Fernando Correia 92304323b1 docs(tutorial): correct typos and clarify a few sections 2012-08-30 22:38:54 -07:00
Brice Burgess c28123a872 fix(docs): indicate support for passing a string as the controller property on $routeProvider's route object 2012-08-30 22:38:53 -07:00
brettcannon d798423813 doc(misc) Mention how attribute names map to directive names. 2012-08-30 22:38:52 -07:00
Sahat Yalkabov 2583e77cc7 doc(module) changed simpleApp to myApp in the Module page guide for consistency 2012-08-30 21:34:43 -07:00
Steve Nicolai f66836fee4 doc(devguide) - Fix typos and small grammatical errors in the developer guide. 2012-08-30 21:34:42 -07:00
Uri Goldshtein 0ccc445d53 Loading from Google CDN
As you guys had mansion, we can and need to do it through Google CDN for better performance,
so i've updated it accordingly
2012-08-30 21:34:42 -07:00
Tyson Benson b7d5fa1cbe docs(typos): fix typos in dev guide 2012-08-30 21:34:42 -07:00
German Galvis 8bb3942453 fix(scenario): Adding meta tag to avoid cache issues 2012-08-30 21:34:42 -07:00
phil 51a79cebcb docs(api): fix typo on home page
Refference -> Reference
2012-08-30 21:34:41 -07:00
csugden 36bcf64008 Update docs/content/guide/overview.ngdoc
Corrects video information
2012-08-30 21:34:41 -07:00
Jamie Krug 5c6630605b fix(docs): Fix typos and improve grammar. 2012-08-30 21:34:41 -07:00
Jamie Krug 125827406c fix(docs): Fix bad links. 2012-08-30 21:34:41 -07:00
Colin Frei 62c21422a6 docs(module) fix typo 2012-08-30 21:34:41 -07:00
Zhenbo Zhang 98d489712e fix(ng-repeat) to work with primitive types 2012-08-30 21:34:41 -07:00
Igor Minar 2cab2d8ef1 test(locationSpec): fix broken tests after vojta's commit 2012-08-30 16:54:07 -07:00
Vojta Jina 8fa2bb72bc fix(mocks): free up memory after every spec 2012-08-30 16:53:52 -07:00
Igor Minar d151f94937 chore(docs): ask GAE to serve docs-keywords.js 2012-08-30 15:59:29 -07:00
Igor Minar 5b74b7185b test(bootstrap): test exception siling during bootstrap
Closes #1018
2012-08-30 15:18:11 -07:00
Igor Minar 4f0be2ae4e test(ngApp): add missing test for [ng-app] bootstrap 2012-08-30 15:18:02 -07:00
Igor Minar bb9badeb2a fix(jqLite): better support for xhtml
it turns out that some stuff doesn't work in xhtml as it does in html.

for example &nbsp; can't be innerHTML-ed and auto-closing of elements
doesn't work.

the reporter of the referenced issue claimed that innerHTML vs text on
script made a difference but that doesn't appear to be true in my testing.

I'm not including test for this because testacular doesn't currently
run tests in xhtml yet.

Closes #1301
2012-08-30 10:52:36 -07:00
Igor Minar c287c8361d chore(docs): correctly link docs images 2012-08-30 08:59:17 -07:00
Igor Minar ade7127c79 chore(Rakefile): fix the default task 2012-08-30 08:58:51 -07:00
Igor Minar d341483f1f chore(Rakefile): remove bogus symlink from build 2012-08-30 08:58:39 -07:00
Igor Minar b36acbc857 chore(docs): use symlinks to build docs
so that we can just edit source files without rebuilding docs.

this works for all docs files, except for those that are generated
or rewritten during build.
2012-08-28 16:08:03 -07:00
Igor Minar a4fea38b94 chore(Rakefile): various build script changes
- restructure rake tasks

  this splits up the concatination and minification into two
  tasks so that we can just build angular.js quickly without wasting
  time with minification which is often not needed when just debugging
  some issue on 3rd party site.

- use symlinks when creating final zip file

- switch from btar to zip

- get rid of version numbers from filenames

- rewrite version numbers in all index files

Closes #1226
2012-08-28 16:07:47 -07:00
Misko Hevery 300c5c0c99 doc($log): correct non-working example 2012-08-27 21:20:51 -07:00
Misko Hevery 152537c4e9 doc(guide): add concepts 2012-08-27 21:20:51 -07:00
Misko Hevery 8b46bf6bc9 fix(ngdoc): failing test 2012-08-27 21:20:50 -07:00
Colin Frei aef861eb41 doc(directive) correct typos 2012-08-27 21:20:50 -07:00
Misko Hevery f61d36861d fix(docs) typo 2012-08-27 21:20:50 -07:00
Misko Hevery 2af0348cea fix(ng-list): remove data bound flicker 2012-08-27 21:20:50 -07:00
Misko Hevery 78c5743494 doc(misc) updated getting started to reflect the new homepage 2012-08-27 21:20:49 -07:00
Misko Hevery 2cb9fbd043 doc(guide) simplify the guide home page 2012-08-27 21:20:49 -07:00
Igor Minar e9dad5dbf4 chore(Rakefile): rewrite version numbers in all index files 2012-08-27 12:25:40 -07:00
Igor Minar 54895fc2a1 chore(docs): support _escaped_fragment_ hack for crawler 2012-08-25 02:31:20 -07:00
Igor Minar 60a12b4161 chore(docs): use GAE and Google CDN for docs
Short summary: if you use local node server everything should work as before,
if you use GAE, everything should work now as well, but we pull assets from CDN.

- GAE doesn't support ':' in filenames, so I had to replace it with '_'
  but only in the filename, all servers were reconfigured to rewrite the
  urls from : to _ when doing file lookup
- We now pull angular assets from google CDN when deployed on GAE (locally
  or in production). When running on a non GAE server we pull assets from
  ../ directory as before
- Since only certain versions of Angular are available on CDN and we want
  to be able to autodeploy docs, I had to pin down the Angular files
  to a "stable" version when running on GAE
2012-08-24 15:00:36 -07:00
Igor Minar cd7e58ba41 docs(a): expose hidden docs
It seems that docs for these directive were previously hidden by accident
2012-08-24 14:59:52 -07:00
johnlindquist 9391475dc3 docs(ngRoute): fix typo
aftre -> after
2012-08-24 14:59:43 -07:00
phil 7840803add docs(tutorial): fix typo in step_00
Just removed an extra comma. No big deal.
2012-08-24 14:59:21 -07:00
Igor Minar 7d77de2834 fix($compile): denormalize directive templates
Since developers are allowed to customize start/end interpolation
strings, but third-party directive creators don't know about these
customizations, we should standardize on {{ }} in templates of
reusable (third-party) directives. During the compilation, these
templates are then denormalized to use whatever the custom
start/end symbol is, effectively translating the template into the
syntax of the runtime environment.

This addresses an issue raised at http://goo.gl/e8VPV

Existing code should not be affected by this change since project
that do use custom interpolation markers are not expected to use
{{ }} in existing directive templates.
2012-08-13 14:35:32 -07:00
Igor Minar ab044cada6 refactor($compile): code cleanup 2012-08-13 12:36:42 -07:00
Brian Ford d010e0cc7d fix(ngPluralize): fixes ng-pluralize when using non-standard start/end symbols
Closes #1134
2012-08-13 12:36:33 -07:00
Igor Minar 40f728b1aa style(ngPluralizeSpec): fix indentation 2012-08-13 12:36:22 -07:00
Igor Minar 23abb26405 feat($interpolate): expose start/end symbols in run phase
previously the startSymbol() and endSymbol() getters were exposed only via provider
in the config phase
2012-08-13 12:36:14 -07:00
Igor Minar fd55bc8e1d docs($interpolateProvider): fixing docs 2012-08-13 09:51:57 -07:00
Igor Minar 541aaa4e08 fix($interpolate): $interpolateProvider.endSymbol() returns startSymbol
I also added missing tests.
2012-08-13 09:51:45 -07:00
Igor Minar f22c422547 docs($interpolate): fix typo in description 2012-08-13 09:51:35 -07:00
Vojta Jina 0e461f0c07 docs($compileProvider): remove duplicate of .directive() 2012-08-12 11:04:32 -07:00
Vojta Jina 5074448443 docs: fix broken links to $compileProvider.directive() 2012-08-12 11:04:20 -07:00
Brian Ford 8d66af11e6 fix(docs): fixed documentation for using linky 2012-08-10 16:38:01 -07:00
Brian Ford 169948bb47 chore(ngDoc): add support for custom @usage metadata 2012-08-10 16:37:54 -07:00
Brian Ford 58d9469574 fix(docs): added note about using JSON3 as a polyfill for IE7 2012-08-10 16:37:42 -07:00
Brian Ford 8d858a2360 fix(docs): added note about needing JSON shim for IE7 and earlier 2012-08-10 16:36:56 -07:00
Igor Minar 5540748890 fix(option): support option elements in datalist
previously we expected to find option elements only within select element and if
that was not the case we throw an error. This made it impossible to include datalist
element with nested option elements in the template.

Closes #1165
2012-08-10 16:14:49 -07:00
unirgy f8a52be817 docs($rootScope): fix $on listener signature doc
Added args in $on() listener syntax declaration
2012-08-10 14:52:57 -07:00
Igor Minar 3b5f1105f6 test(jqLite): add missing test for $destroy event 2012-08-10 14:21:11 -07:00
Igor Minar 663ccc5449 fix(form): prevent page reload when form destroyed
this fix ensures that we prevent the default action on form submission
(full page reload) even in cases when the form is being destroyed as
a result of the submit event handler (e.g. when route change is
triggered).

The fix is more complicated than I'd like it to be mainly because
we need to ensure that we don't create circular references between
js closures and dom elements via DOM event handlers that would then
result in a memory leak.

Also the differences between IE8, IE9 and normal browsers make testing
this ugly.

Closes #1238
2012-08-10 14:21:02 -07:00
Igor Minar 263f47819f test(form): fix broken preventDefault test
the original test relied on incorrect assumptions about how jasmine async
tests work (when setTimeout is triggered) and how browser reloads a page
(the sequence of events) and thus the test passes even when the default
is not prevented.

this change fixes the test by registering an extra submit event handler
that checks if the default was prevented.

if the default was not prevented, the test will fail and the page will
be reloaded causing the test runner to panic.
2012-08-10 14:20:52 -07:00
Igor Minar 6b75475ce3 refactor(formSpec): group preventDefault specs into a describe 2012-08-10 14:20:28 -07:00
Igor Minar 07c354a8c0 docs(faq): update faq docs 2012-08-10 14:20:13 -07:00
Igor Minar 1391579599 docs(styles): fix the cog icon alignment 2012-08-10 14:19:57 -07:00
Vojta Jina 5d2bd1d84c chore(nodeserver): add font mime type 2012-08-10 14:19:47 -07:00
Vojta Jina bf77e212af docs(guide): hide scenario for directive example
scenario test for this example would be tricky, we need to teach
the runner how to inject mocks first.
2012-08-10 14:19:37 -07:00
Vojta Jina eef2f9c31e docs(design): fix icons
Copy fontawesome during build
2012-08-10 14:18:45 -07:00
brettcannon 438627c2c3 fix(docs): "in depth" -> "in-depth" 2012-08-10 14:18:06 -07:00
91 changed files with 4368 additions and 6584 deletions
+2 -122
View File
@@ -1,123 +1,3 @@
<a name="1.1.3"></a>
# 1.1.3 radioactive-gargle (2013-02-20)
_Note: 1.1.x releases are [considered unstable](http://blog.angularjs.org/2012/07/angularjs-10-12-roadmap.html).
They pass all tests but we reserve the right to change new features/apis in between minor releases. Check them
out and please give us feedback._
_Note: This release also contains all bug fixes available in [1.0.5](#1.0.5)._
## Bug Fixes
- **$compile:**
- initialize interpolated attributes before directive linking
([bb8448c0](https://github.com/angular/angular.js/commit/bb8448c011127306df08c7479b66e5afe7a0fa94))
- interpolate @ locals before the link function runs
([2ed53087](https://github.com/angular/angular.js/commit/2ed53087d7dd06d728e333a449265f7685275548))
- **$http:**
- do not encode special characters `@$:,` in params
([288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf))
- **$resource:**
- params should expand array values properly
([2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01))
## Features
- **$http:** allow overriding the XSRF header and cookie name
([8155c3a2](https://github.com/angular/angular.js/commit/8155c3a29ea0eb14806913b8ac08ba7727e1969c))
- **$parse:** added `constant` and `literal` properties
([1ed63858](https://github.com/angular/angular.js/commit/1ed638582d2f2c7f89384d9712f4cfac52cc5b70))
- **$resource:** expose promise based api via $then and $resolved
([dba6bc73](https://github.com/angular/angular.js/commit/dba6bc73e802fdae685a9f351d3e23c7efa8568a))
- **$routeProvider:** add support to catch-all parameters in routes
([7eafbb98](https://github.com/angular/angular.js/commit/7eafbb98c64c0dc079d7d3ec589f1270b7f6fea5))
- **Scope:**
- expose transcluded and isolate scope info for batarang
([649b8922](https://github.com/angular/angular.js/commit/649b892205615a144dafff9984c0e6ab10ed341d))
- only evaluate constant $watch expressions once
([1d7a95df](https://github.com/angular/angular.js/commit/1d7a95df565192fc02a18b0b297b39dd615eaeb5))
- **angular.noConflict:** added api to restore previous angular namespace reference
([12ba6cec](https://github.com/angular/angular.js/commit/12ba6cec4fb79521101744e02a7e09f9fbb591c4))
- **Directives:**
- **ngSwitch:** support multiple matches on ngSwitchWhen and ngSwitchDefault
([0af17204](https://github.com/angular/angular.js/commit/0af172040e03811c59d01682968241e3df226774),
[#1074](https://github.com/angular/angular.js/issues/1074))
- **Filters:**
- **date:** add `[.,]sss` formatter for milliseconds
([df744f3a](https://github.com/angular/angular.js/commit/df744f3af46fc227a934f16cb63c7a6038e7133b))
- **filter:** add comparison function to filter
([ace54ff0](https://github.com/angular/angular.js/commit/ace54ff08c4593195b49eadb04d258e6409d969e))
## Breaking Changes
- **$http:** due to [288b69a3](https://github.com/angular/angular.js/commit/288b69a314e9bd14458b6647532eb62aad5c5cdf),
$http now follows RFC3986 and does not encode special characters like `$@,:` in params.
If your application needs to encode these characters, encode them manually, before sending the request.
- **$resource:** due to [2a212344](https://github.com/angular/angular.js/commit/2a2123441c2b749b8f316a24c3ca3f77a9132a01),
if the server relied on the buggy behavior of serializing arrays as http query arguments then
either the backend should be fixed or a simple serialization of the array should be done
on the client before calling the resource service.
<a name="1.0.5"></a>
# 1.0.5 flatulent-propulsion (2013-02-20)
## Bug Fixes
- **$compile:**
- sanitize values bound to a[href]
([9532234b](https://github.com/angular/angular.js/commit/9532234bf1c408af9a6fd2c4743fdb585b920531))
- rename $compileNote to compileNode
([92ca7efa](https://github.com/angular/angular.js/commit/92ca7efaa4bc4f37da3008b234e19343a1fa4207),
[#1941](https://github.com/angular/angular.js/issues/1941))
- should not leak memory when there are top level empty text nodes
([791804bd](https://github.com/angular/angular.js/commit/791804bdbfa6da7a39283623bd05628a01cd8720))
- allow startingTag method to handle text / comment nodes
([755beb2b](https://github.com/angular/angular.js/commit/755beb2b66ce9f9f9a218f2355bbaf96d94fbc15))
- **$cookies:** set cookies on Safari&IE when base[href] is undefined
([70909245](https://github.com/angular/angular.js/commit/7090924515214752b919b0c5630b3ea5e7c77223),
[#1190](https://github.com/angular/angular.js/issues/1190))
- **$http:**
- patch for Firefox bug w/ CORS and response headers
([e19b04c9](https://github.com/angular/angular.js/commit/e19b04c9ec985821edf1269c628cfa261f81d631),
[#1468](https://github.com/angular/angular.js/issues/1468))
- **$resource:**
- update RegExp to allow urlParams with out leading slash
([b7e1fb05](https://github.com/angular/angular.js/commit/b7e1fb0515798e1b4f3f2426f6b050951bee2617))
- **Directives:**
- **a:** workaround IE bug affecting mailto urls
([37e8b122](https://github.com/angular/angular.js/commit/37e8b12265291918396bfee65d444a8f63697b73),
[#1949](https://github.com/angular/angular.js/issues/1949))
- **ngClass:** keep track of old ngClass value manually
([5f5d4fea](https://github.com/angular/angular.js/commit/5f5d4feadbfa9d8ecc8150041dfd2bca2b2e9fea),
[#1637](https://github.com/angular/angular.js/issues/1637))
- **ngSwitch:** make ngSwitch compatible with controller backwards-compatiblity module
([9b7c1d0f](https://github.com/angular/angular.js/commit/9b7c1d0f7ce442d4ad2ec587e66d2d335e64fa4e))
- **Filters:**
- **date:** invert timezone sign and always display sign
([b001c8ec](https://github.com/angular/angular.js/commit/b001c8ece5472626bf49cf82753e8ac1aafd2513),
[#1261](https://github.com/angular/angular.js/issues/1261))
- **number:** fix formatting when "0" passed as fractionSize
([f5835963](https://github.com/angular/angular.js/commit/f5835963d5982003a713dd354eefd376ed39ac02))
- **scenario runner:** include error messages in XML output
([d46fe3c2](https://github.com/angular/angular.js/commit/d46fe3c23fa269dcc10249148f2af14f3db6b066))
- **Misc:**
- don't use instanceof to detect arrays
([3c2aee01](https://github.com/angular/angular.js/commit/3c2aee01b0b299995eb92f4255159585b0f53c10),
[#1966](https://github.com/angular/angular.js/issues/1966))
- angular.forEach should correctly iterate over objects with length prop
([ec54712f](https://github.com/angular/angular.js/commit/ec54712ff3dab1ade44f94fa82d67edeffa79a1d),
[#1840](https://github.com/angular/angular.js/issues/1840))
<a name="1.1.2"></a>
# 1.1.2 tofu-animation (2013-01-22)
@@ -193,8 +73,6 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
- HTTP method should be case-insensitive
([8991680d](https://github.com/angular/angular.js/commit/8991680d8ab632dda60cd70c780868c803c74509),
[#1403](https://github.com/angular/angular.js/issues/1403))
- correct leading slash removal in resource URLs
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
- **$route:**
- support route params not separated with slashes.
([c6392616](https://github.com/angular/angular.js/commit/c6392616ea5245bd0d2f77dded0b948d9e2637c8))
@@ -222,6 +100,8 @@ _Note: This release also contains all bug fixes available in [1.0.4](#1.0.4)._
- **ngRepeat:** correctly apply $last if repeating over object
([7e746015](https://github.com/angular/angular.js/commit/7e746015ea7dec3e9eb81bc4678fa9b6a83bc47c),
[#1789](https://github.com/angular/angular.js/issues/1789))
- **ngResource:** correct leading slash removal.
([b2f46251](https://github.com/angular/angular.js/commit/b2f46251aca76c8568ee7d4bab54edbc9d7a186a))
- **ngSwitch:** don't leak when destroyed while not attached
([a26234f7](https://github.com/angular/angular.js/commit/a26234f7183013e2fcc9b35377e181ad96dc9917),
[#1621](https://github.com/angular/angular.js/issues/1621))
+4
View File
@@ -26,6 +26,10 @@ Building AngularJS
Running Tests
-------------
Running tests requires installation of [Testacular](http://vojtajina.github.com/testacular):
sudo npm install -g testacular
To execute all unit tests, use:
rake test:unit
+25 -6
View File
@@ -21,8 +21,6 @@ task :default => [:package]
desc 'Init the build workspace'
task :init do
%x(npm install)
FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR)
v = YAML::load( File.open( 'version.yaml' ) )
@@ -59,6 +57,25 @@ task :concat_scenario => :init do
end
desc 'Concat JSTD Scenario Adapter'
task :concat_jstd_scenario_adapter => :init do
concat_file('jstd-scenario-adapter.js', [
'src/ngScenario/jstd-scenario-adapter/angular.prefix',
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
'src/ngScenario/jstd-scenario-adapter/angular.suffix',
])
# TODO(vojta) use jstd configuration when implemented
# (instead of including jstd-adapter-config.js)
File.open(path_to('jstd-scenario-adapter-config.js'), 'w') do |f|
f.write("/**\r\n" +
" * Configuration for jstd scenario adapter \n */\n" +
"var jstdScenarioAdapter = {\n relativeUrlPrefix: '/build/docs/'\n};\n")
end
end
desc 'Concat AngularJS files'
task :concat => :init do
@@ -98,7 +115,7 @@ end
desc 'Minify JavaScript'
task :minify => [:init, :concat, :concat_scenario] do
task :minify => [:init, :concat, :concat_scenario, :concat_jstd_scenario_adapter] do
[ 'angular.js',
'angular-cookies.js',
'angular-loader.js',
@@ -107,7 +124,11 @@ task :minify => [:init, :concat, :concat_scenario] do
'angular-bootstrap.js',
'angular-bootstrap-prettify.js'
].each do |file|
fork { closure_compile(file) }
unless ENV['TRAVIS']
fork { closure_compile(file) }
else
closure_compile(file)
end
end
Process.waitall
@@ -345,8 +366,6 @@ end
def start_testacular(config, singleRun, browsers, misc_options)
Rake::Task[:init].invoke
sh "./node_modules/testacular/bin/testacular start " +
"#{config} " +
"#{'--single-run=true' if singleRun} " +
+3 -3
View File
@@ -234,7 +234,7 @@ The separation of the controller and the view is important because:
The model is the data which is used merged with the template to produce the view. To be able to
render the model into the view, the model has to be able to be referenced from the scope. Unlike many
other frameworks Angular makes no restrictions or requirements on the model. There are no classes
other frameworks Angular makes no restrictions or requirements an the model. There are no classes
to inherit from or special accessor methods for accessing or changing the model. The model can be
primitive, object hash, or a full object Type. In short the model is a plain JavaScript object.
@@ -248,9 +248,9 @@ primitive, object hash, or a full object Type. In short the model is a plain Jav
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-view.png">
The view is what the user sees. The view begins its life as a template, is merged with the
The view is what the users sees. The view begins its life as a template, it is merged with the
model and finally rendered into the browser DOM. Angular takes a very different approach to
rendering the view compared to most other templating systems.
rendering the view, compared to most other templating systems.
* **Others** - Most templating systems begin as an HTML string with special templating markup.
Often the template markup breaks the HTML syntax which means that the template can not be
@@ -2,7 +2,9 @@
@name Developer Guide: Templates: Understanding Angular Filters
@description
Angular filters format data for display to the user.
Angular filters format data for display to the user. In addition to formatting data, filters can
also modify the DOM. This allows filters to handle tasks such as conditionally applying CSS styles
to filtered output.
For example, you might have a data object that needs to be formatted according to the locale before
displaying it to the user. You can pass expressions through a chain of filters like this:
+1 -1
View File
@@ -95,7 +95,7 @@ Compilation of HTML happens in three phases:
var $compile = ...; // injected into your code
var scope = ...;
var html = '<div ng-bind="exp"></div>';
var html = '<div ng-bind='exp'></div>';
// Step 1: parse HTML into DOM element
var template = angular.element(html);
+3 -3
View File
@@ -158,9 +158,9 @@ angular.module('myModule', []).
angular.module('myModule', []).
config(function($provide, $compileProvider, $filterProvider) {
$provide.value('a', 123);
$provide.factory('a', function() { return 123; });
$compileProvider.directive('directiveName', ...);
$provide.value('a', 123)
$provide.factory('a', function() { return 123; })
$compileProvider.directive('directiveName', ...).
$filterProvider.register('filterName', ...);
});
</pre>
+1 -2
View File
@@ -222,8 +222,7 @@ To run the E2E test suite:
To create and submit a change:
1. <a name="CLA"></a>
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
1. Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code changes to be
accepted, the CLA must be signed. It's a quick process, we promise!
For individuals we have a [simple click-through form](http://code.google.com/legal/individual-cla-v1.0.html). For
+2 -2
View File
@@ -109,7 +109,7 @@ __`app/index.html`:__
<html ng-app>
The `ng-app` attribute represents an Angular directive (named `ngApp`; Angular uses
The `ng-app` attribute is represents an Angular directive (named `ngApp`; Angular uses
`name-with-dashes` for attribute names and `camelCase` for the corresponding directive name)
used to flag an element which Angular should consider to be the root element of our application.
This gives application developers the freedom to tell Angular if the entire html page or only a
@@ -127,7 +127,7 @@ being the element on which the `ngApp` directive was defined.
* Double-curly binding with an expression:
Nothing here {{'yet' + '!'}}
Nothing here {{'yet' + '!'}}`
This line demonstrates the core feature of Angular's templating capabilities a binding, denoted
by double-curlies `{{ }}` as well as a simple expression `'yet' + '!'` used in this binding.
+5 -23
View File
@@ -55,15 +55,12 @@ describe('ngdoc', function() {
'@name a\n' +
'@param {*} a short\n' +
'@param {Type} b med\n' +
'@param {Class=} [c=2] long\nline\n' +
'@param {function(number, string=)} d fn with optional arguments');
'@param {Class=} [c=2] long\nline');
doc.parse();
expect(doc.param).toEqual([
{name:'a', description:'<p>short</p>', type:'*', optional:false, 'default':undefined},
{name:'b', description:'<p>med</p>', type:'Type', optional:false, 'default':undefined},
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'},
{name:'d', description:'<p>fn with optional arguments</p>',
type: 'function(number, string=)', optional: false, 'default':undefined}
{name:'c', description:'<p>long\nline</p>', type:'Class', optional:true, 'default':'2'}
]);
});
@@ -321,9 +318,9 @@ describe('ngdoc', function() {
});
it('should not parse @property without a type', function() {
var doc = new Doc("@property fake", 'test.js', '44');
var doc = new Doc("@property fake");
expect(function() { doc.parse(); }).
toThrow(new Error("Not a valid 'property' format: fake (found in: test.js:44)"));
toThrow(new Error("Not a valid 'property' format: fake"));
});
it('should parse @property with type', function() {
@@ -353,30 +350,15 @@ describe('ngdoc', function() {
describe('@returns', function() {
it('should not parse @returns without type', function() {
var doc = new Doc("@returns lala");
expect(function() { doc.parse(); }).
toThrow();
expect(doc.parse).toThrow();
});
it('should not parse @returns with invalid type', function() {
var doc = new Doc("@returns {xx}x} lala", 'test.js', 34);
expect(function() { doc.parse(); }).
toThrow(new Error("Not a valid 'returns' format: {xx}x} lala (found in: test.js:34)"));
});
it('should parse @returns with type and description', function() {
var doc = new Doc("@name a\n@returns {string} descrip tion");
doc.parse();
expect(doc.returns).toEqual({type: 'string', description: '<p>descrip tion</p>'});
});
it('should parse @returns with complex type and description', function() {
var doc = new Doc("@name a\n@returns {function(string, number=)} description");
doc.parse();
expect(doc.returns).toEqual({type: 'function(string, number=)', description: '<p>description</p>'});
});
it('should transform description of @returns with markdown', function() {
var doc = new Doc("@name a\n@returns {string} descrip *tion*");
doc.parse();
+42 -57
View File
@@ -214,25 +214,23 @@ Doc.prototype = {
if (atName) {
var text = trim(atText.join('\n')), match;
if (atName == 'param') {
match = text.match(/^\{([^}]+)\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
// 1 1 23 3 4 4 5 5 2 6 6
match = text.match(/^\{([^}=]+)(=)?\}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
// 1 12 2 34 4 5 5 6 6 3 7 7
if (!match) {
throw new Error("Not a valid 'param' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
throw new Error("Not a valid 'param' format: " + text);
}
var optional = (match[1].slice(-1) === '=');
var param = {
name: match[4] || match[3],
description:self.markdown(text.replace(match[0], match[6])),
type: optional ? match[1].substring(0, match[1].length-1) : match[1],
optional: optional,
'default':match[5]
name: match[5] || match[4],
description:self.markdown(text.replace(match[0], match[7])),
type: match[1],
optional: !!match[2],
'default':match[6]
};
self.param.push(param);
} else if (atName == 'returns' || atName == 'return') {
match = text.match(/^\{([^}]+)\}\s+(.*)/);
match = text.match(/^\{([^}=]+)\}\s+(.*)/);
if (!match) {
throw new Error("Not a valid 'returns' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
throw new Error("Not a valid 'returns' format: " + text + ' in ' + self.file + ':' + self.line);
}
self.returns = {
type: match[1],
@@ -247,7 +245,7 @@ Doc.prototype = {
} else if(atName == 'property') {
match = text.match(/^\{(\S+)\}\s+(\S+)(\s+(.*))?/);
if (!match) {
throw new Error("Not a valid 'property' format: " + text + ' (found in: ' + self.file + ':' + self.line + ')');
throw new Error("Not a valid 'property' format: " + text);
}
var property = new Doc({
type: match[1],
@@ -385,53 +383,40 @@ Doc.prototype = {
var self = this;
dom.h('Usage', function() {
var restrict = self.restrict || 'AC';
if (restrict.match(/E/)) {
dom.text('This directive can be used as custom element, but we aware of ');
dom.text('as element (see ');
dom.tag('a', {href:'guide/ie'}, 'IE restrictions');
dom.text('.');
}
if (self.usage) {
dom.tag('pre', function() {
dom.tag('code', function() {
dom.text(self.usage);
});
dom.text(')');
dom.code(function() {
dom.text('<');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"');
dom.text('>\n</');
dom.text(dashCase(self.shortName));
dom.text('>');
});
}
if (restrict.match(/A/)) {
var element = self.element || 'ANY';
dom.text('as attribute');
dom.code(function() {
dom.text('<' + element + ' ');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"', true);
dom.text('>\n ...\n');
dom.text('</' + element + '>');
});
}
if (restrict.match(/C/)) {
dom.text('as class');
var element = self.element || 'ANY';
dom.code(function() {
dom.text('<' + element + ' class="');
dom.text(dashCase(self.shortName));
renderParams(' ', ': ', ';', true);
dom.text('">\n ...\n');
dom.text('</' + element + '>');
});
} else {
if (restrict.match(/E/)) {
dom.text('as element:');
dom.code(function() {
dom.text('<');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"');
dom.text('>\n</');
dom.text(dashCase(self.shortName));
dom.text('>');
});
}
if (restrict.match(/A/)) {
var element = self.element || 'ANY';
dom.text('as attribute');
dom.code(function() {
dom.text('<' + element + ' ');
dom.text(dashCase(self.shortName));
renderParams('\n ', '="', '"', true);
dom.text('>\n ...\n');
dom.text('</' + element + '>');
});
}
if (restrict.match(/C/)) {
dom.text('as class');
var element = self.element || 'ANY';
dom.code(function() {
dom.text('<' + element + ' class="');
dom.text(dashCase(self.shortName));
renderParams(' ', ': ', ';', true);
dom.text('">\n ...\n');
dom.text('</' + element + '>');
});
}
}
self.html_usage_directiveInfo(dom);
self.html_usage_parameters(dom);
+3651 -3687
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
1.8.2
1.7.2
+4 -4
View File
@@ -2,9 +2,9 @@
"name": "AngularJS",
"version": "0.0.0",
"dependencies" : {
"testacular" : "0.5.9",
"jasmine-node" : "1.2.3",
"q-fs" : "0.1.36",
"qq" : "0.3.5"
"testacular" : "canary",
"jasmine-node" : "*",
"q-fs" : "*",
"qq" : "*"
}
}
+11 -61
View File
@@ -49,7 +49,8 @@ if ('i' !== 'I'.toLowerCase()) {
function fromCharCode(code) {return String.fromCharCode(code);}
var /** holds major version number for IE or NaN for real browsers */
var Error = window.Error,
/** holds major version number for IE or NaN for real browsers */
msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]),
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
@@ -57,32 +58,12 @@ var /** holds major version number for IE or NaN for real browsers */
push = [].push,
toString = Object.prototype.toString,
_angular = window.angular,
/** @name angular */
angular = window.angular || (window.angular = {}),
angularModule,
nodeName_,
uid = ['0', '0', '0'];
/**
* @ngdoc function
* @name angular.noConflict
* @function
*
* @description
* Restores the previous global value of angular and returns the current instance. Other libraries may already use the
* angular namespace. Or a previous version of angular is already loaded on the page. In these cases you may want to
* restore the previous namespace and keep a reference to angular.
*
* @return {Object} The current angular namespace
*/
function noConflict() {
var a = window.angular;
window.angular = _angular;
return a;
}
/**
* @ngdoc function
* @name angular.forEach
@@ -110,30 +91,6 @@ function noConflict() {
* @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
*/
/**
* @private
* @param {*} obj
* @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, ...)
*/
function isArrayLike(obj) {
if (!obj || (typeof obj.length !== 'number')) return false;
// We have on object which has length property. Should we treat it as array?
if (typeof obj.hasOwnProperty != 'function' &&
typeof obj.constructor != 'function') {
// This is here for IE8: it is a bogus object treat it as array;
return true;
} else {
return obj instanceof JQLite || // JQLite
(jQuery && obj instanceof jQuery) || // jQuery
toString.call(obj) !== '[object Object]' || // some browser native object
typeof obj.callee === 'function'; // arguments (on IE8 looks like regular obj)
}
}
function forEach(obj, iterator, context) {
var key;
if (obj) {
@@ -145,7 +102,7 @@ function forEach(obj, iterator, context) {
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context);
} else if (isArrayLike(obj)) {
} else if (isObject(obj) && isNumber(obj.length)) {
for (key = 0; key < obj.length; key++)
iterator.call(context, obj[key], key);
} else {
@@ -190,7 +147,7 @@ function reverseParams(iteratorFn) {
/**
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
* the number string gets longer over time, and it can also overflow, where as the nextId
* the number string gets longer over time, and it can also overflow, where as the the nextId
* will grow much slower, it is a string, and it will never overflow.
*
* @returns an unique alpha-numeric string
@@ -586,7 +543,9 @@ function copy(source, destination){
} else {
if (source === destination) throw Error("Can't copy equivalent objects or arrays");
if (isArray(source)) {
destination.length = 0;
while(destination.length) {
destination.pop();
}
for ( var i = 0; i < source.length; i++) {
destination.push(copy(source[i]));
}
@@ -797,18 +756,9 @@ function startingTag(element) {
// are not allowed to have children. So we just ignore it.
element.html('');
} catch(e) {}
// As Per DOM Standards
var TEXT_NODE = 3;
var elemHtml = jqLite('<div>').append(element).html();
try {
return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) :
elemHtml.
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
} catch(e) {
return lowercase(elemHtml);
}
return jqLite('<div>').append(element).html().
match(/^(<[^>]+>)/)[1].
replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
}
@@ -892,7 +842,7 @@ function encodeUriQuery(val, pctEncodeSpaces) {
* Use this directive to auto-bootstrap on application. Only
* one directive can be used per HTML document. The directive
* designates the root of the application and is typically placed
* at the root of the page.
* ot the root of the page.
*
* In the example below if the `ngApp` directive would not be placed
* on the `html` element then the document would not be compiled
+1 -2
View File
@@ -48,8 +48,7 @@ function publishExternalAPI(angular){
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {counter: 0},
'noConflict': noConflict
'callbacks': {counter: 0}
});
angularModule = setupModuleLoader(window);
+7 -6
View File
@@ -192,7 +192,7 @@ function annotate(fn) {
* This method does not work with code minfication / obfuscation. For this reason the following annotation strategies
* are supported.
*
* # The `$inject` property
* # The `$injector` property
*
* If a function has an `$inject` property and its value is an array of strings, then the strings represent names of
* services to be injected into the function.
@@ -410,10 +410,9 @@ function createInjector(modulesToLoad) {
decorator: decorator
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function() {
throw Error("Unknown provider: " + path.join(' <- '));
})),
providerInjector = createInternalInjector(providerCache, function() {
throw Error("Unknown provider: " + path.join(' <- '));
}),
instanceCache = {},
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(servicename) {
@@ -490,7 +489,9 @@ function createInjector(modulesToLoad) {
try {
for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
var invokeArgs = invokeQueue[i],
provider = providerInjector.get(invokeArgs[0]);
provider = invokeArgs[0] == '$injector'
? providerInjector
: providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
}
+4 -4
View File
@@ -4,10 +4,10 @@ var directive = {};
var service = { value: {} };
var DEPENDENCIES = {
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
'angular.js': 'http://code.angularjs.org/' + angular.version.full + 'angular.min.js',
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-resource.min.js',
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-sanitize.min.js',
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + 'angular-cookies.min.js'
};
+3 -4
View File
@@ -353,11 +353,11 @@ var JQLitePrototype = JQLite.prototype = {
// value on get.
//////////////////////////////////////////
var BOOLEAN_ATTR = {};
forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) {
BOOLEAN_ATTR[lowercase(value)] = value;
});
var BOOLEAN_ELEMENTS = {};
forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
forEach('input,select,option,textarea,button,form'.split(','), function(value) {
BOOLEAN_ELEMENTS[uppercase(value)] = true;
});
@@ -659,9 +659,8 @@ forEach({
append: function(element, node) {
forEach(new JQLite(node), function(child){
if (element.nodeType === 1 || element.nodeType === 11) {
if (element.nodeType === 1)
element.appendChild(child);
}
});
},
+1 -1
View File
@@ -237,7 +237,7 @@ function Browser(window, document, $log, $sniffer) {
*/
self.baseHref = function() {
var href = baseElement.attr('href');
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : '';
return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href;
};
//////////////////////////////////////////////////////////////
+1 -3
View File
@@ -14,7 +14,7 @@
* @returns {object} Newly created cache object with the following set of methods:
*
* - `{object}` `info()` — Returns id, size, and options of cache.
* - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns it.
* - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache.
* - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
* - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
* - `{void}` `removeAll()` — Removes all cached values.
@@ -53,8 +53,6 @@ function $CacheFactoryProvider() {
if (size > capacity) {
this.remove(staleEnd.key);
}
return value;
},
+9 -69
View File
@@ -155,8 +155,7 @@ function $CompileProvider($provide) {
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ',
urlSanitizationWhitelist = /^\s*(https?|ftp|mailto):/;
MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: ';
/**
@@ -210,41 +209,11 @@ function $CompileProvider($provide) {
};
/**
* @ngdoc function
* @name ng.$compileProvider#urlSanitizationWhitelist
* @methodOf ng.$compileProvider
* @function
*
* @description
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into an
* absolute url. Afterwards the url is matched against the `urlSanitizationWhitelist` regular
* expression. If a match is found the original url is written into the dom. Otherwise the
* absolute url is prefixed with `'unsafe:'` string and only then it is written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
* chaining otherwise.
*/
this.urlSanitizationWhitelist = function(regexp) {
if (isDefined(regexp)) {
urlSanitizationWhitelist = regexp;
return this;
}
return urlSanitizationWhitelist;
};
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
'$controller', '$rootScope', '$document',
'$controller', '$rootScope',
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
$controller, $rootScope, $document) {
$controller, $rootScope) {
var Attributes = function(element, attr) {
this.$$element = element;
@@ -266,8 +235,7 @@ function $CompileProvider($provide) {
*/
$set: function(key, value, writeAttr, attrName) {
var booleanKey = getBooleanAttrName(this.$$element[0], key),
$$observers = this.$$observers,
normalizedVal;
$$observers = this.$$observers;
if (booleanKey) {
this.$$element.prop(key, value);
@@ -286,19 +254,6 @@ function $CompileProvider($provide) {
}
}
// sanitize a[href] values
if (nodeName_(this.$$element[0]) === 'A' && key === 'href') {
urlSanitizationNode.setAttribute('href', value);
// href property always returns normalized absolute url, so we can match against that
normalizedVal = urlSanitizationNode.href;
if (!normalizedVal.match(urlSanitizationWhitelist)) {
this[key] = value = 'unsafe:' + normalizedVal;
}
}
if (writeAttr !== false) {
if (value === null || value === undefined) {
this.$$element.removeAttr(attrName);
@@ -342,8 +297,7 @@ function $CompileProvider($provide) {
}
};
var urlSanitizationNode = $document[0].createElement('a'),
startSymbol = $interpolate.startSymbol(),
var startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
? identity
@@ -376,14 +330,7 @@ function $CompileProvider($provide) {
var $linkNode = cloneConnectFn
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
: $compileNodes;
// Attach scope only to non-text nodes.
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
var node = $linkNode[i];
if (node.nodeType == 1 /* element */ || node.nodeType == 9 /* document */) {
$linkNode.eq(i).data('$scope', scope);
}
}
$linkNode.data('$scope', scope);
safeAddClass($linkNode, 'ng-scope');
if (cloneConnectFn) cloneConnectFn($linkNode, scope);
if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode);
@@ -473,7 +420,6 @@ function $CompileProvider($provide) {
(function(transcludeFn) {
return function(cloneFn) {
var transcludeScope = scope.$new();
transcludeScope.$$transcluded = true;
return transcludeFn(transcludeScope, cloneFn).
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
@@ -779,8 +725,6 @@ function $CompileProvider($provide) {
lastValue,
parentGet, parentSet;
scope.$$isolateBindings[scopeName] = mode + attrName;
switch (mode) {
case '@': {
@@ -788,10 +732,6 @@ function $CompileProvider($provide) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = parentScope;
if( attrs[attrName] ) {
// If the attribute has been provided then we trigger an interpolation to ensure the value is there for use in the link fn
scope[scopeName] = $interpolate(attrs[attrName])(parentScope);
}
break;
}
@@ -995,7 +935,7 @@ function $CompileProvider($provide) {
}
directives.unshift(derivedSyncDirective);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn);
afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn);
@@ -1075,10 +1015,10 @@ function $CompileProvider($provide) {
function addAttrInterpolateDirective(node, directives, value, name) {
var interpolateFn = $interpolate(value, true);
// no interpolation found -> ignore
if (!interpolateFn) return;
directives.push({
priority: 100,
compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) {
@@ -1090,7 +1030,7 @@ function $CompileProvider($provide) {
interpolateFn = $interpolate(attr[name], true);
}
attr[name] = interpolateFn(scope);
attr[name] = undefined;
($$observers[name] || ($$observers[name] = [])).$$inter = true;
(attr.$$observers && attr.$$observers[name].$$scope || scope).
$watch(interpolateFn, function interpolateFnWatchAction(value) {
+5 -15
View File
@@ -11,25 +11,15 @@
*
* The reasoning for this change is to allow easy creation of action links with `ngClick` directive
* without changing the location or causing page reloads, e.g.:
* `<a href="" ng-click="model.$save()">Save</a>`
* <a href="" ng-click="model.$save()">Save</a>
*/
var htmlAnchorDirective = valueFn({
restrict: 'E',
compile: function(element, attr) {
if (msie <= 8) {
// turn <a href ng-click="..">link</a> into a stylable link in IE
// but only if it doesn't have name attribute, in which case it's an anchor
if (!attr.href && !attr.name) {
attr.$set('href', '');
}
// add a comment node to anchors to workaround IE bug that causes element content to be reset
// to new attribute content if attribute is updated with value containing @ and element also
// contains value with @
// see issue #1949
element.append(document.createComment('IE fix'));
// turn <a href ng-click="..">link</a> into a link in IE
// but only if it doesn't have name attribute, in which case it's an anchor
if (!attr.href) {
attr.$set('href', '');
}
return function(scope, element) {
+3 -35
View File
@@ -66,7 +66,7 @@
it('should execute ng-click but not reload when no href but name specified', function() {
element('#link-5').click();
expect(input('value').val()).toEqual('5');
expect(element('#link-5').attr('href')).toBe(undefined);
expect(element('#link-5').attr('href')).toBe('');
});
it('should only change url when only ng-href', function() {
@@ -272,37 +272,6 @@
* @param {string} expression Angular expression that will be evaluated.
*/
/**
* @ngdoc directive
* @name ng.directive:ngOpen
* @restrict A
*
* @description
* The HTML specs do not require browsers to preserve the special attributes such as open.
* (The presence of them means true and absence means false)
* This prevents the angular compiler from correctly retrieving the binding expression.
* To solve this problem, we introduce the `ngOpen` directive.
*
* @example
<doc:example>
<doc:source>
Check me check multiple: <input type="checkbox" ng-model="open"><br/>
<details id="details" ng-open="open">
<summary>Show/Hide me</summary>
</details>
</doc:source>
<doc:scenario>
it('should toggle open', function() {
expect(element('#details').prop('open')).toBeFalsy();
input('open').check();
expect(element('#details').prop('open')).toBeTruthy();
});
</doc:scenario>
</doc:example>
*
* @element DETAILS
* @param {string} expression Angular expression that will be evaluated.
*/
var ngAttributeAliasDirectives = {};
@@ -340,9 +309,8 @@ forEach(['src', 'href'], function(attrName) {
// on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
// to set the property as well to achieve the desired effect.
// we use attr[attrName] value since $set can sanitize the url.
if (msie) element.prop(attrName, attr[attrName]);
// to set the property as well to achieve the desired effect
if (msie) element.prop(attrName, value);
});
}
};
+2 -31
View File
@@ -5,8 +5,7 @@ var nullFormCtrl = {
$addControl: noop,
$removeControl: noop,
$setValidity: noop,
$setDirty: noop,
$setPristine: noop
$setDirty: noop
};
/**
@@ -38,8 +37,7 @@ function FormController(element, attrs) {
var form = this,
parentForm = element.parent().controller('form') || nullFormCtrl,
invalidCount = 0, // used to easily determine if we are valid
errors = form.$error = {},
controls = [];
errors = form.$error = {};
// init state
form.$name = attrs.name;
@@ -63,8 +61,6 @@ function FormController(element, attrs) {
}
form.$addControl = function(control) {
controls.push(control);
if (control.$name && !form.hasOwnProperty(control.$name)) {
form[control.$name] = control;
}
@@ -77,8 +73,6 @@ function FormController(element, attrs) {
forEach(errors, function(queue, validationToken) {
form.$setValidity(validationToken, true, control);
});
arrayRemove(controls, control);
};
form.$setValidity = function(validationToken, isValid, control) {
@@ -126,29 +120,6 @@ function FormController(element, attrs) {
parentForm.$setDirty();
};
/**
* @ngdoc function
* @name ng.directive:form.FormController#$setPristine
* @methodOf ng.directive:form.FormController
*
* @description
* Sets the form to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the form to its pristine
* state (ng-pristine class). This method will also propagate to all the controls contained
* in this form.
*
* Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
* saving or resetting it.
*/
form.$setPristine = function () {
element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
form.$dirty = false;
form.$pristine = true;
forEach(controls, function(control) {
control.$setPristine();
});
};
}
+3 -34
View File
@@ -28,8 +28,6 @@ var inputType = {
* patterns defined as scope expressions.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trimming the
* input.
*
* @example
<doc:example>
@@ -37,12 +35,12 @@ var inputType = {
<script>
function Ctrl($scope) {
$scope.text = 'guest';
$scope.word = /^\s*\w*\s*$/;
$scope.word = /^\w*$/;
}
</script>
<form name="myForm" ng-controller="Ctrl">
Single word: <input type="text" name="input" ng-model="text"
ng-pattern="word" required ng-trim="false">
ng-pattern="word" required>
<span class="error" ng-show="myForm.input.$error.required">
Required!</span>
<span class="error" ng-show="myForm.input.$error.pattern">
@@ -71,12 +69,6 @@ var inputType = {
input('text').enter('hello world');
expect(binding('myForm.input.$valid')).toEqual('false');
});
it('should not be trimmed', function() {
input('text').enter('untrimmed ');
expect(binding('text')).toEqual('untrimmed ');
expect(binding('myForm.input.$valid')).toEqual('true');
});
</doc:scenario>
</doc:example>
*/
@@ -390,14 +382,7 @@ function isEmpty(value) {
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
var listener = function() {
var value = element.val();
// By default we will trim the value
// If the attribute ng-trim exists we will avoid trimming
// e.g. <input ng-model="foo" ng-trim="false">
if (toBoolean(attr.ngTrim || 'T')) {
value = trim(value);
}
var value = trim(element.val());
if (ctrl.$viewValue !== value) {
scope.$apply(function() {
@@ -978,22 +963,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
parentForm.$setValidity(validationErrorKey, isValid, this);
};
/**
* @ngdoc function
* @name ng.directive:ngModel.NgModelController#$setPristine
* @methodOf ng.directive:ngModel.NgModelController
*
* @description
* Sets the control to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the control to its pristine
* state (ng-pristine class).
*/
this.$setPristine = function () {
this.$dirty = false;
this.$pristine = true;
$element.removeClass(DIRTY_CLASS).addClass(PRISTINE_CLASS);
};
/**
* @ngdoc function
+2 -4
View File
@@ -3,7 +3,6 @@
function classDirective(name, selector) {
name = 'ngClass' + name;
return ngDirective(function(scope, element, attr) {
var oldVal = undefined;
scope.$watch(attr[name], ngClassWatchAction, true);
@@ -27,14 +26,13 @@ function classDirective(name, selector) {
}
function ngClassWatchAction(newVal) {
function ngClassWatchAction(newVal, oldVal) {
if (selector === true || scope.$index % 2 === selector) {
if (oldVal && (newVal !== oldVal)) {
removeClass(oldVal);
}
addClass(newVal);
}
oldVal = newVal;
}
@@ -67,7 +65,7 @@ function classDirective(name, selector) {
*
* The directive won't add duplicate classes if a particular class was already set.
*
* When the expression changes, the previously added classes are removed and only then the
* When the expression changes, the previously added classes are removed and only then the classes
* new classes are added.
*
* @element ANY
+1 -33
View File
@@ -37,7 +37,7 @@
*/
var ngEventDirectives = {};
forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup'.split(' '),
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '),
function(name) {
var directiveName = directiveNormalize('ng-' + name);
ngEventDirectives[directiveName] = ['$parse', function($parse) {
@@ -164,38 +164,6 @@ forEach(
*/
/**
* @ngdoc directive
* @name ng.directive:ngKeydown
*
* @description
* Specify custom behavior on keydown event.
*
* @element ANY
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
*/
/**
* @ngdoc directive
* @name ng.directive:ngKeyup
*
* @description
* Specify custom behavior on keyup event.
*
* @element ANY
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
* @example
* See {@link ng.directive:ngClick ngClick}
*/
/**
* @ngdoc directive
* @name ng.directive:ngSubmit
+18 -30
View File
@@ -20,11 +20,8 @@
* On child elments add:
*
* * `ngSwitchWhen`: the case statement to match against. If match then this
* case will be displayed. If the same match appears multiple times, all the
* elements will be displayed.
* * `ngSwitchDefault`: the default case when no other case match. If there
* are multiple default cases, all of them will be displayed when no other
* case match.
* case will be displayed.
* * `ngSwitchDefault`: the default case when no other casses match.
*
* @example
<doc:example>
@@ -66,34 +63,27 @@ var NG_SWITCH = 'ng-switch';
var ngSwitchDirective = valueFn({
restrict: 'EA',
require: 'ngSwitch',
// asks for $scope to fool the BC controller module
controller: ['$scope', function ngSwitchController() {
controller: function ngSwitchController() {
this.cases = {};
}],
},
link: function(scope, element, attr, ctrl) {
var watchExpr = attr.ngSwitch || attr.on,
selectedTranscludes,
selectedElements,
selectedScopes = [];
selectedTransclude,
selectedElement,
selectedScope;
scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
for (var i= 0, ii=selectedScopes.length; i<ii; i++) {
selectedScopes[i].$destroy();
selectedElements[i].remove();
if (selectedElement) {
selectedScope.$destroy();
selectedElement.remove();
selectedElement = selectedScope = null;
}
selectedElements = [];
selectedScopes = [];
if ((selectedTranscludes = ctrl.cases['!' + value] || ctrl.cases['?'])) {
if ((selectedTransclude = ctrl.cases['!' + value] || ctrl.cases['?'])) {
scope.$eval(attr.change);
forEach(selectedTranscludes, function(selectedTransclude) {
var selectedScope = scope.$new();
selectedScopes.push(selectedScope);
selectedTransclude(selectedScope, function(caseElement) {
selectedElements.push(caseElement);
element.append(caseElement);
});
selectedScope = scope.$new();
selectedTransclude(selectedScope, function(caseElement) {
selectedElement = caseElement;
element.append(caseElement);
});
}
});
@@ -106,8 +96,7 @@ var ngSwitchWhenDirective = ngDirective({
require: '^ngSwitch',
compile: function(element, attrs, transclude) {
return function(scope, element, attr, ctrl) {
ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
ctrl.cases['!' + attrs.ngSwitchWhen].push(transclude);
ctrl.cases['!' + attrs.ngSwitchWhen] = transclude;
};
}
});
@@ -118,8 +107,7 @@ var ngSwitchDefaultDirective = ngDirective({
require: '^ngSwitch',
compile: function(element, attrs, transclude) {
return function(scope, element, attr, ctrl) {
ctrl.cases['?'] = (ctrl.cases['?'] || []);
ctrl.cases['?'].push(transclude);
ctrl.cases['?'] = transclude;
};
}
});
+1 -1
View File
@@ -147,7 +147,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
if (current.controller) {
locals.$scope = lastScope;
controller = $controller(current.controller, locals);
element.children().data('$ngControllerController', controller);
element.contents().data('$ngControllerController', controller);
}
link(lastScope);
+17 -63
View File
@@ -32,22 +32,6 @@
* called for each element of `array`. The final result is an array of those elements that
* the predicate returned true for.
*
* @param {function(expected, actual)|true|undefined} comparator Comparator which is used in
* determining if the expected value (from the filter expression) and actual value (from
* the object in the array) should be considered a match.
*
* Can be one of:
*
* - `function(expected, actual)`:
* The function will be given the object value and the predicate value to compare and
* should return true if the item should be included in filtered result.
*
* - `true`: A shorthand for `function(expected, actual) { return angular.equals(expected, actual)}`.
* this is essentially strict comparison of expected and actual.
*
* - `false|undefined`: A short hand for a function which will look for a substring match in case
* insensitive way.
*
* @example
<doc:example>
<doc:source>
@@ -55,8 +39,7 @@
{name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'},
{name:'Adam', phone:'555-5678'},
{name:'Julie', phone:'555-8765'},
{name:'Juliette', phone:'555-5678'}]"></div>
{name:'Julie', phone:'555-8765'}]"></div>
Search: <input ng-model="searchText">
<table id="searchTextResults">
@@ -70,10 +53,9 @@
Any: <input ng-model="search.$"> <br>
Name only <input ng-model="search.name"><br>
Phone only <input ng-model="search.phone"å><br>
Equality <input type="checkbox" ng-model="strict"><br>
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th><tr>
<tr ng-repeat="friend in friends | filter:search:strict">
<tr ng-repeat="friend in friends | filter:search">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<tr>
@@ -93,20 +75,14 @@
it('should search in specific fields when filtering with a predicate object', function() {
input('search.$').enter('i');
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Mike', 'Julie', 'Juliette']);
});
it('should use a equal comparison when comparator is true', function() {
input('search.name').enter('Julie');
input('strict').check();
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
toEqual(['Julie']);
toEqual(['Mary', 'Mike', 'Julie']);
});
</doc:scenario>
</doc:example>
*/
function filterFilter() {
return function(array, expression, comperator) {
if (!isArray(array)) return array;
return function(array, expression) {
if (!(array instanceof Array)) return array;
var predicates = [];
predicates.check = function(value) {
for (var j = 0; j < predicates.length; j++) {
@@ -116,43 +92,20 @@ function filterFilter() {
}
return true;
};
switch(typeof comperator) {
case "function":
break;
case "boolean":
if(comperator == true) {
comperator = function(obj, text) {
return angular.equals(obj, text);
}
break;
}
default:
comperator = function(obj, text) {
text = (''+text).toLowerCase();
return (''+obj).toLowerCase().indexOf(text) > -1
};
}
var search = function(obj, text){
if (typeof text == 'string' && text.charAt(0) === '!') {
if (text.charAt(0) === '!') {
return !search(obj, text.substr(1));
}
switch (typeof obj) {
case "boolean":
case "number":
case "string":
return comperator(obj, text);
return ('' + obj).toLowerCase().indexOf(text) > -1;
case "object":
switch (typeof text) {
case "object":
return comperator(obj, text);
break;
default:
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
break;
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
return false;
case "array":
@@ -175,18 +128,19 @@ function filterFilter() {
for (var key in expression) {
if (key == '$') {
(function() {
if (!expression[key]) return;
var path = key
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(value, expression[path]);
return search(value, text);
});
})();
} else {
(function() {
if (!expression[key]) return;
var path = key;
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(getter(value,path), expression[path]);
return search(getter(value, path), text);
});
})();
}
+9 -22
View File
@@ -168,7 +168,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
fraction += '0';
}
if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize);
}
parts.push(isNegative ? pattern.negPre : pattern.posPre);
@@ -211,12 +211,8 @@ function dateStrGetter(name, shortForm) {
}
function timeZoneGetter(date) {
var zone = -1 * date.getTimezoneOffset();
var paddedZone = (zone >= 0) ? "+" : "";
paddedZone += padNumber(zone / 60, 2) + padNumber(Math.abs(zone % 60), 2);
return paddedZone;
var offset = date.getTimezoneOffset();
return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2);
}
function ampmGetter(date, formats) {
@@ -241,9 +237,6 @@ var DATE_FORMATS = {
m: dateGetter('Minutes', 1),
ss: dateGetter('Seconds', 2),
s: dateGetter('Seconds', 1),
// while ISO 8601 requires fractions to be prefixed with `.` or `,`
// we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
sss: dateGetter('Milliseconds', 3),
EEEE: dateStrGetter('Day'),
EEE: dateStrGetter('Day', true),
a: ampmGetter,
@@ -282,7 +275,6 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
* * `'m'`: Minute in hour (0-59)
* * `'ss'`: Second in minute, padded (00-59)
* * `'s'`: Second in minute (0-59)
* * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
* * `'a'`: am/pm marker
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200)
*
@@ -306,8 +298,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
*
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
* specified in the string input, the time is considered to be in the local timezone.
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ).
* @param {string=} format Formatting rules (see Description). If not specified,
* `mediumDate` is used.
* @returns {string} Formatted string or the input if input is not recognized as date/millis.
@@ -327,7 +318,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
expect(binding("1288323623006 | date:'medium'")).
toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
});
@@ -339,22 +330,18 @@ function dateFilter($locale) {
var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
// 1 2 3 4 5 6 7 8 9 10 11
function jsonStringToDate(string) {
function jsonStringToDate(string){
var match;
if (match = string.match(R_ISO8601_STR)) {
var date = new Date(0),
tzHour = 0,
tzMin = 0,
dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
timeSetter = match[8] ? date.setUTCHours : date.setHours;
tzMin = 0;
if (match[9]) {
tzHour = int(match[9] + match[10]);
tzMin = int(match[9] + match[11]);
}
dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
timeSetter.call(date, int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3]));
date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0));
return date;
}
return string;
+33 -50
View File
@@ -6,20 +6,20 @@
* @function
*
* @description
* Creates a new array or string containing only a specified number of elements. The elements
* are taken from either the beginning or the end of the source array or string, as specified by
* the value and sign (positive or negative) of `limit`.
* Creates a new array containing only a specified number of elements in an array. The elements
* are taken from either the beginning or the end of the source array, as specified by the
* value and sign (positive or negative) of `limit`.
*
* Note: This function is used to augment the `Array` type in Angular expressions. See
* {@link ng.$filter} for more information about Angular arrays.
*
* @param {Array|string} input Source array or string to be limited.
* @param {string|number} limit The length of the returned array or string. If the `limit` number
* is positive, `limit` number of items from the beginning of the source array/string are copied.
* If the number is negative, `limit` number of items from the end of the source array/string
* are copied. The `limit` will be trimmed if it exceeds `array.length`
* @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
* had less than `limit` elements.
* @param {Array} array Source array to be limited.
* @param {string|Number} limit The length of the returned array. If the `limit` number is
* positive, `limit` number of items from the beginning of the source array are copied.
* If the number is negative, `limit` number of items from the end of the source array are
* copied. The `limit` will be trimmed if it exceeds `array.length`
* @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit`
* elements.
*
* @example
<doc:example>
@@ -27,76 +27,59 @@
<script>
function Ctrl($scope) {
$scope.numbers = [1,2,3,4,5,6,7,8,9];
$scope.letters = "abcdefghi";
$scope.numLimit = 3;
$scope.letterLimit = 3;
$scope.limit = 3;
}
</script>
<div ng-controller="Ctrl">
Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
<p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
<p>Output letters: {{ letters | limitTo:letterLimit }}</p>
Limit {{numbers}} to: <input type="integer" ng-model="limit">
<p>Output: {{ numbers | limitTo:limit }}</p>
</div>
</doc:source>
<doc:scenario>
it('should limit the number array to first three items', function() {
expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3');
expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3');
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]');
expect(binding('letters | limitTo:letterLimit')).toEqual('abc');
it('should limit the numer array to first three items', function() {
expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3');
expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]');
});
it('should update the output when -3 is entered', function() {
input('numLimit').enter(-3);
input('letterLimit').enter(-3);
expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]');
expect(binding('letters | limitTo:letterLimit')).toEqual('ghi');
input('limit').enter(-3);
expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]');
});
it('should not exceed the maximum size of input array', function() {
input('numLimit').enter(100);
input('letterLimit').enter(100);
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]');
expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi');
input('limit').enter(100);
expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]');
});
</doc:scenario>
</doc:example>
*/
function limitToFilter(){
return function(input, limit) {
if (!isArray(input) && !isString(input)) return input;
return function(array, limit) {
if (!(array instanceof Array)) return array;
limit = int(limit);
if (isString(input)) {
//NaN check on limit
if (limit) {
return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
} else {
return "";
}
}
var out = [],
i, n;
// check that array is iterable
if (!array || !(array instanceof Array))
return out;
// if abs(limit) exceeds maximum length, trim it
if (limit > input.length)
limit = input.length;
else if (limit < -input.length)
limit = -input.length;
if (limit > array.length)
limit = array.length;
else if (limit < -array.length)
limit = -array.length;
if (limit > 0) {
i = 0;
n = limit;
} else {
i = input.length + limit;
n = input.length;
i = array.length + limit;
n = array.length;
}
for (; i<n; i++) {
out.push(input[i]);
out.push(array[i]);
}
return out;
+1 -1
View File
@@ -89,7 +89,7 @@
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
return function(array, sortPredicate, reverseOrder) {
if (!isArray(array)) return array;
if (!(array instanceof Array)) return array;
if (!sortPredicate) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
sortPredicate = map(sortPredicate, function(predicate){
+20 -82
View File
@@ -29,43 +29,6 @@ function parseHeaders(headers) {
}
var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/;
/**
* Parse a request and location URL and determine whether this is a same-domain request.
*
* @param {string} requestUrl The url of the request.
* @param {string} locationUrl The current browser location url.
* @returns {boolean} Whether the request is for the same domain.
*/
function isSameDomain(requestUrl, locationUrl) {
var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl);
// if requestUrl is relative, the regex does not match.
if (match == null) return true;
var domain1 = {
protocol: match[2],
host: match[4],
port: int(match[6]) || DEFAULT_PORTS[match[2]] || null,
// IE8 sets unmatched groups to '' instead of undefined.
relativeProtocol: match[2] === undefined || match[2] === ''
};
match = URL_MATCH.exec(locationUrl);
var domain2 = {
protocol: match[1],
host: match[3],
port: int(match[5]) || DEFAULT_PORTS[match[1]] || null
};
return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) &&
domain1.host == domain2.host &&
(domain1.port == domain2.port || (domain1.relativeProtocol &&
domain2.port == DEFAULT_PORTS[domain2.protocol]));
}
/**
* Returns a function that provides access to parsed headers.
*
@@ -125,7 +88,7 @@ function $HttpProvider() {
JSON_END = /[\}\]]\s*$/,
PROTECTION_PREFIX = /^\)\]\}',?\n/;
var defaults = this.defaults = {
var $config = this.defaults = {
// transform incoming response data
transformResponse: [function(data) {
if (isString(data)) {
@@ -145,14 +108,12 @@ function $HttpProvider() {
// default headers
headers: {
common: {
'Accept': 'application/json, text/plain, */*'
'Accept': 'application/json, text/plain, */*',
'X-Requested-With': 'XMLHttpRequest'
},
post: {'Content-Type': 'application/json;charset=utf-8'},
put: {'Content-Type': 'application/json;charset=utf-8'}
},
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN'
}
};
var providerResponseInterceptors = this.responseInterceptors = [];
@@ -254,6 +215,7 @@ function $HttpProvider() {
*
* - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
* - `Accept: application/json, text/plain, * / *`
* - `X-Requested-With: XMLHttpRequest`
* - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests)
* - `Content-Type: application/json`
* - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests)
@@ -386,10 +348,9 @@ function $HttpProvider() {
* {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which
* an unauthorized site can gain your user's private data. Angular provides following mechanism
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
* (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
* JavaScript that runs on your domain could read the cookie, your server can be assured that
* the XHR came from JavaScript running on your domain. The header will not be set for
* cross-domain requests.
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
* runs on your domain could read the cookie, your server can be assured that the XHR came from
* JavaScript running on your domain.
*
* To take advantage of this, your server needs to set a token in a JavaScript readable session
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
@@ -399,9 +360,6 @@ function $HttpProvider() {
* up its own tokens). We recommend that the token is a digest of your site's authentication
* cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
*
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
* properties of either $httpProvider.defaults, or the per-request config object.
*
*
* @param {object} config Object describing the request to be made and how it should be
* processed. The object has following properties:
@@ -412,8 +370,6 @@ function $HttpProvider() {
* `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified.
* - **data** `{string|Object}` Data to be sent as the request message data.
* - **headers** `{Object}` Map of strings representing HTTP headers to send to the server.
* - **xsrfHeaderName** `{string}` Name of HTTP header to populate with the XSRF token.
* - **xsrfCookieName** `{string}` Name of cookie containing the XSRF token.
* - **transformRequest** `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
@@ -428,8 +384,6 @@ function $HttpProvider() {
* - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
* requests with credentials} for more information.
* - **responseType** - `{string}` - see {@link
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
*
* @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
* standard `then` method and two http specific methods: `success` and `error`. The `then`
@@ -522,17 +476,10 @@ function $HttpProvider() {
function $http(config) {
config.method = uppercase(config.method);
var xsrfHeader = {},
xsrfCookieName = config.xsrfCookieName || defaults.xsrfCookieName,
xsrfHeaderName = config.xsrfHeaderName || defaults.xsrfHeaderName,
xsrfToken = isSameDomain(config.url, $browser.url()) ?
$browser.cookies()[xsrfCookieName] : undefined;
xsrfHeader[xsrfHeaderName] = xsrfToken;
var reqTransformFn = config.transformRequest || defaults.transformRequest,
respTransformFn = config.transformResponse || defaults.transformResponse,
defHeaders = defaults.headers,
reqHeaders = extend(xsrfHeader,
var reqTransformFn = config.transformRequest || $config.transformRequest,
respTransformFn = config.transformResponse || $config.transformResponse,
defHeaders = $config.headers,
reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
promise;
@@ -542,10 +489,6 @@ function $HttpProvider() {
delete reqHeaders['Content-Type'];
}
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
config.withCredentials = defaults.withCredentials;
}
// send request
promise = sendReq(config, reqData, reqHeaders);
@@ -677,11 +620,11 @@ function $HttpProvider() {
*
* @description
* Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
* default headers, withCredentials as well as request and response transformations.
* default headers as well as request and response transformations.
*
* See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
*/
$http.defaults = defaults;
$http.defaults = $config;
return $http;
@@ -716,7 +659,7 @@ function $HttpProvider() {
* Makes the request
*
* !!! ACCESSES CLOSURE VARS:
* $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
* $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests
*/
function sendReq(config, reqData, reqHeaders) {
var deferred = $q.defer(),
@@ -757,7 +700,7 @@ function $HttpProvider() {
// if we won't have the response in cache, send the request to the backend
if (!cachedResp) {
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
config.withCredentials, config.responseType);
config.withCredentials);
}
return promise;
@@ -812,15 +755,10 @@ function $HttpProvider() {
var parts = [];
forEachSorted(params, function(value, key) {
if (value == null || value == undefined) return;
if (!isArray(value)) value = [value];
forEach(value, function(v) {
if (isObject(v)) {
v = toJson(v);
}
parts.push(encodeUriQuery(key) + '=' +
encodeUriQuery(v));
});
if (isObject(value)) {
value = toJson(value);
}
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
});
return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
}
+3 -29
View File
@@ -32,7 +32,7 @@ function $HttpBackendProvider() {
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
// TODO(vojta): fix the signature
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
return function(method, url, post, callback, headers, timeout, withCredentials) {
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();
@@ -65,30 +65,8 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
// always async
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var responseHeaders = xhr.getAllResponseHeaders();
// TODO(vojta): remove once Firefox 21 gets released.
// begin: workaround to overcome Firefox CORS http response headers bug
// https://bugzilla.mozilla.org/show_bug.cgi?id=608735
// Firefox already patched in nightly. Should land in Firefox 21.
// CORS "simple response headers" http://www.w3.org/TR/cors/
var value,
simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
"Expires", "Last-Modified", "Pragma"];
if (!responseHeaders) {
responseHeaders = "";
forEach(simpleHeaders, function (header) {
var value = xhr.getResponseHeader(header);
if (value) {
responseHeaders += header + ": " + value + "\n";
}
});
}
// end of the workaround.
completeRequest(callback, status || xhr.status, xhr.response || xhr.responseText,
responseHeaders);
completeRequest(
callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders());
}
};
@@ -96,10 +74,6 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
xhr.withCredentials = true;
}
if (responseType) {
xhr.responseType = responseType;
}
xhr.send(post || '');
if (timeout > 0) {
+10 -16
View File
@@ -52,7 +52,7 @@ function $InterpolateProvider() {
};
this.$get = ['$parse', '$exceptionHandler', function($parse, $exceptionHandler) {
this.$get = ['$parse', function($parse) {
var startSymbolLength = startSymbol.length,
endSymbolLength = endSymbol.length;
@@ -124,24 +124,18 @@ function $InterpolateProvider() {
if (!mustHaveExpression || hasInterpolation) {
concat.length = length;
fn = function(context) {
try {
for(var i = 0, ii = length, part; i<ii; i++) {
if (typeof (part = parts[i]) == 'function') {
part = part(context);
if (part == null || part == undefined) {
part = '';
} else if (typeof part != 'string') {
part = toJson(part);
}
for(var i = 0, ii = length, part; i<ii; i++) {
if (typeof (part = parts[i]) == 'function') {
part = part(context);
if (part == null || part == undefined) {
part = '';
} else if (typeof part != 'string') {
part = toJson(part);
}
concat[i] = part;
}
return concat.join('');
}
catch(err) {
var newErr = new Error('Error while interpolating: ' + text + '\n' + err.toString());
$exceptionHandler(newErr);
concat[i] = part;
}
return concat.join('');
};
fn.exp = text;
fn.parts = parts;
+1 -45
View File
@@ -33,33 +33,7 @@
</example>
*/
/**
* @ngdoc object
* @name ng.$logProvider
* @description
* Use the `$logProvider` to configure how the application logs messages
*/
function $LogProvider(){
var debug = true,
self = this;
/**
* @ngdoc property
* @name ng.$logProvider#debugEnabled
* @methodOf ng.$logProvider
* @description
* @param {string=} flag enable or disable debug level messages
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.debugEnabled = function(flag) {
if (isDefined(flag)) {
debug = flag;
return this;
} else {
return debug;
}
};
this.$get = ['$window', function($window){
return {
/**
@@ -100,25 +74,7 @@ function $LogProvider(){
* @description
* Write an error message
*/
error: consoleLog('error'),
/**
* @ngdoc method
* @name ng.$log#debug
* @methodOf ng.$log
*
* @description
* Write a debug message
*/
debug: (function () {
var fn = consoleLog('debug');
return function() {
if (debug) {
fn.apply(self, arguments);
}
}
}())
error: consoleLog('error')
};
function formatError(arg) {
+19 -57
View File
@@ -20,8 +20,6 @@ var OPERATORS = {
'%':function(self, locals, a,b){return a(self, locals)%b(self, locals);},
'^':function(self, locals, a,b){return a(self, locals)^b(self, locals);},
'=':noop,
'===':function(self, locals, a, b){return a(self, locals)===b(self, locals);},
'!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);},
'==':function(self, locals, a,b){return a(self, locals)==b(self, locals);},
'!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);},
'<':function(self, locals, a,b){return a(self, locals)<b(self, locals);},
@@ -72,14 +70,9 @@ function lex(text, csp){
continue;
} else {
var ch2 = ch + peek(),
ch3 = ch2 + peek(2),
fn = OPERATORS[ch],
fn2 = OPERATORS[ch2],
fn3 = OPERATORS[ch3];
if (fn3) {
tokens.push({index:index, text:ch3, fn:fn3});
index += 3;
} else if (fn2) {
fn2 = OPERATORS[ch2];
if (fn2) {
tokens.push({index:index, text:ch2, fn:fn2});
index += 2;
} else if (fn) {
@@ -101,9 +94,8 @@ function lex(text, csp){
return chars.indexOf(lastCh) != -1;
}
function peek(i) {
var num = i || 1;
return index + num < text.length ? text.charAt(index + num) : false;
function peek() {
return index + 1 < text.length ? text.charAt(index + 1) : false;
}
function isNumber(ch) {
return '0' <= ch && ch <= '9';
@@ -303,8 +295,6 @@ function parser(text, json, $filter, csp){
if (tokens.length !== 0) {
throwError("is an unexpected token", tokens[0]);
}
value.literal = !!value.literal;
value.constant = !!value.constant;
return value;
///////////////////////////////////
@@ -352,19 +342,15 @@ function parser(text, json, $filter, csp){
}
function unaryFn(fn, right) {
return extend(function(self, locals) {
return function(self, locals) {
return fn(self, locals, right);
}, {
constant:right.constant
});
};
}
function binaryFn(left, fn, right) {
return extend(function(self, locals) {
return function(self, locals) {
return fn(self, locals, left, right);
}, {
constant:left.constant && right.constant
});
};
}
function statements() {
@@ -470,7 +456,7 @@ function parser(text, json, $filter, csp){
function equality() {
var left = relational();
var token;
if ((token = expect('==','!=','===','!=='))) {
if ((token = expect('==','!='))) {
left = binaryFn(left, token.fn, equality());
}
return left;
@@ -532,9 +518,6 @@ function parser(text, json, $filter, csp){
if (!primary) {
throwError("not a primary expression", token);
}
if (token.json) {
primary.constant = primary.literal = true;
}
}
var next, context;
@@ -623,32 +606,23 @@ function parser(text, json, $filter, csp){
// This is used with json array declaration
function arrayDeclaration () {
var elementFns = [];
var allConstant = true;
if (peekToken().text != ']') {
do {
var elementFn = expression();
elementFns.push(elementFn);
if (!elementFn.constant) {
allConstant = false;
}
elementFns.push(expression());
} while (expect(','));
}
consume(']');
return extend(function(self, locals){
return function(self, locals){
var array = [];
for ( var i = 0; i < elementFns.length; i++) {
array.push(elementFns[i](self, locals));
}
return array;
}, {
literal:true,
constant:allConstant
});
};
}
function object () {
var keyValues = [];
var allConstant = true;
if (peekToken().text != '}') {
do {
var token = expect(),
@@ -656,13 +630,10 @@ function parser(text, json, $filter, csp){
consume(":");
var value = expression();
keyValues.push({key:key, value:value});
if (!value.constant) {
allConstant = false;
}
} while (expect(','));
}
consume('}');
return extend(function(self, locals){
return function(self, locals){
var object = {};
for ( var i = 0; i < keyValues.length; i++) {
var keyValue = keyValues[i];
@@ -670,10 +641,7 @@ function parser(text, json, $filter, csp){
object[keyValue.key] = value;
}
return object;
}, {
literal:true,
constant:allConstant
});
};
}
}
@@ -872,18 +840,12 @@ function getterFn(path, csp) {
* @param {string} expression String expression to compile.
* @returns {function(context, locals)} a function which represents the compiled expression:
*
* * `context` `{object}` an object against which any expressions embedded in the strings
* are evaluated against (tipically a scope object).
* * `locals` `{object=}` local variables context object, useful for overriding values in
* `context`.
* * `context`: an object against which any expressions embedded in the strings are evaluated
* against (Topically a scope object).
* * `locals`: local variables context object, useful for overriding values in `context`.
*
* The returned function also has the following properties:
* * `literal` `{boolean}` whether the expression's top-level node is a JavaScript
* literal.
* * `constant` `{boolean}` whether the expression is made entirely of JavaScript
* constant literals.
* * `assign` `{?function(context, value)}` if the expression is assignable, this will be
* set to a function to change its value on the given context.
* The return function also has an `assign` property, if the expression is assignable, which
* allows one to set values to expressions.
*
*/
function $ParseProvider() {
+9 -9
View File
@@ -12,7 +12,7 @@
* interface for interacting with an object that represents the result of an action that is
* performed asynchronously, and may or may not be finished at any given point in time.
*
* From the perspective of dealing with error handling, deferred and promise APIs are to
* From the perspective of dealing with error handling, deferred and promise apis are to
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
*
* <pre>
@@ -47,7 +47,7 @@
*
* At first it might not be obvious why this extra complexity is worth the trouble. The payoff
* comes in the way of
* [guarantees that promise and deferred APIs make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
* [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md).
*
* Additionally the promise api allows for composition that is very hard to do with the
* traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
@@ -59,7 +59,7 @@
*
* A new instance of deferred is constructed by calling `$q.defer()`.
*
* The purpose of the deferred object is to expose the associated Promise instance as well as APIs
* The purpose of the deferred object is to expose the associated Promise instance as well as apis
* that can be used for signaling the successful or unsuccessful completion of the task.
*
* **Methods**
@@ -102,7 +102,7 @@
* return result + 1;
* });
*
* // promiseB will be resolved immediately after promiseA is resolved and its value will be
* // promiseB will be resolved immediately after promiseA is resolved and it's value will be
* // the result of promiseA incremented by 1
* </pre>
*
@@ -127,7 +127,7 @@
* # Testing
*
* <pre>
* it('should simulate promise', inject(function($q, $rootScope) {
* it('should simulate promise', inject(function($q, $rootSCope) {
* var deferred = $q.defer();
* var promise = deferred.promise;
* var resolvedValue;
@@ -136,7 +136,7 @@
* expect(resolvedValue).toBeUndefined();
*
* // Simulate resolving of promise
* deferred.resolve(123);
* defered.resolve(123);
* // Note that the 'then' function does not get called synchronously.
* // This is because we want the promise API to always be async, whether or not
* // it got called synchronously or asynchronously.
@@ -312,12 +312,12 @@ function qFactory(nextTick, exceptionHandler) {
* @methodOf ng.$q
* @description
* Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
* This is useful when you are dealing with an object that might or might not be a promise, or if
* This is useful when you are dealing with on object that might or might not be a promise, or if
* the promise comes from a source that can't be trusted.
*
* @param {*} value Value or a promise
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
* each value corresponding to the promise at the same index in the `promises` array. If any of
* each value coresponding to the promise at the same index in the `promises` array. If any of
* the promises is resolved with a rejection, this resulting promise will be resolved with the
* same rejection.
*/
@@ -379,7 +379,7 @@ function qFactory(nextTick, exceptionHandler) {
*
* @param {Array.<Promise>} promises An array of promises.
* @returns {Promise} Returns a single promise that will be resolved with an array of values,
* each value corresponding to the promise at the same index in the `promises` array. If any of
* each value coresponding to the promise at the same index in the `promises` array. If any of
* the promises is resolved with a rejection, this resulting promise will be resolved with the
* same rejection.
*/
+16 -25
View File
@@ -137,7 +137,6 @@ function $RootScopeProvider(){
this.$$destroyed = false;
this.$$asyncQueue = [];
this.$$listeners = {};
this.$$isolateBindings = {};
}
/**
@@ -197,6 +196,7 @@ function $RootScopeProvider(){
child['this'] = child;
child.$$listeners = {};
child.$parent = this;
child.$$asyncQueue = [];
child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
child.$$prevSibling = this.$$childTail;
if (this.$$childHead) {
@@ -300,14 +300,6 @@ function $RootScopeProvider(){
watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
}
if (typeof watchExp == 'string' && get.constant) {
var originalFn = watcher.fn;
watcher.fn = function(newVal, oldVal, scope) {
originalFn.call(this, newVal, oldVal, scope);
arrayRemove(array, watcher);
};
}
if (!array) {
array = scope.$$watchers = [];
}
@@ -371,7 +363,7 @@ function $RootScopeProvider(){
$digest: function() {
var watch, value, last,
watchers,
asyncQueue = this.$$asyncQueue,
asyncQueue,
length,
dirty, ttl = TTL,
next, current, target = this,
@@ -380,19 +372,18 @@ function $RootScopeProvider(){
beginPhase('$digest');
do { // "while dirty" loop
do {
dirty = false;
current = target;
while(asyncQueue.length) {
try {
current.$eval(asyncQueue.shift());
} catch (e) {
$exceptionHandler(e);
do {
asyncQueue = current.$$asyncQueue;
while(asyncQueue.length) {
try {
current.$eval(asyncQueue.shift());
} catch (e) {
$exceptionHandler(e);
}
}
}
do { // "traverse the scopes" loop
if ((watchers = current.$$watchers)) {
// process our watches
length = watchers.length;
@@ -627,6 +618,10 @@ function $RootScopeProvider(){
* Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of
* event life cycle.
*
* @param {string} name Event name to listen on.
* @param {function(event)} listener Function to call when the event is emitted.
* @returns {function()} Returns a deregistration function for this listener.
*
* The event listener function format is: `function(event, args...)`. The `event` object
* passed into the listener has the following attributes:
*
@@ -637,10 +632,6 @@ function $RootScopeProvider(){
* propagation (available only for events that were `$emit`-ed).
* - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true.
* - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
*
* @param {string} name Event name to listen on.
* @param {function(event, args...)} listener Function to call when the event is emitted.
* @returns {function()} Returns a deregistration function for this listener.
*/
$on: function(name, listener) {
var namedListeners = this.$$listeners[name];
@@ -818,7 +809,7 @@ function $RootScopeProvider(){
/**
* function used as an initial value for watchers.
* because it's unique we can easily tell it apart from other values
* because it's uniqueue we can easily tell it apart from other values
*/
function initWatchVal() {}
}];
+14 -51
View File
@@ -23,18 +23,9 @@ function $RouteProvider(){
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon (`:name`). All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a star (`*name`). All characters are
* eagerly stored in `$routeParams` under the given `name` when the route matches.
*
* For example, routes like `/color/:color/largecode/*largecode/edit` will match
* `/color/brown/largecode/code/with/slashs/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashs`.
*
* `path` can contain named groups starting with a colon (`:name`). All characters up to the
* next slash are matched and stored in `$routeParams` under the given `name` when the route
* matches.
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
@@ -44,24 +35,12 @@ function $RouteProvider(){
* - `controller` `{(string|function()=}` Controller fn that should be associated with newly
* created scope or the name of a {@link angular.Module#controller registered controller}
* if passed as a string.
* - `template` `{string=|function()=}` html template as a string or function that returns
* an html template as a string which should be used by {@link ng.directive:ngView ngView} or
* - `template` `{string=}` html template as a string that should be used by
* {@link ng.directive:ngView ngView} or
* {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
* template that should be used by {@link ng.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* this property takes precedence over `templateUrl`.
* - `templateUrl` `{string=}` path to an html template that should be used by
* {@link ng.directive:ngView ngView}.
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
* be injected into the controller. If any of these dependencies are promises, they will be
* resolved and converted to a value before the controller is instantiated and the
@@ -350,12 +329,12 @@ function $RouteProvider(){
// regex only once and then reuse it
// Escape regexp special characters.
when = '^' + when.replace(/[-\/\\^$:*+?.()|[\]{}]/g, "\\$&") + '$';
when = '^' + when.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + '$';
var regex = '',
params = [],
dst = {};
var re = /\\([:*])(\w+)/g,
var re = /:(\w+)/g,
paramMatch,
lastMatchedIndex = 0;
@@ -363,15 +342,8 @@ function $RouteProvider(){
// Find each :param in `when` and replace it with a capturing group.
// Append all other sections of when unchanged.
regex += when.slice(lastMatchedIndex, paramMatch.index);
switch(paramMatch[1]) {
case ':':
regex += '([^\\/]*)';
break;
case '*':
regex += '(.*)';
break;
}
params.push(paramMatch[2]);
regex += '([^\\/]*)';
params.push(paramMatch[1]);
lastMatchedIndex = re.lastIndex;
}
// Append trailing path part.
@@ -423,18 +395,9 @@ function $RouteProvider(){
values.push(isString(value) ? $injector.get(value) : $injector.invoke(value));
});
if (isDefined(template = next.template)) {
if (isFunction(template)) {
template = template(next.params);
}
} else if (isDefined(template = next.templateUrl)) {
if (isFunction(template)) {
template = template(next.params);
}
if (isDefined(template)) {
next.loadedTemplateUrl = template;
template = $http.get(template, {cache: $templateCache}).
then(function(response) { return response.data; });
}
template = $http.get(template, {cache: $templateCache}).
then(function(response) { return response.data; });
}
if (isDefined(template)) {
keys.push('$template');
+6 -7
View File
@@ -5,7 +5,6 @@
*
* @name ng.$sniffer
* @requires $window
* @requires $document
*
* @property {boolean} history Does the browser support html5 history api ?
* @property {boolean} hashchange Does the browser support hashchange event ?
@@ -14,10 +13,9 @@
* This is very simple implementation of testing browser's features.
*/
function $SnifferProvider() {
this.$get = ['$window', '$document', function($window, $document) {
this.$get = ['$window', function($window) {
var eventSupport = {},
android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]),
document = $document[0];
android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]);
return {
// Android has history.pushState, but it does not update location correctly
@@ -27,7 +25,7 @@ function $SnifferProvider() {
history: !!($window.history && $window.history.pushState && !(android < 4)),
hashchange: 'onhashchange' in $window &&
// IE8 compatible mode lies
(!document.documentMode || document.documentMode > 7),
(!$window.document.documentMode || $window.document.documentMode > 7),
hasEvent: function(event) {
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
// it. In particular the event is not fired when backspace or delete key are pressed or
@@ -35,13 +33,14 @@ function $SnifferProvider() {
if (event == 'input' && msie == 9) return false;
if (isUndefined(eventSupport[event])) {
var divElm = document.createElement('div');
var divElm = $window.document.createElement('div');
eventSupport[event] = 'on' + event in divElm;
}
return eventSupport[event];
},
csp: document.securityPolicy ? document.securityPolicy.isActive : false
// TODO(i): currently there is no way to feature detect CSP without triggering alerts
csp: false
};
}];
}
-12
View File
@@ -19,18 +19,6 @@ angular.module('ngCookies', ['ng']).
* this object, new cookies are created/deleted at the end of current $eval.
*
* @example
<doc:example>
<doc:source>
<script>
function ExampleController($cookies) {
// Retrieving a cookie
var favoriteCookie = $cookies.myFavorite;
// Setting a cookie
$cookies.myFavorite = 'oatmeal';
}
</script>
</doc:source>
</doc:example>
*/
factory('$cookies', ['$rootScope', '$browser', function ($rootScope, $browser) {
var cookies = {},
+36 -67
View File
@@ -456,7 +456,6 @@ angular.mock.$LogProvider = function() {
* newYearInBratislava.getDate() => 1;
* newYearInBratislava.getHours() => 0;
* newYearInBratislava.getMinutes() => 0;
* newYearInBratislava.getSeconds() => 0;
* </pre>
*
*/
@@ -513,10 +512,6 @@ angular.mock.$LogProvider = function() {
return self.date.getSeconds();
};
self.getMilliseconds = function() {
return self.date.getMilliseconds();
};
self.getTimezoneOffset = function() {
return offset * 60;
};
@@ -567,7 +562,7 @@ angular.mock.$LogProvider = function() {
}
//hide all methods not implemented in this mock that the Date prototype exposes
var unimplementedMethods = ['getUTCDay',
var unimplementedMethods = ['getMilliseconds', 'getUTCDay',
'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
@@ -1357,49 +1352,17 @@ function MockXhr() {
* @description
*
* This service is just a simple decorator for {@link ng.$timeout $timeout} service
* that adds a "flush" and "verifyNoPendingTasks" methods.
*/
* that adds a "flush" method.
*/
angular.mock.$TimeoutDecorator = function($delegate, $browser) {
/**
* @ngdoc method
* @name ngMock.$timeout#flush
* @methodOf ngMock.$timeout
* @description
*
* Flushes the queue of pending tasks.
*/
$delegate.flush = function() {
$browser.defer.flush();
};
/**
* @ngdoc method
* @name ngMock.$timeout#verifyNoPendingTasks
* @methodOf ngMock.$timeout
* @description
*
* Verifies that there are no pending tasks that need to be flushed.
*/
$delegate.verifyNoPendingTasks = function() {
if ($browser.deferredFns.length) {
throw Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' +
formatPendingTasksAsString($browser.deferredFns));
}
};
function formatPendingTasksAsString(tasks) {
var result = [];
angular.forEach(tasks, function(task) {
result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}');
});
return result.join(', ');
}
return $delegate;
};
/**
* @ngdoc method
* @name ngMock.$timeout#flush
* @methodOf ngMock.$timeout
* @description
*
* Flushes the queue of pending tasks.
*/
/**
*
@@ -1425,9 +1388,15 @@ angular.module('ngMock', ['ng']).provider({
$httpBackend: angular.mock.$HttpBackendProvider,
$rootElement: angular.mock.$RootElementProvider
}).config(function($provide) {
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
$provide.decorator('$timeout', function($delegate, $browser) {
$delegate.flush = function() {
$browser.defer.flush();
};
return $delegate;
});
});
/**
* @ngdoc overview
* @name ngMockE2E
@@ -1641,20 +1610,14 @@ window.jstestdriver && (function(window) {
})(window);
(window.jasmine || window.mocha) && (function(window) {
var currentSpec = null;
beforeEach(function() {
currentSpec = this;
});
window.jasmine && (function(window) {
afterEach(function() {
var injector = currentSpec.$injector;
var spec = getCurrentSpec();
var injector = spec.$injector;
currentSpec.$injector = null;
currentSpec.$modules = null;
currentSpec = null;
spec.$injector = null;
spec.$modules = null;
if (injector) {
injector.get('$rootElement').unbind();
@@ -1676,8 +1639,13 @@ window.jstestdriver && (function(window) {
angular.callbacks.counter = 0;
});
function getCurrentSpec() {
return jasmine.getEnv().currentSpec;
}
function isSpecRunning() {
return currentSpec && currentSpec.queue.running;
var spec = getCurrentSpec();
return spec && spec.queue.running;
}
/**
@@ -1702,10 +1670,11 @@ window.jstestdriver && (function(window) {
return isSpecRunning() ? workFn() : workFn;
/////////////////////
function workFn() {
if (currentSpec.$injector) {
var spec = getCurrentSpec();
if (spec.$injector) {
throw Error('Injector already created, can not register a module!');
} else {
var modules = currentSpec.$modules || (currentSpec.$modules = []);
var modules = spec.$modules || (spec.$modules = []);
angular.forEach(moduleFns, function(module) {
modules.push(module);
});
@@ -1772,13 +1741,13 @@ window.jstestdriver && (function(window) {
return isSpecRunning() ? workFn() : workFn;
/////////////////////
function workFn() {
var modules = currentSpec.$modules || [];
var spec = getCurrentSpec();
var modules = spec.$modules || [];
modules.unshift('ngMock');
modules.unshift('ng');
var injector = currentSpec.$injector;
var injector = spec.$injector;
if (!injector) {
injector = currentSpec.$injector = angular.injector(modules);
injector = spec.$injector = angular.injector(modules);
}
for(var i = 0, ii = blockFns.length; i < ii; i++) {
try {
+42 -104
View File
@@ -19,13 +19,12 @@
* the need to interact with the low level {@link ng.$http $http} service.
*
* @param {string} url A parameterized URL template with parameters prefixed by `:` as in
* `/user/:username`. If you are using a URL with a port number (e.g.
* `/user/:username`. If you are using a URL with a port number (e.g.
* `http://example.com:8080/api`), you'll need to escape the colon character before the port
* number, like this: `$resource('http://example.com\\:8080/api')`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods. If any of the parameter value is a function, it will be executed every time
* when a param value needs to be obtained for a request (unless the param was overriden).
* `actions` methods.
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
@@ -37,40 +36,21 @@
* the data object (useful for non-GET operations).
*
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
* default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#Parameters $http.config}:
* default set of resource actions. The declaration should be created in the following format:
*
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
* action2: {method:?, params:?, isArray:?, headers:?, ...},
* {action1: {method:?, params:?, isArray:?},
* action2: {method:?, params:?, isArray:?},
* ...}
*
* Where:
*
* - **`action`** {string} The name of action. This name becomes the name of the method on your
* - `action` {string} The name of action. This name becomes the name of the method on your
* resource object.
* - **`method`** {string} HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
* and `JSONP`.
* - **`params`** {Object=} Optional set of pre-bound parameters for this action. If any of the
* parameter value is a function, it will be executed every time when a param value needs to be
* obtained for a request (unless the param was overriden).
* - **`isArray`** {boolean=} If true then the returned object for this action is an array, see
* - `method` {string} HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
* and `JSONP`
* - `params` {object=} Optional set of pre-bound parameters for this action.
* - isArray {boolean=} If true then the returned object for this action is an array, see
* `returns` section.
* - **`transformRequest`** `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* - **`transformResponse`** `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* - **`cache`** `{boolean|Cache}` If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
* caching.
* - **`timeout`** `{number}` timeout in milliseconds.
* - **`withCredentials`** - `{boolean}` - whether to to set the `withCredentials` flag on the
* XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5
* requests with credentials} for more information.
* - **`responseType`** - `{string}` - see {@link
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
@@ -83,9 +63,9 @@
*
* Calling these methods invoke an {@link ng.$http} with the specified http method,
* destination and parameters. When the data is returned from the server then the object is an
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
* read, update, delete) on server-side data like this:
* instance of the resource class `save`, `remove` and `delete` actions are available on it as
* methods with the `$` prefix. This allows you to easily perform CRUD operations (create, read,
* update, delete) on server-side data like this:
* <pre>
var User = $resource('/user/:userId', {userId:'@id'});
var user = User.get({userId:123}, function() {
@@ -110,24 +90,6 @@
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
*
* The Resource instances and collection have these additional properties:
*
* - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying
* {@link ng.$http $http} call.
*
* The success callback for the `$then` method will be resolved if the underlying `$http` requests
* succeeds.
*
* The success callback is called with a single object which is the {@link ng.$http http response}
* object extended with a new property `resource`. This `resource` property is a reference to the
* result of the resource action — resource object or array of resources.
*
* The error callback is called with the {@link ng.$http http response} object when an http
* error occurs.
*
* - `$resolved`: true if the promise has been resolved (either with success or rejection);
* Knowing if the Resource has been resolved is useful in data-binding.
*
* @example
*
* # Credit card resource
@@ -170,7 +132,7 @@
* The object returned from this function execution is a resource "class" which has "static" method
* for each action in the definition.
*
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and `headers`.
* Calling these methods invoke `$http` on the `url` template with the given `method` and `params`.
* When the data is returned from the server then the object is an instance of the resource type and
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
* operations (create, read, update, delete) on server-side data.
@@ -183,9 +145,9 @@
});
</pre>
*
* It's worth noting that the success callback for `get`, `query` and other method gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
* It's worth noting that the success callback for `get`, `query` and other method gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
*
<pre>
var User = $resource('/user/:userId', {userId:'@id'});
@@ -308,7 +270,7 @@ angular.module('ngResource', ['ng']).
this.defaults = defaults || {};
var urlParams = this.urlParams = {};
forEach(template.split(/\W/), function(param){
if (param && (new RegExp("(^|[^\\\\]):" + param + "\\W").test(template))) {
if (param && template.match(new RegExp("[^\\\\]:" + param + "\\W"))) {
urlParams[param] = true;
}
});
@@ -316,7 +278,7 @@ angular.module('ngResource', ['ng']).
}
Route.prototype = {
setUrlParams: function(config, params) {
url: function(params) {
var self = this,
url = this.template,
val,
@@ -339,17 +301,16 @@ angular.module('ngResource', ['ng']).
});
}
});
// set the url
config.url = url.replace(/\/?#$/, '').replace(/\/*$/, '');
// set params - delegate param encoding to $http
url = url.replace(/\/?#$/, '');
var query = [];
forEach(params, function(value, key){
if (!self.urlParams[key]) {
config.params = config.params || {};
config.params[key] = value;
query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value));
}
});
query.sort();
url = url.replace(/\/*$/, '');
return url + (query.length ? '?' + query.join('&') : '');
}
};
@@ -363,7 +324,6 @@ angular.module('ngResource', ['ng']).
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key){
if (isFunction(value)) { value = value(); }
ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value;
});
return ids;
@@ -381,8 +341,6 @@ angular.module('ngResource', ['ng']).
var data;
var success = noop;
var error = null;
var promise;
switch(arguments.length) {
case 4:
error = a4;
@@ -418,45 +376,25 @@ angular.module('ngResource', ['ng']).
}
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
var httpConfig = {},
promise;
$http({
method: action.method,
url: route.url(extend({}, extractParams(data, action.params || {}), params)),
data: data
}).then(function(response) {
var data = response.data;
forEach(action, function(value, key) {
if (key != 'params' && key != 'isArray' ) {
httpConfig[key] = copy(value);
}
});
httpConfig.data = data;
route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params));
function markResolved() { value.$resolved = true; }
promise = $http(httpConfig);
value.$resolved = false;
promise.then(markResolved, markResolved);
value.$then = promise.then(function(response) {
var data = response.data;
var then = value.$then, resolved = value.$resolved;
if (data) {
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
copy(data, value);
value.$then = then;
value.$resolved = resolved;
if (data) {
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
value.push(new Resource(item));
});
} else {
copy(data, value);
}
}
}
(success||noop)(value, response.headers);
response.resource = value;
return response;
}, error).then;
(success||noop)(value, response.headers);
}, error);
return value;
};
+2 -23
View File
@@ -8,7 +8,6 @@
* plain email address links.
*
* @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
* @returns {string} Html-linkified text.
*
* @usage
@@ -25,7 +24,6 @@
'mailto:us@somewhere.org,\n'+
'another@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.';
$scope.snippetWithTarget = 'http://angularjs.org/';
}
</script>
<div ng-controller="Ctrl">
@@ -45,15 +43,6 @@
<div ng-bind-html="snippet | linky"></div>
</td>
</tr>
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
</td>
</tr>
<tr id="escaped-html">
<td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
@@ -86,11 +75,6 @@
toBe('new <a href="http://link">http://link</a>.');
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
});
it('should work with the target property', function() {
expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
});
</doc:scenario>
</doc:example>
*/
@@ -98,7 +82,7 @@ angular.module('ngSanitize').filter('linky', function() {
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
MAILTO_REGEXP = /^mailto:/;
return function(text, target) {
return function(text) {
if (!text) return text;
var match;
var raw = text;
@@ -107,10 +91,6 @@ angular.module('ngSanitize').filter('linky', function() {
var writer = htmlSanitizeWriter(html);
var url;
var i;
var properties = {};
if (angular.isDefined(target)) {
properties.target = target;
}
while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence
url = match[0];
@@ -118,8 +98,7 @@ angular.module('ngSanitize').filter('linky', function() {
if (match[2] == match[3]) url = 'mailto:' + url;
i = match.index;
writer.chars(raw.substr(0, i));
properties.href = url;
writer.start('a', properties);
writer.start('a', {href:url});
writer.chars(match[0].replace(MAILTO_REGEXP, ''));
writer.end('a');
raw = raw.substring(i + match[0].length);
+1 -1
View File
@@ -123,7 +123,7 @@ var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:
BEGING_END_TAGE_REGEXP = /^<\s*\//,
COMMENT_REGEXP = /<!--(.*?)-->/g,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/,
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
-27
View File
@@ -298,8 +298,6 @@ angular.scenario.dsl('select', function() {
option = select.find('option:contains("' + value + '")');
if (option.length) {
select.val(option.val());
} else {
return done("option '" + value + "' not found");
}
}
select.trigger('change');
@@ -327,7 +325,6 @@ angular.scenario.dsl('select', function() {
* Usage:
* element(selector, label).count() get the number of elements that match selector
* element(selector, label).click() clicks an element
* element(selector, label).mouseover() mouseover an element
* element(selector, label).query(fn) executes fn(selectedElements, done)
* element(selector, label).{method}() gets the value (as defined by jQuery, ex. val)
* element(selector, label).{method}(value) sets the value (as defined by jQuery, ex. val)
@@ -368,30 +365,6 @@ angular.scenario.dsl('element', function() {
});
};
chain.dblclick = function() {
return this.addFutureAction("element '" + this.label + "' dblclick", function($window, $document, done) {
var elements = $document.elements();
var href = elements.attr('href');
var eventProcessDefault = elements.trigger('dblclick')[0];
if (href && elements[0].nodeName.toUpperCase() === 'A' && eventProcessDefault) {
this.application.navigateTo(href, function() {
done();
}, done);
} else {
done();
}
});
};
chain.mouseover = function() {
return this.addFutureAction("element '" + this.label + "' mouseover", function($window, $document, done) {
var elements = $document.elements();
elements.trigger('mouseover');
done();
});
};
chain.query = function(fn) {
return this.addFutureAction('element ' + this.label + ' custom query', function($window, $document, done) {
fn.call(this, $document.elements(), done);
@@ -0,0 +1,177 @@
'use strict';
/**
* JSTestDriver adapter for angular scenario tests
*
* Example of jsTestDriver.conf for running scenario tests with JSTD:
<pre>
server: http://localhost:9877
load:
- lib/angular-scenario.js
- lib/jstd-scenario-adapter-config.js
- lib/jstd-scenario-adapter.js
# your test files go here #
proxy:
- {matcher: "/your-prefix/*", server: "http://localhost:8000/"}
</pre>
*
* For more information on how to configure jstd proxy, see {@link http://code.google.com/p/js-test-driver/wiki/Proxy}
* Note the order of files - it's important !
*
* Example of jstd-scenario-adapter-config.js
<pre>
var jstdScenarioAdapter = {
relativeUrlPrefix: '/your-prefix/'
};
</pre>
*
* Whenever you use <code>browser().navigateTo('relativeUrl')</code> in your scenario test, the relativeUrlPrefix will be prepended.
* You have to configure this to work together with JSTD proxy.
*
* Let's assume you are using the above configuration (jsTestDriver.conf and jstd-scenario-adapter-config.js):
* Now, when you call <code>browser().navigateTo('index.html')</code> in your scenario test, the browser will open /your-prefix/index.html.
* That matches the proxy, so JSTD will proxy this request to http://localhost:8000/index.html.
*/
/**
* Custom type of test case
*
* @const
* @see jstestdriver.TestCaseInfo
*/
var SCENARIO_TYPE = 'scenario';
/**
* Plugin for JSTestDriver
* Connection point between scenario's jstd output and jstestdriver.
*
* @see jstestdriver.PluginRegistrar
*/
function JstdPlugin() {
var nop = function() {};
this.reportResult = nop;
this.reportEnd = nop;
this.runScenario = nop;
this.name = 'Angular Scenario Adapter';
/**
* Called for each JSTD TestCase
*
* Handles only SCENARIO_TYPE test cases. There should be only one fake TestCase.
* Runs all scenario tests (under one fake TestCase) and report all results to JSTD.
*
* @param {jstestdriver.TestRunConfiguration} configuration
* @param {Function} onTestDone
* @param {Function} onAllTestsComplete
* @returns {boolean} True if this type of test is handled by this plugin, false otherwise
*/
this.runTestConfiguration = function(configuration, onTestDone, onAllTestsComplete) {
if (configuration.getTestCaseInfo().getType() != SCENARIO_TYPE) return false;
this.reportResult = onTestDone;
this.reportEnd = onAllTestsComplete;
this.runScenario();
return true;
};
this.getTestRunsConfigurationFor = function(testCaseInfos, expressions, testRunsConfiguration) {
testRunsConfiguration.push(
new jstestdriver.TestRunConfiguration(
new jstestdriver.TestCaseInfo(
'Angular Scenario Tests', function() {}, SCENARIO_TYPE), []));
return true;
};
}
/**
* Singleton instance of the plugin
* Accessed using closure by:
* - jstd output (reports to this plugin)
* - initScenarioAdapter (register the plugin to jstd)
*/
var plugin = new JstdPlugin();
/**
* Initialise scenario jstd-adapter
* (only if jstestdriver is defined)
*
* @param {Object} jstestdriver Undefined when run from browser (without jstd)
* @param {Function} initScenarioAndRun Function that inits scenario and runs all the tests
* @param {Object=} config Configuration object, supported properties:
* - relativeUrlPrefix: prefix for all relative links when navigateTo()
*/
function initScenarioAdapter(jstestdriver, initScenarioAndRun, config) {
if (jstestdriver) {
// create and register ScenarioPlugin
jstestdriver.pluginRegistrar.register(plugin);
plugin.runScenario = initScenarioAndRun;
/**
* HACK (angular.scenario.Application.navigateTo)
*
* We need to navigate to relative urls when running from browser (without JSTD),
* because we want to allow running scenario tests without creating its own virtual host.
* For example: http://angular.local/build/docs/docs-scenario.html
*
* On the other hand, when running with JSTD, we need to navigate to absolute urls,
* because of JSTD proxy. (proxy, because of same domain policy)
*
* So this hack is applied only if running with JSTD and change all relative urls to absolute.
*/
var appProto = angular.scenario.Application.prototype,
navigateTo = appProto.navigateTo,
relativeUrlPrefix = config && config.relativeUrlPrefix || '/';
appProto.navigateTo = function(url, loadFn, errorFn) {
if (url.charAt(0) != '/' && url.charAt(0) != '#' &&
url != 'about:blank' && !url.match(/^https?/)) {
url = relativeUrlPrefix + url;
}
return navigateTo.call(this, url, loadFn, errorFn);
};
}
}
/**
* Builds proper TestResult object from given model spec
*
* TODO(vojta) report error details
*
* @param {angular.scenario.ObjectModel.Spec} spec
* @returns {jstestdriver.TestResult}
*/
function createTestResultFromSpec(spec) {
var map = {
success: 'PASSED',
error: 'ERROR',
failure: 'FAILED'
};
return new jstestdriver.TestResult(
spec.fullDefinitionName,
spec.name,
jstestdriver.TestResult.RESULT[map[spec.status]],
spec.error || '',
spec.line || '',
spec.duration);
}
/**
* Generates JSTD output (jstestdriver.TestResult)
*/
angular.scenario.output('jstd', function(context, runner, model) {
model.on('SpecEnd', function(spec) {
plugin.reportResult(createTestResultFromSpec(spec));
});
model.on('RunnerEnd', function() {
plugin.reportEnd();
});
});
@@ -0,0 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window) {
@@ -0,0 +1,2 @@
initScenarioAdapter(window.jstestdriver, angular.scenario.setUpAndRun, window.jstdScenarioAdapter);
})(window);
+1 -1
View File
@@ -43,7 +43,7 @@ angular.scenario.output('xml', function(context, runner, model) {
if (step.error) {
var error = $('<error></error>');
stepContext.append(error);
error.text(formatException(step.error));
error.text(formatException(stepContext.error));
}
});
});
+3 -123
View File
@@ -31,7 +31,7 @@ describe('angular', function() {
expect(copy(date) === date).toBeFalsy();
});
it("should deeply copy an array into an existing array", function() {
it("should copy array", function() {
var src = [1, {name:"value"}];
var dst = [{key:"v"}];
expect(copy(src, dst)).toBe(dst);
@@ -40,15 +40,6 @@ describe('angular', function() {
expect(dst[1]).not.toBe(src[1]);
});
it("should deeply copy an array into a new array", function() {
var src = [1, {name:"value"}];
var dst = copy(src);
expect(src).toEqual([1, {name:"value"}]);
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst[1]).not.toBe(src[1]);
});
it('should copy empty array', function() {
var src = [];
var dst = [{key: "v"}];
@@ -56,7 +47,7 @@ describe('angular', function() {
expect(dst).toEqual([]);
});
it("should deeply copy an object into an existing object", function() {
it("should copy object", function() {
var src = {a:{name:"value"}};
var dst = {b:{key:"v"}};
expect(copy(src, dst)).toBe(dst);
@@ -65,16 +56,6 @@ describe('angular', function() {
expect(dst.a).not.toBe(src.a);
});
it("should deeply copy an object into an existing object", function() {
var src = {a:{name:"value"}};
var dst = copy(src, dst);
expect(src).toEqual({a:{name:"value"}});
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
expect(dst.a).toEqual(src.a);
expect(dst.a).not.toBe(src.a);
});
it("should copy primitives", function() {
expect(copy(null)).toEqual(null);
expect(copy('')).toBe('');
@@ -276,7 +257,7 @@ describe('angular', function() {
function MyObj() {
this.bar = 'barVal';
this.baz = 'bazVal';
}
};
MyObj.prototype.foo = 'fooVal';
var obj = new MyObj(),
@@ -286,77 +267,6 @@ describe('angular', function() {
expect(log).toEqual(['bar:barVal', 'baz:bazVal']);
});
it('should handle JQLite and jQuery objects like arrays', function() {
var jqObject = jqLite("<p><span>s1</span><span>s2</span></p>").find("span"),
log = [];
forEach(jqObject, function(value, key) { log.push(key + ':' + value.innerHTML)});
expect(log).toEqual(['0:s1', '1:s2']);
});
it('should handle NodeList objects like arrays', function() {
var nodeList = jqLite("<p><span>a</span><span>b</span><span>c</span></p>")[0].childNodes,
log = [];
forEach(nodeList, function(value, key) { log.push(key + ':' + value.innerHTML)});
expect(log).toEqual(['0:a', '1:b', '2:c']);
});
it('should handle HTMLCollection objects like arrays', function() {
document.body.innerHTML = "<p>" +
"<a name='x'>a</a>" +
"<a name='y'>b</a>" +
"<a name='x'>c</a>" +
"</p>";
var htmlCollection = document.getElementsByName('x'),
log = [];
forEach(htmlCollection, function(value, key) { log.push(key + ':' + value.innerHTML)});
expect(log).toEqual(['0:a', '1:c']);
});
it('should handle arguments objects like arrays', function() {
var args,
log = [];
(function(){ args = arguments}('a', 'b', 'c'));
forEach(args, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['0:a', '1:b', '2:c']);
});
it('should handle objects with length property as objects', function() {
var obj = {
'foo' : 'bar',
'length': 2
},
log = [];
forEach(obj, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['foo:bar', 'length:2']);
});
it('should handle objects of custom types with length property as objects', function() {
function CustomType() {
this.length = 2;
this.foo = 'bar'
}
var obj = new CustomType(),
log = [];
forEach(obj, function(value, key) { log.push(key + ':' + value)});
expect(log).toEqual(['length:2', 'foo:bar']);
});
});
@@ -673,13 +583,6 @@ describe('angular', function() {
toBe('<ng-abc x="2A">');
});
});
describe('startingTag', function() {
it('should allow passing in Nodes instead of Elements', function() {
var txtNode = document.createTextNode('some text');
expect(startingTag(txtNode)).toBe('some text');
});
});
describe('snake_case', function(){
it('should convert to snake_case', function() {
@@ -737,27 +640,4 @@ describe('angular', function() {
expect(toJson({key: $rootScope})).toEqual('{"key":"$SCOPE"}');
}));
});
describe('noConflict', function() {
var globalAngular;
beforeEach(function() {
globalAngular = angular;
});
afterEach(function() {
angular = globalAngular;
});
it('should return angular', function() {
var a = angular.noConflict();
expect(a).toBe(globalAngular);
});
it('should restore original angular', function() {
var a = angular.noConflict();
expect(angular).toBeUndefined();
});
});
});
+1 -1
View File
@@ -175,7 +175,7 @@ describe('Binder', function() {
$rootScope.error['throw'] = function() {throw 'MyError';};
errorLogs.length = 0;
$rootScope.$apply();
expect(errorLogs.shift().message).toBe('Error while interpolating: {{error.throw()}}\nMyError');
expect(errorLogs.shift()).toBe('MyError');
$rootScope.error['throw'] = function() {return 'ok';};
$rootScope.$apply();
+8 -15
View File
@@ -3,13 +3,11 @@
describe('injector', function() {
var providers;
var injector;
var providerInjector;
beforeEach(module(function($provide, $injector) {
beforeEach(module(function($provide) {
providers = function(name, factory, annotations) {
$provide.factory(name, extend(factory, annotations||{}));
};
providerInjector = $injector;
}));
beforeEach(inject(function($injector){
injector = $injector;
@@ -74,11 +72,6 @@ describe('injector', function() {
});
it('should create a new $injector for the run phase', inject(function($injector) {
expect($injector).not.toBe(providerInjector);
}));
describe('invoke', function() {
var args;
@@ -556,26 +549,26 @@ describe('injector', function() {
it('should decorate the missing service error with module name', function() {
angular.module('TestModule', [], function(xyzzy) {});
angular.module('TestModule', [], function($injector) {});
expect(function() {
createInjector(['TestModule']);
}).toThrow('Unknown provider: xyzzy from TestModule');
}).toThrow('Unknown provider: $injector from TestModule');
});
it('should decorate the missing service error with module function', function() {
function myModule(xyzzy){}
function myModule($injector){}
expect(function() {
createInjector([myModule]);
}).toThrow('Unknown provider: xyzzy from ' + myModule);
}).toThrow('Unknown provider: $injector from ' + myModule);
});
it('should decorate the missing service error with module array function', function() {
function myModule(xyzzy){}
function myModule($injector){}
expect(function() {
createInjector([['xyzzy', myModule]]);
}).toThrow('Unknown provider: xyzzy from ' + myModule);
createInjector([['$injector', myModule]]);
}).toThrow('Unknown provider: $injector from ' + myModule);
});
+1 -6
View File
@@ -955,14 +955,9 @@ describe('jqLite', function() {
expect(root.append('text')).toEqual(root);
expect(root.html()).toEqual('text');
});
it('should append to document fragment', function() {
it('should not append anything if parent node is not of type element', function() {
var root = jqLite(document.createDocumentFragment());
expect(root.append('<p>foo</p>')).toBe(root);
expect(root.children().length).toBe(1);
});
it('should not append anything if parent node is not of type element or docfrag', function() {
var root = jqLite('<p>some text node</p>').contents();
expect(root.append('<p>foo</p>')).toBe(root);
expect(root.children().length).toBe(0);
});
});
+6 -6
View File
@@ -109,7 +109,7 @@ beforeEach(function() {
if (this.actual.callCount != 1) {
if (this.actual.callCount == 0) {
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
'Expected spy ' + this.actual.identity + ' to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was never called.',
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was.'
@@ -117,16 +117,16 @@ beforeEach(function() {
}
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was called ' + this.actual.callCount + ' times.',
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
'Expected spy ' + this.actual.identity + ' to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was never called.',
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
} else {
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
'Expected spy ' + this.actual.identity + ' to have been called with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall),
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall)
];
}
+2 -14
View File
@@ -279,18 +279,6 @@ describe('browser', function() {
});
});
describe('put via cookies(cookieName, string), if no <base href> ', function () {
beforeEach(function () {
fakeDocument.basePath = undefined;
});
it('should default path in cookie to "" (empty string)', function () {
browser.cookies('cookie', 'bender');
// This only fails in Safari and IE when cookiePath returns undefined
// Where it now succeeds since baseHref return '' instead of undefined
expect(document.cookie).toEqual('cookie=bender');
});
});
describe('get via cookies()[cookieName]', function() {
@@ -567,9 +555,9 @@ describe('browser', function() {
expect(browser.baseHref()).toEqual('/base/path/');
});
it('should return \'\' (empty string) if no <base href>', function() {
it('should return undefined if no <base href>', function() {
fakeDocument.basePath = undefined;
expect(browser.baseHref()).toEqual('');
expect(browser.baseHref()).toBeUndefined();
});
it('should remove domain from <base href>', function() {
-6
View File
@@ -104,12 +104,6 @@ describe('$cacheFactory', function() {
cache.remove(123);
expect(cache.info().size).toBe(0);
}));
it("should return value from put", inject(function($cacheFactory) {
var obj = {};
expect(cache.put('k1', obj)).toBe(obj);
}));
});
+15 -248
View File
@@ -116,17 +116,12 @@ describe('$compile', function() {
describe('compile phase', function() {
it('should attach scope to the document node when it is compiled explicitly', inject(function($document){
$compile($document)($rootScope);
expect($document.scope()).toBe($rootScope);
}));
it('should wrap root text nodes in spans', inject(function($compile, $rootScope) {
element = jqLite('<div>A&lt;a&gt;B&lt;/a&gt;C</div>');
var text = element.contents();
expect(text[0].nodeName).toEqual('#text');
text = $compile(text)($rootScope);
expect(text[0].nodeName).toEqual('SPAN');
expect(lowercase(text[0].nodeName)).toEqual('span');
expect(element.find('span').text()).toEqual('A<a>B</a>C');
}));
@@ -142,41 +137,6 @@ describe('$compile', function() {
expect(spans.text().indexOf('C')).toEqual(0);
});
it('should not leak memory when there are top level empty text nodes', function() {
var calcCacheSize = function() {
var size = 0;
forEach(jqLite.cache, function(item, key) { size++; });
return size;
};
// We compile the contents of element (i.e. not element itself)
// Then delete these contents and check the cache has been reset to zero
// First with only elements at the top level
element = jqLite('<div><div></div></div>');
$compile(element.contents())($rootScope);
element.html('');
expect(calcCacheSize()).toEqual(0);
// Next with non-empty text nodes at the top level
// (in this case the compiler will wrap them in a <span>)
element = jqLite('<div>xxx</div>');
$compile(element.contents())($rootScope);
element.html('');
expect(calcCacheSize()).toEqual(0);
// Next with comment nodes at the top level
element = jqLite('<div><!-- comment --></div>');
$compile(element.contents())($rootScope);
element.html('');
expect(calcCacheSize()).toEqual(0);
// Finally with empty text nodes at the top level
element = jqLite('<div> \n<div></div> </div>');
$compile(element.contents())($rootScope);
element.html('');
expect(calcCacheSize()).toEqual(0);
});
describe('multiple directives per element', function() {
it('should allow multiple directives per element', inject(function($compile, $rootScope, log){
@@ -1421,10 +1381,9 @@ describe('$compile', function() {
}));
it('should set interpolated attrs to initial interpolation value', inject(function($rootScope, $compile) {
$rootScope.whatever = 'test value';
it('should set interpolated attrs to undefined', inject(function($rootScope, $compile) {
$compile('<div some-attr="{{whatever}}" observer></div>')($rootScope);
expect(directiveAttrs.someAttr).toBe($rootScope.whatever);
expect(directiveAttrs.someAttr).toBeUndefined();
}));
@@ -1579,25 +1538,6 @@ describe('$compile', function() {
expect(element.text()).toEqual('WORKS');
});
});
it('should support $observe inside link function on directive object', function() {
module(function() {
directive('testLink', valueFn({
templateUrl: 'test-link.html',
link: function(scope, element, attrs) {
attrs.$observe( 'testLink', function ( val ) {
scope.testAttr = val;
});
}
}));
});
inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('test-link.html', '{{testAttr}}' );
element = $compile('<div test-link="{{1+2}}"></div>')($rootScope);
$rootScope.$apply();
expect(element.text()).toBe('3');
});
});
});
@@ -1853,21 +1793,27 @@ describe('$compile', function() {
describe('attribute', function() {
it('should copy simple attribute', inject(function() {
compile('<div><span my-component attr="some text">');
expect(componentScope.attr).toEqual(undefined);
expect(componentScope.attrAlias).toEqual(undefined);
$rootScope.$apply();
expect(componentScope.attr).toEqual('some text');
expect(componentScope.attrAlias).toEqual('some text');
expect(componentScope.attrAlias).toEqual(componentScope.attr);
}));
it('should set up the interpolation before it reaches the link function', inject(function() {
$rootScope.name = 'misko';
compile('<div><span my-component attr="hello {{name}}">');
expect(componentScope.attr).toEqual('hello misko');
expect(componentScope.attrAlias).toEqual('hello misko');
}));
it('should update when interpolated attribute updates', inject(function() {
compile('<div><span my-component attr="hello {{name}}">');
expect(componentScope.attr).toEqual(undefined);
expect(componentScope.attrAlias).toEqual(undefined);
$rootScope.name = 'misko';
$rootScope.$apply();
expect(componentScope.attr).toEqual('hello misko');
expect(componentScope.attrAlias).toEqual('hello misko');
$rootScope.name = 'igor';
$rootScope.$apply();
@@ -1992,21 +1938,6 @@ describe('$compile', function() {
compile('<div><span bad-declaration>');
}).toThrow('Invalid isolate scope definition for directive badDeclaration: xxx');
}));
it('should expose a $$isolateBindings property onto the scope', inject(function() {
compile('<div><span my-component>');
expect(typeof componentScope.$$isolateBindings).toBe('object');
expect(componentScope.$$isolateBindings.attr).toBe('@attr');
expect(componentScope.$$isolateBindings.attrAlias).toBe('@attr');
expect(componentScope.$$isolateBindings.ref).toBe('=ref');
expect(componentScope.$$isolateBindings.refAlias).toBe('=ref');
expect(componentScope.$$isolateBindings.reference).toBe('=reference');
expect(componentScope.$$isolateBindings.expr).toBe('&expr');
expect(componentScope.$$isolateBindings.exprAlias).toBe('&expr');
}));
});
@@ -2333,169 +2264,5 @@ describe('$compile', function() {
expect(element.text()).toBe('-->|x|');
}));
it('should add a $$transcluded property onto the transcluded scope', function() {
module(function() {
directive('trans', function() {
return {
transclude: true,
replace: true,
scope: true,
template: '<div><span>I:{{$$transcluded}}</span><div ng-transclude></div></div>'
};
});
});
inject(function(log, $rootScope, $compile) {
element = $compile('<div><div trans>T:{{$$transcluded}}</div></div>')
($rootScope);
$rootScope.$apply();
expect(jqLite(element.find('span')[0]).text()).toEqual('I:');
expect(jqLite(element.find('span')[1]).text()).toEqual('T:true');
});
});
});
describe('href sanitization', function() {
it('should sanitize javascript: urls', inject(function($compile, $rootScope) {
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element.attr('href')).toBe('unsafe:javascript:doEvilStuff()');
}));
it('should sanitize data: urls', inject(function($compile, $rootScope) {
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "data:evilPayload";
$rootScope.$apply();
expect(element.attr('href')).toBe('unsafe:data:evilPayload');
}));
it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
// case-sensitive
$rootScope.testUrl = "JaVaScRiPt:doEvilStuff()";
$rootScope.$apply();
expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
// tab in protocol
$rootScope.testUrl = "java\u0009script:doEvilStuff()";
$rootScope.$apply();
expect(element[0].href).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
// space before
$rootScope.testUrl = " javascript:doEvilStuff()";
$rootScope.$apply();
expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
// ws chars before
$rootScope.testUrl = " \u000e javascript:doEvilStuff()";
$rootScope.$apply();
expect(element[0].href).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
// post-fixed with proper url
$rootScope.testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
$rootScope.$apply();
expect(element[0].href).toBeOneOf(
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
);
}));
it('should sanitize ngHref bindings as well', inject(function($compile, $rootScope) {
element = $compile('<a ng-href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element[0].href).toBe('unsafe:javascript:doEvilStuff()');
}));
it('should not sanitize valid urls', inject(function($compile, $rootScope) {
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe('foo/bar');
$rootScope.testUrl = "/foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe('/foo/bar');
$rootScope.testUrl = "../foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe('../foo/bar');
$rootScope.testUrl = "#foo";
$rootScope.$apply();
expect(element.attr('href')).toBe('#foo');
$rootScope.testUrl = "http://foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe('http://foo/bar');
$rootScope.testUrl = " http://foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe(' http://foo/bar');
$rootScope.testUrl = "https://foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe('https://foo/bar');
$rootScope.testUrl = "ftp://foo/bar";
$rootScope.$apply();
expect(element.attr('href')).toBe('ftp://foo/bar');
$rootScope.testUrl = "mailto:foo@bar.com";
$rootScope.$apply();
expect(element.attr('href')).toBe('mailto:foo@bar.com');
}));
it('should not sanitize href on elements other than anchor', inject(function($compile, $rootScope) {
element = $compile('<div href="{{testUrl}}"></div>')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element.attr('href')).toBe('javascript:doEvilStuff()');
}));
it('should not sanitize attributes other than href', inject(function($compile, $rootScope) {
element = $compile('<a title="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element.attr('title')).toBe('javascript:doEvilStuff()');
}));
it('should allow reconfiguration of the href whitelist', function() {
module(function($compileProvider) {
expect($compileProvider.urlSanitizationWhitelist() instanceof RegExp).toBe(true);
var returnVal = $compileProvider.urlSanitizationWhitelist(/javascript:/);
expect(returnVal).toBe($compileProvider);
});
inject(function($compile, $rootScope) {
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
$rootScope.testUrl = "javascript:doEvilStuff()";
$rootScope.$apply();
expect(element.attr('href')).toBe('javascript:doEvilStuff()');
$rootScope.testUrl = "http://recon/figured";
$rootScope.$apply();
expect(element.attr('href')).toBe('unsafe:http://recon/figured');
});
});
});
});
+4 -19
View File
@@ -1,13 +1,7 @@
'use strict';
describe('a', function() {
var element, $compile, $rootScope;
beforeEach(inject(function(_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
var element;
afterEach(function(){
@@ -15,7 +9,8 @@ describe('a', function() {
});
it('should prevent default action to be executed when href is empty', function() {
it('should prevent default action to be executed when href is empty',
inject(function($rootScope, $compile) {
var orgLocation = document.location.href,
preventDefaultCalled = false,
event;
@@ -47,15 +42,5 @@ describe('a', function() {
}
expect(document.location.href).toEqual(orgLocation);
});
it('should prevent IE for changing text content when setting attribute', function() {
// see issue #1949
element = jqLite('<a href="">hello@you</a>');
$compile(element);
element.attr('href', 'bye@me');
expect(element.text()).toBe('hello@you');
});
}));
});
-10
View File
@@ -74,16 +74,6 @@ describe('boolean attr directives', function() {
$rootScope.$digest();
expect(element.attr('multiple')).toBeTruthy();
}));
it('should bind open', inject(function($rootScope, $compile) {
element = $compile('<details ng-open="isOpen"></details>')($rootScope)
$rootScope.isOpen=false;
$rootScope.$digest();
expect(element.attr('open')).toBeFalsy();
$rootScope.isOpen=true;
$rootScope.$digest();
expect(element.attr('open')).toBeTruthy();
}));
});
-106
View File
@@ -430,110 +430,4 @@ describe('form', function() {
expect(doc).toBeDirty();
});
});
describe('$setPristine', function() {
it('should reset pristine state of form and controls', function() {
doc = $compile(
'<form name="testForm">' +
'<input ng-model="named1" name="foo">' +
'<input ng-model="named2" name="bar">' +
'</form>')(scope);
scope.$digest();
var form = doc,
formCtrl = scope.testForm,
input1 = form.find('input').eq(0),
input1Ctrl = input1.controller('ngModel'),
input2 = form.find('input').eq(1),
input2Ctrl = input2.controller('ngModel');
input1Ctrl.$setViewValue('xx');
input2Ctrl.$setViewValue('yy');
scope.$apply();
expect(form).toBeDirty();
expect(input1).toBeDirty();
expect(input2).toBeDirty();
formCtrl.$setPristine();
expect(form).toBePristine();
expect(formCtrl.$pristine).toBe(true);
expect(formCtrl.$dirty).toBe(false);
expect(input1).toBePristine();
expect(input1Ctrl.$pristine).toBe(true);
expect(input1Ctrl.$dirty).toBe(false);
expect(input2).toBePristine();
expect(input2Ctrl.$pristine).toBe(true);
expect(input2Ctrl.$dirty).toBe(false);
});
it('should reset pristine state of anonymous form controls', function() {
doc = $compile(
'<form name="testForm">' +
'<input ng-model="anonymous">' +
'</form>')(scope);
scope.$digest();
var form = doc,
formCtrl = scope.testForm,
input = form.find('input').eq(0),
inputCtrl = input.controller('ngModel');
inputCtrl.$setViewValue('xx');
scope.$apply();
expect(form).toBeDirty();
expect(input).toBeDirty();
formCtrl.$setPristine();
expect(form).toBePristine();
expect(formCtrl.$pristine).toBe(true);
expect(formCtrl.$dirty).toBe(false);
expect(input).toBePristine();
expect(inputCtrl.$pristine).toBe(true);
expect(inputCtrl.$dirty).toBe(false);
});
it('should reset pristine state of nested forms', function() {
doc = $compile(
'<form name="testForm">' +
'<div ng-form>' +
'<input ng-model="named" name="foo">' +
'</div>' +
'</form>')(scope);
scope.$digest();
var form = doc,
formCtrl = scope.testForm,
nestedForm = form.find('div'),
nestedFormCtrl = nestedForm.controller('form'),
nestedInput = form.find('input').eq(0),
nestedInputCtrl = nestedInput.controller('ngModel');
nestedInputCtrl.$setViewValue('xx');
scope.$apply();
expect(form).toBeDirty();
expect(nestedForm).toBeDirty();
expect(nestedInput).toBeDirty();
formCtrl.$setPristine();
expect(form).toBePristine();
expect(formCtrl.$pristine).toBe(true);
expect(formCtrl.$dirty).toBe(false);
expect(nestedForm).toBePristine();
expect(nestedFormCtrl.$pristine).toBe(true);
expect(nestedFormCtrl.$dirty).toBe(false);
expect(nestedInput).toBePristine();
expect(nestedInputCtrl.$pristine).toBe(true);
expect(nestedInputCtrl.$dirty).toBe(false);
});
});
});
-20
View File
@@ -117,18 +117,6 @@ describe('NgModelController', function() {
});
});
describe('setPristine', function() {
it('should set control to its pristine state', function() {
ctrl.$setViewValue('edit');
expect(ctrl.$dirty).toBe(true);
expect(ctrl.$pristine).toBe(false);
ctrl.$setPristine();
expect(ctrl.$dirty).toBe(false);
expect(ctrl.$pristine).toBe(true);
});
});
describe('view -> model', function() {
@@ -394,14 +382,6 @@ describe('input', function() {
});
it('should update the model and not trim the value', function() {
compileInput('<input type="text" ng-model="name" name="alias" ng-trim="false" />');
changeInputValueTo(' a ');
expect(scope.name).toEqual(' a ');
});
it('should allow complex reference binding', function() {
compileInput('<input type="text" ng-model="obj[\'abc\'].name"/>');
-11
View File
@@ -236,17 +236,6 @@ describe('ngClass', function() {
}));
it('should not mess up class value due to observing an interpolated class attribute', inject(function($rootScope, $compile) {
$rootScope.foo = true;
$rootScope.$watch("anything", function() {
$rootScope.foo = false;
});
element = $compile('<div ng-class="{foo:foo}"></div>')($rootScope);
$rootScope.$digest();
expect(element.hasClass('foo')).toBe(false);
}));
it('should update ngClassOdd/Even when model is changed by filtering', inject(function($rootScope, $compile) {
element = $compile('<ul>' +
'<li ng-repeat="i in items" ' +
-29
View File
@@ -1,29 +0,0 @@
'use strict';
describe('ngKeyup and ngKeydown directives', function() {
var element;
afterEach(function() {
dealoc(element);
});
it('should get called on a keyup', inject(function($rootScope, $compile) {
element = $compile('<input ng-keyup="touched = true">')($rootScope);
$rootScope.$digest();
expect($rootScope.touched).toBeFalsy();
browserTrigger(element, 'keyup');
expect($rootScope.touched).toEqual(true);
}));
it('should get called on a keydown', inject(function($rootScope, $compile) {
element = $compile('<input ng-keydown="touched = true">')($rootScope);
$rootScope.$digest();
expect($rootScope.touched).toBeFalsy();
browserTrigger(element, 'keydown');
expect($rootScope.touched).toEqual(true);
}));
});
-45
View File
@@ -36,36 +36,6 @@ describe('ngSwitch', function() {
}));
it('should show all switch-whens that match the current value', inject(function($rootScope, $compile) {
element = $compile(
'<ul ng-switch="select">' +
'<li ng-switch-when="1">first:{{name}}</li>' +
'<li ng-switch-when="1">, first too:{{name}}</li>' +
'<li ng-switch-when="2">second:{{name}}</li>' +
'<li ng-switch-when="true">true:{{name}}</li>' +
'</ul>')($rootScope);
expect(element.html()).toEqual('<!-- ngSwitchWhen: 1 -->' +
'<!-- ngSwitchWhen: 1 -->' +
'<!-- ngSwitchWhen: 2 -->' +
'<!-- ngSwitchWhen: true -->');
$rootScope.select = 1;
$rootScope.$apply();
expect(element.text()).toEqual('first:, first too:');
$rootScope.name="shyam";
$rootScope.$apply();
expect(element.text()).toEqual('first:shyam, first too:shyam');
$rootScope.select = 2;
$rootScope.$apply();
expect(element.text()).toEqual('second:shyam');
$rootScope.name = 'misko';
$rootScope.$apply();
expect(element.text()).toEqual('second:misko');
$rootScope.select = true;
$rootScope.$apply();
expect(element.text()).toEqual('true:misko');
}));
it('should switch on switch-when-default', inject(function($rootScope, $compile) {
element = $compile(
'<ng:switch on="select">' +
@@ -80,21 +50,6 @@ describe('ngSwitch', function() {
}));
it('should show all switch-when-default', inject(function($rootScope, $compile) {
element = $compile(
'<ul ng-switch="select">' +
'<li ng-switch-when="1">one</li>' +
'<li ng-switch-default>other</li>' +
'<li ng-switch-default>, other too</li>' +
'</ul>')($rootScope);
$rootScope.$apply();
expect(element.text()).toEqual('other, other too');
$rootScope.select = 1;
$rootScope.$apply();
expect(element.text()).toEqual('one');
}));
it('should call change on switch', inject(function($rootScope, $compile) {
element = $compile(
'<ng:switch on="url" change="name=\'works\'">' +
+1 -25
View File
@@ -429,7 +429,7 @@ describe('ngView', function() {
$rootScope.$digest();
expect($rootScope.load).toHaveBeenCalledOnce();
});
});
})
it('should set $scope and $controllerController on the view', function() {
@@ -459,28 +459,4 @@ describe('ngView', function() {
expect(div.controller()).toBe($route.current.scope.ctrl);
});
});
it('should not set $scope or $controllerController on top level text elements in the view', function() {
function MyCtrl($scope) {}
module(function($routeProvider) {
$routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: MyCtrl});
});
inject(function($templateCache, $location, $rootScope, $route) {
$templateCache.put('tpl.html', '<div></div> ');
$location.url('/foo');
$rootScope.$digest();
forEach(element.contents(), function(node) {
if ( node.nodeType == 3 ) {
expect(jqLite(node).scope()).not.toBe($route.current.scope);
expect(jqLite(node).controller()).not.toBeDefined();
} else {
expect(jqLite(node).scope()).toBe($route.current.scope);
expect(jqLite(node).controller()).toBeDefined();
}
});
});
});
});
-53
View File
@@ -66,57 +66,4 @@ describe('Filter: filter', function() {
expect(filter(items, '!isk').length).toBe(1);
expect(filter(items, '!isk')[0]).toEqual(items[1]);
});
describe('should support comparator', function() {
it('as equality when true', function() {
var items = ['misko', 'adam', 'adamson'];
var expr = 'adam';
expect(filter(items, expr, true)).toEqual([items[1]]);
expect(filter(items, expr, false)).toEqual([items[1], items[2]]);
var items = [
{key: 'value1', nonkey: 1},
{key: 'value2', nonkey: 2},
{key: 'value12', nonkey: 3},
{key: 'value1', nonkey:4},
{key: 'Value1', nonkey:5}
];
var expr = {key: 'value1'};
expect(filter(items, expr, true)).toEqual([items[0], items[3]]);
var items = [
{key: 1, nonkey: 1},
{key: 2, nonkey: 2},
{key: 12, nonkey: 3},
{key: 1, nonkey:4}
];
var expr = { key: 1 };
expect(filter(items, expr, true)).toEqual([items[0], items[3]]);
var expr = 12;
expect(filter(items, expr, true)).toEqual([items[2]]);
});
it('and use the function given to compare values', function() {
var items = [
{key: 1, nonkey: 1},
{key: 2, nonkey: 2},
{key: 12, nonkey: 3},
{key: 1, nonkey:14}
];
var expr = {key: 10};
var comparator = function (obj,value) {
return obj > value;
}
expect(filter(items, expr, comparator)).toEqual([items[2]]);
expr = 10;
expect(filter(items, expr, comparator)).toEqual([items[2], items[3]]);
});
});
});
+22 -63
View File
@@ -71,17 +71,6 @@ describe('filters', function() {
var num = formatNumber(123.1116, pattern, ',', '.');
expect(num).toBe('123.112');
});
it('should format the same with string as well as numeric fractionSize', function(){
var num = formatNumber(123.1, pattern, ',', '.', "0");
expect(num).toBe('123');
var num = formatNumber(123.1, pattern, ',', '.', 0);
expect(num).toBe('123');
var num = formatNumber(123.1, pattern, ',', '.', "3");
expect(num).toBe('123.100');
var num = formatNumber(123.1, pattern, ',', '.', 3);
expect(num).toBe('123.100');
});
});
describe('currency', function() {
@@ -173,9 +162,9 @@ describe('filters', function() {
describe('date', function() {
var morning = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.001Z'); //7am
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.012Z'); //12pm
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.123Z'); //12am
var morning = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.000Z'); //7am
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.000Z'); //12pm
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.000Z'); //12am
var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');
var date;
@@ -203,23 +192,14 @@ describe('filters', function() {
expect(date(morning, "yy-MM-dd HH:mm:ss")).
toEqual('10-09-03 07:05:08');
expect(date(morning, "yy-MM-dd HH:mm:ss.sss")).
toEqual('10-09-03 07:05:08.001');
expect(date(midnight, "yyyy-M-d h=H:m:saZ")).
toEqual('2010-9-3 12=0:5:8AM-0500');
toEqual('2010-9-3 12=0:5:8AM0500');
expect(date(midnight, "yyyy-MM-dd hh=HH:mm:ssaZ")).
toEqual('2010-09-03 12=00:05:08AM-0500');
expect(date(midnight, "yyyy-MM-dd hh=HH:mm:ss.sssaZ")).
toEqual('2010-09-03 12=00:05:08.123AM-0500');
toEqual('2010-09-03 12=00:05:08AM0500');
expect(date(noon, "yyyy-MM-dd hh=HH:mm:ssaZ")).
toEqual('2010-09-03 12=12:05:08PM-0500');
expect(date(noon, "yyyy-MM-dd hh=HH:mm:ss.sssaZ")).
toEqual('2010-09-03 12=12:05:08.012PM-0500');
toEqual('2010-09-03 12=12:05:08PM0500');
expect(date(noon, "EEE, MMM d, yyyy")).
toEqual('Fri, Sep 3, 2010');
@@ -231,30 +211,14 @@ describe('filters', function() {
toEqual('September 03, 1');
});
it('should format timezones correctly (as per ISO_8601)', function() {
//Note: TzDate's first argument is offset, _not_ timezone.
var utc = new angular.mock.TzDate( 0, '2010-09-03T12:05:08.000Z');
var eastOfUTC = new angular.mock.TzDate(-5, '2010-09-03T12:05:08.000Z');
var westOfUTC = new angular.mock.TzDate(+5, '2010-09-03T12:05:08.000Z');
expect(date(utc, "yyyy-MM-ddTHH:mm:ssZ")).
toEqual('2010-09-03T12:05:08+0000')
expect(date(eastOfUTC, "yyyy-MM-ddTHH:mm:ssZ")).
toEqual('2010-09-03T17:05:08+0500')
expect(date(westOfUTC, "yyyy-MM-ddTHH:mm:ssZ")).
toEqual('2010-09-03T07:05:08-0500')
});
it('should treat single quoted strings as string literals', function() {
expect(date(midnight, "yyyy'de' 'a'x'dd' 'adZ' h=H:m:saZ")).
toEqual('2010de axdd adZ 12=0:5:8AM-0500');
toEqual('2010de axdd adZ 12=0:5:8AM0500');
});
it('should treat a sequence of two single quotes as a literal single quote', function() {
expect(date(midnight, "yyyy'de' 'a''dd' 'adZ' h=H:m:saZ")).
toEqual("2010de a'dd adZ 12=0:5:8AM-0500");
toEqual("2010de a'dd adZ 12=0:5:8AM0500");
});
it('should accept default formats', function() {
@@ -289,37 +253,32 @@ describe('filters', function() {
});
it('should support various iso8061 date strings with timezone as input', function() {
var format = 'yyyy-MM-dd ss';
it('should support various iso8061 date strings as input', function() {
var format = 'yyyy-MM ss';
//full ISO8061
expect(date('2003-09-10T13:02:03.000Z', format)).toEqual('2003-09-10 03');
expect(date('2003-09-10T13:02:03.000Z', format)).toEqual('2003-09 03');
expect(date('2003-09-10T13:02:03.000+00:00', format)).toEqual('2003-09-10 03');
expect(date('2003-09-10T13:02:03.000+00:00', format)).toEqual('2003-09 03');
expect(date('20030910T033203-0930', format)).toEqual('2003-09-10 03');
expect(date('2003-09-10T13:02:03-08:00', format)).toEqual('2003-09 03');
expect(date('20030910T033203-0930', format)).toEqual('2003-09 03');
//no timezone
expect(date('2003-09-10T13:02:03.000', format)).toEqual('2003-09 03');
//no millis
expect(date('2003-09-10T13:02:03Z', format)).toEqual('2003-09-10 03');
expect(date('2003-09-10T13:02:03Z', format)).toEqual('2003-09 03');
//no seconds
expect(date('2003-09-10T13:02Z', format)).toEqual('2003-09-10 00');
expect(date('2003-09-10T13:02Z', format)).toEqual('2003-09 00');
//no minutes
expect(date('2003-09-10T13Z', format)).toEqual('2003-09-10 00');
});
it('should parse iso8061 date strings without timezone as local time', function() {
var format = 'yyyy-MM-dd HH-mm-ss';
//full ISO8061 without timezone
expect(date('2003-09-10T03:02:04.000', format)).toEqual('2003-09-10 03-02-04');
expect(date('20030910T030204', format)).toEqual('2003-09-10 03-02-04');
expect(date('2003-09-10T13Z', format)).toEqual('2003-09 00');
//no time
expect(date('2003-09-10', format)).toEqual('2003-09-10 00-00-00');
expect(date('2003-09-10', format)).toEqual('2003-09 00');
});
it('should support different degrees of subsecond precision', function () {
+5 -24
View File
@@ -2,12 +2,10 @@
describe('Filter: limitTo', function() {
var items;
var str
var limitTo;
beforeEach(inject(function($filter) {
items = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
str = "tuvwxyz";
limitTo = $filter('limitTo');
}));
@@ -15,16 +13,12 @@ describe('Filter: limitTo', function() {
it('should return the first X items when X is positive', function() {
expect(limitTo(items, 3)).toEqual(['a', 'b', 'c']);
expect(limitTo(items, '3')).toEqual(['a', 'b', 'c']);
expect(limitTo(str, 3)).toEqual("tuv");
expect(limitTo(str, '3')).toEqual("tuv");
});
it('should return the last X items when X is negative', function() {
expect(limitTo(items, -3)).toEqual(['f', 'g', 'h']);
expect(limitTo(items, '-3')).toEqual(['f', 'g', 'h']);
expect(limitTo(str, -3)).toEqual("xyz");
expect(limitTo(str, '-3')).toEqual("xyz");
});
@@ -36,17 +30,11 @@ describe('Filter: limitTo', function() {
expect(limitTo(items, undefined)).toEqual([]);
});
it('should return an empty string when X cannot be parsed', function() {
expect(limitTo(str, 'bogus')).toEqual("");
expect(limitTo(str, 'null')).toEqual("");
expect(limitTo(str, 'undefined')).toEqual("");
expect(limitTo(str, null)).toEqual("");
expect(limitTo(str, undefined)).toEqual("");
});
it('should return input if not String or Array', function() {
expect(limitTo(1,1)).toEqual(1);
it('should return an empty array when input is not Array type', function() {
expect(limitTo('bogus', 1)).toEqual('bogus');
expect(limitTo(null, 1)).toEqual(null);
expect(limitTo(undefined, 1)).toEqual(undefined);
expect(limitTo(null, 1)).toEqual(null);
expect(limitTo(undefined, 1)).toEqual(undefined);
expect(limitTo({}, 1)).toEqual({});
@@ -54,18 +42,11 @@ describe('Filter: limitTo', function() {
it('should return a copy of input array if X is exceeds array length', function () {
expect(limitTo(items, 9)).toEqual(items);
expect(limitTo(items, 19)).toEqual(items);
expect(limitTo(items, '9')).toEqual(items);
expect(limitTo(items, -9)).toEqual(items);
expect(limitTo(items, '-9')).toEqual(items);
expect(limitTo(items, 9)).not.toBe(items);
});
it('should return the entire string if X exceeds input length', function() {
expect(limitTo(str, 9)).toEqual(str);
expect(limitTo(str, '9')).toEqual(str);
expect(limitTo(str, -9)).toEqual(str);
expect(limitTo(str, '-9')).toEqual(str);
})
});
-21
View File
@@ -116,9 +116,6 @@ describe('$httpBackend', function() {
};
this.getAllResponseHeaders = valueFn('');
// for temporary Firefox CORS workaround
// see https://github.com/angular/angular.js/issues/1468
this.getResponseHeader = valueFn('');
}
callback.andCallFake(function(status, response) {
@@ -138,24 +135,6 @@ describe('$httpBackend', function() {
});
it('should set responseType and return xhr.response', function() {
$backend('GET', '/whatever', null, callback, {}, null, null, 'blob');
var xhrInstance = MockXhr.$$lastInstance;
expect(xhrInstance.responseType).toBe('blob');
callback.andCallFake(function(status, response) {
expect(response).toBe(xhrInstance.response);
});
xhrInstance.response = {some: 'object'};
xhrInstance.readyState = 4;
xhrInstance.onreadystatechange();
expect(callback).toHaveBeenCalledOnce();
});
describe('JSONP', function() {
var SCRIPT_URL = /([^\?]*)\?cb=angular\.callbacks\.(.*)/;
+13 -88
View File
@@ -144,26 +144,9 @@ describe('$http', function() {
it('should jsonify objects in params map', inject(function($httpBackend, $http) {
$httpBackend.expect('GET', '/url?a=1&b=%7B%22c%22:3%7D').respond('');
$httpBackend.expect('GET', '/url?a=1&b=%7B%22c%22%3A3%7D').respond('');
$http({url: '/url', params: {a:1, b:{c:3}}, method: 'GET'});
}));
it('should expand arrays in params map', inject(function($httpBackend, $http) {
$httpBackend.expect('GET', '/url?a=1&a=2&a=3').respond('');
$http({url: '/url', params: {a: [1,2,3]}, method: 'GET'});
}));
it('should not encode @ in url params', function() {
//encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt
//with regards to the character set (pchar) allowed in path segments
//so we need this test to make sure that we don't over-encode the params and break stuff
//like buzz api which uses @self
$httpBackend.expect('GET', '/Path?!do%26h=g%3Da+h&:bar=$baz@1').respond('');
$http({url: '/Path', params: {':bar': '$baz@1', '!do&h': 'g=a h'}, method: 'GET'});
});
});
@@ -388,7 +371,8 @@ describe('$http', function() {
it('should set default headers for GET request', function() {
$httpBackend.expect('GET', '/url', undefined, function(headers) {
return headers['Accept'] == 'application/json, text/plain, */*';
return headers['Accept'] == 'application/json, text/plain, */*' &&
headers['X-Requested-With'] == 'XMLHttpRequest';
}).respond('');
$http({url: '/url', method: 'GET', headers: {}});
@@ -399,6 +383,7 @@ describe('$http', function() {
it('should set default headers for POST request', function() {
$httpBackend.expect('POST', '/url', 'messageBody', function(headers) {
return headers['Accept'] == 'application/json, text/plain, */*' &&
headers['X-Requested-With'] == 'XMLHttpRequest' &&
headers['Content-Type'] == 'application/json;charset=utf-8';
}).respond('');
@@ -410,6 +395,7 @@ describe('$http', function() {
it('should set default headers for PUT request', function() {
$httpBackend.expect('PUT', '/url', 'messageBody', function(headers) {
return headers['Accept'] == 'application/json, text/plain, */*' &&
headers['X-Requested-With'] == 'XMLHttpRequest' &&
headers['Content-Type'] == 'application/json;charset=utf-8';
}).respond('');
@@ -420,7 +406,8 @@ describe('$http', function() {
it('should set default headers for custom HTTP method', function() {
$httpBackend.expect('FOO', '/url', undefined, function(headers) {
return headers['Accept'] == 'application/json, text/plain, */*';
return headers['Accept'] == 'application/json, text/plain, */*' &&
headers['X-Requested-With'] == 'XMLHttpRequest';
}).respond('');
$http({url: '/url', method: 'FOO', headers: {}});
@@ -431,6 +418,7 @@ describe('$http', function() {
it('should override default headers with custom', function() {
$httpBackend.expect('POST', '/url', 'messageBody', function(headers) {
return headers['Accept'] == 'Rewritten' &&
headers['X-Requested-With'] == 'XMLHttpRequest' &&
headers['Content-Type'] == 'Rewritten';
}).respond('');
@@ -441,17 +429,6 @@ describe('$http', function() {
$httpBackend.flush();
});
it('should not set XSRF cookie for cross-domain requests', inject(function($browser) {
$browser.cookies('XSRF-TOKEN', 'secret');
$browser.url('http://host.com/base');
$httpBackend.expect('GET', 'http://www.test.com/url', undefined, function(headers) {
return headers['X-XSRF-TOKEN'] === undefined;
}).respond('');
$http({url: 'http://www.test.com/url', method: 'GET', headers: {}});
$httpBackend.flush();
}));
it('should not send Content-Type header if request data/body is undefined', function() {
$httpBackend.expect('POST', '/url', undefined, function(headers) {
@@ -464,27 +441,22 @@ describe('$http', function() {
it('should set the XSRF cookie into a XSRF header', inject(function($browser) {
function checkXSRF(secret, header) {
function checkXSRF(secret) {
return function(headers) {
return headers[header || 'X-XSRF-TOKEN'] == secret;
return headers['X-XSRF-TOKEN'] == secret;
};
}
$browser.cookies('XSRF-TOKEN', 'secret');
$browser.cookies('aCookie', 'secret2');
$httpBackend.expect('GET', '/url', undefined, checkXSRF('secret')).respond('');
$httpBackend.expect('POST', '/url', undefined, checkXSRF('secret')).respond('');
$httpBackend.expect('PUT', '/url', undefined, checkXSRF('secret')).respond('');
$httpBackend.expect('DELETE', '/url', undefined, checkXSRF('secret')).respond('');
$httpBackend.expect('GET', '/url', undefined, checkXSRF('secret', 'aHeader')).respond('');
$httpBackend.expect('GET', '/url', undefined, checkXSRF('secret2')).respond('');
$http({url: '/url', method: 'GET'});
$http({url: '/url', method: 'POST', headers: {'S-ome': 'Header'}});
$http({url: '/url', method: 'PUT', headers: {'Another': 'Header'}});
$http({url: '/url', method: 'DELETE', headers: {}});
$http({url: '/url', method: 'GET', xsrfHeaderName: 'aHeader'})
$http({url: '/url', method: 'GET', xsrfCookieName: 'aCookie'})
$httpBackend.flush();
}));
@@ -987,13 +959,12 @@ describe('$http', function() {
});
it('should pass timeout, withCredentials and responseType', function() {
it('should pass timeout and withCredentials', function() {
var $httpBackend = jasmine.createSpy('$httpBackend');
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials) {
expect(timeout).toBe(12345);
expect(withCredentials).toBe(true);
expect(responseType).toBe('json');
});
module(function($provide) {
@@ -1001,56 +972,10 @@ describe('$http', function() {
});
inject(function($http) {
$http({
method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true, responseType: 'json'
});
$http({method: 'GET', url: 'some.html', timeout: 12345, withCredentials: true});
expect($httpBackend).toHaveBeenCalledOnce();
});
$httpBackend.verifyNoOutstandingExpectation = noop;
});
it('should use withCredentials from default', function() {
var $httpBackend = jasmine.createSpy('$httpBackend');
$httpBackend.andCallFake(function(m, u, d, c, h, timeout, withCredentials, responseType) {
expect(withCredentials).toBe(true);
});
module(function($provide) {
$provide.value('$httpBackend', $httpBackend);
});
inject(function($http) {
$http.defaults.withCredentials = true;
$http({
method: 'GET', url: 'some.html', timeout: 12345, responseType: 'json'
});
expect($httpBackend).toHaveBeenCalledOnce();
});
$httpBackend.verifyNoOutstandingExpectation = noop;
});
describe('isSameDomain', function() {
it('should support various combinations of urls', function() {
expect(isSameDomain('path/morepath',
'http://www.adomain.com')).toBe(true);
expect(isSameDomain('http://www.adomain.com/path',
'http://www.adomain.com')).toBe(true);
expect(isSameDomain('//www.adomain.com/path',
'http://www.adomain.com')).toBe(true);
expect(isSameDomain('//www.adomain.com/path',
'https://www.adomain.com')).toBe(true);
expect(isSameDomain('//www.adomain.com/path',
'http://www.adomain.com:1234')).toBe(false);
expect(isSameDomain('https://www.adomain.com/path',
'http://www.adomain.com')).toBe(false);
expect(isSameDomain('http://www.adomain.com:1234/path',
'http://www.adomain.com')).toBe(false);
expect(isSameDomain('http://www.anotherdomain.com/path',
'http://www.adomain.com')).toBe(false);
});
});
});
-23
View File
@@ -26,29 +26,6 @@ describe('$interpolate', function() {
expect($interpolate('{{ false }}')()).toEqual('false');
}));
it('should rethrow exceptions', inject(function($interpolate, $rootScope) {
$rootScope.err = function () {
throw new Error('oops');
};
expect(function () {
$interpolate('{{err()}}')($rootScope);
}).toThrow('Error while interpolating: {{err()}}\nError: oops');
}));
it('should stop interpolation when encountering an exception', inject(function($interpolate, $compile, $rootScope) {
$rootScope.err = function () {
throw new Error('oops');
};
var dom = jqLite('<div>{{1 + 1}}</div><div>{{err()}}</div><div>{{1 + 2}}</div>');
$compile(dom)($rootScope);
expect(function () {
$rootScope.$apply();
}).toThrow('Error while interpolating: {{err()}}\nError: oops');
expect(dom[0].innerHTML).toEqual('2');
expect(dom[1].innerHTML).toEqual('{{err()}}');
expect(dom[2].innerHTML).toEqual('{{1 + 2}}');
}));
it('should return interpolation function', inject(function($interpolate, $rootScope) {
$rootScope.name = 'Misko';
+8 -45
View File
@@ -1,24 +1,17 @@
'use strict';
function initService(debugEnabled) {
return module(function($logProvider){
$logProvider.debugEnabled(debugEnabled);
});
}
describe('$log', function() {
var $window, logger, log, warn, info, error, debug;
var $window, logger, log, warn, info, error;
beforeEach(module(function($provide){
$window = {navigator: {}, document: {}};
$window = {navigator: {}};
logger = '';
log = function() { logger+= 'log;'; };
warn = function() { logger+= 'warn;'; };
info = function() { logger+= 'info;'; };
error = function() { logger+= 'error;'; };
debug = function() { logger+= 'debug;'; };
$provide.provider('$log', $LogProvider);
$provide.value('$exceptionHandler', angular.mock.rethrow);
@@ -30,16 +23,14 @@ describe('$log', function() {
$window.console = {log: log,
warn: warn,
info: info,
error: error,
debug: debug};
error: error};
},
function($log) {
$log.log();
$log.warn();
$log.info();
$log.error();
$log.debug();
expect(logger).toEqual('log;warn;info;error;debug;');
expect(logger).toEqual('log;warn;info;error;');
}
));
@@ -53,8 +44,7 @@ describe('$log', function() {
$log.warn();
$log.info();
$log.error();
$log.debug();
expect(logger).toEqual('log;log;log;log;log;');
expect(logger).toEqual('log;log;log;log;');
}
));
@@ -65,7 +55,6 @@ describe('$log', function() {
$log.warn();
$log.info();
$log.error();
$log.debug();
}
));
@@ -75,48 +64,22 @@ describe('$log', function() {
log.apply = log.call =
warn.apply = warn.call =
info.apply = info.call =
error.apply = error.call =
debug.apply = debug.call = null;
error.apply = error.call = null;
$window.console = {log: log,
warn: warn,
info: info,
error: error,
debug: debug};
error: error};
},
function($log) {
$log.log.apply($log);
$log.warn.apply($log);
$log.info.apply($log);
$log.error.apply($log);
$log.debug.apply($log);
expect(logger).toEqual('log;warn;info;error;debug;');
expect(logger).toEqual('log;warn;info;error;');
})
);
describe("$log.debug", function () {
beforeEach(initService(false));
it("should skip debugging output if disabled", inject(
function(){
$window.console = {log: log,
warn: warn,
info: info,
error: error,
debug: debug};
},
function($log) {
$log.log();
$log.warn();
$log.info();
$log.error();
$log.debug();
expect(logger).toEqual('log;warn;info;error;');
}
));
});
describe('$log.error', function() {
var e, $log, errorArgs;
+3 -81
View File
@@ -91,8 +91,8 @@ describe('parser', function() {
expect(tokens[1].text).toEqual('b');
});
it('should tokenize relation and equality', function() {
var tokens = lex("! == != < > <= >= === !==");
it('should tokenize relation', function() {
var tokens = lex("! == != < > <= >=");
expect(tokens[0].text).toEqual('!');
expect(tokens[1].text).toEqual('==');
expect(tokens[2].text).toEqual('!=');
@@ -100,8 +100,6 @@ describe('parser', function() {
expect(tokens[4].text).toEqual('>');
expect(tokens[5].text).toEqual('<=');
expect(tokens[6].text).toEqual('>=');
expect(tokens[7].text).toEqual('===');
expect(tokens[8].text).toEqual('!==');
});
it('should tokenize statements', function() {
@@ -199,20 +197,12 @@ describe('parser', function() {
expect(scope.$eval("false")).toBeFalsy();
expect(scope.$eval("!true")).toBeFalsy();
expect(scope.$eval("1==1")).toBeTruthy();
expect(scope.$eval("1==true")).toBeTruthy();
expect(scope.$eval("1===1")).toBeTruthy();
expect(scope.$eval("1==='1'")).toBeFalsy();
expect(scope.$eval("1===true")).toBeFalsy();
expect(scope.$eval("'true'===true")).toBeFalsy();
expect(scope.$eval("1!==2")).toBeTruthy();
expect(scope.$eval("1!=='1'")).toBeTruthy();
expect(scope.$eval("1!=2")).toBeTruthy();
expect(scope.$eval("1<2")).toBeTruthy();
expect(scope.$eval("1<=1")).toBeTruthy();
expect(scope.$eval("1>2")).toEqual(1>2);
expect(scope.$eval("2>=1")).toEqual(2>=1);
expect(scope.$eval("true==2<3")).toEqual(true == 2<3);
expect(scope.$eval("true===2<3")).toEqual(true === 2<3);
expect(scope.$eval("true==2<3")).toEqual(true === 2<3);
});
it('should parse logical', function() {
@@ -671,74 +661,6 @@ describe('parser', function() {
expect($parse('a.b')({a: {b: 0}}, {a: null})).toEqual(undefined);
}));
});
describe('literal', function() {
it('should mark scalar value expressions as literal', inject(function($parse) {
expect($parse('0').literal).toBe(true);
expect($parse('"hello"').literal).toBe(true);
expect($parse('true').literal).toBe(true);
expect($parse('false').literal).toBe(true);
expect($parse('null').literal).toBe(true);
expect($parse('undefined').literal).toBe(true);
}));
it('should mark array expressions as literal', inject(function($parse) {
expect($parse('[]').literal).toBe(true);
expect($parse('[1, 2, 3]').literal).toBe(true);
expect($parse('[1, identifier]').literal).toBe(true);
}));
it('should mark object expressions as literal', inject(function($parse) {
expect($parse('{}').literal).toBe(true);
expect($parse('{x: 1}').literal).toBe(true);
expect($parse('{foo: bar}').literal).toBe(true);
}));
it('should not mark function calls or operator expressions as literal', inject(function($parse) {
expect($parse('1 + 1').literal).toBe(false);
expect($parse('call()').literal).toBe(false);
expect($parse('[].length').literal).toBe(false);
}));
});
describe('constant', function() {
it('should mark scalar value expressions as constant', inject(function($parse) {
expect($parse('12.3').constant).toBe(true);
expect($parse('"string"').constant).toBe(true);
expect($parse('true').constant).toBe(true);
expect($parse('false').constant).toBe(true);
expect($parse('null').constant).toBe(true);
expect($parse('undefined').constant).toBe(true);
}));
it('should mark arrays as constant if they only contain constant elements', inject(function($parse) {
expect($parse('[]').constant).toBe(true);
expect($parse('[1, 2, 3]').constant).toBe(true);
expect($parse('["string", null]').constant).toBe(true);
expect($parse('[[]]').constant).toBe(true);
expect($parse('[1, [2, 3], {4: 5}]').constant).toBe(true);
}));
it('should not mark arrays as constant if they contain any non-constant elements', inject(function($parse) {
expect($parse('[foo]').constant).toBe(false);
expect($parse('[x + 1]').constant).toBe(false);
expect($parse('[bar[0]]').constant).toBe(false);
}));
it('should mark complex expressions involving constant values as constant', inject(function($parse) {
expect($parse('!true').constant).toBe(true);
expect($parse('1 - 1').constant).toBe(true);
expect($parse('"foo" + "bar"').constant).toBe(true);
expect($parse('5 != null').constant).toBe(true);
expect($parse('{standard: 4/3, wide: 16/9}').constant).toBe(true);
}));
it('should not mark any expression involving variables or function calls as constant', inject(function($parse) {
expect($parse('true.toString()').constant).toBe(false);
expect($parse('foo(1, 2, 3)').constant).toBe(false);
expect($parse('"name" + id').constant).toBe(false);
}));
});
});
});
});
+4 -16
View File
@@ -99,14 +99,6 @@ describe('Scope', function() {
expect(spy).wasCalled();
}));
it('should not keep constant expressions on watch queue', inject(function($rootScope) {
$rootScope.$watch('1 + 1', function() {});
expect($rootScope.$$watchers.length).toEqual(1);
$rootScope.$digest();
expect($rootScope.$$watchers.length).toEqual(0);
}));
it('should delegate exceptions', function() {
module(function($exceptionHandlerProvider) {
@@ -127,14 +119,10 @@ describe('Scope', function() {
var log = '';
$rootScope.$watch('a', function() { log += 'a'; });
$rootScope.$watch('b', function() { log += 'b'; });
// constant expressions have slightly different handling,
// let's ensure they are kept in the same list as others
$rootScope.$watch('1', function() { log += '1'; });
$rootScope.$watch('c', function() { log += 'c'; });
$rootScope.$watch('2', function() { log += '2'; });
$rootScope.a = $rootScope.b = $rootScope.c = 1;
$rootScope.$digest();
expect(log).toEqual('ab1c2');
expect(log).toEqual('abc');
}));
@@ -228,7 +216,7 @@ describe('Scope', function() {
});
it('should prevent infinite recursion and print watcher function name or body',
it('should prevent infinite recursion and print print watcher function name or body',
inject(function($rootScope) {
$rootScope.$watch(function watcherA() {return $rootScope.a;}, function() {$rootScope.b++;});
$rootScope.$watch(function() {return $rootScope.b;}, function() {$rootScope.a++;});
@@ -340,7 +328,7 @@ describe('Scope', function() {
}));
it('should always call the watcher with newVal and oldVal equal on the first run',
it('should always call the watchr with newVal and oldVal equal on the first run',
inject(function($rootScope) {
var log = [];
function logger(scope, newVal, oldVal) {
@@ -469,7 +457,7 @@ describe('Scope', function() {
child.$evalAsync(function(scope) { log += 'child.async;'; });
child.$watch('value', function() { log += 'child.$digest;'; });
$rootScope.$digest();
expect(log).toEqual('parent.async;child.async;parent.$digest;child.$digest;');
expect(log).toEqual('parent.async;parent.$digest;child.async;child.$digest;');
}));
it('should cause a $digest rerun', inject(function($rootScope) {
-104
View File
@@ -59,63 +59,6 @@ describe('$route', function() {
});
});
it('should route and fire change event when catch-all params are used', function() {
var log = '',
lastRoute,
nextRoute;
module(function($routeProvider) {
$routeProvider.when('/Book1/:book/Chapter/:chapter/*highlight/edit',
{controller: noop, templateUrl: 'Chapter.html'});
$routeProvider.when('/Book2/:book/*highlight/Chapter/:chapter',
{controller: noop, templateUrl: 'Chapter.html'});
$routeProvider.when('/Blank', {});
});
inject(function($route, $location, $rootScope) {
$rootScope.$on('$routeChangeStart', function(event, next, current) {
log += 'before();';
expect(current).toBe($route.current);
lastRoute = current;
nextRoute = next;
});
$rootScope.$on('$routeChangeSuccess', function(event, current, last) {
log += 'after();';
expect(current).toBe($route.current);
expect(lastRoute).toBe(last);
expect(nextRoute).toBe(current);
});
$location.path('/Book1/Moby/Chapter/Intro/one/edit').search('p=123');
$rootScope.$digest();
$httpBackend.flush();
expect(log).toEqual('before();after();');
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one', p:'123'});
log = '';
$location.path('/Blank').search('ignore');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current.params).toEqual({ignore:true});
log = '';
$location.path('/Book1/Moby/Chapter/Intro/one/two/edit').search('p=123');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
log = '';
$location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', highlight:'one/two', p:'123'});
log = '';
$location.path('/NONE');
$rootScope.$digest();
expect(log).toEqual('before();after();');
expect($route.current).toEqual(null);
});
});
it('should not change route when location is canceled', function() {
module(function($routeProvider) {
@@ -772,53 +715,6 @@ describe('$route', function() {
});
it('should allow using a function as a template', function() {
var customTemplateWatcher = jasmine.createSpy('customTemplateWatcher');
function customTemplateFn(routePathParams) {
customTemplateWatcher(routePathParams);
expect(routePathParams).toEqual({id: 'id3'});
return '<h1>' + routePathParams.id + '</h1>';
}
module(function($routeProvider){
$routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
$routeProvider.when('/foo/:id', {template: customTemplateFn});
});
inject(function($route, $location, $rootScope) {
$location.path('/foo/id3');
$rootScope.$digest();
expect(customTemplateWatcher).toHaveBeenCalledWith({id: 'id3'});
});
});
it('should allow using a function as a templateUrl', function() {
var customTemplateUrlWatcher = jasmine.createSpy('customTemplateUrlWatcher');
function customTemplateUrlFn(routePathParams) {
customTemplateUrlWatcher(routePathParams);
expect(routePathParams).toEqual({id: 'id3'});
return 'foo.html';
}
module(function($routeProvider){
$routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'});
$routeProvider.when('/foo/:id', {templateUrl: customTemplateUrlFn});
});
inject(function($route, $location, $rootScope) {
$location.path('/foo/id3');
$rootScope.$digest();
expect(customTemplateUrlWatcher).toHaveBeenCalledWith({id: 'id3'});
expect($route.current.loadedTemplateUrl).toEqual('foo.html');
});
});
describe('reload', function() {
it('should reload even if reloadOnSearch is false', function() {
+6 -24
View File
@@ -2,10 +2,9 @@
describe('$sniffer', function() {
function sniffer($window, $document) {
function sniffer($window) {
$window.navigator = {};
$document = jqLite($document || {});
return new $SnifferProvider().$get[2]($window, $document);
return new $SnifferProvider().$get[1]($window);
}
describe('history', function() {
@@ -21,15 +20,15 @@ describe('$sniffer', function() {
describe('hashchange', function() {
it('should be true if onhashchange property defined', function() {
expect(sniffer({onhashchange: true}, {}).hashchange).toBe(true);
expect(sniffer({onhashchange: true, document: {}}).hashchange).toBe(true);
});
it('should be false if onhashchange property not defined', function() {
expect(sniffer({}, {}).hashchange).toBe(false);
expect(sniffer({document: {}}).hashchange).toBe(false);
});
it('should be false if documentMode is 7 (IE8 comp mode)', function() {
expect(sniffer({onhashchange: true}, {documentMode: 7}).hashchange).toBe(false);
expect(sniffer({onhashchange: true, document: {documentMode: 7}}).hashchange).toBe(false);
});
});
@@ -43,7 +42,7 @@ describe('$sniffer', function() {
if (elm === 'div') return mockDivElement;
});
$sniffer = sniffer({}, mockDocument);
$sniffer = sniffer({document: mockDocument});
});
@@ -79,21 +78,4 @@ describe('$sniffer', function() {
expect($sniffer.hasEvent('input')).toBe((msie == 9) ? false : true);
});
});
describe('csp', function() {
it('should be false if document.securityPolicy.isActive not available', function() {
expect(sniffer({}, {}).csp).toBe(false);
});
it('should use document.securityPolicy.isActive if available', function() {
var createDocumentWithCSP = function(csp) {
return {securityPolicy: {isActive: csp}};
};
expect(sniffer({}, createDocumentWithCSP(false)).csp).toBe(false);
expect(sniffer({}, createDocumentWithCSP(true)).csp).toBe(true);
});
});
});
-24
View File
@@ -109,13 +109,6 @@ describe('ngMock', function() {
});
it('should fake getMilliseconds method', function() {
expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.003Z').getMilliseconds()).toBe(3);
expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.023Z').getMilliseconds()).toBe(23);
expect(new angular.mock.TzDate(0, '2010-09-03T23:05:08.123Z').getMilliseconds()).toBe(123);
});
it('should create a date representing new year in Bratislava', function() {
var newYearInBratislava = new angular.mock.TzDate(-1, '2009-12-31T23:00:00.000Z');
expect(newYearInBratislava.getTimezoneOffset()).toBe(-60);
@@ -124,7 +117,6 @@ describe('ngMock', function() {
expect(newYearInBratislava.getDate()).toBe(1);
expect(newYearInBratislava.getHours()).toBe(0);
expect(newYearInBratislava.getMinutes()).toBe(0);
expect(newYearInBratislava.getSeconds()).toBe(0);
});
@@ -335,22 +327,6 @@ describe('ngMock', function() {
$timeout.flush();
expect(logger).toEqual(['t1', 't3', 't2']);
}));
it('should throw an exception when not flushed', inject(function($timeout){
$timeout(noop);
var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}';
expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(expectedError);
}));
it('should do nothing when all tasks have been flushed', inject(function($timeout) {
$timeout(noop);
$timeout.flush();
expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow();
}));
});
+7 -297
View File
@@ -14,14 +14,7 @@ describe("resource", function() {
},
patch: {
method: 'PATCH'
},
conditionalPut: {
method: 'PUT',
headers: {
'If-None-Match': '*'
}
}
});
callback = jasmine.createSpy();
}));
@@ -119,33 +112,14 @@ describe("resource", function() {
it('should not encode @ in url params', function() {
//encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt
//with regards to the character set (pchar) allowed in path segments
//so we need this test to make sure that we don't over-encode the params and break stuff like
//buzz api which uses @self
//encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt
//with regards to the character set (pchar) allowed in path segments
//so we need this test to make sure that we don't over-encode the params and break stuff like
//buzz api which uses @self
var R = $resource('/Path/:a');
$httpBackend.expect('GET', '/Path/doh@fo%20o?!do%26h=g%3Da+h&:bar=$baz@1').respond('{}');
R.get({a: 'doh@fo o', ':bar': '$baz@1', '!do&h': 'g=a h'});
});
it('should encode array params', function() {
var R = $resource('/Path/:a');
$httpBackend.expect('GET', '/Path/doh&foo?bar=baz1&bar=baz2').respond('{}');
R.get({a: 'doh&foo', bar: ['baz1', 'baz2']});
});
it('should allow relative paths in resource url', function () {
var R = $resource(':relativePath');
$httpBackend.expect('GET', 'data.json').respond('{}');
R.get({ relativePath: 'data.json' });
});
it('should handle + in url params', function () {
var R = $resource('/api/myapp/:myresource?from=:from&to=:to&histlen=:histlen');
$httpBackend.expect('GET', '/api/myapp/pear+apple?from=2012-04-01&to=2012-04-29&histlen=3').respond('{}');
R.get({ myresource: 'pear+apple', from : '2012-04-01', to : '2012-04-29', histlen : 3 });
$httpBackend.expect('GET', '/Path/doh@fo%20o?!do%26h=g%3Da+h&:bar=$baz@1').respond('{}');
R.get({a: 'doh@fo o', ':bar': '$baz@1', '!do&h': 'g=a h'});
});
@@ -239,15 +213,6 @@ describe("resource", function() {
});
it('should send correct headers', function() {
$httpBackend.expectPUT('/CreditCard/123', undefined, function(headers) {
return headers['If-None-Match'] == "*";
}).respond({id:123});
CreditCard.conditionalPut({id: {key:123}});
});
it("should read partial resource", function() {
$httpBackend.expect('GET', '/CreditCard').respond([{id:{key:123}}]);
var ccs = CreditCard.query();
@@ -283,7 +248,7 @@ describe("resource", function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
var ccs = CreditCard.query({key: 'value'}, callback);
expect(ccs).toEqualData([]);
expect(ccs).toEqual([]);
expect(callback).not.toHaveBeenCalled();
$httpBackend.flush();
@@ -400,35 +365,6 @@ describe("resource", function() {
});
it('should support dynamic default parameters (global)', function() {
var currentGroup = 'students',
Person = $resource('/Person/:group/:id', { group: function() { return currentGroup; }});
$httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'});
var fedor = Person.get({id: 'fedor'});
$httpBackend.flush();
expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com'});
});
it('should support dynamic default parameters (action specific)', function() {
var currentGroup = 'students',
Person = $resource('/Person/:group/:id', {}, {
fetch: {method: 'GET', params: {group: function() { return currentGroup; }}}
});
$httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'});
var fedor = Person.fetch({id: 'fedor'});
$httpBackend.flush();
expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com'});
});
it('should exercise full stack', function() {
var Person = $resource('/Person/:id');
@@ -439,210 +375,6 @@ describe("resource", function() {
});
describe('promise api', function() {
var $rootScope;
beforeEach(inject(function(_$rootScope_) {
$rootScope = _$rootScope_;
}));
describe('single resource', function() {
it('should add promise $then method to the result object', function() {
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
var cc = CreditCard.get({id: 123});
cc.$then(callback);
expect(callback).not.toHaveBeenCalled();
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
expect(response).toEqualData({
data: {id: 123, number: '9876'},
status: 200,
config: {method: 'GET', data: undefined, url: '/CreditCard/123'},
resource: {id: 123, number: '9876', $resolved: true}
});
expect(typeof response.resource.$save).toBe('function');
});
it('should keep $then around after promise resolution', function() {
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
var cc = CreditCard.get({id: 123});
cc.$then(callback);
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
callback.reset();
cc.$then(callback);
$rootScope.$apply(); //flush async queue
expect(callback).toHaveBeenCalledOnceWith(response);
});
it('should allow promise chaining via $then method', function() {
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
var cc = CreditCard.get({id: 123});
cc.$then(function(response) { return 'new value'; }).then(callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnceWith('new value');
});
it('should allow error callback registration via $then method', function() {
$httpBackend.expect('GET', '/CreditCard/123').respond(404, 'resource not found');
var cc = CreditCard.get({id: 123});
cc.$then(null, callback);
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
expect(response).toEqualData({
data : 'resource not found',
status : 404,
config : { method : 'GET', data : undefined, url : '/CreditCard/123' }
});
});
it('should add $resolved boolean field to the result object', function() {
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
var cc = CreditCard.get({id: 123});
expect(cc.$resolved).toBe(false);
cc.$then(callback);
expect(cc.$resolved).toBe(false);
$httpBackend.flush();
expect(cc.$resolved).toBe(true);
});
it('should set $resolved field to true when an error occurs', function() {
$httpBackend.expect('GET', '/CreditCard/123').respond(404, 'resource not found');
var cc = CreditCard.get({id: 123});
cc.$then(null, callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(cc.$resolved).toBe(true);
});
});
describe('resource collection', function() {
it('should add promise $then method to the result object', function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
var ccs = CreditCard.query({key: 'value'});
ccs.$then(callback);
expect(callback).not.toHaveBeenCalled();
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
expect(response).toEqualData({
data: [{id: 1}, {id :2}],
status: 200,
config: {method: 'GET', data: undefined, url: '/CreditCard', params: {key: 'value'}},
resource: [ { id : 1 }, { id : 2 } ]
});
expect(typeof response.resource[0].$save).toBe('function');
expect(typeof response.resource[1].$save).toBe('function');
});
it('should keep $then around after promise resolution', function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
var ccs = CreditCard.query({key: 'value'});
ccs.$then(callback);
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
callback.reset();
ccs.$then(callback);
$rootScope.$apply(); //flush async queue
expect(callback).toHaveBeenCalledOnceWith(response);
});
it('should allow promise chaining via $then method', function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
var ccs = CreditCard.query({key: 'value'});
ccs.$then(function(response) { return 'new value'; }).then(callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnceWith('new value');
});
it('should allow error callback registration via $then method', function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond(404, 'resource not found');
var ccs = CreditCard.query({key: 'value'});
ccs.$then(null, callback);
$httpBackend.flush();
var response = callback.mostRecentCall.args[0];
expect(response).toEqualData({
data : 'resource not found',
status : 404,
config : { method : 'GET', data : undefined, url : '/CreditCard', params: {key: 'value'}}
});
});
it('should add $resolved boolean field to the result object', function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
var ccs = CreditCard.query({key: 'value'}, callback);
expect(ccs.$resolved).toBe(false);
ccs.$then(callback);
expect(ccs.$resolved).toBe(false);
$httpBackend.flush();
expect(ccs.$resolved).toBe(true);
});
it('should set $resolved field to true when an error occurs', function() {
$httpBackend.expect('GET', '/CreditCard?key=value').respond(404, 'resource not found');
var ccs = CreditCard.query({key: 'value'});
ccs.$then(null, callback);
$httpBackend.flush();
expect(callback).toHaveBeenCalledOnce();
expect(ccs.$resolved).toBe(true);
});
});
});
describe('failure mode', function() {
var ERROR_CODE = 500,
ERROR_RESPONSE = 'Server Error',
@@ -675,26 +407,4 @@ describe("resource", function() {
expect(callback).not.toHaveBeenCalled();
});
});
it('should transform request/response', function() {
var Person = $resource('/Person/:id', {}, {
save: {
method: 'POST',
params: {id: '@id'},
transformRequest: function(data) {
return angular.toJson({ __id: data.id });
},
transformResponse: function(data) {
return { id: data.__id };
}
}
});
$httpBackend.expect('POST', '/Person/123', { __id: 123 }).respond({ __id: 456 });
var person = new Person({id:123});
person.$save();
$httpBackend.flush();
expect(person.id).toEqual(456);
});
});
+2 -5
View File
@@ -1,11 +1,8 @@
'use strict';
describe('ngBindHtml', function() {
beforeEach(module('ngSanitize'));
it('should set html', inject(function($rootScope, $compile) {
var element = $compile('<div ng-bind-html="html"></div>')($rootScope);
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
$rootScope.html = '<div unknown>hello</div>';
$rootScope.$digest();
expect(angular.lowercase(element.html())).toEqual('<div>hello</div>');
@@ -13,7 +10,7 @@ describe('ngBindHtml', function() {
it('should reset html when value is null or undefined', inject(function($compile, $rootScope) {
var element = $compile('<div ng-bind-html="html"></div>')($rootScope);
element = $compile('<div ng-bind-html="html"></div>')($rootScope);
angular.forEach([null, undefined, ''], function(val) {
$rootScope.html = 'some val';
-7
View File
@@ -24,11 +24,4 @@ describe('linky', function() {
expect(linky("send email to me@example.com, but")).
toEqual('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
});
it('should handle target:', function() {
expect(linky("http://example.com", "_blank")).
toEqual('<a target="_blank" href="http://example.com">http://example.com</a>')
expect(linky("http://example.com", "someNamedIFrame")).
toEqual('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>')
});
});
+2 -3
View File
@@ -188,7 +188,7 @@ describe('HTML', function() {
expect(html).toEqual('<div>');
});
describe('explicitly disallow', function() {
describe('explicitly dissallow', function() {
it('should not allow attributes', function() {
writer.start('div', {id:'a', name:'a', style:'a'});
expect(html).toEqual('<div>');
@@ -230,11 +230,10 @@ describe('HTML', function() {
expect(isUri('https://abc')).toBeTruthy();
expect(isUri('ftp://abc')).toBeTruthy();
expect(isUri('mailto:me@example.com')).toBeTruthy();
expect(isUri('tel:123-123-1234')).toBeTruthy();
expect(isUri('#anchor')).toBeTruthy();
});
it('should not be URI', function() {
it('should not be UIR', function() {
expect(isUri('')).toBeFalsy();
expect(isUri('javascript:alert')).toBeFalsy();
});
-62
View File
@@ -270,16 +270,6 @@ describe("angular.scenario.dsl", function() {
expect($root.futureError).toMatch(/did not match/);
});
it('should fail to select an option that does not exist', function(){
doc.append(
'<select ng-model="test">' +
' <option value=A>one</option>' +
' <option value=B selected>two</option>' +
'</select>'
);
$root.dsl.select('test').option('three');
expect($root.futureError).toMatch(/not found/);
});
});
describe('Element', function() {
@@ -315,58 +305,6 @@ describe("angular.scenario.dsl", function() {
dealoc(elm);
});
it('should execute dblclick', function() {
var clicked;
// Hash is important, otherwise we actually
// go to a different page and break the runner
doc.append('<a href="#"></a>');
doc.find('a').dblclick(function() {
clicked = true;
});
$root.dsl.element('a').dblclick();
});
it('should navigate page if dblclick on anchor', function() {
expect($window.location).not.toEqual('#foo');
doc.append('<a href="#foo"></a>');
$root.dsl.element('a').dblclick();
expect($window.location).toMatch(/#foo$/);
});
it('should not navigate if dblclick event was cancelled', function() {
var initLocation = $window.location,
elm = jqLite('<a href="#foo"></a>');
doc.append(elm);
elm.bind('dblclick', function(event) {
event.preventDefault();
});
$root.dsl.element('a').dblclick();
expect($window.location).toBe(initLocation);
dealoc(elm);
});
it('should execute mouseover', function() {
var mousedOver;
doc.append('<div></div>');
doc.find('div').mouseover(function() {
mousedOver = true;
});
$root.dsl.element('div').mouseover();
expect(mousedOver).toBe(true);
});
it('should bubble up the mouseover event', function() {
var mousedOver;
doc.append('<div id="outer"><div id="inner"></div></div>');
doc.find('#outer').mouseover(function() {
mousedOver = true;
});
$root.dsl.element('#inner').mouseover();
expect(mousedOver).toBe(true);
});
it('should count matching elements', function() {
doc.append('<span></span><span></span>');
$root.dsl.element('span').count();
+1 -14
View File
@@ -1,6 +1,6 @@
'use strict';
describe('angular.scenario.output.xml', function() {
describe('angular.scenario.output.json', function() {
var output, context;
var runner, model, $window;
var spec, step;
@@ -33,17 +33,4 @@ describe('angular.scenario.output.xml', function() {
expect(context.find('it').attr('status')).toEqual('success');
expect(context.find('it step').attr('status')).toEqual('success');
});
it('should output errors to the XML', function() {
runner.emit('SpecBegin', spec);
runner.emit('StepBegin', spec, step);
runner.emit('StepFailure', spec, step, 'error reason');
runner.emit('StepEnd', spec, step);
runner.emit('SpecEnd', spec);
runner.emit('RunnerEnd');
expect(context.find('it').attr('status')).toEqual('failure');
expect(context.find('it step').attr('status')).toEqual('failure');
expect(context.find('it step').text()).toEqual('error reason');
});
});
+2 -2
View File
@@ -13,7 +13,7 @@ var util = require('util');
var MAX_LENGTH = 70;
var PATTERN = /^(?:fixup!\s*)?(\w*)(\(([\w\$\.\-\*/]*)\))?\: (.*)$/;
var PATTERN = /^(\w*)(\(([\w\$\.\-\*/]*)\))?\: (.*)$/;
var IGNORED = /^WIP\:/;
var TYPES = {
feat: true,
@@ -51,7 +51,7 @@ var validateMessage = function(message) {
var match = PATTERN.exec(message);
if (!match) {
error('does not match "<type>(<scope>): <subject>" ! was: ' + message);
error('does not match "<type>(<scope>): <subject>" !');
return false;
}
+1 -2
View File
@@ -22,7 +22,6 @@ describe('validate-commit-msg.js', function() {
describe('validateMessage', function() {
it('should be valid', function() {
expect(m.validateMessage('fixup! fix($compile): something')).toBe(VALID);
expect(m.validateMessage('fix($compile): something')).toBe(VALID);
expect(m.validateMessage('feat($location): something')).toBe(VALID);
expect(m.validateMessage('docs($filter): something')).toBe(VALID);
@@ -51,7 +50,7 @@ describe('validate-commit-msg.js', function() {
var msg = 'not correct format';
expect(m.validateMessage(msg)).toBe(INVALID);
expect(errors).toEqual(['INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: not correct format']);
expect(errors).toEqual(['INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" !']);
});
+3 -3
View File
@@ -1,5 +1,5 @@
# AngularJS build config file
---
version: 1.1.3
codename: radioactive-gargle
stable: 1.0.4
version: 1.0.4
codename: bewildering-hair
stable: 1.0.3