Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38fb542838 | |||
| 7ab5098c14 | |||
| bcca80548d | |||
| 736c8fbbae | |||
| 947562220d | |||
| 333523483f | |||
| 68ceb17272 | |||
| 5bd6596856 | |||
| b3f2a20832 | |||
| e8d8c7a8d7 | |||
| 7a91d7fa7e | |||
| c6bd58eb58 | |||
| c2e45c769e | |||
| b08427dde9 | |||
| ffd075b440 | |||
| 3fcd228441 | |||
| 8383ecfcdf | |||
| eed2333298 | |||
| a2809dacc4 | |||
| b837a31afa | |||
| 66b0fcd3c0 | |||
| 2efe82309a | |||
| a090400f09 | |||
| 84e0eea164 | |||
| bcf12e70e5 | |||
| 1ca98b2c09 |
@@ -17,3 +17,4 @@ angular.xcodeproj
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
/tmp/
|
||||
/scripts/bower/bower-*
|
||||
|
||||
+3
-1
@@ -7,11 +7,13 @@ env:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
- SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready
|
||||
- BROWSER_STACK_USERNAME=VojtaJina
|
||||
- BROWSER_STACK_ACCESS_KEY=HAfHZaypxAc3PEUrUU9a
|
||||
- LOGS_DIR=/tmp/angular-build/logs
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
- ./lib/sauce/sauce_connect_setup.sh
|
||||
- ./lib/browser-stack/start-tunnel.sh
|
||||
- npm install -g grunt-cli
|
||||
- grunt bower
|
||||
- grunt bower
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
<a name="1.2.3"></a>
|
||||
# 1.2.3 unicorn-zapper (2013-11-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- ensure blocked keyframe animations are unblocked before the DOM operation
|
||||
([2efe8230](https://github.com/angular/angular.js/commit/2efe82309ac8ff4f67df8b6e40a539ea31e15804),
|
||||
[#5106](https://github.com/angular/angular.js/issues/5106))
|
||||
- ensure animations are disabled during bootstrap to prevent unwanted structural animations
|
||||
([eed23332](https://github.com/angular/angular.js/commit/eed2333298412fbad04eda97ded3487c845b9eb9),
|
||||
[#5130](https://github.com/angular/angular.js/issues/5130))
|
||||
- **$sanitize:** use the same whitelist mechanism as `$compile` does
|
||||
([33352348](https://github.com/angular/angular.js/commit/333523483f3ce6dd3177b697a5e5a7177ca364c8),
|
||||
[#3748](https://github.com/angular/angular.js/issues/3748))
|
||||
- **input:** react to form auto completion, through the `change` event, on modern browsers
|
||||
([a090400f](https://github.com/angular/angular.js/commit/a090400f09d7993d102f527609879cdc74abae60),
|
||||
[#1460](https://github.com/angular/angular.js/issues/1460))
|
||||
- **$attrs:** add `$attrs.$attr` to externs so that it isn't renamed on js minification
|
||||
([bcca8054](https://github.com/angular/angular.js/commit/bcca80548dde85ffe3838c943ba8e5c2deb1c721))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
No new features in this release
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
There are no breaking changes in this release (promise!)
|
||||
|
||||
|
||||
|
||||
<a name="1.2.2"></a>
|
||||
# 1.2.2 consciousness-inertia (2013-11-22)
|
||||
|
||||
|
||||
+94
-64
@@ -32,29 +32,108 @@ project.
|
||||
## Submission Guidelines
|
||||
|
||||
### Submitting an Issue
|
||||
Before you submit your issue follow the following guidelines:
|
||||
|
||||
* Search the archive first, it's likely that your question was already answered.
|
||||
* A live example demonstrating the issue, will get an answer faster.
|
||||
* Create one using [Plunker][plunker] or [JSFiddle][jsfiddle].
|
||||
* If you get help, help others. Good karma rulez!
|
||||
Before you submit your issue search the archive, maybe your question was already answered.
|
||||
|
||||
If your issue appears to be a bug, and hasn't been reported, open a new issue.
|
||||
Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues.
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
|
||||
* **Related issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069
|
||||
|
||||
**If you get help, help others. Good karma rulez!**
|
||||
|
||||
### Submitting a Pull Request
|
||||
Before you submit your pull request follow the following guidelines:
|
||||
Before you submit your pull request consider the following guidelines:
|
||||
|
||||
* Search GitHub for an open or closed Pull Request that relates to your submission. You don't want
|
||||
to duplicate effort.
|
||||
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
requests. We cannot accept code without this.
|
||||
* Make your changes in a new git branch
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
* Create your patch, including appropriate test cases.
|
||||
* Follow our Coding Rules
|
||||
* Follow our Git Commit Guidelines
|
||||
* Build your changes locally and on Travis (by pushing to GitHub) to ensure all the tests pass.
|
||||
* Sign the Contributor License Agreement (CLA). We cannot accept code without this.
|
||||
* Commit your changes and create a descriptive commit message (the
|
||||
commit message is used to generate release notes, please check out our
|
||||
[commit message conventions](#commit-message-format) and our commit message presubmit hook
|
||||
`validate-commit-msg.js`):
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
|
||||
* Build your changes locally to ensure all the tests pass
|
||||
|
||||
```shell
|
||||
grunt test
|
||||
```
|
||||
|
||||
* Push your branch to Github:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
* In Github, send a pull request to `angular:master`.
|
||||
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
|
||||
repository to update the Pull Request.
|
||||
repository to update the Pull Request:
|
||||
|
||||
```shell
|
||||
git rebase master -i
|
||||
git push -f
|
||||
```
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
* Delete the remote branch on Github:
|
||||
|
||||
```shell
|
||||
git push origin --delete my-fix-branch
|
||||
```
|
||||
|
||||
* Check out the master branch:
|
||||
|
||||
```shell
|
||||
git checkout master -f
|
||||
```
|
||||
|
||||
* Delete the local branch:
|
||||
|
||||
```shell
|
||||
git branch -D my-fix-branch
|
||||
```
|
||||
|
||||
* Update your master with the latest upstream version:
|
||||
|
||||
```shell
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
### GitHub Pull Request Helper
|
||||
|
||||
We track Pull Requests by attaching labels and assigning to milestones. For some reason GitHub
|
||||
does not provide a good UI for managing labels on Pull Requests (unlike Issues). We have developed
|
||||
a simple Chrome Extension that enables you to view (and manage if you have permission) the labels
|
||||
on Pull Requests. You can get the extension from the Chrome WebStore -
|
||||
[GitHub PR Helper][github-pr-helper]
|
||||
|
||||
## Coding Rules
|
||||
To ensure consistency throughout the source code, keep these rules in mind as you are working:
|
||||
@@ -146,56 +225,6 @@ You can find out more detailed information about contributing in the
|
||||
[AngularJS documentation][contributing].
|
||||
|
||||
|
||||
## Submitting Your Changes
|
||||
|
||||
To create and submit a change:
|
||||
|
||||
1. Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
requests.
|
||||
|
||||
2. Create and checkout a new branch off the master branch for your changes:
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
3. Create your patch, including appropriate test cases.
|
||||
|
||||
4. Commit your changes and create a descriptive commit message (the commit message is used to
|
||||
generate release notes, please check out our [commit message conventions](#commit-message-format)
|
||||
and our commit message presubmit hook `validate-commit-msg.js`):
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
|
||||
5. Push your branch to Github:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
6. In Github, send a pull request to `angular:master`.
|
||||
|
||||
That's it! Thank you for your contribution!
|
||||
|
||||
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
|
||||
from the main (upstream) repository:
|
||||
|
||||
```shell
|
||||
# Delete the remote branch on Github:
|
||||
git push origin :my-fix-branch
|
||||
|
||||
# Check out the master branch:
|
||||
git checkout master
|
||||
|
||||
# Delete the local branch:
|
||||
git branch -D my-fix-branch
|
||||
|
||||
# Update your master with the latest upstream version:
|
||||
git pull --ff upstream master
|
||||
```
|
||||
|
||||
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
|
||||
@@ -214,3 +243,4 @@ git pull --ff upstream master
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
|
||||
|
||||
+1
-1
@@ -178,7 +178,7 @@ module.exports = function(grunt) {
|
||||
},
|
||||
mocks: {
|
||||
dest: 'build/angular-mocks.js',
|
||||
src: files['angularModules']['ngMock'],
|
||||
src: util.wrap(files['angularModules']['ngMock'], 'module'),
|
||||
strict: false
|
||||
},
|
||||
sanitize: {
|
||||
|
||||
Vendored
+1
@@ -27,6 +27,7 @@ angularFiles = {
|
||||
'src/ng/parse.js',
|
||||
'src/ng/q.js',
|
||||
'src/ng/rootScope.js',
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/timeout.js',
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
<a name="v1.0.0rc3"></a>
|
||||
# v1.0.0rc3 (2012-03-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
|
||||
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
|
||||
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
|
||||
- **$http:**
|
||||
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
|
||||
- **$log:**
|
||||
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
|
||||
- **$resource:**
|
||||
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
|
||||
- **compiler:**
|
||||
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
|
||||
- **e2e runner:**
|
||||
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
|
||||
- **forEach:**
|
||||
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
|
||||
- **forms:**
|
||||
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
|
||||
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
|
||||
- **init:**
|
||||
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
|
||||
- **json:**
|
||||
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
|
||||
- **matchers.toHaveClass:**
|
||||
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
|
||||
- **ng-switch:**
|
||||
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
|
||||
- **ngDocSpec:**
|
||||
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
|
||||
- **ngForm:**
|
||||
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
|
||||
- **ngRepeat:**
|
||||
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
|
||||
- **ngView:**
|
||||
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
|
||||
- **q:**
|
||||
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
|
||||
- **select:**
|
||||
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- **$controller:**
|
||||
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
|
||||
- **$route:**
|
||||
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
|
||||
- **assertArgFn:**
|
||||
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
|
||||
- **http:**
|
||||
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
|
||||
- **injector:**
|
||||
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
|
||||
- **input.radio:**
|
||||
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
|
||||
- **jqLite:**
|
||||
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
|
||||
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
|
||||
- **ngValue:**
|
||||
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
|
||||
- **scope:**
|
||||
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
|
||||
- **scope.$eval:**
|
||||
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
|
||||
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
|
||||
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
|
||||
|
||||
Vendored
+9
@@ -225,6 +225,11 @@ angular.uppercase = function(s) {};
|
||||
*/
|
||||
angular.Attributes;
|
||||
|
||||
/**
|
||||
* @type {Object.<string, string>}
|
||||
*/
|
||||
angular.Attributes.$attr;
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {string}
|
||||
@@ -1578,6 +1583,7 @@ angular.$q.when = function(value) {};
|
||||
* @typedef {{
|
||||
* resolve: function(*=),
|
||||
* reject: function(*=),
|
||||
* notify: function(*=),
|
||||
* promise: angular.$q.Promise
|
||||
* }}
|
||||
*/
|
||||
@@ -1589,6 +1595,9 @@ angular.$q.Deferred.resolve = function(opt_value) {};
|
||||
/** @param {*=} opt_reason */
|
||||
angular.$q.Deferred.reject = function(opt_reason) {};
|
||||
|
||||
/** @param {*=} opt_value */
|
||||
angular.$q.Deferred.notify = function(opt_value) {};
|
||||
|
||||
/** @type {angular.$q.Promise} */
|
||||
angular.$q.Deferred.promise;
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ To examine the scope in the debugger:
|
||||
2. The debugger allows you to access the currently selected element in the console as `$0`
|
||||
variable.
|
||||
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()`
|
||||
3. To retrieve the associated scope in console execute: `angular.element($0).scope()` or just type $scope
|
||||
|
||||
|
||||
## Scope Events Propagation
|
||||
|
||||
@@ -335,9 +335,8 @@
|
||||
|
||||
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content slide-reveal"></div>
|
||||
|
||||
<div id="disqus" class="disqus">
|
||||
<h2>Discussion</h2>
|
||||
<div id="disqus_thread" class="content-panel-content"></div>
|
||||
<div class="alert alert-info">
|
||||
<a href="http://blog.angularjs.org/2013/11/farewell-disqus.html">Where did Disqus go?</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -680,7 +680,6 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
var currentPageId = $location.path();
|
||||
$scope.partialTitle = $scope.currentPage.shortName;
|
||||
$window._gaq.push(['_trackPageview', currentPageId]);
|
||||
loadDisqus(currentPageId);
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
@@ -892,29 +891,6 @@ docsApp.controller.DocsController = function($scope, $location, $window, $cookie
|
||||
return namespace;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadDisqus(currentPageId) {
|
||||
// http://docs.disqus.com/help/2/
|
||||
window.disqus_shortname = 'angularjs-next';
|
||||
window.disqus_identifier = currentPageId;
|
||||
window.disqus_url = 'http://docs.angularjs.org' + currentPageId;
|
||||
|
||||
if ($location.host() == 'localhost') {
|
||||
return; // don't display disqus on localhost, comment this out if needed
|
||||
//window.disqus_developer = 1;
|
||||
}
|
||||
|
||||
// http://docs.disqus.com/developers/universal/
|
||||
(function() {
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
dsq.src = 'http://angularjs.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] ||
|
||||
document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
|
||||
angular.element(document.getElementById('disqus_thread')).html('');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
+55
-4
@@ -15,6 +15,14 @@ module.exports = function(config, specificOptions) {
|
||||
tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
|
||||
},
|
||||
|
||||
// BrowserStack config for Travis CI
|
||||
browserStack: {
|
||||
startTunnel: false,
|
||||
project: 'AngularJS',
|
||||
name: specificOptions.testName,
|
||||
build: process.env.TRAVIS_BUILD_NUMBER
|
||||
},
|
||||
|
||||
// For more browsers on Sauce Labs see:
|
||||
// https://saucelabs.com/docs/platforms/webdriver
|
||||
customLaunchers: {
|
||||
@@ -49,16 +57,59 @@ module.exports = function(config, specificOptions) {
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 2012',
|
||||
version: '10'
|
||||
},
|
||||
|
||||
'BS_Chrome': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'chrome',
|
||||
os: 'OS X',
|
||||
os_version: 'Mountain Lion'
|
||||
},
|
||||
'BS_Safari': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'safari',
|
||||
os: 'OS X',
|
||||
os_version: 'Mountain Lion'
|
||||
},
|
||||
'BS_Firefox': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'firefox',
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
'BS_IE_8': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '8.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_IE_9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '9.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_IE_10': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '10.0',
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
'BS_IE_11': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '11.0',
|
||||
os: 'Windows',
|
||||
os_version: '8.1'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
// TODO(vojta): remove once SauceLabs supports websockets.
|
||||
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||
config.transports = ['xhr-polling'];
|
||||
|
||||
// Debug logging into a file, that we print out at the end of the build.
|
||||
config.loggers.push({
|
||||
type: 'file',
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
var fs = require('fs');
|
||||
var http = require('http');
|
||||
var BrowserStackTunnel = require('browserstacktunnel-wrapper');
|
||||
|
||||
var HOSTNAME = 'localhost';
|
||||
var PORTS = [9090, 9876];
|
||||
var ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY;
|
||||
var READY_FILE = process.env.SAUCE_CONNECT_READY_FILE;
|
||||
|
||||
// We need to start fake servers, otherwise the tunnel does not start.
|
||||
var fakeServers = [];
|
||||
var hosts = [];
|
||||
|
||||
PORTS.forEach(function(port) {
|
||||
fakeServers.push(http.createServer(function() {}).listen(port));
|
||||
hosts.push({
|
||||
name: HOSTNAME,
|
||||
port: port,
|
||||
sslFlag: 0
|
||||
});
|
||||
});
|
||||
|
||||
var tunnel = new BrowserStackTunnel({
|
||||
key: ACCESS_KEY,
|
||||
hosts: hosts
|
||||
});
|
||||
|
||||
|
||||
tunnel.start(function(error) {
|
||||
console.log('** callback **')
|
||||
if (error) {
|
||||
console.error('Can not establish the tunnel', error);
|
||||
} else {
|
||||
console.log('Tunnel established.');
|
||||
fakeServers.forEach(function(server) {
|
||||
server.close();
|
||||
});
|
||||
|
||||
if (READY_FILE) {
|
||||
fs.writeFile(READY_FILE, '');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
tunnel.on('error', function(error) {
|
||||
console.error(error);
|
||||
});
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
node ./lib/browser-stack/start-tunnel.js &
|
||||
+6
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"version": "1.2.2",
|
||||
"cdnVersion": "1.2.1",
|
||||
"codename": "consciousness-inertia",
|
||||
"version": "1.2.3",
|
||||
"cdnVersion": "1.2.2",
|
||||
"codename": "unicorn-zapper",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -37,7 +37,9 @@
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"semver": "~2.1.0",
|
||||
"lodash": "~2.1.0"
|
||||
"lodash": "~2.1.0",
|
||||
"karma-browserstack-launcher": "~0.0.4",
|
||||
"browserstacktunnel-wrapper": "~1.1.1"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Angular Bower Script
|
||||
|
||||
Script for updating the Angular bower repos from a code.angularjs.org package
|
||||
|
||||
Requires `node` (for parsing `bower.json`) and `wget` (for fetching the `angular.zip` from `code.angularjs.org`)
|
||||
|
||||
|
||||
## Instructions
|
||||
|
||||
You need to run `./init.sh` the first time you use this script to clone all the repos.
|
||||
|
||||
For subsequent updates:
|
||||
|
||||
```shell
|
||||
./publish.sh NEW_VERSION
|
||||
```
|
||||
|
||||
Where `NEW_VERSION` is a version number like `1.2.3`.
|
||||
|
||||
|
||||
## License
|
||||
MIT
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# init all of the bower repos
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
git clone git@github.com:angular/bower-$repo.git
|
||||
done
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# update all the things
|
||||
#
|
||||
|
||||
set -e # fail if any command fails
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
NEW_VERSION=$1
|
||||
|
||||
ZIP_FILE=angular-$NEW_VERSION.zip
|
||||
ZIP_FILE_URL=http://code.angularjs.org/$NEW_VERSION/angular-$NEW_VERSION.zip
|
||||
ZIP_DIR=angular-$NEW_VERSION
|
||||
|
||||
REPOS=(
|
||||
angular \
|
||||
angular-animate \
|
||||
angular-cookies \
|
||||
angular-i18n \
|
||||
angular-loader \
|
||||
angular-mocks \
|
||||
angular-route \
|
||||
angular-resource \
|
||||
angular-sanitize \
|
||||
angular-scenario \
|
||||
angular-touch \
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# download and unzip the file
|
||||
#
|
||||
|
||||
#wget $ZIP_FILE_URL
|
||||
unzip $ZIP_FILE
|
||||
|
||||
|
||||
#
|
||||
# move the files from the zip
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
if [ -f $ZIP_DIR/$repo.js ] # ignore i18l
|
||||
then
|
||||
cd bower-$repo
|
||||
git checkout master
|
||||
git reset --hard HEAD
|
||||
cd ..
|
||||
mv $ZIP_DIR/$repo.* bower-$repo/
|
||||
fi
|
||||
done
|
||||
|
||||
# move i18n files
|
||||
mv $ZIP_DIR/i18n/*.js bower-angular-i18n/
|
||||
|
||||
# move csp.css
|
||||
mv $ZIP_DIR/angular-csp.css bower-angular
|
||||
|
||||
|
||||
#
|
||||
# get the old version number
|
||||
#
|
||||
|
||||
OLD_VERSION=$(node -e "console.log(require('./bower-angular/bower').version)" | sed -e 's/\r//g')
|
||||
echo $OLD_VERSION
|
||||
echo $NEW_VERSION
|
||||
|
||||
#
|
||||
# update bower.json
|
||||
# tag each repo
|
||||
#
|
||||
|
||||
for repo in "${REPOS[@]}"
|
||||
do
|
||||
cd bower-$repo
|
||||
pwd
|
||||
sed -i '' -e "s/$OLD_VERSION/$NEW_VERSION/g" bower.json
|
||||
git add -A
|
||||
git commit -m "v$NEW_VERSION"
|
||||
git tag v$NEW_VERSION
|
||||
git push origin master
|
||||
git push origin v$NEW_VERSION
|
||||
cd ..
|
||||
done
|
||||
+1
-1
@@ -544,7 +544,7 @@ var trim = (function() {
|
||||
// TODO: we should move this into IE/ES5 polyfill
|
||||
if (!String.prototype.trim) {
|
||||
return function(value) {
|
||||
return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value;
|
||||
return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
|
||||
};
|
||||
}
|
||||
return function(value) {
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
$ParseProvider,
|
||||
$RootScopeProvider,
|
||||
$QProvider,
|
||||
$$SanitizeUriProvider,
|
||||
$SceProvider,
|
||||
$SceDelegateProvider,
|
||||
$SnifferProvider,
|
||||
@@ -136,6 +137,10 @@ function publishExternalAPI(angular){
|
||||
|
||||
angularModule('ng', ['ngLocale'], ['$provide',
|
||||
function ngModule($provide) {
|
||||
// $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
|
||||
$provide.provider({
|
||||
$$sanitizeUri: $$SanitizeUriProvider
|
||||
});
|
||||
$provide.provider('$compile', $CompileProvider).
|
||||
directive({
|
||||
a: htmlAnchorDirective,
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document, undefined) {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
|
||||
+13
-23
@@ -283,7 +283,7 @@
|
||||
* </div>
|
||||
*
|
||||
* <div class="alert alert-error">
|
||||
* **Note:** The `transclude` function that is passed to the compile function is deperecated, as it
|
||||
* **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
|
||||
* e.g. does not know about the right outer scope. Please use the transclude function that is passed
|
||||
* to the link function instead.
|
||||
* </div>
|
||||
@@ -493,14 +493,12 @@ var $compileMinErr = minErr('$compile');
|
||||
*
|
||||
* @description
|
||||
*/
|
||||
$CompileProvider.$inject = ['$provide'];
|
||||
function $CompileProvider($provide) {
|
||||
$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
|
||||
function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var hasDirectives = {},
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
@@ -584,10 +582,11 @@ function $CompileProvider($provide) {
|
||||
*/
|
||||
this.aHrefSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
aHrefSanitizationWhitelist = regexp;
|
||||
$$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
|
||||
return this;
|
||||
} else {
|
||||
return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
|
||||
}
|
||||
return aHrefSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
@@ -614,18 +613,18 @@ function $CompileProvider($provide) {
|
||||
*/
|
||||
this.imgSrcSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
imgSrcSanitizationWhitelist = regexp;
|
||||
$$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
|
||||
return this;
|
||||
} else {
|
||||
return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
|
||||
}
|
||||
return imgSrcSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
this.$get = [
|
||||
'$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse',
|
||||
'$controller', '$rootScope', '$document', '$sce', '$animate',
|
||||
'$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
|
||||
function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse,
|
||||
$controller, $rootScope, $document, $sce, $animate) {
|
||||
$controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
|
||||
|
||||
var Attributes = function(element, attr) {
|
||||
this.$$element = element;
|
||||
@@ -730,16 +729,7 @@ function $CompileProvider($provide) {
|
||||
// sanitize a[href] and img[src] values
|
||||
if ((nodeName === 'A' && key === 'href') ||
|
||||
(nodeName === 'IMG' && key === 'src')) {
|
||||
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
|
||||
if (!msie || msie >= 8 ) {
|
||||
normalizedVal = urlResolve(value).href;
|
||||
if (normalizedVal !== '') {
|
||||
if ((key === 'href' && !normalizedVal.match(aHrefSanitizationWhitelist)) ||
|
||||
(key === 'src' && !normalizedVal.match(imgSrcSanitizationWhitelist))) {
|
||||
this[key] = value = 'unsafe:' + normalizedVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
this[key] = value = $$sanitizeUri(value, key === 'src');
|
||||
}
|
||||
|
||||
if (writeAttr !== false) {
|
||||
|
||||
@@ -449,15 +449,15 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
deferListener();
|
||||
});
|
||||
|
||||
// if user paste into input using mouse, we need "change" event to catch it
|
||||
element.on('change', listener);
|
||||
|
||||
// if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
|
||||
if ($sniffer.hasEvent('paste')) {
|
||||
element.on('paste cut', deferListener);
|
||||
}
|
||||
}
|
||||
|
||||
// if user paste into input using mouse on older browser
|
||||
// or form autocomplete on newer browser, we need "change" event to catch it
|
||||
element.on('change', listener);
|
||||
|
||||
ctrl.$render = function() {
|
||||
element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
|
||||
|
||||
@@ -28,12 +28,11 @@ var XHR = window.XMLHttpRequest || function() {
|
||||
*/
|
||||
function $HttpBackendProvider() {
|
||||
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
||||
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks,
|
||||
$document[0], $window.location.protocol.replace(':', ''));
|
||||
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
}];
|
||||
}
|
||||
|
||||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
|
||||
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument) {
|
||||
var ABORTED = -1;
|
||||
|
||||
// TODO(vojta): fix the signature
|
||||
@@ -113,14 +112,14 @@ function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument,
|
||||
}
|
||||
|
||||
function completeRequest(callback, status, response, headersString) {
|
||||
var protocol = locationProtocol || urlResolve(url).protocol;
|
||||
var protocol = urlResolve(url).protocol;
|
||||
|
||||
// cancel timeout and subsequent timeout promise resolution
|
||||
timeoutId && $browserDefer.cancel(timeoutId);
|
||||
jsonpDone = xhr = null;
|
||||
|
||||
// fix status code for file protocol (it's always 0)
|
||||
status = (protocol == 'file') ? (response ? 200 : 404) : status;
|
||||
status = (protocol == 'file' && status === 0) ? (response ? 200 : 404) : status;
|
||||
|
||||
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
||||
status = status == 1223 ? 204 : status;
|
||||
|
||||
@@ -179,7 +179,47 @@ function LocationHashbangUrl(appBase, hashPrefix) {
|
||||
hashPrefix);
|
||||
}
|
||||
parseAppUrl(withoutHashUrl, this, appBase);
|
||||
|
||||
this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
|
||||
|
||||
this.$$compose();
|
||||
|
||||
/*
|
||||
* In Windows, on an anchor node on documents loaded from
|
||||
* the filesystem, the browser will return a pathname
|
||||
* prefixed with the drive name ('/C:/path') when a
|
||||
* pathname without a drive is set:
|
||||
* * a.setAttribute('href', '/foo')
|
||||
* * a.pathname === '/C:/foo' //true
|
||||
*
|
||||
* Inside of Angular, we're always using pathnames that
|
||||
* do not include drive names for routing.
|
||||
*/
|
||||
function removeWindowsDriveName (path, url, base) {
|
||||
/*
|
||||
Matches paths for file protocol on windows,
|
||||
such as /C:/foo/bar, and captures only /foo/bar.
|
||||
*/
|
||||
var windowsFilePathExp = /^\/?.*?:(\/.*)/;
|
||||
|
||||
var firstPathSegmentMatch;
|
||||
|
||||
//Get the relative path from the input URL.
|
||||
if (url.indexOf(base) === 0) {
|
||||
url = url.replace(base, '');
|
||||
}
|
||||
|
||||
/*
|
||||
* The input URL intentionally contains a
|
||||
* first path segment that ends with a colon.
|
||||
*/
|
||||
if (windowsFilePathExp.exec(url)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
firstPathSegmentMatch = windowsFilePathExp.exec(path);
|
||||
return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
|
||||
*/
|
||||
function $$SanitizeUriProvider() {
|
||||
var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
|
||||
/**
|
||||
* @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 `aHrefSanitizationWhitelist`
|
||||
* 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 is it 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.aHrefSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
aHrefSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return aHrefSanitizationWhitelist;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
|
||||
* urls during img[src] sanitization.
|
||||
*
|
||||
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
|
||||
*
|
||||
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into
|
||||
* an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
|
||||
* 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 is it 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.imgSrcSanitizationWhitelist = function(regexp) {
|
||||
if (isDefined(regexp)) {
|
||||
imgSrcSanitizationWhitelist = regexp;
|
||||
return this;
|
||||
}
|
||||
return imgSrcSanitizationWhitelist;
|
||||
};
|
||||
|
||||
this.$get = function() {
|
||||
return function sanitizeUri(uri, isImage) {
|
||||
var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
|
||||
var normalizedVal;
|
||||
// NOTE: urlResolve() doesn't support IE < 8 so we don't sanitize for that case.
|
||||
if (!msie || msie >= 8 ) {
|
||||
normalizedVal = urlResolve(uri).href;
|
||||
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
|
||||
return 'unsafe:'+normalizedVal;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
};
|
||||
};
|
||||
}
|
||||
+4
-44
@@ -7,11 +7,6 @@
|
||||
// exactly the behavior needed here. There is little value is mocking these out for this
|
||||
// service.
|
||||
var urlParsingNode = document.createElement("a");
|
||||
/*
|
||||
Matches paths for file protocol on windows,
|
||||
such as /C:/foo/bar, and captures only /foo/bar.
|
||||
*/
|
||||
var windowsFilePathExp = /^\/?.*?:(\/.*)/;
|
||||
var originUrl = urlResolve(window.location.href, true);
|
||||
|
||||
|
||||
@@ -68,8 +63,7 @@ var originUrl = urlResolve(window.location.href, true);
|
||||
*
|
||||
*/
|
||||
function urlResolve(url, base) {
|
||||
var href = url,
|
||||
pathname;
|
||||
var href = url;
|
||||
|
||||
if (msie) {
|
||||
// Normalize before parse. Refer Implementation Notes on why this is
|
||||
@@ -80,21 +74,6 @@ function urlResolve(url, base) {
|
||||
|
||||
urlParsingNode.setAttribute('href', href);
|
||||
|
||||
/*
|
||||
* In Windows, on an anchor node on documents loaded from
|
||||
* the filesystem, the browser will return a pathname
|
||||
* prefixed with the drive name ('/C:/path') when a
|
||||
* pathname without a drive is set:
|
||||
* * a.setAttribute('href', '/foo')
|
||||
* * a.pathname === '/C:/foo' //true
|
||||
*
|
||||
* Inside of Angular, we're always using pathnames that
|
||||
* do not include drive names for routing.
|
||||
*/
|
||||
|
||||
pathname = removeWindowsDriveName(urlParsingNode.pathname, url, base);
|
||||
pathname = (pathname.charAt(0) === '/') ? pathname : '/' + pathname;
|
||||
|
||||
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
|
||||
return {
|
||||
href: urlParsingNode.href,
|
||||
@@ -104,11 +83,12 @@ function urlResolve(url, base) {
|
||||
hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
|
||||
hostname: urlParsingNode.hostname,
|
||||
port: urlParsingNode.port,
|
||||
pathname: pathname
|
||||
pathname: (urlParsingNode.pathname.charAt(0) === '/')
|
||||
? urlParsingNode.pathname
|
||||
: '/' + urlParsingNode.pathname
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a request URL and determine whether this is a same-origin request as the application document.
|
||||
*
|
||||
@@ -121,23 +101,3 @@ function urlIsSameOrigin(requestUrl) {
|
||||
return (parsed.protocol === originUrl.protocol &&
|
||||
parsed.host === originUrl.host);
|
||||
}
|
||||
|
||||
function removeWindowsDriveName (path, url, base) {
|
||||
var firstPathSegmentMatch;
|
||||
|
||||
//Get the relative path from the input URL.
|
||||
if (url.indexOf(base) === 0) {
|
||||
url = url.replace(base, '');
|
||||
}
|
||||
|
||||
/*
|
||||
* The input URL intentionally contains a
|
||||
* first path segment that ends with a colon.
|
||||
*/
|
||||
if (windowsFilePathExp.exec(url)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
firstPathSegmentMatch = windowsFilePathExp.exec(path);
|
||||
return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
|
||||
}
|
||||
@@ -263,9 +263,16 @@ angular.module('ngAnimate', ['ng'])
|
||||
|
||||
$rootElement.data(NG_ANIMATE_STATE, rootAnimateState);
|
||||
|
||||
// disable animations during bootstrap, but once we bootstrapped, enable animations
|
||||
// disable animations during bootstrap, but once we bootstrapped, wait again
|
||||
// for another digest until enabling animations. The reason why we digest twice
|
||||
// is because all structural animations (enter, leave and move) all perform a
|
||||
// post digest operation before animating. If we only wait for a single digest
|
||||
// to pass then the structural animation would render its animation on page load.
|
||||
// (which is what we're trying to avoid when the application first boots up.)
|
||||
$rootScope.$$postDigest(function() {
|
||||
rootAnimateState.running = false;
|
||||
$rootScope.$$postDigest(function() {
|
||||
rootAnimateState.running = false;
|
||||
});
|
||||
});
|
||||
|
||||
function lookup(name) {
|
||||
@@ -1032,7 +1039,10 @@ angular.module('ngAnimate', ['ng'])
|
||||
}
|
||||
|
||||
function unblockKeyframeAnimations(element) {
|
||||
element[0].style[ANIMATION_PROP] = '';
|
||||
var node = element[0], prop = ANIMATION_PROP;
|
||||
if(node.style[prop] && node.style[prop].length > 0) {
|
||||
element[0].style[prop] = '';
|
||||
}
|
||||
}
|
||||
|
||||
function animateRun(element, className, activeAnimationComplete) {
|
||||
@@ -1063,8 +1073,6 @@ angular.module('ngAnimate', ['ng'])
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-property');
|
||||
appliedStyles.push(CSS_PREFIX + 'transition-duration');
|
||||
}
|
||||
} else {
|
||||
unblockKeyframeAnimations(element);
|
||||
}
|
||||
|
||||
if(ii > 0) {
|
||||
@@ -1167,6 +1175,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
var cancel = preReflowCancellation;
|
||||
afterReflow(function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
//once the reflow is complete then we point cancel to
|
||||
//the new cancellation function which will remove all of the
|
||||
//animation properties from the active animation
|
||||
@@ -1232,6 +1241,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
if(cancellationMethod) {
|
||||
afterReflow(function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
return cancellationMethod;
|
||||
@@ -1248,6 +1258,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
if(cancellationMethod) {
|
||||
afterReflow(function() {
|
||||
unblockTransitions(element);
|
||||
unblockKeyframeAnimations(element);
|
||||
animationCompleted();
|
||||
});
|
||||
return cancellationMethod;
|
||||
|
||||
Vendored
+195
-205
@@ -1,13 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*
|
||||
* TODO(vojta): wrap whole file into closure during build
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name angular.mock
|
||||
@@ -560,210 +552,208 @@ angular.mock.$IntervalProvider = function() {
|
||||
* This directive should go inside the anonymous function but a bug in JSHint means that it would
|
||||
* not be enacted early enough to prevent the warning.
|
||||
*/
|
||||
(function() {
|
||||
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
||||
var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/;
|
||||
|
||||
function jsonStringToDate(string) {
|
||||
var match;
|
||||
if (match = string.match(R_ISO8061_STR)) {
|
||||
var date = new Date(0),
|
||||
tzHour = 0,
|
||||
tzMin = 0;
|
||||
if (match[9]) {
|
||||
tzHour = int(match[9] + match[10]);
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
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;
|
||||
function jsonStringToDate(string) {
|
||||
var match;
|
||||
if (match = string.match(R_ISO8061_STR)) {
|
||||
var date = new Date(0),
|
||||
tzHour = 0,
|
||||
tzMin = 0;
|
||||
if (match[9]) {
|
||||
tzHour = int(match[9] + match[10]);
|
||||
tzMin = int(match[9] + match[11]);
|
||||
}
|
||||
return string;
|
||||
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;
|
||||
}
|
||||
|
||||
function int(str) {
|
||||
return parseInt(str, 10);
|
||||
}
|
||||
|
||||
function padNumber(num, digits, trim) {
|
||||
var neg = '';
|
||||
if (num < 0) {
|
||||
neg = '-';
|
||||
num = -num;
|
||||
}
|
||||
num = '' + num;
|
||||
while(num.length < digits) num = '0' + num;
|
||||
if (trim)
|
||||
num = num.substr(num.length - digits);
|
||||
return neg + num;
|
||||
function int(str) {
|
||||
return parseInt(str, 10);
|
||||
}
|
||||
|
||||
function padNumber(num, digits, trim) {
|
||||
var neg = '';
|
||||
if (num < 0) {
|
||||
neg = '-';
|
||||
num = -num;
|
||||
}
|
||||
num = '' + num;
|
||||
while(num.length < digits) num = '0' + num;
|
||||
if (trim)
|
||||
num = num.substr(num.length - digits);
|
||||
return neg + num;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.mock.TzDate
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
|
||||
*
|
||||
* Mock of the Date type which has its timezone specified via constructor arg.
|
||||
*
|
||||
* The main purpose is to create Date-like instances with timezone fixed to the specified timezone
|
||||
* offset, so that we can test code that depends on local timezone settings without dependency on
|
||||
* the time zone settings of the machine where the code is running.
|
||||
*
|
||||
* @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
|
||||
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
|
||||
*
|
||||
* @example
|
||||
* !!!! WARNING !!!!!
|
||||
* This is not a complete Date object so only methods that were implemented can be called safely.
|
||||
* To make matters worse, TzDate instances inherit stuff from Date via a prototype.
|
||||
*
|
||||
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
|
||||
* incomplete we might be missing some non-standard methods. This can result in errors like:
|
||||
* "Date.prototype.foo called on incompatible Object".
|
||||
*
|
||||
* <pre>
|
||||
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
|
||||
* newYearInBratislava.getTimezoneOffset() => -60;
|
||||
* newYearInBratislava.getFullYear() => 2010;
|
||||
* newYearInBratislava.getMonth() => 0;
|
||||
* newYearInBratislava.getDate() => 1;
|
||||
* newYearInBratislava.getHours() => 0;
|
||||
* newYearInBratislava.getMinutes() => 0;
|
||||
* newYearInBratislava.getSeconds() => 0;
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
angular.mock.TzDate = function (offset, timestamp) {
|
||||
var self = new Date(0);
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name angular.mock.TzDate
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`.
|
||||
*
|
||||
* Mock of the Date type which has its timezone specified via constructor arg.
|
||||
*
|
||||
* The main purpose is to create Date-like instances with timezone fixed to the specified timezone
|
||||
* offset, so that we can test code that depends on local timezone settings without dependency on
|
||||
* the time zone settings of the machine where the code is running.
|
||||
*
|
||||
* @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored)
|
||||
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
|
||||
*
|
||||
* @example
|
||||
* !!!! WARNING !!!!!
|
||||
* This is not a complete Date object so only methods that were implemented can be called safely.
|
||||
* To make matters worse, TzDate instances inherit stuff from Date via a prototype.
|
||||
*
|
||||
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
|
||||
* incomplete we might be missing some non-standard methods. This can result in errors like:
|
||||
* "Date.prototype.foo called on incompatible Object".
|
||||
*
|
||||
* <pre>
|
||||
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
|
||||
* newYearInBratislava.getTimezoneOffset() => -60;
|
||||
* newYearInBratislava.getFullYear() => 2010;
|
||||
* newYearInBratislava.getMonth() => 0;
|
||||
* newYearInBratislava.getDate() => 1;
|
||||
* newYearInBratislava.getHours() => 0;
|
||||
* newYearInBratislava.getMinutes() => 0;
|
||||
* newYearInBratislava.getSeconds() => 0;
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
angular.mock.TzDate = function (offset, timestamp) {
|
||||
var self = new Date(0);
|
||||
if (angular.isString(timestamp)) {
|
||||
var tsStr = timestamp;
|
||||
|
||||
self.origDate = jsonStringToDate(timestamp);
|
||||
self.origDate = jsonStringToDate(timestamp);
|
||||
|
||||
timestamp = self.origDate.getTime();
|
||||
if (isNaN(timestamp))
|
||||
throw {
|
||||
name: "Illegal Argument",
|
||||
message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
|
||||
};
|
||||
} else {
|
||||
self.origDate = new Date(timestamp);
|
||||
}
|
||||
|
||||
var localOffset = new Date(timestamp).getTimezoneOffset();
|
||||
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
|
||||
self.date = new Date(timestamp + self.offsetDiff);
|
||||
|
||||
self.getTime = function() {
|
||||
return self.date.getTime() - self.offsetDiff;
|
||||
};
|
||||
|
||||
self.toLocaleDateString = function() {
|
||||
return self.date.toLocaleDateString();
|
||||
};
|
||||
|
||||
self.getFullYear = function() {
|
||||
return self.date.getFullYear();
|
||||
};
|
||||
|
||||
self.getMonth = function() {
|
||||
return self.date.getMonth();
|
||||
};
|
||||
|
||||
self.getDate = function() {
|
||||
return self.date.getDate();
|
||||
};
|
||||
|
||||
self.getHours = function() {
|
||||
return self.date.getHours();
|
||||
};
|
||||
|
||||
self.getMinutes = function() {
|
||||
return self.date.getMinutes();
|
||||
};
|
||||
|
||||
self.getSeconds = function() {
|
||||
return self.date.getSeconds();
|
||||
};
|
||||
|
||||
self.getMilliseconds = function() {
|
||||
return self.date.getMilliseconds();
|
||||
};
|
||||
|
||||
self.getTimezoneOffset = function() {
|
||||
return offset * 60;
|
||||
};
|
||||
|
||||
self.getUTCFullYear = function() {
|
||||
return self.origDate.getUTCFullYear();
|
||||
};
|
||||
|
||||
self.getUTCMonth = function() {
|
||||
return self.origDate.getUTCMonth();
|
||||
};
|
||||
|
||||
self.getUTCDate = function() {
|
||||
return self.origDate.getUTCDate();
|
||||
};
|
||||
|
||||
self.getUTCHours = function() {
|
||||
return self.origDate.getUTCHours();
|
||||
};
|
||||
|
||||
self.getUTCMinutes = function() {
|
||||
return self.origDate.getUTCMinutes();
|
||||
};
|
||||
|
||||
self.getUTCSeconds = function() {
|
||||
return self.origDate.getUTCSeconds();
|
||||
};
|
||||
|
||||
self.getUTCMilliseconds = function() {
|
||||
return self.origDate.getUTCMilliseconds();
|
||||
};
|
||||
|
||||
self.getDay = function() {
|
||||
return self.date.getDay();
|
||||
};
|
||||
|
||||
// provide this method only on browsers that already have it
|
||||
if (self.toISOString) {
|
||||
self.toISOString = function() {
|
||||
return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
|
||||
padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
|
||||
padNumber(self.origDate.getUTCDate(), 2) + 'T' +
|
||||
padNumber(self.origDate.getUTCHours(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
|
||||
padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
|
||||
timestamp = self.origDate.getTime();
|
||||
if (isNaN(timestamp))
|
||||
throw {
|
||||
name: "Illegal Argument",
|
||||
message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string"
|
||||
};
|
||||
}
|
||||
} else {
|
||||
self.origDate = new Date(timestamp);
|
||||
}
|
||||
|
||||
//hide all methods not implemented in this mock that the Date prototype exposes
|
||||
var unimplementedMethods = ['getUTCDay',
|
||||
'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
|
||||
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
|
||||
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
|
||||
'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
|
||||
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
|
||||
var localOffset = new Date(timestamp).getTimezoneOffset();
|
||||
self.offsetDiff = localOffset*60*1000 - offset*1000*60*60;
|
||||
self.date = new Date(timestamp + self.offsetDiff);
|
||||
|
||||
angular.forEach(unimplementedMethods, function(methodName) {
|
||||
self[methodName] = function() {
|
||||
throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
|
||||
};
|
||||
});
|
||||
|
||||
return self;
|
||||
self.getTime = function() {
|
||||
return self.date.getTime() - self.offsetDiff;
|
||||
};
|
||||
|
||||
//make "tzDateInstance instanceof Date" return true
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
})();
|
||||
self.toLocaleDateString = function() {
|
||||
return self.date.toLocaleDateString();
|
||||
};
|
||||
|
||||
self.getFullYear = function() {
|
||||
return self.date.getFullYear();
|
||||
};
|
||||
|
||||
self.getMonth = function() {
|
||||
return self.date.getMonth();
|
||||
};
|
||||
|
||||
self.getDate = function() {
|
||||
return self.date.getDate();
|
||||
};
|
||||
|
||||
self.getHours = function() {
|
||||
return self.date.getHours();
|
||||
};
|
||||
|
||||
self.getMinutes = function() {
|
||||
return self.date.getMinutes();
|
||||
};
|
||||
|
||||
self.getSeconds = function() {
|
||||
return self.date.getSeconds();
|
||||
};
|
||||
|
||||
self.getMilliseconds = function() {
|
||||
return self.date.getMilliseconds();
|
||||
};
|
||||
|
||||
self.getTimezoneOffset = function() {
|
||||
return offset * 60;
|
||||
};
|
||||
|
||||
self.getUTCFullYear = function() {
|
||||
return self.origDate.getUTCFullYear();
|
||||
};
|
||||
|
||||
self.getUTCMonth = function() {
|
||||
return self.origDate.getUTCMonth();
|
||||
};
|
||||
|
||||
self.getUTCDate = function() {
|
||||
return self.origDate.getUTCDate();
|
||||
};
|
||||
|
||||
self.getUTCHours = function() {
|
||||
return self.origDate.getUTCHours();
|
||||
};
|
||||
|
||||
self.getUTCMinutes = function() {
|
||||
return self.origDate.getUTCMinutes();
|
||||
};
|
||||
|
||||
self.getUTCSeconds = function() {
|
||||
return self.origDate.getUTCSeconds();
|
||||
};
|
||||
|
||||
self.getUTCMilliseconds = function() {
|
||||
return self.origDate.getUTCMilliseconds();
|
||||
};
|
||||
|
||||
self.getDay = function() {
|
||||
return self.date.getDay();
|
||||
};
|
||||
|
||||
// provide this method only on browsers that already have it
|
||||
if (self.toISOString) {
|
||||
self.toISOString = function() {
|
||||
return padNumber(self.origDate.getUTCFullYear(), 4) + '-' +
|
||||
padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' +
|
||||
padNumber(self.origDate.getUTCDate(), 2) + 'T' +
|
||||
padNumber(self.origDate.getUTCHours(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCMinutes(), 2) + ':' +
|
||||
padNumber(self.origDate.getUTCSeconds(), 2) + '.' +
|
||||
padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z';
|
||||
};
|
||||
}
|
||||
|
||||
//hide all methods not implemented in this mock that the Date prototype exposes
|
||||
var unimplementedMethods = ['getUTCDay',
|
||||
'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
|
||||
'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear',
|
||||
'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds',
|
||||
'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString',
|
||||
'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf'];
|
||||
|
||||
angular.forEach(unimplementedMethods, function(methodName) {
|
||||
self[methodName] = function() {
|
||||
throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock");
|
||||
};
|
||||
});
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
//make "tzDateInstance instanceof Date" return true
|
||||
angular.mock.TzDate.prototype = Date.prototype;
|
||||
/* jshint +W101 */
|
||||
|
||||
angular.mock.animate = angular.module('mock.animate', ['ng'])
|
||||
@@ -1919,9 +1909,13 @@ angular.mock.clearDataCache = function() {
|
||||
|
||||
|
||||
|
||||
(window.jasmine || window.mocha) && (function(window) {
|
||||
if(window.jasmine || window.mocha) {
|
||||
|
||||
var currentSpec = null,
|
||||
isSpecRunning = function() {
|
||||
return currentSpec && (window.mocha || currentSpec.queue.running);
|
||||
};
|
||||
|
||||
var currentSpec = null;
|
||||
|
||||
beforeEach(function() {
|
||||
currentSpec = this;
|
||||
@@ -1954,10 +1948,6 @@ angular.mock.clearDataCache = function() {
|
||||
angular.callbacks.counter = 0;
|
||||
});
|
||||
|
||||
function isSpecRunning() {
|
||||
return currentSpec && (window.mocha || currentSpec.queue.running);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.mock.module
|
||||
@@ -2112,4 +2102,4 @@ angular.mock.clearDataCache = function() {
|
||||
}
|
||||
}
|
||||
};
|
||||
})(window);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* global htmlSanitizeWriter: false */
|
||||
/* global sanitizeText: false */
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
@@ -100,7 +100,7 @@
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', function() {
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
@@ -110,28 +110,40 @@ angular.module('ngSanitize').filter('linky', function() {
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
// TODO(vojta): use $sanitize instead
|
||||
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];
|
||||
// if we did not match ftp/http/mailto then assume mailto
|
||||
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.chars(match[0].replace(MAILTO_REGEXP, ''));
|
||||
writer.end('a');
|
||||
addText(raw.substr(0, i));
|
||||
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
writer.chars(raw);
|
||||
return html.join('');
|
||||
addText(raw);
|
||||
return $sanitize(html.join(''));
|
||||
|
||||
function addText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
html.push(sanitizeText(text));
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
html.push('<a ');
|
||||
if (angular.isDefined(target)) {
|
||||
html.push('target="');
|
||||
html.push(target);
|
||||
html.push('" ');
|
||||
}
|
||||
html.push('href="');
|
||||
html.push(url);
|
||||
html.push('">');
|
||||
addText(text);
|
||||
html.push('</a>');
|
||||
}
|
||||
};
|
||||
});
|
||||
}]);
|
||||
|
||||
+36
-10
@@ -46,6 +46,8 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
* The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
|
||||
* `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
|
||||
*
|
||||
* @param {string} html Html input.
|
||||
* @returns {string} Sanitized html.
|
||||
@@ -128,11 +130,24 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
var $sanitize = function(html) {
|
||||
function $SanitizeProvider() {
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
||||
return !/^unsafe/.test($$sanitizeUri(uri, isImage));
|
||||
}));
|
||||
return buf.join('');
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf));
|
||||
return buf.join('');
|
||||
};
|
||||
var writer = htmlSanitizeWriter(buf, angular.noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
@@ -145,7 +160,6 @@ var START_TAG_REGEXP =
|
||||
COMMENT_REGEXP = /<!--(.*?)-->/g,
|
||||
DOCTYPE_REGEXP = /<!DOCTYPE([^>]*?)>/i,
|
||||
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
|
||||
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/i,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
|
||||
|
||||
@@ -353,8 +367,18 @@ function htmlParser( html, handler ) {
|
||||
*/
|
||||
var hiddenPre=document.createElement("pre");
|
||||
function decodeEntities(value) {
|
||||
hiddenPre.innerHTML=value.replace(/</g,"<");
|
||||
return hiddenPre.innerText || hiddenPre.textContent || '';
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
// Note: IE8 does not preserve spaces at the start/end of innerHTML
|
||||
var spaceRe = /^(\s*)([\s\S]*?)(\s*)$/;
|
||||
var parts = spaceRe.exec(value);
|
||||
parts[0] = '';
|
||||
if (parts[2]) {
|
||||
hiddenPre.innerHTML=parts[2].replace(/</g,"<");
|
||||
parts[2] = hiddenPre.innerText || hiddenPre.textContent;
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -384,7 +408,7 @@ function encodeEntities(value) {
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf){
|
||||
function htmlSanitizeWriter(buf, uriValidator){
|
||||
var ignore = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
@@ -398,7 +422,9 @@ function htmlSanitizeWriter(buf){
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key){
|
||||
var lkey=angular.lowercase(key);
|
||||
if (validAttrs[lkey]===true && (uriAttrs[lkey]!==true || value.match(URI_REGEXP))) {
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
@@ -430,4 +456,4 @@ function htmlSanitizeWriter(buf){
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).value('$sanitize', $sanitize);
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2012 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document){
|
||||
|
||||
+53
-261
@@ -3834,6 +3834,7 @@ describe('$compile', function() {
|
||||
|
||||
|
||||
describe('img[src] sanitization', function() {
|
||||
|
||||
it('should NOT require trusted values for img src', inject(function($rootScope, $compile, $sce) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = 'http://example.com/image.png';
|
||||
@@ -3845,127 +3846,6 @@ describe('$compile', function() {
|
||||
expect(element.attr('src')).toEqual('http://example.com/image2.png');
|
||||
}));
|
||||
|
||||
it('should sanitize javascript: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
it('should sanitize non-image data: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
|
||||
$rootScope.testUrl = "data:,foo";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe("unsafe:data:,foo");
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize data: URIs for images', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{dataUri}}"></img>')($rootScope);
|
||||
|
||||
// image data uri
|
||||
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||
$rootScope.dataUri = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
}));
|
||||
|
||||
|
||||
// Fails on IE <= 10 with "TypeError: Access is denied" when trying to set img[src]
|
||||
if (!msie || msie > 10) {
|
||||
it('should sanitize mailto: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "mailto:foo@bar.com";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('unsafe:mailto:foo@bar.com');
|
||||
}));
|
||||
}
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
|
||||
// case-sensitive
|
||||
$rootScope.testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
$rootScope.testUrl = "java\u0009script:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
|
||||
|
||||
// space before
|
||||
$rootScope.testUrl = " javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
$rootScope.testUrl = " \u000e javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toMatch(/(http:\/\/|unsafe:javascript:doEvilStuff\(\))/);
|
||||
|
||||
// post-fixed with proper url
|
||||
$rootScope.testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
$rootScope.$apply();
|
||||
expect(element[0].src).toBeOneOf(
|
||||
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
|
||||
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
|
||||
);
|
||||
}));
|
||||
|
||||
it('should sanitize ng-src bindings as well', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img ng-src="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
|
||||
expect(element[0].src).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize valid urls', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
|
||||
$rootScope.testUrl = "foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('foo/bar');
|
||||
|
||||
$rootScope.testUrl = "/foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('/foo/bar');
|
||||
|
||||
$rootScope.testUrl = "../foo/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('../foo/bar');
|
||||
|
||||
$rootScope.testUrl = "#foo";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('#foo');
|
||||
|
||||
$rootScope.testUrl = "http://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('http://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = " http://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe(' http://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = "https://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('https://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = "ftp://foo.com/bar";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('ftp://foo.com/bar');
|
||||
|
||||
$rootScope.testUrl = "file:///foo/bar.html";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('file:///foo/bar.html');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize attributes other than src', inject(function($compile, $rootScope) {
|
||||
element = $compile('<img title="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
@@ -3974,141 +3854,42 @@ describe('$compile', function() {
|
||||
expect(element.attr('title')).toBe('javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
it('should use $$sanitizeUriProvider for reconfiguration of the src whitelist', function() {
|
||||
module(function($compileProvider, $$sanitizeUriProvider) {
|
||||
var newRe = /javascript:/,
|
||||
returnVal;
|
||||
expect($compileProvider.imgSrcSanitizationWhitelist()).toBe($$sanitizeUriProvider.imgSrcSanitizationWhitelist());
|
||||
|
||||
it('should allow reconfiguration of the src whitelist', function() {
|
||||
module(function($compileProvider) {
|
||||
expect($compileProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
var returnVal = $compileProvider.imgSrcSanitizationWhitelist(/javascript:/);
|
||||
returnVal = $compileProvider.imgSrcSanitizationWhitelist(newRe);
|
||||
expect(returnVal).toBe($compileProvider);
|
||||
expect($$sanitizeUriProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
|
||||
expect($compileProvider.imgSrcSanitizationWhitelist()).toBe(newRe);
|
||||
});
|
||||
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
|
||||
// Fails on IE <= 11 with "TypeError: Object doesn't support this property or method" when
|
||||
// trying to set img[src]
|
||||
if (!msie || msie > 11) {
|
||||
$rootScope.testUrl = "javascript:doEvilStuff()";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
$rootScope.testUrl = "http://recon/figured";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('unsafe:http://recon/figured');
|
||||
inject(function() {
|
||||
// needed to the module definition above is run...
|
||||
});
|
||||
});
|
||||
|
||||
it('should use $$sanitizeUri', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<img src="{{testUrl}}"></img>')($rootScope);
|
||||
$rootScope.testUrl = "someUrl";
|
||||
|
||||
$$sanitizeUri.andReturn('someSanitizedUrl');
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('src')).toBe('someSanitizedUrl');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('a[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');
|
||||
|
||||
$rootScope.testUrl = "file:///foo/bar.html";
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('file:///foo/bar.html');
|
||||
}));
|
||||
|
||||
|
||||
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()";
|
||||
@@ -4117,7 +3898,6 @@ describe('$compile', function() {
|
||||
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()";
|
||||
@@ -4126,26 +3906,38 @@ describe('$compile', function() {
|
||||
expect(element.attr('title')).toBe('javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
it('should use $$sanitizeUriProvider for reconfiguration of the href whitelist', function() {
|
||||
module(function($compileProvider, $$sanitizeUriProvider) {
|
||||
var newRe = /javascript:/,
|
||||
returnVal;
|
||||
expect($compileProvider.aHrefSanitizationWhitelist()).toBe($$sanitizeUriProvider.aHrefSanitizationWhitelist());
|
||||
|
||||
it('should allow reconfiguration of the href whitelist', function() {
|
||||
module(function($compileProvider) {
|
||||
expect($compileProvider.aHrefSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
var returnVal = $compileProvider.aHrefSanitizationWhitelist(/javascript:/);
|
||||
returnVal = $compileProvider.aHrefSanitizationWhitelist(newRe);
|
||||
expect(returnVal).toBe($compileProvider);
|
||||
expect($$sanitizeUriProvider.aHrefSanitizationWhitelist()).toBe(newRe);
|
||||
expect($compileProvider.aHrefSanitizationWhitelist()).toBe(newRe);
|
||||
});
|
||||
|
||||
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');
|
||||
inject(function() {
|
||||
// needed to the module definition above is run...
|
||||
});
|
||||
});
|
||||
|
||||
it('should use $$sanitizeUri', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<a href="{{testUrl}}"></a>')($rootScope);
|
||||
$rootScope.testUrl = "someUrl";
|
||||
|
||||
$$sanitizeUri.andReturn('someSanitizedUrl');
|
||||
$rootScope.$apply();
|
||||
expect(element.attr('href')).toBe('someSanitizedUrl');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith($rootScope.testUrl, false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('interpolation on HTML DOM event handler attributes onclick, onXYZ, formaction', function() {
|
||||
|
||||
@@ -468,6 +468,32 @@ describe('input', function() {
|
||||
expect(scope.name).toEqual('adam');
|
||||
});
|
||||
|
||||
describe('"change" event', function() {
|
||||
function assertBrowserSupportsChangeEvent(inputEventSupported) {
|
||||
// Force browser to report a lack of an 'input' event
|
||||
$sniffer.hasEvent = function(eventName) {
|
||||
if (eventName === 'input' && !inputEventSupported) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
compileInput('<input type="text" ng-model="name" name="alias" />');
|
||||
|
||||
inputElm.val('mark');
|
||||
browserTrigger(inputElm, 'change');
|
||||
expect(scope.name).toEqual('mark');
|
||||
}
|
||||
|
||||
it('should update the model event if the browser does not support the "input" event',function() {
|
||||
assertBrowserSupportsChangeEvent(false);
|
||||
});
|
||||
|
||||
it('should update the model event if the browser supports the "input" ' +
|
||||
'event so that form auto complete works',function() {
|
||||
assertBrowserSupportsChangeEvent(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('"paste" and "cut" events', function() {
|
||||
beforeEach(function() {
|
||||
// Force browser to report a lack of an 'input' event
|
||||
|
||||
@@ -373,6 +373,10 @@ describe('ngClass animations', function() {
|
||||
// Enable animations by triggering the first item in the postDigest queue
|
||||
digestQueue.shift()();
|
||||
|
||||
// wait for the 2nd animation bootstrap digest to pass
|
||||
$rootScope.$digest();
|
||||
digestQueue.shift()();
|
||||
|
||||
$rootScope.val = 'crazy';
|
||||
var element = angular.element('<div ng-class="val"></div>');
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
@@ -436,13 +436,61 @@ describe('$httpBackend', function() {
|
||||
|
||||
|
||||
it('should convert 0 to 404 if no content - relative url', function() {
|
||||
$backend = createHttpBackend($browser, MockXhr, null, null, null, 'file');
|
||||
var originalUrlParsingNode = urlParsingNode;
|
||||
|
||||
$backend('GET', '/whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
//temporarily overriding the DOM element to pretend that the test runs origin with file:// protocol
|
||||
urlParsingNode = {
|
||||
hash : "#/C:/",
|
||||
host : "",
|
||||
hostname : "",
|
||||
href : "file:///C:/base#!/C:/foo",
|
||||
pathname : "/C:/foo",
|
||||
port : "",
|
||||
protocol : "file:",
|
||||
search : "",
|
||||
setAttribute: angular.noop
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
$backend = createHttpBackend($browser, MockXhr);
|
||||
|
||||
$backend('GET', '/whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(404);
|
||||
|
||||
} finally {
|
||||
urlParsingNode = originalUrlParsingNode;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it('should return original backend status code if different from 0', function () {
|
||||
$backend = createHttpBackend($browser, MockXhr);
|
||||
|
||||
// request to http://
|
||||
$backend('POST', 'http://rest_api/create_whatever', null, callback);
|
||||
respond(201, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(404);
|
||||
expect(callback.mostRecentCall.args[0]).toBe(201);
|
||||
|
||||
|
||||
// request to file://
|
||||
$backend('POST', 'file://rest_api/create_whatever', null, callback);
|
||||
respond(201, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(201);
|
||||
|
||||
// request to file:// with HTTP status >= 300
|
||||
$backend('POST', 'file://rest_api/create_whatever', null, callback);
|
||||
respond(503, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(503);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,7 +38,6 @@ describe('$location', function() {
|
||||
if ($sniffer.msie) return;
|
||||
//reset urlParsingNode
|
||||
urlParsingNode = urlParsingNodePlaceholder;
|
||||
expect(urlParsingNode.pathname).not.toBe('/C:/foo');
|
||||
}));
|
||||
|
||||
|
||||
@@ -324,7 +323,7 @@ describe('$location', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should parse hashband url into path and search', function() {
|
||||
it('should parse hashbang url into path and search', function() {
|
||||
expect(url.protocol()).toBe('http');
|
||||
expect(url.host()).toBe('www.server.org');
|
||||
expect(url.port()).toBe(1234);
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
'use strict';
|
||||
|
||||
describe('sanitizeUri', function() {
|
||||
var sanitizeHref, sanitizeImg, sanitizeUriProvider, testUrl;
|
||||
beforeEach(function() {
|
||||
module(function(_$$sanitizeUriProvider_) {
|
||||
sanitizeUriProvider = _$$sanitizeUriProvider_;
|
||||
});
|
||||
inject(function($$sanitizeUri) {
|
||||
sanitizeHref = function(uri) {
|
||||
return $$sanitizeUri(uri, false);
|
||||
};
|
||||
sanitizeImg = function(uri) {
|
||||
return $$sanitizeUri(uri, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function isEvilInCurrentBrowser(uri) {
|
||||
var a = document.createElement('a');
|
||||
a.setAttribute('href', uri);
|
||||
return a.href.substring(0, 4) !== 'http';
|
||||
}
|
||||
|
||||
describe('img[src] sanitization', function() {
|
||||
|
||||
it('should sanitize javascript: urls', function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
});
|
||||
|
||||
it('should sanitize non-image data: urls', function() {
|
||||
testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
|
||||
expect(sanitizeImg(testUrl)).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
|
||||
|
||||
testUrl = "data:,foo";
|
||||
expect(sanitizeImg(testUrl)).toBe("unsafe:data:,foo");
|
||||
});
|
||||
|
||||
it('should not sanitize data: URIs for images', function() {
|
||||
// image data uri
|
||||
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||
testUrl = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
expect(sanitizeImg(testUrl)).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
});
|
||||
|
||||
it('should sanitize mailto: urls', function() {
|
||||
testUrl = "mailto:foo@bar.com";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:mailto:foo@bar.com');
|
||||
});
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', function() {
|
||||
// case-sensitive
|
||||
testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
testUrl = "java\u0009script:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeImg(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// space before
|
||||
testUrl = " javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
testUrl = " \u000e javascript:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeImg(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// post-fixed with proper url
|
||||
testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
expect(sanitizeImg(testUrl)).toBeOneOf(
|
||||
'unsafe:javascript:doEvilStuff(); http://make.me/look/good',
|
||||
'unsafe:javascript:doEvilStuff();%20http://make.me/look/good'
|
||||
);
|
||||
});
|
||||
|
||||
it('should sanitize ng-src bindings as well', function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
});
|
||||
|
||||
|
||||
it('should not sanitize valid urls', function() {
|
||||
testUrl = "foo/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('foo/bar');
|
||||
|
||||
testUrl = "/foo/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('/foo/bar');
|
||||
|
||||
testUrl = "../foo/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('../foo/bar');
|
||||
|
||||
testUrl = "#foo";
|
||||
expect(sanitizeImg(testUrl)).toBe('#foo');
|
||||
|
||||
testUrl = "http://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('http://foo.com/bar');
|
||||
|
||||
testUrl = " http://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe(' http://foo.com/bar');
|
||||
|
||||
testUrl = "https://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('https://foo.com/bar');
|
||||
|
||||
testUrl = "ftp://foo.com/bar";
|
||||
expect(sanitizeImg(testUrl)).toBe('ftp://foo.com/bar');
|
||||
|
||||
testUrl = "file:///foo/bar.html";
|
||||
expect(sanitizeImg(testUrl)).toBe('file:///foo/bar.html');
|
||||
});
|
||||
|
||||
|
||||
it('should allow reconfiguration of the src whitelist', function() {
|
||||
var returnVal;
|
||||
expect(sanitizeUriProvider.imgSrcSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
returnVal = sanitizeUriProvider.imgSrcSanitizationWhitelist(/javascript:/);
|
||||
expect(returnVal).toBe(sanitizeUriProvider);
|
||||
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeImg(testUrl)).toBe('javascript:doEvilStuff()');
|
||||
|
||||
testUrl = "http://recon/figured";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:http://recon/figured');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('a[href] sanitization', function() {
|
||||
|
||||
it('should sanitize javascript: urls', inject(function() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize data: urls', inject(function() {
|
||||
testUrl = "data:evilPayload";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:data:evilPayload');
|
||||
}));
|
||||
|
||||
|
||||
it('should sanitize obfuscated javascript: urls', inject(function() {
|
||||
// case-sensitive
|
||||
testUrl = "JaVaScRiPt:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// tab in protocol
|
||||
testUrl = "java\u0009script:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeHref(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// space before
|
||||
testUrl = " javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
|
||||
// ws chars before
|
||||
testUrl = " \u000e javascript:doEvilStuff()";
|
||||
if (isEvilInCurrentBrowser(testUrl)) {
|
||||
expect(sanitizeHref(testUrl)).toEqual('unsafe:javascript:doEvilStuff()');
|
||||
}
|
||||
|
||||
// post-fixed with proper url
|
||||
testUrl = "javascript:doEvilStuff(); http://make.me/look/good";
|
||||
expect(sanitizeHref(testUrl)).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() {
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
}));
|
||||
|
||||
|
||||
it('should not sanitize valid urls', inject(function() {
|
||||
testUrl = "foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('foo/bar');
|
||||
|
||||
testUrl = "/foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('/foo/bar');
|
||||
|
||||
testUrl = "../foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('../foo/bar');
|
||||
|
||||
testUrl = "#foo";
|
||||
expect(sanitizeHref(testUrl)).toBe('#foo');
|
||||
|
||||
testUrl = "http://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('http://foo/bar');
|
||||
|
||||
testUrl = " http://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe(' http://foo/bar');
|
||||
|
||||
testUrl = "https://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('https://foo/bar');
|
||||
|
||||
testUrl = "ftp://foo/bar";
|
||||
expect(sanitizeHref(testUrl)).toBe('ftp://foo/bar');
|
||||
|
||||
testUrl = "mailto:foo@bar.com";
|
||||
expect(sanitizeHref(testUrl)).toBe('mailto:foo@bar.com');
|
||||
|
||||
testUrl = "file:///foo/bar.html";
|
||||
expect(sanitizeHref(testUrl)).toBe('file:///foo/bar.html');
|
||||
}));
|
||||
|
||||
it('should allow reconfiguration of the href whitelist', function() {
|
||||
var returnVal;
|
||||
expect(sanitizeUriProvider.aHrefSanitizationWhitelist() instanceof RegExp).toBe(true);
|
||||
returnVal = sanitizeUriProvider.aHrefSanitizationWhitelist(/javascript:/);
|
||||
expect(returnVal).toBe(sanitizeUriProvider);
|
||||
|
||||
testUrl = "javascript:doEvilStuff()";
|
||||
expect(sanitizeHref(testUrl)).toBe('javascript:doEvilStuff()');
|
||||
|
||||
testUrl = "http://recon/figured";
|
||||
expect(sanitizeHref(testUrl)).toBe('unsafe:http://recon/figured');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
describe('urlUtils', function() {
|
||||
describe('parse', function() {
|
||||
describe('urlResolve', function() {
|
||||
it('should normalize a relative url', function () {
|
||||
expect(urlResolve("foo").href).toMatch(/^https?:\/\/[^/]+\/foo$/);
|
||||
});
|
||||
@@ -14,6 +14,13 @@ describe('urlUtils', function() {
|
||||
expect(parsed.hostname).not.toBe("");
|
||||
expect(parsed.pathname).not.toBe("");
|
||||
});
|
||||
|
||||
|
||||
it('should return pathname as / if empty path provided', function () {
|
||||
//IE counts / as empty, necessary to use / so that pathname is not context.html
|
||||
var parsed = urlResolve('/');
|
||||
expect(parsed.pathname).toBe('/');
|
||||
})
|
||||
});
|
||||
|
||||
describe('isSameOrigin', function() {
|
||||
|
||||
+2562
-2480
File diff suppressed because it is too large
Load Diff
+122
-41
@@ -5,12 +5,15 @@ describe('HTML', function() {
|
||||
var expectHTML;
|
||||
|
||||
beforeEach(module('ngSanitize'));
|
||||
|
||||
beforeEach(inject(function($sanitize) {
|
||||
beforeEach(function() {
|
||||
expectHTML = function(html){
|
||||
return expect($sanitize(html));
|
||||
var sanitize;
|
||||
inject(function($sanitize) {
|
||||
sanitize = $sanitize;
|
||||
});
|
||||
return expect(sanitize(html));
|
||||
};
|
||||
}));
|
||||
});
|
||||
|
||||
describe('htmlParser', function() {
|
||||
if (angular.isUndefined(window.htmlParser)) return;
|
||||
@@ -183,13 +186,22 @@ describe('HTML', function() {
|
||||
toEqual('');
|
||||
});
|
||||
|
||||
it('should keep spaces as prefix/postfix', function() {
|
||||
expectHTML(' a ').toEqual(' a ');
|
||||
});
|
||||
|
||||
it('should allow multiline strings', function() {
|
||||
expectHTML('\na\n').toEqual(' a\ ');
|
||||
});
|
||||
|
||||
describe('htmlSanitizerWriter', function() {
|
||||
if (angular.isUndefined(window.htmlSanitizeWriter)) return;
|
||||
|
||||
var writer, html;
|
||||
var writer, html, uriValidator;
|
||||
beforeEach(function() {
|
||||
html = '';
|
||||
writer = htmlSanitizeWriter({push:function(text){html+=text;}});
|
||||
uriValidator = jasmine.createSpy('uriValidator');
|
||||
writer = htmlSanitizeWriter({push:function(text){html+=text;}}, uriValidator);
|
||||
});
|
||||
|
||||
it('should write basic HTML', function() {
|
||||
@@ -258,41 +270,106 @@ describe('HTML', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isUri', function() {
|
||||
|
||||
function isUri(value) {
|
||||
return value.match(URI_REGEXP);
|
||||
}
|
||||
|
||||
it('should be URI', function() {
|
||||
expect(isUri('http://abc')).toBeTruthy();
|
||||
expect(isUri('HTTP://abc')).toBeTruthy();
|
||||
expect(isUri('https://abc')).toBeTruthy();
|
||||
expect(isUri('HTTPS://abc')).toBeTruthy();
|
||||
expect(isUri('ftp://abc')).toBeTruthy();
|
||||
expect(isUri('FTP://abc')).toBeTruthy();
|
||||
expect(isUri('mailto:me@example.com')).toBeTruthy();
|
||||
expect(isUri('MAILTO:me@example.com')).toBeTruthy();
|
||||
expect(isUri('tel:123-123-1234')).toBeTruthy();
|
||||
expect(isUri('TEL:123-123-1234')).toBeTruthy();
|
||||
expect(isUri('#anchor')).toBeTruthy();
|
||||
describe('uri validation', function() {
|
||||
it('should call the uri validator', function() {
|
||||
writer.start('a', {href:'someUrl'}, false);
|
||||
expect(uriValidator).toHaveBeenCalledWith('someUrl', false);
|
||||
uriValidator.reset();
|
||||
writer.start('img', {src:'someImgUrl'}, false);
|
||||
expect(uriValidator).toHaveBeenCalledWith('someImgUrl', true);
|
||||
uriValidator.reset();
|
||||
writer.start('someTag', {src:'someNonUrl'}, false);
|
||||
expect(uriValidator).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not be URI', function() {
|
||||
expect(isUri('')).toBeFalsy();
|
||||
expect(isUri('javascript:alert')).toBeFalsy();
|
||||
it('should drop non valid uri attributes', function() {
|
||||
uriValidator.andReturn(false);
|
||||
writer.start('a', {href:'someUrl'}, false);
|
||||
expect(html).toEqual('<a>');
|
||||
|
||||
html = '';
|
||||
uriValidator.andReturn(true);
|
||||
writer.start('a', {href:'someUrl'}, false);
|
||||
expect(html).toEqual('<a href="someUrl">');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('uri checking', function() {
|
||||
beforeEach(function() {
|
||||
this.addMatchers({
|
||||
toBeValidUrl: function() {
|
||||
var sanitize;
|
||||
inject(function($sanitize) {
|
||||
sanitize = $sanitize;
|
||||
});
|
||||
var input = '<a href="'+this.actual+'"></a>';
|
||||
return sanitize(input) === input;
|
||||
},
|
||||
toBeValidImageSrc: function() {
|
||||
var sanitize;
|
||||
inject(function($sanitize) {
|
||||
sanitize = $sanitize;
|
||||
});
|
||||
var input = '<img src="'+this.actual+'"/>';
|
||||
return sanitize(input) === input;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('javascript URL attribute', function() {
|
||||
beforeEach(function() {
|
||||
this.addMatchers({
|
||||
toBeValidUrl: function() {
|
||||
return URI_REGEXP.exec(this.actual);
|
||||
}
|
||||
});
|
||||
it('should use $$sanitizeUri for links', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function() {
|
||||
$$sanitizeUri.andReturn('someUri');
|
||||
|
||||
expectHTML('<a href="someUri"></a>').toEqual('<a href="someUri"></a>');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith('someUri', false);
|
||||
|
||||
$$sanitizeUri.andReturn('unsafe:someUri');
|
||||
expectHTML('<a href="someUri"></a>').toEqual('<a></a>');
|
||||
});
|
||||
});
|
||||
|
||||
it('should use $$sanitizeUri for links', function() {
|
||||
var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri');
|
||||
module(function($provide) {
|
||||
$provide.value('$$sanitizeUri', $$sanitizeUri);
|
||||
});
|
||||
inject(function() {
|
||||
$$sanitizeUri.andReturn('someUri');
|
||||
|
||||
expectHTML('<img src="someUri"/>').toEqual('<img src="someUri"/>');
|
||||
expect($$sanitizeUri).toHaveBeenCalledWith('someUri', true);
|
||||
|
||||
$$sanitizeUri.andReturn('unsafe:someUri');
|
||||
expectHTML('<img src="someUri"/>').toEqual('<img/>');
|
||||
});
|
||||
});
|
||||
|
||||
it('should be URI', function() {
|
||||
expect('').toBeValidUrl();
|
||||
expect('http://abc').toBeValidUrl();
|
||||
expect('HTTP://abc').toBeValidUrl();
|
||||
expect('https://abc').toBeValidUrl();
|
||||
expect('HTTPS://abc').toBeValidUrl();
|
||||
expect('ftp://abc').toBeValidUrl();
|
||||
expect('FTP://abc').toBeValidUrl();
|
||||
expect('mailto:me@example.com').toBeValidUrl();
|
||||
expect('MAILTO:me@example.com').toBeValidUrl();
|
||||
expect('tel:123-123-1234').toBeValidUrl();
|
||||
expect('TEL:123-123-1234').toBeValidUrl();
|
||||
expect('#anchor').toBeValidUrl();
|
||||
expect('/page1.md').toBeValidUrl();
|
||||
});
|
||||
|
||||
it('should not be URI', function() {
|
||||
expect('javascript:alert').not.toBeValidUrl();
|
||||
});
|
||||
|
||||
describe('javascript URLs', function() {
|
||||
it('should ignore javascript:', function() {
|
||||
expect('JavaScript:abc').not.toBeValidUrl();
|
||||
expect(' \n Java\n Script:abc').not.toBeValidUrl();
|
||||
@@ -318,15 +395,19 @@ describe('HTML', function() {
|
||||
});
|
||||
|
||||
it('should ignore hex encoded whitespace javascript:', function() {
|
||||
expect('jav	ascript:alert("A");').not.toBeValidUrl();
|
||||
expect('jav
ascript:alert("B");').not.toBeValidUrl();
|
||||
expect('jav
 ascript:alert("C");').not.toBeValidUrl();
|
||||
expect('jav\u0000ascript:alert("D");').not.toBeValidUrl();
|
||||
expect('java\u0000\u0000script:alert("D");').not.toBeValidUrl();
|
||||
expect('  java\u0000\u0000script:alert("D");').not.toBeValidUrl();
|
||||
expect('jav	ascript:alert();').not.toBeValidUrl();
|
||||
expect('jav
ascript:alert();').not.toBeValidUrl();
|
||||
expect('jav
 ascript:alert();').not.toBeValidUrl();
|
||||
expect('jav\u0000ascript:alert();').not.toBeValidUrl();
|
||||
expect('java\u0000\u0000script:alert();').not.toBeValidUrl();
|
||||
expect('  java\u0000\u0000script:alert();').not.toBeValidUrl();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('sanitizeText', function() {
|
||||
it('should escape text', function() {
|
||||
expect(sanitizeText('a<div>&</div>c')).toEqual('a<div>&</div>c');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
-2
@@ -5,5 +5,5 @@ set -e
|
||||
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
|
||||
grunt parallel:travis --reporters dots \
|
||||
--browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10 \
|
||||
--e2e-browsers SL_Chrome
|
||||
--browsers BS_Chrome,BS_Safari,BS_Firefox,BS_IE_8,BS_IE_9,BS_IE_10,BS_IE_11 \
|
||||
--e2e-browsers BS_Chrome
|
||||
|
||||
Reference in New Issue
Block a user