Merge pull request #8164 from martinvw/feature/bower-components
[jhipster] Removed bower_components
This commit is contained in:
commit
5095a8fc6a
|
@ -18,6 +18,11 @@ npm-debug.log.*
|
|||
######################
|
||||
.sass-cache/
|
||||
|
||||
######################
|
||||
# Bower
|
||||
######################
|
||||
bower_components
|
||||
|
||||
######################
|
||||
# Eclipse
|
||||
######################
|
||||
|
|
|
@ -23,6 +23,10 @@ We use [Gulp][] as our build system. Install the Gulp command-line tool globally
|
|||
|
||||
yarn global add gulp-cli
|
||||
|
||||
Besides that we should download our bower dependencies:
|
||||
|
||||
bower instal
|
||||
|
||||
Run the following commands in two separate terminals to create a blissful development experience where your browser
|
||||
auto-refreshes when files change on your hard drive.
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "angular-aria",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-aria.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-aria",
|
||||
"_release": "1.5.8",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.5.8",
|
||||
"commit": "91710f3ee26d4e2858af0c91ec462aa43360da47"
|
||||
},
|
||||
"_source": "https://github.com/angular/bower-angular-aria.git",
|
||||
"_target": "1.5.8",
|
||||
"_originalSource": "angular-aria"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Angular
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,67 +0,0 @@
|
|||
# packaged angular-aria
|
||||
|
||||
This repo is for distribution on `npm` and `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngAria).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
You can install this package either with `npm` or with `bower`.
|
||||
|
||||
### npm
|
||||
|
||||
```shell
|
||||
npm install angular-aria
|
||||
```
|
||||
Then add `ngAria` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', [require('angular-aria')]);
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```shell
|
||||
bower install angular-aria
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-aria/angular-aria.js"></script>
|
||||
```
|
||||
|
||||
Then add `ngAria` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngAria']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngAria).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,405 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.5.8
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngAria
|
||||
* @description
|
||||
*
|
||||
* The `ngAria` module provides support for common
|
||||
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
|
||||
* attributes that convey state or semantic information about the application for users
|
||||
* of assistive technologies, such as screen readers.
|
||||
*
|
||||
* <div doc-module-components="ngAria"></div>
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
|
||||
* directives are supported:
|
||||
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
|
||||
* `ngDblClick`, and `ngMessages`.
|
||||
*
|
||||
* Below is a more detailed breakdown of the attributes handled by ngAria:
|
||||
*
|
||||
* | Directive | Supported Attributes |
|
||||
* |---------------------------------------------|----------------------------------------------------------------------------------------|
|
||||
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
|
||||
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
|
||||
* | {@link ng.directive:ngRequired ngRequired} | aria-required
|
||||
* | {@link ng.directive:ngChecked ngChecked} | aria-checked
|
||||
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly |
|
||||
* | {@link ng.directive:ngValue ngValue} | aria-checked |
|
||||
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
|
||||
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
|
||||
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
|
||||
* | {@link module:ngMessages ngMessages} | aria-live |
|
||||
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
|
||||
*
|
||||
* Find out more information about each directive by reading the
|
||||
* {@link guide/accessibility ngAria Developer Guide}.
|
||||
*
|
||||
* ## Example
|
||||
* Using ngDisabled with ngAria:
|
||||
* ```html
|
||||
* <md-checkbox ng-disabled="disabled">
|
||||
* ```
|
||||
* Becomes:
|
||||
* ```html
|
||||
* <md-checkbox ng-disabled="disabled" aria-disabled="true">
|
||||
* ```
|
||||
*
|
||||
* ## Disabling Attributes
|
||||
* It's possible to disable individual attributes added by ngAria with the
|
||||
* {@link ngAria.$ariaProvider#config config} method. For more details, see the
|
||||
* {@link guide/accessibility Developer Guide}.
|
||||
*/
|
||||
/* global -ngAriaModule */
|
||||
var ngAriaModule = angular.module('ngAria', ['ng']).
|
||||
provider('$aria', $AriaProvider);
|
||||
|
||||
/**
|
||||
* Internal Utilities
|
||||
*/
|
||||
var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
|
||||
|
||||
var isNodeOneOf = function(elem, nodeTypeArray) {
|
||||
if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $ariaProvider
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring the ARIA attributes injected and managed by ngAria.
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myApp', ['ngAria'], function config($ariaProvider) {
|
||||
* $ariaProvider.config({
|
||||
* ariaValue: true,
|
||||
* tabindex: false
|
||||
* });
|
||||
* });
|
||||
*```
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngAria} module to be installed.
|
||||
*
|
||||
*/
|
||||
function $AriaProvider() {
|
||||
var config = {
|
||||
ariaHidden: true,
|
||||
ariaChecked: true,
|
||||
ariaReadonly: true,
|
||||
ariaDisabled: true,
|
||||
ariaRequired: true,
|
||||
ariaInvalid: true,
|
||||
ariaValue: true,
|
||||
tabindex: true,
|
||||
bindKeypress: true,
|
||||
bindRoleForClick: true
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ariaProvider#config
|
||||
*
|
||||
* @param {object} config object to enable/disable specific ARIA attributes
|
||||
*
|
||||
* - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
|
||||
* - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
|
||||
* - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
|
||||
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
|
||||
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
|
||||
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
|
||||
* - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
|
||||
* - **tabindex** – `{boolean}` – Enables/disables tabindex tags
|
||||
* - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
|
||||
* `li` elements with ng-click
|
||||
* - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
|
||||
* using ng-click, making them more accessible to users of assistive technologies
|
||||
*
|
||||
* @description
|
||||
* Enables/disables various ARIA attributes
|
||||
*/
|
||||
this.config = function(newConfig) {
|
||||
config = angular.extend(config, newConfig);
|
||||
};
|
||||
|
||||
function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
|
||||
return function(scope, elem, attr) {
|
||||
var ariaCamelName = attr.$normalize(ariaAttr);
|
||||
if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
|
||||
scope.$watch(attr[attrName], function(boolVal) {
|
||||
// ensure boolean value
|
||||
boolVal = negate ? !boolVal : !!boolVal;
|
||||
elem.attr(ariaAttr, boolVal);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $aria
|
||||
*
|
||||
* @description
|
||||
* @priority 200
|
||||
*
|
||||
* The $aria service contains helper methods for applying common
|
||||
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
|
||||
*
|
||||
* ngAria injects common accessibility attributes that tell assistive technologies when HTML
|
||||
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
|
||||
* let's review a code snippet from ngAria itself:
|
||||
*
|
||||
*```js
|
||||
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
|
||||
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
|
||||
* }])
|
||||
*```
|
||||
* Shown above, the ngAria module creates a directive with the same signature as the
|
||||
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
|
||||
* solely managing accessibility attributes on custom elements. The internal `$aria` service is
|
||||
* used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
|
||||
* developer, `aria-disabled` is injected as an attribute with its value synchronized to the
|
||||
* value in `ngDisabled`.
|
||||
*
|
||||
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
|
||||
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
|
||||
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
|
||||
*
|
||||
* The full list of directives that interface with ngAria:
|
||||
* * **ngModel**
|
||||
* * **ngChecked**
|
||||
* * **ngReadonly**
|
||||
* * **ngRequired**
|
||||
* * **ngDisabled**
|
||||
* * **ngValue**
|
||||
* * **ngShow**
|
||||
* * **ngHide**
|
||||
* * **ngClick**
|
||||
* * **ngDblclick**
|
||||
* * **ngMessages**
|
||||
*
|
||||
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
|
||||
* directive.
|
||||
*
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngAria} module to be installed.
|
||||
*/
|
||||
this.$get = function() {
|
||||
return {
|
||||
config: function(key) {
|
||||
return config[key];
|
||||
},
|
||||
$$watchExpr: watchExpr
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
|
||||
}])
|
||||
.directive('ngHide', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
|
||||
}])
|
||||
.directive('ngValue', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngChecked', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngReadonly', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngRequired', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngModel', ['$aria', function($aria) {
|
||||
|
||||
function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
|
||||
return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
|
||||
}
|
||||
|
||||
function shouldAttachRole(role, elem) {
|
||||
// if element does not have role attribute
|
||||
// AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
|
||||
// AND element is not INPUT
|
||||
return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
|
||||
}
|
||||
|
||||
function getShape(attr, elem) {
|
||||
var type = attr.type,
|
||||
role = attr.role;
|
||||
|
||||
return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
|
||||
((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
|
||||
(type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : '';
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
|
||||
compile: function(elem, attr) {
|
||||
var shape = getShape(attr, elem);
|
||||
|
||||
return {
|
||||
pre: function(scope, elem, attr, ngModel) {
|
||||
if (shape === 'checkbox') {
|
||||
//Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
|
||||
ngModel.$isEmpty = function(value) {
|
||||
return value === false;
|
||||
};
|
||||
}
|
||||
},
|
||||
post: function(scope, elem, attr, ngModel) {
|
||||
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
|
||||
|
||||
function ngAriaWatchModelValue() {
|
||||
return ngModel.$modelValue;
|
||||
}
|
||||
|
||||
function getRadioReaction(newVal) {
|
||||
var boolVal = (attr.value == ngModel.$viewValue);
|
||||
elem.attr('aria-checked', boolVal);
|
||||
}
|
||||
|
||||
function getCheckboxReaction() {
|
||||
elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
|
||||
}
|
||||
|
||||
switch (shape) {
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
if (shouldAttachRole(shape, elem)) {
|
||||
elem.attr('role', shape);
|
||||
}
|
||||
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
|
||||
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
|
||||
getRadioReaction : getCheckboxReaction);
|
||||
}
|
||||
if (needsTabIndex) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
break;
|
||||
case 'range':
|
||||
if (shouldAttachRole(shape, elem)) {
|
||||
elem.attr('role', 'slider');
|
||||
}
|
||||
if ($aria.config('ariaValue')) {
|
||||
var needsAriaValuemin = !elem.attr('aria-valuemin') &&
|
||||
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
|
||||
var needsAriaValuemax = !elem.attr('aria-valuemax') &&
|
||||
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
|
||||
var needsAriaValuenow = !elem.attr('aria-valuenow');
|
||||
|
||||
if (needsAriaValuemin) {
|
||||
attr.$observe('min', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemin', newVal);
|
||||
});
|
||||
}
|
||||
if (needsAriaValuemax) {
|
||||
attr.$observe('max', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemax', newVal);
|
||||
});
|
||||
}
|
||||
if (needsAriaValuenow) {
|
||||
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
|
||||
elem.attr('aria-valuenow', newVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (needsTabIndex) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
|
||||
&& shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
|
||||
// ngModel.$error.required is undefined on custom controls
|
||||
attr.$observe('required', function() {
|
||||
elem.attr('aria-required', !!attr['required']);
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
|
||||
scope.$watch(function ngAriaInvalidWatch() {
|
||||
return ngModel.$invalid;
|
||||
}, function ngAriaInvalidReaction(newVal) {
|
||||
elem.attr('aria-invalid', !!newVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
.directive('ngDisabled', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngMessages', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngMessages',
|
||||
link: function(scope, elem, attr, ngMessages) {
|
||||
if (!elem.attr('aria-live')) {
|
||||
elem.attr('aria-live', 'assertive');
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('ngClick',['$aria', '$parse', function($aria, $parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function(elem, attr) {
|
||||
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
|
||||
return function(scope, elem, attr) {
|
||||
|
||||
if (!isNodeOneOf(elem, nodeBlackList)) {
|
||||
|
||||
if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
|
||||
elem.attr('role', 'button');
|
||||
}
|
||||
|
||||
if ($aria.config('tabindex') && !elem.attr('tabindex')) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
|
||||
if ($aria.config('bindKeypress') && !attr.ngKeypress) {
|
||||
elem.on('keypress', function(event) {
|
||||
var keyCode = event.which || event.keyCode;
|
||||
if (keyCode === 32 || keyCode === 13) {
|
||||
scope.$apply(callback);
|
||||
}
|
||||
|
||||
function callback() {
|
||||
fn(scope, { $event: event });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
.directive('ngDblclick', ['$aria', function($aria) {
|
||||
return function(scope, elem, attr) {
|
||||
if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.5.8
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(t,p){'use strict';var b="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};p.module("ngAria",["ng"]).provider("$aria",function(){function a(a,b,m,h){return function(d,f,e){var q=e.$normalize(b);!c[q]||l(f,m)||e[q]||d.$watch(e[a],function(a){a=h?!a:!!a;f.attr(b,a)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};
|
||||
this.config=function(a){c=p.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",b,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",b,!1)}]).directive("ngReadonly",
|
||||
["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",b,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",b,!1)}]).directive("ngModel",["$aria",function(a){function c(c,h,d,f){return a.config(h)&&!d.attr(c)&&(f||!l(d,b))}function g(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function k(a,c){var d=a.type,f=a.role;return"checkbox"===(d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"===
|
||||
f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(b,h){var d=k(h,b);return{pre:function(a,e,c,b){"checkbox"===d&&(b.$isEmpty=function(a){return!1===a})},post:function(f,e,b,n){function h(){return n.$modelValue}function k(a){e.attr("aria-checked",b.value==n.$viewValue)}function l(){e.attr("aria-checked",!n.$isEmpty(n.$viewValue))}var m=c("tabindex","tabindex",e,!1);switch(d){case "radio":case "checkbox":g(d,e)&&e.attr("role",
|
||||
d);c("aria-checked","ariaChecked",e,!1)&&f.$watch(h,"radio"===d?k:l);m&&e.attr("tabindex",0);break;case "range":g(d,e)&&e.attr("role","slider");if(a.config("ariaValue")){var p=!e.attr("aria-valuemin")&&(b.hasOwnProperty("min")||b.hasOwnProperty("ngMin")),r=!e.attr("aria-valuemax")&&(b.hasOwnProperty("max")||b.hasOwnProperty("ngMax")),s=!e.attr("aria-valuenow");p&&b.$observe("min",function(a){e.attr("aria-valuemin",a)});r&&b.$observe("max",function(a){e.attr("aria-valuemax",a)});s&&f.$watch(h,function(a){e.attr("aria-valuenow",
|
||||
a)})}m&&e.attr("tabindex",0)}!b.hasOwnProperty("ngRequired")&&n.$validators.required&&c("aria-required","ariaRequired",e,!1)&&b.$observe("required",function(){e.attr("aria-required",!!b.required)});c("aria-invalid","ariaInvalid",e,!0)&&f.$watch(function(){return n.$invalid},function(a){e.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",b,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",
|
||||
link:function(a,b,g,k){b.attr("aria-live")||b.attr("aria-live","assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(g,k){var m=c(k.ngClick,null,!0);return function(c,d,f){if(!l(d,b)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!f.ngKeypress))d.on("keypress",function(a){function b(){m(c,{$event:a})}var d=a.which||a.keyCode;32!==d&&
|
||||
13!==d||c.$apply(b)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,g,k){!a.config("tabindex")||g.attr("tabindex")||l(g,b)||g.attr("tabindex",0)}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-aria.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "angular-aria",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-aria.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-aria');
|
||||
module.exports = 'ngAria';
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "angular-aria",
|
||||
"version": "1.5.8",
|
||||
"description": "AngularJS module for making accessibility easy",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"framework",
|
||||
"browser",
|
||||
"accessibility",
|
||||
"a11y",
|
||||
"client-side"
|
||||
],
|
||||
"author": "Angular Core Team <angular-core+npm@google.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular.js/issues"
|
||||
},
|
||||
"homepage": "http://angularjs.org",
|
||||
"jspm": {
|
||||
"shim": {
|
||||
"angular-aria": {
|
||||
"deps": ["angular"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"author": {
|
||||
"name": "https://github.com/angular-ui/bootstrap/graphs/contributors"
|
||||
},
|
||||
"name": "angular-bootstrap",
|
||||
"keywords": [
|
||||
"angular",
|
||||
"angular-ui",
|
||||
"bootstrap"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [],
|
||||
"description": "Native AngularJS (Angular) directives for Bootstrap.",
|
||||
"version": "1.3.3",
|
||||
"main": [
|
||||
"./ui-bootstrap-tpls.js"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": ">=1.4.0"
|
||||
},
|
||||
"homepage": "https://github.com/angular-ui/bootstrap-bower",
|
||||
"_release": "1.3.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.3.3",
|
||||
"commit": "d45246707f5bf9533e3824861a29abd36757db45"
|
||||
},
|
||||
"_source": "https://github.com/angular-ui/bootstrap-bower.git",
|
||||
"_target": "1.3.3",
|
||||
"_originalSource": "angular-bootstrap"
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
.DS_Store
|
|
@ -1 +0,0 @@
|
|||
bower.json
|
|
@ -1,120 +0,0 @@
|
|||
### UI Bootstrap - [AngularJS](http://angularjs.org/) directives specific to [Bootstrap](http://getbootstrap.com)
|
||||
|
||||
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular-ui/bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Build Status](https://secure.travis-ci.org/angular-ui/bootstrap.svg)](http://travis-ci.org/angular-ui/bootstrap)
|
||||
[![devDependency Status](https://david-dm.org/angular-ui/bootstrap/dev-status.svg?branch=master)](https://david-dm.org/angular-ui/bootstrap#info=devDependencies)
|
||||
|
||||
### Quick links
|
||||
- [Demo](#demo)
|
||||
- [Installation](#installation)
|
||||
- [NPM](#install-with-npm)
|
||||
- [Bower](#install-with-bower)
|
||||
- [NuGet](#install-with-nuget)
|
||||
- [Custom](#custom-build)
|
||||
- [Manual](#manual-download)
|
||||
- [Support](#support)
|
||||
- [FAQ](#faq)
|
||||
- [Supported browsers](#supported-browsers)
|
||||
- [Need help?](#need-help)
|
||||
- [Found a bug?](#found-a-bug)
|
||||
- [Contributing to the project](#contributing-to-the-project)
|
||||
- [Development, meeting minutes, roadmap and more.](#development-meeting-minutes-roadmap-and-more)
|
||||
|
||||
|
||||
# Demo
|
||||
|
||||
Do you want to see directives in action? Visit http://angular-ui.github.io/bootstrap/!
|
||||
|
||||
# Installation
|
||||
|
||||
Installation is easy as UI Bootstrap has minimal dependencies - only the AngularJS and Twitter Bootstrap's CSS are required.
|
||||
Note: Since version 0.13.0, UI Bootstrap depends on [ngAnimate](https://docs.angularjs.org/api/ngAnimate) for transitions and animations, such as the accordion, carousel, etc. Include `ngAnimate` in the module dependencies for your app in order to enable animation.
|
||||
|
||||
#### Install with NPM
|
||||
|
||||
```sh
|
||||
$ npm install angular-ui-bootstrap
|
||||
```
|
||||
|
||||
This will install AngularJS and Bootstrap NPM packages.
|
||||
|
||||
#### Install with Bower
|
||||
```sh
|
||||
$ bower install angular-bootstrap
|
||||
```
|
||||
|
||||
Note: do not install 'angular-ui-bootstrap'. A separate repository - [bootstrap-bower](https://github.com/angular-ui/bootstrap-bower) - hosts the compiled javascript file and bower.json.
|
||||
|
||||
#### Install with NuGet
|
||||
To install AngularJS UI Bootstrap, run the following command in the Package Manager Console
|
||||
|
||||
```sh
|
||||
PM> Install-Package Angular.UI.Bootstrap
|
||||
```
|
||||
|
||||
#### Custom build
|
||||
|
||||
Head over to http://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it.
|
||||
|
||||
#### Manual download
|
||||
|
||||
After downloading dependencies (or better yet, referencing them from your favorite CDN) you need to download build version of this project. All the files and their purposes are described here:
|
||||
https://github.com/angular-ui/bootstrap/tree/gh-pages#build-files
|
||||
Don't worry, if you are not sure which file to take, opt for `ui-bootstrap-tpls-[version].min.js`.
|
||||
|
||||
### Adding dependency to your project
|
||||
|
||||
When you are done downloading all the dependencies and project files the only remaining part is to add dependencies on the `ui.bootstrap` AngularJS module:
|
||||
|
||||
```js
|
||||
angular.module('myModule', ['ui.bootstrap']);
|
||||
```
|
||||
|
||||
If you're a Browserify or Webpack user, you can do:
|
||||
|
||||
```js
|
||||
var uibs = require('angular-ui-bootstrap');
|
||||
|
||||
angular.module('myModule', [uibs]);
|
||||
```
|
||||
|
||||
# Support
|
||||
|
||||
## FAQ
|
||||
|
||||
https://github.com/angular-ui/bootstrap/wiki/FAQ
|
||||
|
||||
## Supported browsers
|
||||
|
||||
Directives from this repository are automatically tested with the following browsers:
|
||||
* Chrome (stable and canary channel)
|
||||
* Firefox
|
||||
* IE 9 and 10
|
||||
* Opera
|
||||
* Safari
|
||||
|
||||
Modern mobile browsers should work without problems.
|
||||
|
||||
|
||||
## Need help?
|
||||
Need help using UI Bootstrap?
|
||||
|
||||
* Live help in the IRC (`#angularjs` channel at the `freenode` network). Use this [webchat](https://webchat.freenode.net/) or your own IRC client.
|
||||
* Ask a question in [StackOverflow](http://stackoverflow.com/) under the [angular-ui-bootstrap](http://stackoverflow.com/questions/tagged/angular-ui-bootstrap) tag.
|
||||
|
||||
**Please do not create new issues in this repository to ask questions about using UI Bootstrap**
|
||||
|
||||
## Found a bug?
|
||||
Please take a look at [CONTRIBUTING.md](CONTRIBUTING.md#you-think-youve-found-a-bug) and submit your issue [here](https://github.com/angular-ui/bootstrap/issues/new).
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
# Contributing to the project
|
||||
|
||||
We are always looking for the quality contributions! Please check the [CONTRIBUTING.md](CONTRIBUTING.md) for the contribution guidelines.
|
||||
|
||||
# Development, meeting minutes, roadmap and more.
|
||||
|
||||
Head over to the [Wiki](https://github.com/angular-ui/bootstrap/wiki) for notes on development for UI Bootstrap, meeting minutes from the UI Bootstrap team, roadmap plans, project philosophy and more.
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"author": {
|
||||
"name": "https://github.com/angular-ui/bootstrap/graphs/contributors"
|
||||
},
|
||||
"name": "angular-bootstrap",
|
||||
"keywords": [
|
||||
"angular",
|
||||
"angular-ui",
|
||||
"bootstrap"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [],
|
||||
"description": "Native AngularJS (Angular) directives for Bootstrap.",
|
||||
"version": "1.3.3",
|
||||
"main": ["./ui-bootstrap-tpls.js"],
|
||||
"dependencies": {
|
||||
"angular": ">=1.4.0"
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require('./ui-bootstrap-tpls');
|
||||
module.exports = 'ui.bootstrap';
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "angular-ui-bootstrap",
|
||||
"version": "1.3.3",
|
||||
"description": "Bootstrap widgets for Angular",
|
||||
"main": "index.js",
|
||||
"homepage": "http://angular-ui.github.io/bootstrap/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular-ui/bootstrap.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"bootstrap",
|
||||
"angular-ui",
|
||||
"components",
|
||||
"client-side"
|
||||
],
|
||||
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
|
||||
"peerDependencies": {
|
||||
"angular": ">= 1.4.0-beta.0 || >= 1.5.0-beta.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/* Include this file in your html if you are using the CSP mode. */
|
||||
|
||||
.ng-animate.item:not(.left):not(.right) {
|
||||
-webkit-transition: 0s ease-in-out left;
|
||||
transition: 0s ease-in-out left
|
||||
}
|
||||
.uib-datepicker .uib-title {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.uib-day button, .uib-month button, .uib-year button {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.uib-left, .uib-right {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.uib-position-measure {
|
||||
display: block !important;
|
||||
visibility: hidden !important;
|
||||
position: absolute !important;
|
||||
top: -9999px !important;
|
||||
left: -9999px !important;
|
||||
}
|
||||
|
||||
.uib-position-scrollbar-measure {
|
||||
position: absolute !important;
|
||||
top: -9999px !important;
|
||||
width: 50px !important;
|
||||
height: 50px !important;
|
||||
overflow: scroll !important;
|
||||
}
|
||||
|
||||
.uib-position-body-scrollbar-measure {
|
||||
overflow: scroll !important;
|
||||
}
|
||||
.uib-datepicker-popup.dropdown-menu {
|
||||
display: block;
|
||||
float: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.uib-button-bar {
|
||||
padding: 10px 9px 2px;
|
||||
}
|
||||
|
||||
[uib-tooltip-popup].tooltip.top-left > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.top-right > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.bottom-left > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.bottom-right > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.left-top > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.left-bottom > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.right-top > .tooltip-arrow,
|
||||
[uib-tooltip-popup].tooltip.right-bottom > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.top-left > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.top-right > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.bottom-left > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.bottom-right > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.left-top > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.left-bottom > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.right-top > .tooltip-arrow,
|
||||
[uib-tooltip-html-popup].tooltip.right-bottom > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.top-left > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.top-right > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.bottom-left > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.bottom-right > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.left-top > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.left-bottom > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.right-top > .tooltip-arrow,
|
||||
[uib-tooltip-template-popup].tooltip.right-bottom > .tooltip-arrow,
|
||||
[uib-popover-popup].popover.top-left > .arrow,
|
||||
[uib-popover-popup].popover.top-right > .arrow,
|
||||
[uib-popover-popup].popover.bottom-left > .arrow,
|
||||
[uib-popover-popup].popover.bottom-right > .arrow,
|
||||
[uib-popover-popup].popover.left-top > .arrow,
|
||||
[uib-popover-popup].popover.left-bottom > .arrow,
|
||||
[uib-popover-popup].popover.right-top > .arrow,
|
||||
[uib-popover-popup].popover.right-bottom > .arrow,
|
||||
[uib-popover-html-popup].popover.top-left > .arrow,
|
||||
[uib-popover-html-popup].popover.top-right > .arrow,
|
||||
[uib-popover-html-popup].popover.bottom-left > .arrow,
|
||||
[uib-popover-html-popup].popover.bottom-right > .arrow,
|
||||
[uib-popover-html-popup].popover.left-top > .arrow,
|
||||
[uib-popover-html-popup].popover.left-bottom > .arrow,
|
||||
[uib-popover-html-popup].popover.right-top > .arrow,
|
||||
[uib-popover-html-popup].popover.right-bottom > .arrow,
|
||||
[uib-popover-template-popup].popover.top-left > .arrow,
|
||||
[uib-popover-template-popup].popover.top-right > .arrow,
|
||||
[uib-popover-template-popup].popover.bottom-left > .arrow,
|
||||
[uib-popover-template-popup].popover.bottom-right > .arrow,
|
||||
[uib-popover-template-popup].popover.left-top > .arrow,
|
||||
[uib-popover-template-popup].popover.left-bottom > .arrow,
|
||||
[uib-popover-template-popup].popover.right-top > .arrow,
|
||||
[uib-popover-template-popup].popover.right-bottom > .arrow {
|
||||
top: auto;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: auto;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[uib-popover-popup].popover,
|
||||
[uib-popover-html-popup].popover,
|
||||
[uib-popover-template-popup].popover {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.uib-time input {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
[uib-typeahead-popup].dropdown-menu {
|
||||
display: block;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"name": "angular-cache-buster",
|
||||
"version": "0.4.3",
|
||||
"homepage": "https://github.com/saintmac/angular-cache-buster",
|
||||
"authors": [
|
||||
"saintmac <martin.saintmac@gmail.com>"
|
||||
],
|
||||
"description": "Cache Buster for AngularJS $http (and $resource). Especially useful with Internet Explorer (IE8, IE9)",
|
||||
"main": "angular-cache-buster.js",
|
||||
"keywords": [
|
||||
"angularjs",
|
||||
"cache",
|
||||
"buster",
|
||||
"ie8",
|
||||
"ie9",
|
||||
"$http",
|
||||
"$resource"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"devDependencies": {
|
||||
"angular": "~1.2.13",
|
||||
"angular-mocks": "~1.2.13"
|
||||
},
|
||||
"_release": "0.4.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "0.4.3",
|
||||
"commit": "c6c378db5cfc2431773e05743e72e1e97fa1ef16"
|
||||
},
|
||||
"_source": "https://github.com/saintmac/angular-cache-buster.git",
|
||||
"_target": "0.4.3",
|
||||
"_originalSource": "angular-cache-buster"
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
Cache Buster for Angular JS $http and $resource.
|
||||
Especially useful with Internet Explorer (IE8, IE9)
|
||||
|
||||
# install
|
||||
|
||||
bower install angular-cache-buster --save
|
||||
|
||||
In your app module definition, add `ngCacheBuster` as a dependency
|
||||
|
||||
angular.module('myApp', ['ngCacheBuster']);
|
||||
|
||||
# configure
|
||||
|
||||
AngularCacheBuster adds a cache buster to any $http requests (and hence to $resource requests).
|
||||
Since you probably want to maintain browser caching for your views, partials or other routes, you can supply a list of regexes that will be matched against all URL's. By default the supplied matchlist is a whitelist (i.e. busting everything not matching an entry in the list) but you can also set it to be a blacklist, (i.e. busting everything except the matching entries)
|
||||
|
||||
For instance, if you want to bust everything except views in a 'partials' folder and images in a 'images' folder , you can configure AngularCacheBuster this way:
|
||||
|
||||
angular.module('yourApp', ['ngCacheBuster'])
|
||||
.config(function(httpRequestInterceptorCacheBusterProvider){
|
||||
httpRequestInterceptorCacheBusterProvider.setMatchlist([/.*partials.*/,/.*images.*/]);
|
||||
});
|
||||
|
||||
If instead you want to allow everything to be cached, except your "/api/users" and "api/orders" (assuming they are the only things that change frequently), you can supply a matchlist as before, but pass in a second boolean argument "blacklist" set to true as well:
|
||||
|
||||
|
||||
angular.module('yourApp', ['ngCacheBuster'])
|
||||
.config(function(httpRequestInterceptorCacheBusterProvider){
|
||||
httpRequestInterceptorCacheBusterProvider.setMatchlist([/.*orders.*/,/.*users.*/],true);
|
||||
});
|
||||
|
||||
# use
|
||||
|
||||
That's it! All your resource calls will have a cache buster added for anything not in the whitelist, or if you specified "blacklist", for everything matching the blacklist,
|
||||
|
||||
# test
|
||||
`karma start` to launch the tests
|
|
@ -1,62 +0,0 @@
|
|||
angular.module('ngCacheBuster', [])
|
||||
.config(['$httpProvider', function($httpProvider) {
|
||||
return $httpProvider.interceptors.push('httpRequestInterceptorCacheBuster');
|
||||
}])
|
||||
.provider('httpRequestInterceptorCacheBuster', function() {
|
||||
|
||||
this.matchlist = [/.*partials.*/, /.*views.*/ ];
|
||||
this.logRequests = false;
|
||||
|
||||
//Default to whitelist (i.e. block all except matches)
|
||||
this.black=false;
|
||||
|
||||
//Select blacklist or whitelist, default to whitelist
|
||||
this.setMatchlist = function(list,black) {
|
||||
this.black = typeof black != 'undefined' ? black : false
|
||||
this.matchlist = list;
|
||||
};
|
||||
|
||||
|
||||
this.setLogRequests = function(logRequests) {
|
||||
this.logRequests = logRequests;
|
||||
};
|
||||
|
||||
this.$get = ['$q', '$log', function($q, $log) {
|
||||
var matchlist = this.matchlist;
|
||||
var logRequests = this.logRequests;
|
||||
var black = this.black;
|
||||
if (logRequests) {
|
||||
$log.log("Blacklist? ",black);
|
||||
}
|
||||
return {
|
||||
'request': function(config) {
|
||||
//Blacklist by default, match with whitelist
|
||||
var busted= !black;
|
||||
|
||||
for(var i=0; i< matchlist.length; i++){
|
||||
if(config.url.match(matchlist[i])) {
|
||||
busted=black; break;
|
||||
}
|
||||
}
|
||||
|
||||
//Bust if the URL was on blacklist or not on whitelist
|
||||
if (busted) {
|
||||
var d = new Date();
|
||||
config.url = config.url.replace(/[?|&]cacheBuster=\d+/,'');
|
||||
//Some url's allready have '?' attached
|
||||
config.url+=config.url.indexOf('?') === -1 ? '?' : '&'
|
||||
config.url += 'cacheBuster=' + d.getTime();
|
||||
}
|
||||
|
||||
if (logRequests) {
|
||||
var log='request.url =' + config.url
|
||||
busted ? $log.warn(log) : $log.info(log)
|
||||
}
|
||||
|
||||
return config || $q.when(config);
|
||||
}
|
||||
}
|
||||
}];
|
||||
});
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"name": "angular-cache-buster",
|
||||
"version": "0.4.3",
|
||||
"homepage": "https://github.com/saintmac/angular-cache-buster",
|
||||
"authors": [
|
||||
"saintmac <martin.saintmac@gmail.com>"
|
||||
],
|
||||
"description": "Cache Buster for AngularJS $http (and $resource). Especially useful with Internet Explorer (IE8, IE9)",
|
||||
"main": "angular-cache-buster.js",
|
||||
"keywords": [
|
||||
"angularjs",
|
||||
"cache",
|
||||
"buster",
|
||||
"ie8",
|
||||
"ie9",
|
||||
"$http",
|
||||
"$resource"
|
||||
],
|
||||
"license": "MIT",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"devDependencies": {
|
||||
"angular": "~1.2.13",
|
||||
"angular-mocks": "~1.2.13"
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
// Karma configuration
|
||||
// Generated on Sat Feb 22 2014 23:17:37 GMT+0100 (CET)
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
|
||||
// base path, that will be used to resolve files and exclude
|
||||
basePath: '',
|
||||
|
||||
|
||||
// frameworks to use
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'bower_components/angular/angular.js',
|
||||
'bower_components/angular-mocks/angular-mocks.js',
|
||||
'angular-cache-buster.js',
|
||||
'test/*.js'
|
||||
],
|
||||
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
|
||||
],
|
||||
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
|
||||
reporters: ['progress'],
|
||||
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: true,
|
||||
|
||||
|
||||
// Start these browsers, currently available:
|
||||
// - Chrome
|
||||
// - ChromeCanary
|
||||
// - Firefox
|
||||
// - Opera (has to be installed with `npm install karma-opera-launcher`)
|
||||
// - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
|
||||
// - PhantomJS
|
||||
// - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
|
||||
browsers: ['Chrome'],
|
||||
|
||||
|
||||
// If browser does not capture in given timeout [ms], kill it
|
||||
captureTimeout: 60000,
|
||||
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, it capture browsers, run tests and exit
|
||||
singleRun: false,
|
||||
|
||||
plugins : [
|
||||
'karma-junit-reporter',
|
||||
'karma-chrome-launcher',
|
||||
'karma-firefox-launcher',
|
||||
'karma-script-launcher',
|
||||
'karma-jasmine'
|
||||
]
|
||||
});
|
||||
};
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"name": "angular-cache-buster",
|
||||
"version": "0.4.3",
|
||||
"description": "Cache Buster for AngularJS $http (and $resource). Especially useful with Internet Explorer (IE8, IE9)",
|
||||
"main": "angular-cache-buster.js",
|
||||
"scripts": {
|
||||
"test": "karma start"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/saintmac/angular-cache-buster.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angularjs",
|
||||
"$http",
|
||||
"$resource",
|
||||
"cache",
|
||||
"buster",
|
||||
"internet",
|
||||
"explorer",
|
||||
"ie8",
|
||||
"ie9"
|
||||
],
|
||||
"author": "saintmac <martin.saintmac@gmail.com> (Martin Saint-Macary, http://vyte.in)",
|
||||
"contributors": ["Alfred Bratterud <alfred.bratterud@hioa.no>"],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/saintmac/angular-cache-buster/issues"
|
||||
},
|
||||
"homepage": "https://github.com/saintmac/angular-cache-buster",
|
||||
"devDependencies": {
|
||||
"karma-script-launcher": "~0.1.0",
|
||||
"karma-chrome-launcher": "~0.1.3",
|
||||
"karma-firefox-launcher": "~0.1.3",
|
||||
"karma-html2js-preprocessor": "~0.1.0",
|
||||
"karma-jasmine": "~0.1.5",
|
||||
"karma-coffee-preprocessor": "~0.1.3",
|
||||
"requirejs": "~2.1.11",
|
||||
"karma-requirejs": "~0.2.1",
|
||||
"karma-phantomjs-launcher": "~0.1.2",
|
||||
"karma": "~0.12.15"
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "angular-cookies",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-cookies.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-cookies",
|
||||
"_release": "1.5.8",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.5.8",
|
||||
"commit": "a2247a1efb436f0293289fb74ebd76fbe52a7422"
|
||||
},
|
||||
"_source": "https://github.com/angular/bower-angular-cookies.git",
|
||||
"_target": "1.5.8",
|
||||
"_originalSource": "angular-cookies"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Angular
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,68 +0,0 @@
|
|||
# packaged angular-cookies
|
||||
|
||||
This repo is for distribution on `npm` and `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngCookies).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
You can install this package either with `npm` or with `bower`.
|
||||
|
||||
### npm
|
||||
|
||||
```shell
|
||||
npm install angular-cookies
|
||||
```
|
||||
|
||||
Then add `ngCookies` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', [require('angular-cookies')]);
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```shell
|
||||
bower install angular-cookies
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-cookies/angular-cookies.js"></script>
|
||||
```
|
||||
|
||||
Then add `ngCookies` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngCookies']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngCookies).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,322 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.5.8
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngCookies
|
||||
* @description
|
||||
*
|
||||
* # ngCookies
|
||||
*
|
||||
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngCookies"></div>
|
||||
*
|
||||
* See {@link ngCookies.$cookies `$cookies`} for usage.
|
||||
*/
|
||||
|
||||
|
||||
angular.module('ngCookies', ['ng']).
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $cookiesProvider
|
||||
* @description
|
||||
* Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service.
|
||||
* */
|
||||
provider('$cookies', [function $CookiesProvider() {
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $cookiesProvider#defaults
|
||||
* @description
|
||||
*
|
||||
* Object containing default options to pass when setting cookies.
|
||||
*
|
||||
* The object may have following properties:
|
||||
*
|
||||
* - **path** - `{string}` - The cookie will be available only for this path and its
|
||||
* sub-paths. By default, this is the URL that appears in your `<base>` tag.
|
||||
* - **domain** - `{string}` - The cookie will be available only for this domain and
|
||||
* its sub-domains. For security reasons the user agent will not accept the cookie
|
||||
* if the current domain is not a sub-domain of this domain or equal to it.
|
||||
* - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
|
||||
* or a Date object indicating the exact date/time this cookie will expire.
|
||||
* - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
|
||||
* secured connection.
|
||||
*
|
||||
* Note: By default, the address that appears in your `<base>` tag will be used as the path.
|
||||
* This is important so that cookies will be visible for all routes when html5mode is enabled.
|
||||
*
|
||||
**/
|
||||
var defaults = this.defaults = {};
|
||||
|
||||
function calcOptions(options) {
|
||||
return options ? angular.extend({}, defaults, options) : defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides read/write access to browser's cookies.
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* Up until Angular 1.3, `$cookies` exposed properties that represented the
|
||||
* current browser cookie values. In version 1.4, this behavior has changed, and
|
||||
* `$cookies` now provides a standard api of getters, setters etc.
|
||||
* </div>
|
||||
*
|
||||
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* angular.module('cookiesExample', ['ngCookies'])
|
||||
* .controller('ExampleController', ['$cookies', function($cookies) {
|
||||
* // Retrieving a cookie
|
||||
* var favoriteCookie = $cookies.get('myFavorite');
|
||||
* // Setting a cookie
|
||||
* $cookies.put('myFavorite', 'oatmeal');
|
||||
* }]);
|
||||
* ```
|
||||
*/
|
||||
this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#get
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {string} Raw cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
return $$cookieReader()[key];
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#getObject
|
||||
*
|
||||
* @description
|
||||
* Returns the deserialized value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
getObject: function(key) {
|
||||
var value = this.get(key);
|
||||
return value ? angular.fromJson(value) : value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#getAll
|
||||
*
|
||||
* @description
|
||||
* Returns a key value object with all the cookies
|
||||
*
|
||||
* @returns {Object} All cookies
|
||||
*/
|
||||
getAll: function() {
|
||||
return $$cookieReader();
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#put
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {string} value Raw value to be stored.
|
||||
* @param {Object=} options Options object.
|
||||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
|
||||
*/
|
||||
put: function(key, value, options) {
|
||||
$$cookieWriter(key, value, calcOptions(options));
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#putObject
|
||||
*
|
||||
* @description
|
||||
* Serializes and sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
* @param {Object=} options Options object.
|
||||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
|
||||
*/
|
||||
putObject: function(key, value, options) {
|
||||
this.put(key, angular.toJson(value), options);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#remove
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
* @param {Object=} options Options object.
|
||||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
|
||||
*/
|
||||
remove: function(key, options) {
|
||||
$$cookieWriter(key, undefined, calcOptions(options));
|
||||
}
|
||||
};
|
||||
}];
|
||||
}]);
|
||||
|
||||
angular.module('ngCookies').
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $cookieStore
|
||||
* @deprecated
|
||||
* @requires $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides a key-value (string-object) storage, that is backed by session cookies.
|
||||
* Objects put or retrieved from this storage are automatically serialized or
|
||||
* deserialized by angular's toJson/fromJson.
|
||||
*
|
||||
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* **Note:** The $cookieStore service is **deprecated**.
|
||||
* Please use the {@link ngCookies.$cookies `$cookies`} service instead.
|
||||
* </div>
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* angular.module('cookieStoreExample', ['ngCookies'])
|
||||
* .controller('ExampleController', ['$cookieStore', function($cookieStore) {
|
||||
* // Put cookie
|
||||
* $cookieStore.put('myFavorite','oatmeal');
|
||||
* // Get cookie
|
||||
* var favoriteCookie = $cookieStore.get('myFavorite');
|
||||
* // Removing a cookie
|
||||
* $cookieStore.remove('myFavorite');
|
||||
* }]);
|
||||
* ```
|
||||
*/
|
||||
factory('$cookieStore', ['$cookies', function($cookies) {
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookieStore#get
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
|
||||
*/
|
||||
get: function(key) {
|
||||
return $cookies.getObject(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookieStore#put
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$cookies.putObject(key, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookieStore#remove
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
$cookies.remove(key);
|
||||
}
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
/**
|
||||
* @name $$cookieWriter
|
||||
* @requires $document
|
||||
*
|
||||
* @description
|
||||
* This is a private service for writing cookies
|
||||
*
|
||||
* @param {string} name Cookie name
|
||||
* @param {string=} value Cookie value (if undefined, cookie will be deleted)
|
||||
* @param {Object=} options Object with options that need to be stored for the cookie.
|
||||
*/
|
||||
function $$CookieWriter($document, $log, $browser) {
|
||||
var cookiePath = $browser.baseHref();
|
||||
var rawDocument = $document[0];
|
||||
|
||||
function buildCookieString(name, value, options) {
|
||||
var path, expires;
|
||||
options = options || {};
|
||||
expires = options.expires;
|
||||
path = angular.isDefined(options.path) ? options.path : cookiePath;
|
||||
if (angular.isUndefined(value)) {
|
||||
expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
value = '';
|
||||
}
|
||||
if (angular.isString(expires)) {
|
||||
expires = new Date(expires);
|
||||
}
|
||||
|
||||
var str = encodeURIComponent(name) + '=' + encodeURIComponent(value);
|
||||
str += path ? ';path=' + path : '';
|
||||
str += options.domain ? ';domain=' + options.domain : '';
|
||||
str += expires ? ';expires=' + expires.toUTCString() : '';
|
||||
str += options.secure ? ';secure' : '';
|
||||
|
||||
// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
|
||||
// - 300 cookies
|
||||
// - 20 cookies per unique domain
|
||||
// - 4096 bytes per cookie
|
||||
var cookieLength = str.length + 1;
|
||||
if (cookieLength > 4096) {
|
||||
$log.warn("Cookie '" + name +
|
||||
"' possibly not set or overflowed because it was too large (" +
|
||||
cookieLength + " > 4096 bytes)!");
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
return function(name, value, options) {
|
||||
rawDocument.cookie = buildCookieString(name, value, options);
|
||||
};
|
||||
}
|
||||
|
||||
$$CookieWriter.$inject = ['$document', '$log', '$browser'];
|
||||
|
||||
angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() {
|
||||
this.$get = $$CookieWriter;
|
||||
});
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.5.8
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(n,c){'use strict';function l(b,a,g){var d=g.baseHref(),k=b[0];return function(b,e,f){var g,h;f=f||{};h=f.expires;g=c.isDefined(f.path)?f.path:d;c.isUndefined(e)&&(h="Thu, 01 Jan 1970 00:00:00 GMT",e="");c.isString(h)&&(h=new Date(h));e=encodeURIComponent(b)+"="+encodeURIComponent(e);e=e+(g?";path="+g:"")+(f.domain?";domain="+f.domain:"");e+=h?";expires="+h.toUTCString():"";e+=f.secure?";secure":"";f=e.length+1;4096<f&&a.warn("Cookie '"+b+"' possibly not set or overflowed because it was too large ("+
|
||||
f+" > 4096 bytes)!");k.cookie=e}}c.module("ngCookies",["ng"]).provider("$cookies",[function(){var b=this.defaults={};this.$get=["$$cookieReader","$$cookieWriter",function(a,g){return{get:function(d){return a()[d]},getObject:function(d){return(d=this.get(d))?c.fromJson(d):d},getAll:function(){return a()},put:function(d,a,m){g(d,a,m?c.extend({},b,m):b)},putObject:function(d,b,a){this.put(d,c.toJson(b),a)},remove:function(a,k){g(a,void 0,k?c.extend({},b,k):b)}}}]}]);c.module("ngCookies").factory("$cookieStore",
|
||||
["$cookies",function(b){return{get:function(a){return b.getObject(a)},put:function(a,c){b.putObject(a,c)},remove:function(a){b.remove(a)}}}]);l.$inject=["$document","$log","$browser"];c.module("ngCookies").provider("$$cookieWriter",function(){this.$get=l})})(window,window.angular);
|
||||
//# sourceMappingURL=angular-cookies.min.js.map
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"version":3,
|
||||
"file":"angular-cookies.min.js",
|
||||
"lineCount":8,
|
||||
"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkB,CA2Q3BC,QAASA,EAAc,CAACC,CAAD,CAAYC,CAAZ,CAAkBC,CAAlB,CAA4B,CACjD,IAAIC,EAAaD,CAAAE,SAAA,EAAjB,CACIC,EAAcL,CAAA,CAAU,CAAV,CAmClB,OAAO,SAAQ,CAACM,CAAD,CAAOC,CAAP,CAAcC,CAAd,CAAuB,CAjCW,IAC3CC,CAD2C,CACrCC,CACVF,EAAA,CAgCoDA,CAhCpD,EAAqB,EACrBE,EAAA,CAAUF,CAAAE,QACVD,EAAA,CAAOX,CAAAa,UAAA,CAAkBH,CAAAC,KAAlB,CAAA,CAAkCD,CAAAC,KAAlC,CAAiDN,CACpDL,EAAAc,YAAA,CAAoBL,CAApB,CAAJ,GACEG,CACA,CADU,+BACV,CAAAH,CAAA,CAAQ,EAFV,CAIIT,EAAAe,SAAA,CAAiBH,CAAjB,CAAJ,GACEA,CADF,CACY,IAAII,IAAJ,CAASJ,CAAT,CADZ,CAIIK,EAAAA,CAAMC,kBAAA,CAqB6BV,CArB7B,CAANS,CAAiC,GAAjCA,CAAuCC,kBAAA,CAAmBT,CAAnB,CAE3CQ,EAAA,CADAA,CACA,EADON,CAAA,CAAO,QAAP,CAAkBA,CAAlB,CAAyB,EAChC,GAAOD,CAAAS,OAAA,CAAiB,UAAjB,CAA8BT,CAAAS,OAA9B,CAA+C,EAAtD,CACAF,EAAA,EAAOL,CAAA,CAAU,WAAV,CAAwBA,CAAAQ,YAAA,EAAxB,CAAgD,EACvDH,EAAA,EAAOP,CAAAW,OAAA,CAAiB,SAAjB,CAA6B,EAMhCC,EAAAA,CAAeL,CAAAM,OAAfD,CAA4B,CACb,KAAnB,CAAIA,CAAJ,EACEnB,CAAAqB,KAAA,CAAU,UAAV,CASqChB,CATrC,CACE,6DADF;AAEEc,CAFF,CAEiB,iBAFjB,CASFf,EAAAkB,OAAA,CAJOR,CAG6B,CArCW,CAzPnDjB,CAAA0B,OAAA,CAAe,WAAf,CAA4B,CAAC,IAAD,CAA5B,CAAAC,SAAA,CAOY,UAPZ,CAOwB,CAACC,QAAyB,EAAG,CAwBjD,IAAIC,EAAW,IAAAA,SAAXA,CAA2B,EAiC/B,KAAAC,KAAA,CAAY,CAAC,gBAAD,CAAmB,gBAAnB,CAAqC,QAAQ,CAACC,CAAD,CAAiBC,CAAjB,CAAiC,CACxF,MAAO,CAWLC,IAAKA,QAAQ,CAACC,CAAD,CAAM,CACjB,MAAOH,EAAA,EAAA,CAAiBG,CAAjB,CADU,CAXd,CAyBLC,UAAWA,QAAQ,CAACD,CAAD,CAAM,CAEvB,MAAO,CADHzB,CACG,CADK,IAAAwB,IAAA,CAASC,CAAT,CACL,EAAQlC,CAAAoC,SAAA,CAAiB3B,CAAjB,CAAR,CAAkCA,CAFlB,CAzBpB,CAuCL4B,OAAQA,QAAQ,EAAG,CACjB,MAAON,EAAA,EADU,CAvCd,CAuDLO,IAAKA,QAAQ,CAACJ,CAAD,CAAMzB,CAAN,CAAaC,CAAb,CAAsB,CACjCsB,CAAA,CAAeE,CAAf,CAAoBzB,CAApB,CAAuCC,CAvFpC,CAAUV,CAAAuC,OAAA,CAAe,EAAf,CAAmBV,CAAnB,CAuF0BnB,CAvF1B,CAAV,CAAkDmB,CAuFrD,CADiC,CAvD9B,CAuELW,UAAWA,QAAQ,CAACN,CAAD,CAAMzB,CAAN,CAAaC,CAAb,CAAsB,CACvC,IAAA4B,IAAA,CAASJ,CAAT,CAAclC,CAAAyC,OAAA,CAAehC,CAAf,CAAd,CAAqCC,CAArC,CADuC,CAvEpC,CAsFLgC,OAAQA,QAAQ,CAACR,CAAD,CAAMxB,CAAN,CAAe,CAC7BsB,CAAA,CAAeE,CAAf,CAAoBS,IAAAA,EAApB,CAA2CjC,CAtHxC,CAAUV,CAAAuC,OAAA,CAAe,EAAf,CAAmBV,CAAnB,CAsH8BnB,CAtH9B,CAAV,CAAkDmB,CAsHrD,CAD6B,CAtF1B,CADiF,CAA9E,CAzDqC,CAA7B,CAPxB,CA8JA7B,EAAA0B,OAAA,CAAe,WAAf,CAAAkB,QAAA,CAiCS,cAjCT;AAiCyB,CAAC,UAAD,CAAa,QAAQ,CAACC,CAAD,CAAW,CAErD,MAAO,CAWLZ,IAAKA,QAAQ,CAACC,CAAD,CAAM,CACjB,MAAOW,EAAAV,UAAA,CAAmBD,CAAnB,CADU,CAXd,CAyBLI,IAAKA,QAAQ,CAACJ,CAAD,CAAMzB,CAAN,CAAa,CACxBoC,CAAAL,UAAA,CAAmBN,CAAnB,CAAwBzB,CAAxB,CADwB,CAzBrB,CAsCLiC,OAAQA,QAAQ,CAACR,CAAD,CAAM,CACpBW,CAAAH,OAAA,CAAgBR,CAAhB,CADoB,CAtCjB,CAF8C,CAAhC,CAjCzB,CAqIAjC,EAAA6C,QAAA,CAAyB,CAAC,WAAD,CAAc,MAAd,CAAsB,UAAtB,CAEzB9C,EAAA0B,OAAA,CAAe,WAAf,CAAAC,SAAA,CAAqC,gBAArC,CAAuDoB,QAA+B,EAAG,CACvF,IAAAjB,KAAA,CAAY7B,CAD2E,CAAzF,CAvT2B,CAA1B,CAAD,CA4TGF,MA5TH,CA4TWA,MAAAC,QA5TX;",
|
||||
"sources":["angular-cookies.js"],
|
||||
"names":["window","angular","$$CookieWriter","$document","$log","$browser","cookiePath","baseHref","rawDocument","name","value","options","path","expires","isDefined","isUndefined","isString","Date","str","encodeURIComponent","domain","toUTCString","secure","cookieLength","length","warn","cookie","module","provider","$CookiesProvider","defaults","$get","$$cookieReader","$$cookieWriter","get","key","getObject","fromJson","getAll","put","extend","putObject","toJson","remove","undefined","factory","$cookies","$inject","$$CookieWriterProvider"]
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "angular-cookies",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-cookies.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-cookies');
|
||||
module.exports = 'ngCookies';
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"name": "angular-cookies",
|
||||
"version": "1.5.8",
|
||||
"description": "AngularJS module for cookies",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"framework",
|
||||
"browser",
|
||||
"cookies",
|
||||
"client-side"
|
||||
],
|
||||
"author": "Angular Core Team <angular-core+npm@google.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular.js/issues"
|
||||
},
|
||||
"homepage": "http://angularjs.org",
|
||||
"jspm": {
|
||||
"shim": {
|
||||
"angular-cookies": {
|
||||
"deps": ["angular"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"name": "angular-loading-bar",
|
||||
"main": [
|
||||
"build/loading-bar.js",
|
||||
"build/loading-bar.css"
|
||||
],
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"components",
|
||||
"test",
|
||||
"example"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "^1.2.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular": "~1.2.23",
|
||||
"angular-1.3": "angular#1.3",
|
||||
"angular-1.4": "angular#1.4",
|
||||
"angular-mocks": "~1.2.9",
|
||||
"angular-mocks-1.3": "angular-mocks#1.3",
|
||||
"angular-mocks-1.4": "angular-mocks#1.4",
|
||||
"angular-animate": "~1.2.9",
|
||||
"angular-animate-1.3": "angular-animate#1.3",
|
||||
"angular-animate-1.4": "angular-animate#1.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "~1.2.23"
|
||||
},
|
||||
"homepage": "https://github.com/chieffancypants/angular-loading-bar",
|
||||
"version": "0.9.0",
|
||||
"_release": "0.9.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "0.9.0",
|
||||
"commit": "d734873e52ded18fa27d67f52272ae43267dfd63"
|
||||
},
|
||||
"_source": "https://github.com/chieffancypants/angular-loading-bar.git",
|
||||
"_target": "0.9.0",
|
||||
"_originalSource": "angular-loading-bar"
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
Changelog
|
||||
==========
|
||||
## 0.9.0
|
||||
- resolved issue with parentSelector when parent has no children
|
||||
([#244](https://github.com/chieffancypants/angular-loading-bar/pull/244))
|
||||
([#251](https://github.com/chieffancypants/angular-loading-bar/issues/251))
|
||||
([#239](https://github.com/chieffancypants/angular-loading-bar/issues/239))
|
||||
([#179](https://github.com/chieffancypants/angular-loading-bar/issues/179))
|
||||
- added style property to package.json
|
||||
([#271](https://github.com/chieffancypants/angular-loading-bar/pull/271))
|
||||
([#231](https://github.com/chieffancypants/angular-loading-bar/pull/231))
|
||||
- Removed duplicated property declaration in CSS
|
||||
([#226](https://github.com/chieffancypants/angular-loading-bar/pull/226))
|
||||
|
||||
|
||||
|
||||
## 0.8.0
|
||||
- auto incrementing is now configurable
|
||||
([#209](https://github.com/chieffancypants/angular-loading-bar/pull/209))
|
||||
- removed `version` from bower.json
|
||||
([#207](https://github.com/chieffancypants/angular-loading-bar/pull/207))
|
||||
- added support for webpack and browserify
|
||||
([#200](https://github.com/chieffancypants/angular-loading-bar/pull/200))
|
||||
- spinner border radius 10px -> 50%
|
||||
([#184](https://github.com/chieffancypants/angular-loading-bar/issues/184))
|
||||
|
||||
|
||||
## 0.7.1
|
||||
- Merge correct PR for broken interceptor detection ([#133](https://github.com/chieffancypants/angular-loading-bar/pull/133), [#50](https://github.com/chieffancypants/angular-loading-bar/pull/50))
|
||||
|
||||
## 0.7.0
|
||||
- Changes for animate.enter compatibility for 1.2 and 1.3 ([#170](https://github.com/chieffancypants/angular-loading-bar/pull/170))
|
||||
- Detect errors with other interceptors ([#133](https://github.com/chieffancypants/angular-loading-bar/pull/133), [#50](https://github.com/chieffancypants/angular-loading-bar/pull/50))
|
||||
- Provide more detail on response/responseError events ([#128](https://github.com/chieffancypants/angular-loading-bar/pull/128))
|
||||
- Change angular dependency in bower ([#126](https://github.com/chieffancypants/angular-loading-bar/issues/126))
|
||||
|
||||
## 0.6.0
|
||||
- Customize progress bar template: ([#111](https://github.com/chieffancypants/angular-loading-bar/pull/111))
|
||||
- Only append bar to first parent found ([#108](https://github.com/chieffancypants/angular-loading-bar/pull/108))
|
||||
|
||||
## 0.5.2:
|
||||
Fixes for Angular 1.3 breaking changes:
|
||||
- Circular dependencies: ([#98](https://github.com/chieffancypants/angular-loading-bar/issues/98)), ([#101](https://github.com/chieffancypants/angular-loading-bar/pull/101))
|
||||
- $animate no longer accepts callbacks: ([#102](https://github.com/chieffancypants/angular-loading-bar/pull/102))
|
||||
|
||||
## 0.5.1
|
||||
- Reworked cache logic to allow cache:true ([#96](https://github.com/chieffancypants/angular-loading-bar/pull/96))
|
||||
|
||||
## 0.5.0
|
||||
- Added spinner template configuration ([#82](https://github.com/chieffancypants/angular-loading-bar/pull/82))
|
||||
- $timeout was not canceled properly ([#79](https://github.com/chieffancypants/angular-loading-bar/pull/79))
|
||||
|
||||
## 0.4.3
|
||||
- update z-index to work with other css frameworks ([#69](https://github.com/chieffancypants/angular-loading-bar/pull/69))
|
||||
- ignoreLoadingBar not ignored when calculating percentage complete ([#70](https://github.com/chieffancypants/angular-loading-bar/pull/70))
|
||||
|
||||
## 0.4.2
|
||||
- Split loading bar into different modules so they can be included separately ([#46](https://github.com/chieffancypants/angular-loading-bar/issues/46))
|
||||
|
||||
## 0.4.1
|
||||
- Fix for route views defined on body where loading bar is also attached ([#56](https://github.com/chieffancypants/angular-loading-bar/issues/56))
|
||||
|
||||
## 0.4.0
|
||||
- Initial load percentage is now configurable ([#47](https://github.com/chieffancypants/angular-loading-bar/issues/47))
|
||||
- Peg graphic reworked so the loadingbar does not require CSS changes when not at the very top of the page ([#42](https://github.com/chieffancypants/angular-loading-bar/issues/42), [#45](https://github.com/chieffancypants/angular-loading-bar/issues/45), [#10](https://github.com/chieffancypants/angular-loading-bar/issues/10))
|
||||
- z-index of spinner increased to work with Bootstrap 3 z-indexes ([#53](https://github.com/chieffancypants/angular-loading-bar/issues/53))
|
||||
|
||||
## 0.3.0
|
||||
- Loading bar only appears on XHR requests with high latency ([#27](https://github.com/chieffancypants/angular-loading-bar/issues/27))
|
||||
|
||||
## 0.2.0
|
||||
- Progression bar not calculated correctly for consecutive calls within the 500ms delay ([#29](https://github.com/chieffancypants/angular-loading-bar/issues/29), [#32](https://github.com/chieffancypants/angular-loading-bar/issues/32))
|
||||
- Event broadcasts when loading (#31)
|
||||
|
||||
## 0.1.1
|
||||
- Alias chieffancypants.loadingbar to angular-loading-bar (#25, #19)
|
||||
|
||||
## 0.1.0
|
||||
- Fixed issues with Angular 1.2-rc3+
|
||||
- Ability to ignore particular XHR requests (#21)
|
||||
- Broadcasting of events (#18)
|
|
@ -1,17 +0,0 @@
|
|||
### Submitting a PR
|
||||
Excellent! You've chosen to help advance the project by either fixing a bug, or implementing a new feature. Before you put forth any work on a PR, please follow these steps:
|
||||
|
||||
1. Ensure a similar PR has not already been opened or closed.
|
||||
1. Clearly define the intent of the PR. The more detail, the more likelihood of it getting merged.
|
||||
1. Is this a feature that would benefit the **majority** of users? This is a small library, and it intends to stay that way. If you do not believe most users of the project will benefit from your work, it should probably be added in your own application.
|
||||
1. Be sure to include test cases that cover all newly introduced code. This part is essential, as any PRs without tests will be closed.
|
||||
1. Link any [issues](https://github.com/chieffancypants/angular-loading-bar/issues) that are addressed by the PR.
|
||||
|
||||
### Submitting a bug report
|
||||
If you believe you've found a bug in the source code, and are unable to fix it yourself (by submitting a PR) please follow these steps:
|
||||
|
||||
1. Ensure the bug has not already been reported by searching the [issues](https://github.com/chieffancypants/angular-loading-bar/issues)
|
||||
1. Submit a reduced test case that clearly demonstrates the bug. This means submitting a plunker or jsfiddle with the bare minimum of code necessary to reproduce the bug. Without this, your issue may be closed as invalid.
|
||||
1. Include any relevant browser information
|
||||
1. If you're unable to fix this bug yourself, but can point to why it is occuring, please send that information along (line# or commit)
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/*global module:false*/
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.initConfig({
|
||||
|
||||
// Metadata.
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
banner: '/*! \n * <%= pkg.title || pkg.name %> v<%= pkg.version %>\n' +
|
||||
' * <%= pkg.homepage %>\n' +
|
||||
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
|
||||
' * License: <%= pkg.license %>\n' +
|
||||
' */\n',
|
||||
|
||||
// Task configuration.
|
||||
uglify: {
|
||||
options: {
|
||||
banner: '<%= banner %>',
|
||||
report: 'gzip'
|
||||
},
|
||||
build: {
|
||||
src: 'src/loading-bar.js',
|
||||
dest: 'build/loading-bar.min.js'
|
||||
}
|
||||
},
|
||||
|
||||
cssmin: {
|
||||
options: {
|
||||
banner: '<%= banner %>',
|
||||
report: 'gzip'
|
||||
},
|
||||
minify: {
|
||||
src: 'src/loading-bar.css',
|
||||
dest: 'build/loading-bar.min.css'
|
||||
}
|
||||
},
|
||||
|
||||
karma: {
|
||||
unit: {
|
||||
configFile: 'test/karma-angular-1.2.conf.js',
|
||||
singleRun: true,
|
||||
coverageReporter: {
|
||||
type: 'text',
|
||||
dir: 'coverage/'
|
||||
}
|
||||
},
|
||||
unit13: {
|
||||
configFile: 'test/karma-angular-1.3.conf.js',
|
||||
singleRun: true,
|
||||
coverageReporter: {
|
||||
type: 'text',
|
||||
dir: 'coverage/'
|
||||
}
|
||||
},
|
||||
unit14: {
|
||||
configFile: 'test/karma-angular-1.4.conf.js',
|
||||
singleRun: true,
|
||||
coverageReporter: {
|
||||
type: 'text',
|
||||
dir: 'coverage/'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
configFile: 'test/karma-angular-1.2.conf.js',
|
||||
singleRun: false,
|
||||
reporters: ['progress'] // Don't display coverage
|
||||
}
|
||||
},
|
||||
|
||||
jshint: {
|
||||
jshintrc: '.jshintrc',
|
||||
gruntfile: {
|
||||
src: 'Gruntfile.js'
|
||||
},
|
||||
src: {
|
||||
src: ['src/*.js']
|
||||
}
|
||||
},
|
||||
|
||||
concat: {
|
||||
build: {
|
||||
options: {
|
||||
banner: '<%= banner %>'
|
||||
},
|
||||
files: {
|
||||
'build/loading-bar.css': 'src/loading-bar.css',
|
||||
'build/loading-bar.js': 'src/loading-bar.js',
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-karma');
|
||||
|
||||
grunt.registerTask('default', ['jshint', 'karma:unit', 'karma:unit13', 'karma:unit14', 'uglify', 'cssmin', 'concat:build']);
|
||||
grunt.registerTask('test', ['karma:watch']);
|
||||
grunt.registerTask('build', ['default']);
|
||||
|
||||
};
|
|
@ -1,14 +0,0 @@
|
|||
#### Description of bug:
|
||||
|
||||
|
||||
#### Expected result:
|
||||
|
||||
|
||||
#### Actual result:
|
||||
|
||||
|
||||
#### Browsers affected:
|
||||
|
||||
|
||||
#### URL of reduced test case:
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2014 Wes Cruver
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,13 +0,0 @@
|
|||
#### Summary:
|
||||
|
||||
Provide a general description of the code changes in your pull
|
||||
request. If bugs were fixed, please document the changes and why
|
||||
they were introduced.
|
||||
|
||||
Please ensure that your PR contains test cases that cover all new
|
||||
code and any changes to existing code. Without tests, your PR is
|
||||
likely to be closed without merging.
|
||||
|
||||
#### Related issues:
|
||||
Please review the [issues](https://github.com/chieffancypants/angular-loading-bar/issues)
|
||||
page, and link any issues that are addressed or related to this PR.
|
|
@ -1,190 +0,0 @@
|
|||
angular-loading-bar
|
||||
===================
|
||||
|
||||
The idea is simple: Add a loading bar / progress bar whenever an XHR request goes out in angular. Multiple requests within the same time period get bundled together such that each response increments the progress bar by the appropriate amount.
|
||||
|
||||
This is mostly cool because you simply include it in your app, and it works. There's no complicated setup, and no need to maintain the state of the loading bar; it's all handled automatically by the interceptor.
|
||||
|
||||
**Requirements:** AngularJS 1.2+
|
||||
|
||||
**File Size:** 2.4Kb minified, 0.5Kb gzipped
|
||||
|
||||
|
||||
## Usage:
|
||||
|
||||
1. include the loading bar as a dependency for your app. If you want animations, include `ngAnimate` as well. *note: ngAnimate is optional*
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['angular-loading-bar', 'ngAnimate'])
|
||||
```
|
||||
|
||||
2. include the supplied JS and CSS file (or create your own CSS to override defaults).
|
||||
|
||||
```html
|
||||
<link rel='stylesheet' href='build/loading-bar.min.css' type='text/css' media='all' />
|
||||
<script type='text/javascript' src='build/loading-bar.min.js'></script>
|
||||
```
|
||||
|
||||
3. That's it -- you're done!
|
||||
|
||||
#### via bower:
|
||||
```
|
||||
$ bower install angular-loading-bar
|
||||
```
|
||||
#### via npm:
|
||||
```
|
||||
$ npm install angular-loading-bar
|
||||
```
|
||||
|
||||
#### via CDN:
|
||||
```html
|
||||
<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/angular-loading-bar/0.7.1/loading-bar.min.css' type='text/css' media='all' />
|
||||
<script type='text/javascript' src='//cdnjs.cloudflare.com/ajax/libs/angular-loading-bar/0.7.1/loading-bar.min.js'></script>
|
||||
```
|
||||
|
||||
## Why I created this
|
||||
There are a couple projects similar to this out there, but none were ideal for me. All implementations I've seen require that you maintain state on behalf of the loading bar. In other words, you're setting the value of the loading/progress bar manually from potentially many different locations. This becomes complicated when you have a very large application with several services all making independent XHR requests. It becomes even more complicated if you want these services to be loosly coupled.
|
||||
|
||||
Additionally, Angular was created as a highly testable framework, so it pains me to see Angular modules without tests. That is not the case here as this loading bar ships with 100% code coverage.
|
||||
|
||||
|
||||
**Goals for this project:**
|
||||
|
||||
1. Make it automatic
|
||||
2. Unit tests, 100% coverage
|
||||
3. Must work well with ngAnimate
|
||||
4. Must be styled via external CSS (not inline)
|
||||
5. No jQuery dependencies
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
#### Turn the spinner on or off:
|
||||
The insertion of the spinner can be controlled through configuration. It's on by default, but if you'd like to turn it off, simply configure the service:
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['angular-loading-bar'])
|
||||
.config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
|
||||
cfpLoadingBarProvider.includeSpinner = false;
|
||||
}])
|
||||
```
|
||||
|
||||
#### Turn the loading bar on or off:
|
||||
Like the spinner configuration above, the loading bar can also be turned off for cases where you only want the spinner:
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['angular-loading-bar'])
|
||||
.config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
|
||||
cfpLoadingBarProvider.includeBar = false;
|
||||
}])
|
||||
```
|
||||
|
||||
#### Customize the template:
|
||||
If you'd like to replace the default HTML template you can configure it by providing inline HTML as a string:
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['angular-loading-bar'])
|
||||
.config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
|
||||
cfpLoadingBarProvider.spinnerTemplate = '<div><span class="fa fa-spinner">Loading...</div>';
|
||||
}])
|
||||
```
|
||||
|
||||
#### Latency Threshold
|
||||
By default, the loading bar will only display after it has been waiting for a response for over 100ms. This helps keep things feeling snappy, and avoids the annoyingness of showing a loading bar every few seconds on really chatty applications. This threshold is totally configurable:
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['angular-loading-bar'])
|
||||
.config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
|
||||
cfpLoadingBarProvider.latencyThreshold = 500;
|
||||
}])
|
||||
```
|
||||
|
||||
#### Ignoring particular XHR requests:
|
||||
The loading bar can also be forced to ignore certain requests, for example, when long-polling or periodically sending debugging information back to the server.
|
||||
|
||||
```js
|
||||
// ignore a particular $http GET:
|
||||
$http.get('/status', {
|
||||
ignoreLoadingBar: true
|
||||
});
|
||||
|
||||
// ignore a particular $http POST. Note: POST and GET have different
|
||||
// method signatures:
|
||||
$http.post('/save', data, {
|
||||
ignoreLoadingBar: true
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
```js
|
||||
// ignore particular $resource requests:
|
||||
.factory('Restaurant', function($resource) {
|
||||
return $resource('/api/restaurant/:id', {id: '@id'}, {
|
||||
query: {
|
||||
method: 'GET',
|
||||
isArray: true,
|
||||
ignoreLoadingBar: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## How it works:
|
||||
This library is split into two modules, an $http `interceptor`, and a `service`:
|
||||
|
||||
**Interceptor**
|
||||
The interceptor simply listens for all outgoing XHR requests, and then instructs the loadingBar service to start, stop, and increment accordingly. There is no public API for the interceptor. It can be used stand-alone by including `cfp.loadingBarInterceptor` as a dependency for your module.
|
||||
|
||||
**Service**
|
||||
The service is responsible for the presentation of the loading bar. It injects the loading bar into the DOM, adjusts the width whenever `set()` is called, and `complete()`s the whole show by removing the loading bar from the DOM.
|
||||
|
||||
## Service API (advanced usage)
|
||||
Under normal circumstances you won't need to use this. However, if you wish to use the loading bar without the interceptor, you can do that as well. Simply include the loading bar service as a dependency instead of the main `angular-loading-bar` module:
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['cfp.loadingBar'])
|
||||
```
|
||||
|
||||
|
||||
```js
|
||||
|
||||
cfpLoadingBar.start();
|
||||
// will insert the loading bar into the DOM, and display its progress at 1%.
|
||||
// It will automatically call `inc()` repeatedly to give the illusion that the page load is progressing.
|
||||
|
||||
cfpLoadingBar.inc();
|
||||
// increments the loading bar by a random amount.
|
||||
// It is important to note that the auto incrementing will begin to slow down as
|
||||
// the progress increases. This is to prevent the loading bar from appearing
|
||||
// completed (or almost complete) before the XHR request has responded.
|
||||
|
||||
cfpLoadingBar.set(0.3) // Set the loading bar to 30%
|
||||
cfpLoadingBar.status() // Returns the loading bar's progress.
|
||||
// -> 0.3
|
||||
|
||||
cfpLoadingBar.complete()
|
||||
// Set the loading bar's progress to 100%, and then remove it from the DOM.
|
||||
|
||||
```
|
||||
|
||||
## Events
|
||||
The loading bar broadcasts the following events over $rootScope allowing further customization:
|
||||
|
||||
**`cfpLoadingBar:loading`** triggered upon each XHR request that is not already cached
|
||||
|
||||
**`cfpLoadingBar:loaded`** triggered each time an XHR request recieves a response (either successful or error)
|
||||
|
||||
**`cfpLoadingBar:started`** triggered once upon the first XHR request. Will trigger again if another request goes out after `cfpLoadingBar:completed` has triggered.
|
||||
|
||||
**`cfpLoadingBar:completed`** triggered once when the all XHR requests have returned (either successfully or not)
|
||||
|
||||
## Credits:
|
||||
Credit goes to [rstacruz](https://github.com/rstacruz) for his excellent [nProgress](https://github.com/rstacruz/nprogress).
|
||||
|
||||
## License:
|
||||
Licensed under the MIT license
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"name": "angular-loading-bar",
|
||||
"main": [
|
||||
"build/loading-bar.js",
|
||||
"build/loading-bar.css"
|
||||
],
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"components",
|
||||
"test",
|
||||
"example"
|
||||
],
|
||||
"dependencies": {
|
||||
"angular": "^1.2.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular": "~1.2.23",
|
||||
"angular-1.3": "angular#1.3",
|
||||
"angular-1.4": "angular#1.4",
|
||||
"angular-mocks": "~1.2.9",
|
||||
"angular-mocks-1.3": "angular-mocks#1.3",
|
||||
"angular-mocks-1.4": "angular-mocks#1.4",
|
||||
"angular-animate": "~1.2.9",
|
||||
"angular-animate-1.3": "angular-animate#1.3",
|
||||
"angular-animate-1.4": "angular-animate#1.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "~1.2.23"
|
||||
}
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/*!
|
||||
* angular-loading-bar v0.9.0
|
||||
* https://chieffancypants.github.io/angular-loading-bar
|
||||
* Copyright (c) 2016 Wes Cruver
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
/* Make clicks pass-through */
|
||||
#loading-bar,
|
||||
#loading-bar-spinner {
|
||||
pointer-events: none;
|
||||
-webkit-pointer-events: none;
|
||||
-webkit-transition: 350ms linear all;
|
||||
-moz-transition: 350ms linear all;
|
||||
-o-transition: 350ms linear all;
|
||||
transition: 350ms linear all;
|
||||
}
|
||||
|
||||
#loading-bar.ng-enter,
|
||||
#loading-bar.ng-leave.ng-leave-active,
|
||||
#loading-bar-spinner.ng-enter,
|
||||
#loading-bar-spinner.ng-leave.ng-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#loading-bar.ng-enter.ng-enter-active,
|
||||
#loading-bar.ng-leave,
|
||||
#loading-bar-spinner.ng-enter.ng-enter-active,
|
||||
#loading-bar-spinner.ng-leave {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#loading-bar .bar {
|
||||
-webkit-transition: width 350ms;
|
||||
-moz-transition: width 350ms;
|
||||
-o-transition: width 350ms;
|
||||
transition: width 350ms;
|
||||
|
||||
background: #29d;
|
||||
position: fixed;
|
||||
z-index: 10002;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
border-bottom-right-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
}
|
||||
|
||||
/* Fancy blur effect */
|
||||
#loading-bar .peg {
|
||||
position: absolute;
|
||||
width: 70px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 2px;
|
||||
opacity: .45;
|
||||
-moz-box-shadow: #29d 1px 0 6px 1px;
|
||||
-ms-box-shadow: #29d 1px 0 6px 1px;
|
||||
-webkit-box-shadow: #29d 1px 0 6px 1px;
|
||||
box-shadow: #29d 1px 0 6px 1px;
|
||||
-moz-border-radius: 100%;
|
||||
-webkit-border-radius: 100%;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#loading-bar-spinner {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 10002;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
#loading-bar-spinner .spinner-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
border: solid 2px transparent;
|
||||
border-top-color: #29d;
|
||||
border-left-color: #29d;
|
||||
border-radius: 50%;
|
||||
|
||||
-webkit-animation: loading-bar-spinner 400ms linear infinite;
|
||||
-moz-animation: loading-bar-spinner 400ms linear infinite;
|
||||
-ms-animation: loading-bar-spinner 400ms linear infinite;
|
||||
-o-animation: loading-bar-spinner 400ms linear infinite;
|
||||
animation: loading-bar-spinner 400ms linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes loading-bar-spinner {
|
||||
0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@-moz-keyframes loading-bar-spinner {
|
||||
0% { -moz-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@-o-keyframes loading-bar-spinner {
|
||||
0% { -o-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -o-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@-ms-keyframes loading-bar-spinner {
|
||||
0% { -ms-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes loading-bar-spinner {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
|
@ -1,341 +0,0 @@
|
|||
/*!
|
||||
* angular-loading-bar v0.9.0
|
||||
* https://chieffancypants.github.io/angular-loading-bar
|
||||
* Copyright (c) 2016 Wes Cruver
|
||||
* License: MIT
|
||||
*/
|
||||
/*
|
||||
* angular-loading-bar
|
||||
*
|
||||
* intercepts XHR requests and creates a loading bar.
|
||||
* Based on the excellent nprogress work by rstacruz (more info in readme)
|
||||
*
|
||||
* (c) 2013 Wes Cruver
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
// Alias the loading bar for various backwards compatibilities since the project has matured:
|
||||
angular.module('angular-loading-bar', ['cfp.loadingBarInterceptor']);
|
||||
angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']);
|
||||
|
||||
|
||||
/**
|
||||
* loadingBarInterceptor service
|
||||
*
|
||||
* Registers itself as an Angular interceptor and listens for XHR requests.
|
||||
*/
|
||||
angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar'])
|
||||
.config(['$httpProvider', function ($httpProvider) {
|
||||
|
||||
var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, $log, cfpLoadingBar) {
|
||||
|
||||
/**
|
||||
* The total number of requests made
|
||||
*/
|
||||
var reqsTotal = 0;
|
||||
|
||||
/**
|
||||
* The number of requests completed (either successfully or not)
|
||||
*/
|
||||
var reqsCompleted = 0;
|
||||
|
||||
/**
|
||||
* The amount of time spent fetching before showing the loading bar
|
||||
*/
|
||||
var latencyThreshold = cfpLoadingBar.latencyThreshold;
|
||||
|
||||
/**
|
||||
* $timeout handle for latencyThreshold
|
||||
*/
|
||||
var startTimeout;
|
||||
|
||||
|
||||
/**
|
||||
* calls cfpLoadingBar.complete() which removes the
|
||||
* loading bar from the DOM.
|
||||
*/
|
||||
function setComplete() {
|
||||
$timeout.cancel(startTimeout);
|
||||
cfpLoadingBar.complete();
|
||||
reqsCompleted = 0;
|
||||
reqsTotal = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the response has already been cached
|
||||
* @param {Object} config the config option from the request
|
||||
* @return {Boolean} retrns true if cached, otherwise false
|
||||
*/
|
||||
function isCached(config) {
|
||||
var cache;
|
||||
var defaultCache = $cacheFactory.get('$http');
|
||||
var defaults = $httpProvider.defaults;
|
||||
|
||||
// Choose the proper cache source. Borrowed from angular: $http service
|
||||
if ((config.cache || defaults.cache) && config.cache !== false &&
|
||||
(config.method === 'GET' || config.method === 'JSONP')) {
|
||||
cache = angular.isObject(config.cache) ? config.cache
|
||||
: angular.isObject(defaults.cache) ? defaults.cache
|
||||
: defaultCache;
|
||||
}
|
||||
|
||||
var cached = cache !== undefined ?
|
||||
cache.get(config.url) !== undefined : false;
|
||||
|
||||
if (config.cached !== undefined && cached !== config.cached) {
|
||||
return config.cached;
|
||||
}
|
||||
config.cached = cached;
|
||||
return cached;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
'request': function(config) {
|
||||
// Check to make sure this request hasn't already been cached and that
|
||||
// the requester didn't explicitly ask us to ignore this request:
|
||||
if (!config.ignoreLoadingBar && !isCached(config)) {
|
||||
$rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});
|
||||
if (reqsTotal === 0) {
|
||||
startTimeout = $timeout(function() {
|
||||
cfpLoadingBar.start();
|
||||
}, latencyThreshold);
|
||||
}
|
||||
reqsTotal++;
|
||||
cfpLoadingBar.set(reqsCompleted / reqsTotal);
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
||||
'response': function(response) {
|
||||
if (!response || !response.config) {
|
||||
$log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
|
||||
return response;
|
||||
}
|
||||
|
||||
if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
|
||||
reqsCompleted++;
|
||||
$rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
|
||||
if (reqsCompleted >= reqsTotal) {
|
||||
setComplete();
|
||||
} else {
|
||||
cfpLoadingBar.set(reqsCompleted / reqsTotal);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
||||
'responseError': function(rejection) {
|
||||
if (!rejection || !rejection.config) {
|
||||
$log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
|
||||
if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
|
||||
reqsCompleted++;
|
||||
$rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
|
||||
if (reqsCompleted >= reqsTotal) {
|
||||
setComplete();
|
||||
} else {
|
||||
cfpLoadingBar.set(reqsCompleted / reqsTotal);
|
||||
}
|
||||
}
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
$httpProvider.interceptors.push(interceptor);
|
||||
}]);
|
||||
|
||||
|
||||
/**
|
||||
* Loading Bar
|
||||
*
|
||||
* This service handles adding and removing the actual element in the DOM.
|
||||
* Generally, best practices for DOM manipulation is to take place in a
|
||||
* directive, but because the element itself is injected in the DOM only upon
|
||||
* XHR requests, and it's likely needed on every view, the best option is to
|
||||
* use a service.
|
||||
*/
|
||||
angular.module('cfp.loadingBar', [])
|
||||
.provider('cfpLoadingBar', function() {
|
||||
|
||||
this.autoIncrement = true;
|
||||
this.includeSpinner = true;
|
||||
this.includeBar = true;
|
||||
this.latencyThreshold = 100;
|
||||
this.startSize = 0.02;
|
||||
this.parentSelector = 'body';
|
||||
this.spinnerTemplate = '<div id="loading-bar-spinner"><div class="spinner-icon"></div></div>';
|
||||
this.loadingBarTemplate = '<div id="loading-bar"><div class="bar"><div class="peg"></div></div></div>';
|
||||
|
||||
this.$get = ['$injector', '$document', '$timeout', '$rootScope', function ($injector, $document, $timeout, $rootScope) {
|
||||
var $animate;
|
||||
var $parentSelector = this.parentSelector,
|
||||
loadingBarContainer = angular.element(this.loadingBarTemplate),
|
||||
loadingBar = loadingBarContainer.find('div').eq(0),
|
||||
spinner = angular.element(this.spinnerTemplate);
|
||||
|
||||
var incTimeout,
|
||||
completeTimeout,
|
||||
started = false,
|
||||
status = 0;
|
||||
|
||||
var autoIncrement = this.autoIncrement;
|
||||
var includeSpinner = this.includeSpinner;
|
||||
var includeBar = this.includeBar;
|
||||
var startSize = this.startSize;
|
||||
|
||||
/**
|
||||
* Inserts the loading bar element into the dom, and sets it to 2%
|
||||
*/
|
||||
function _start() {
|
||||
if (!$animate) {
|
||||
$animate = $injector.get('$animate');
|
||||
}
|
||||
|
||||
$timeout.cancel(completeTimeout);
|
||||
|
||||
// do not continually broadcast the started event:
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
var document = $document[0];
|
||||
var parent = document.querySelector ?
|
||||
document.querySelector($parentSelector)
|
||||
: $document.find($parentSelector)[0]
|
||||
;
|
||||
|
||||
if (! parent) {
|
||||
parent = document.getElementsByTagName('body')[0];
|
||||
}
|
||||
|
||||
var $parent = angular.element(parent);
|
||||
var $after = parent.lastChild && angular.element(parent.lastChild);
|
||||
|
||||
$rootScope.$broadcast('cfpLoadingBar:started');
|
||||
started = true;
|
||||
|
||||
if (includeBar) {
|
||||
$animate.enter(loadingBarContainer, $parent, $after);
|
||||
}
|
||||
|
||||
if (includeSpinner) {
|
||||
$animate.enter(spinner, $parent, loadingBarContainer);
|
||||
}
|
||||
|
||||
_set(startSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the loading bar's width to a certain percent.
|
||||
*
|
||||
* @param n any value between 0 and 1
|
||||
*/
|
||||
function _set(n) {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
var pct = (n * 100) + '%';
|
||||
loadingBar.css('width', pct);
|
||||
status = n;
|
||||
|
||||
// increment loadingbar to give the illusion that there is always
|
||||
// progress but make sure to cancel the previous timeouts so we don't
|
||||
// have multiple incs running at the same time.
|
||||
if (autoIncrement) {
|
||||
$timeout.cancel(incTimeout);
|
||||
incTimeout = $timeout(function() {
|
||||
_inc();
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the loading bar by a random amount
|
||||
* but slows down as it progresses
|
||||
*/
|
||||
function _inc() {
|
||||
if (_status() >= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var rnd = 0;
|
||||
|
||||
// TODO: do this mathmatically instead of through conditions
|
||||
|
||||
var stat = _status();
|
||||
if (stat >= 0 && stat < 0.25) {
|
||||
// Start out between 3 - 6% increments
|
||||
rnd = (Math.random() * (5 - 3 + 1) + 3) / 100;
|
||||
} else if (stat >= 0.25 && stat < 0.65) {
|
||||
// increment between 0 - 3%
|
||||
rnd = (Math.random() * 3) / 100;
|
||||
} else if (stat >= 0.65 && stat < 0.9) {
|
||||
// increment between 0 - 2%
|
||||
rnd = (Math.random() * 2) / 100;
|
||||
} else if (stat >= 0.9 && stat < 0.99) {
|
||||
// finally, increment it .5 %
|
||||
rnd = 0.005;
|
||||
} else {
|
||||
// after 99%, don't increment:
|
||||
rnd = 0;
|
||||
}
|
||||
|
||||
var pct = _status() + rnd;
|
||||
_set(pct);
|
||||
}
|
||||
|
||||
function _status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
function _completeAnimation() {
|
||||
status = 0;
|
||||
started = false;
|
||||
}
|
||||
|
||||
function _complete() {
|
||||
if (!$animate) {
|
||||
$animate = $injector.get('$animate');
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('cfpLoadingBar:completed');
|
||||
_set(1);
|
||||
|
||||
$timeout.cancel(completeTimeout);
|
||||
|
||||
// Attempt to aggregate any start/complete calls within 500ms:
|
||||
completeTimeout = $timeout(function() {
|
||||
var promise = $animate.leave(loadingBarContainer, _completeAnimation);
|
||||
if (promise && promise.then) {
|
||||
promise.then(_completeAnimation);
|
||||
}
|
||||
$animate.leave(spinner);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return {
|
||||
start : _start,
|
||||
set : _set,
|
||||
status : _status,
|
||||
inc : _inc,
|
||||
complete : _complete,
|
||||
autoIncrement : this.autoIncrement,
|
||||
includeSpinner : this.includeSpinner,
|
||||
latencyThreshold : this.latencyThreshold,
|
||||
parentSelector : this.parentSelector,
|
||||
startSize : this.startSize
|
||||
};
|
||||
|
||||
|
||||
}]; //
|
||||
}); // wtf javascript. srsly
|
||||
})(); //
|
|
@ -1 +0,0 @@
|
|||
#loading-bar,#loading-bar-spinner{pointer-events:none;-webkit-pointer-events:none;-webkit-transition:350ms linear all;-moz-transition:350ms linear all;-o-transition:350ms linear all;transition:350ms linear all}#loading-bar-spinner.ng-enter,#loading-bar-spinner.ng-leave.ng-leave-active,#loading-bar.ng-enter,#loading-bar.ng-leave.ng-leave-active{opacity:0}#loading-bar-spinner.ng-enter.ng-enter-active,#loading-bar-spinner.ng-leave,#loading-bar.ng-enter.ng-enter-active,#loading-bar.ng-leave{opacity:1}#loading-bar .bar{-webkit-transition:width 350ms;-moz-transition:width 350ms;-o-transition:width 350ms;transition:width 350ms;background:#29d;position:fixed;z-index:10002;top:0;left:0;width:100%;height:2px;border-bottom-right-radius:1px;border-top-right-radius:1px}#loading-bar .peg{position:absolute;width:70px;right:0;top:0;height:2px;opacity:.45;-moz-box-shadow:#29d 1px 0 6px 1px;-ms-box-shadow:#29d 1px 0 6px 1px;-webkit-box-shadow:#29d 1px 0 6px 1px;box-shadow:#29d 1px 0 6px 1px;-moz-border-radius:100%;-webkit-border-radius:100%;border-radius:100%}#loading-bar-spinner{display:block;position:fixed;z-index:10002;top:10px;left:10px}#loading-bar-spinner .spinner-icon{width:14px;height:14px;border:2px solid transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:loading-bar-spinner 400ms linear infinite;-moz-animation:loading-bar-spinner 400ms linear infinite;-ms-animation:loading-bar-spinner 400ms linear infinite;-o-animation:loading-bar-spinner 400ms linear infinite;animation:loading-bar-spinner 400ms linear infinite}@-webkit-keyframes loading-bar-spinner{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes loading-bar-spinner{0%{-moz-transform:rotate(0);transform:rotate(0)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes loading-bar-spinner{0%{-o-transform:rotate(0);transform:rotate(0)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes loading-bar-spinner{0%{-ms-transform:rotate(0);transform:rotate(0)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes loading-bar-spinner{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}
|
|
@ -1,7 +0,0 @@
|
|||
/*!
|
||||
* angular-loading-bar v0.9.0
|
||||
* https://chieffancypants.github.io/angular-loading-bar
|
||||
* Copyright (c) 2016 Wes Cruver
|
||||
* License: MIT
|
||||
*/
|
||||
!function(){"use strict";angular.module("angular-loading-bar",["cfp.loadingBarInterceptor"]),angular.module("chieffancypants.loadingBar",["cfp.loadingBarInterceptor"]),angular.module("cfp.loadingBarInterceptor",["cfp.loadingBar"]).config(["$httpProvider",function(a){var b=["$q","$cacheFactory","$timeout","$rootScope","$log","cfpLoadingBar",function(b,c,d,e,f,g){function h(){d.cancel(j),g.complete(),l=0,k=0}function i(b){var d,e=c.get("$http"),f=a.defaults;!b.cache&&!f.cache||b.cache===!1||"GET"!==b.method&&"JSONP"!==b.method||(d=angular.isObject(b.cache)?b.cache:angular.isObject(f.cache)?f.cache:e);var g=void 0!==d?void 0!==d.get(b.url):!1;return void 0!==b.cached&&g!==b.cached?b.cached:(b.cached=g,g)}var j,k=0,l=0,m=g.latencyThreshold;return{request:function(a){return a.ignoreLoadingBar||i(a)||(e.$broadcast("cfpLoadingBar:loading",{url:a.url}),0===k&&(j=d(function(){g.start()},m)),k++,g.set(l/k)),a},response:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),a):(f.error("Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),a)},responseError:function(a){return a&&a.config?(a.config.ignoreLoadingBar||i(a.config)||(l++,e.$broadcast("cfpLoadingBar:loaded",{url:a.config.url,result:a}),l>=k?h():g.set(l/k)),b.reject(a)):(f.error("Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50"),b.reject(a))}}}];a.interceptors.push(b)}]),angular.module("cfp.loadingBar",[]).provider("cfpLoadingBar",function(){this.autoIncrement=!0,this.includeSpinner=!0,this.includeBar=!0,this.latencyThreshold=100,this.startSize=.02,this.parentSelector="body",this.spinnerTemplate='<div id="loading-bar-spinner"><div class="spinner-icon"></div></div>',this.loadingBarTemplate='<div id="loading-bar"><div class="bar"><div class="peg"></div></div></div>',this.$get=["$injector","$document","$timeout","$rootScope",function(a,b,c,d){function e(){if(k||(k=a.get("$animate")),c.cancel(m),!r){var e=b[0],g=e.querySelector?e.querySelector(n):b.find(n)[0];g||(g=e.getElementsByTagName("body")[0]);var h=angular.element(g),i=g.lastChild&&angular.element(g.lastChild);d.$broadcast("cfpLoadingBar:started"),r=!0,v&&k.enter(o,h,i),u&&k.enter(q,h,o),f(w)}}function f(a){if(r){var b=100*a+"%";p.css("width",b),s=a,t&&(c.cancel(l),l=c(function(){g()},250))}}function g(){if(!(h()>=1)){var a=0,b=h();a=b>=0&&.25>b?(3*Math.random()+3)/100:b>=.25&&.65>b?3*Math.random()/100:b>=.65&&.9>b?2*Math.random()/100:b>=.9&&.99>b?.005:0;var c=h()+a;f(c)}}function h(){return s}function i(){s=0,r=!1}function j(){k||(k=a.get("$animate")),d.$broadcast("cfpLoadingBar:completed"),f(1),c.cancel(m),m=c(function(){var a=k.leave(o,i);a&&a.then&&a.then(i),k.leave(q)},500)}var k,l,m,n=this.parentSelector,o=angular.element(this.loadingBarTemplate),p=o.find("div").eq(0),q=angular.element(this.spinnerTemplate),r=!1,s=0,t=this.autoIncrement,u=this.includeSpinner,v=this.includeBar,w=this.startSize;return{start:e,set:f,status:h,inc:g,complete:j,autoIncrement:this.autoIncrement,includeSpinner:this.includeSpinner,latencyThreshold:this.latencyThreshold,parentSelector:this.parentSelector,startSize:this.startSize}}]})}();
|
|
@ -1,2 +0,0 @@
|
|||
require('./build/loading-bar');
|
||||
module.exports = 'angular-loading-bar';
|
|
@ -1,42 +0,0 @@
|
|||
{
|
||||
"name": "angular-loading-bar",
|
||||
"version": "0.9.0",
|
||||
"description": "An automatic loading bar for AngularJS",
|
||||
"main": "index.js",
|
||||
"style": "build/loading-bar.css",
|
||||
"directories": {
|
||||
"example": "example",
|
||||
"test": "test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/chieffancypants/angular-loading-bar.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"angularjs",
|
||||
"loading",
|
||||
"loadingbar",
|
||||
"progress",
|
||||
"progressbar"
|
||||
],
|
||||
"author": "Wes Cruver",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/chieffancypants/angular-loading-bar/issues"
|
||||
},
|
||||
"homepage": "https://chieffancypants.github.io/angular-loading-bar",
|
||||
"devDependencies": {
|
||||
"karma-jasmine": "^0.1.3",
|
||||
"karma-coffee-preprocessor": "^0.2.0",
|
||||
"karma-phantomjs-launcher": "^0.1.0",
|
||||
"karma": "~0.12.0",
|
||||
"karma-coverage": "^0.1.0",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-contrib-jshint": "~0.6.4",
|
||||
"grunt-contrib-uglify": "^0.9.1",
|
||||
"grunt-contrib-cssmin": "~0.12.0",
|
||||
"grunt-karma": "~0.11.0",
|
||||
"grunt-contrib-concat": "^0.5.0"
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
|
||||
/* Make clicks pass-through */
|
||||
#loading-bar,
|
||||
#loading-bar-spinner {
|
||||
pointer-events: none;
|
||||
-webkit-pointer-events: none;
|
||||
-webkit-transition: 350ms linear all;
|
||||
-moz-transition: 350ms linear all;
|
||||
-o-transition: 350ms linear all;
|
||||
transition: 350ms linear all;
|
||||
}
|
||||
|
||||
#loading-bar.ng-enter,
|
||||
#loading-bar.ng-leave.ng-leave-active,
|
||||
#loading-bar-spinner.ng-enter,
|
||||
#loading-bar-spinner.ng-leave.ng-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#loading-bar.ng-enter.ng-enter-active,
|
||||
#loading-bar.ng-leave,
|
||||
#loading-bar-spinner.ng-enter.ng-enter-active,
|
||||
#loading-bar-spinner.ng-leave {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#loading-bar .bar {
|
||||
-webkit-transition: width 350ms;
|
||||
-moz-transition: width 350ms;
|
||||
-o-transition: width 350ms;
|
||||
transition: width 350ms;
|
||||
|
||||
background: #29d;
|
||||
position: fixed;
|
||||
z-index: 10002;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
border-bottom-right-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
}
|
||||
|
||||
/* Fancy blur effect */
|
||||
#loading-bar .peg {
|
||||
position: absolute;
|
||||
width: 70px;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 2px;
|
||||
opacity: .45;
|
||||
-moz-box-shadow: #29d 1px 0 6px 1px;
|
||||
-ms-box-shadow: #29d 1px 0 6px 1px;
|
||||
-webkit-box-shadow: #29d 1px 0 6px 1px;
|
||||
box-shadow: #29d 1px 0 6px 1px;
|
||||
-moz-border-radius: 100%;
|
||||
-webkit-border-radius: 100%;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
#loading-bar-spinner {
|
||||
display: block;
|
||||
position: fixed;
|
||||
z-index: 10002;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
#loading-bar-spinner .spinner-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
|
||||
border: solid 2px transparent;
|
||||
border-top-color: #29d;
|
||||
border-left-color: #29d;
|
||||
border-radius: 50%;
|
||||
|
||||
-webkit-animation: loading-bar-spinner 400ms linear infinite;
|
||||
-moz-animation: loading-bar-spinner 400ms linear infinite;
|
||||
-ms-animation: loading-bar-spinner 400ms linear infinite;
|
||||
-o-animation: loading-bar-spinner 400ms linear infinite;
|
||||
animation: loading-bar-spinner 400ms linear infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes loading-bar-spinner {
|
||||
0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@-moz-keyframes loading-bar-spinner {
|
||||
0% { -moz-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -moz-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@-o-keyframes loading-bar-spinner {
|
||||
0% { -o-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -o-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@-ms-keyframes loading-bar-spinner {
|
||||
0% { -ms-transform: rotate(0deg); transform: rotate(0deg); }
|
||||
100% { -ms-transform: rotate(360deg); transform: rotate(360deg); }
|
||||
}
|
||||
@keyframes loading-bar-spinner {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
|
@ -1,335 +0,0 @@
|
|||
/*
|
||||
* angular-loading-bar
|
||||
*
|
||||
* intercepts XHR requests and creates a loading bar.
|
||||
* Based on the excellent nprogress work by rstacruz (more info in readme)
|
||||
*
|
||||
* (c) 2013 Wes Cruver
|
||||
* License: MIT
|
||||
*/
|
||||
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
// Alias the loading bar for various backwards compatibilities since the project has matured:
|
||||
angular.module('angular-loading-bar', ['cfp.loadingBarInterceptor']);
|
||||
angular.module('chieffancypants.loadingBar', ['cfp.loadingBarInterceptor']);
|
||||
|
||||
|
||||
/**
|
||||
* loadingBarInterceptor service
|
||||
*
|
||||
* Registers itself as an Angular interceptor and listens for XHR requests.
|
||||
*/
|
||||
angular.module('cfp.loadingBarInterceptor', ['cfp.loadingBar'])
|
||||
.config(['$httpProvider', function ($httpProvider) {
|
||||
|
||||
var interceptor = ['$q', '$cacheFactory', '$timeout', '$rootScope', '$log', 'cfpLoadingBar', function ($q, $cacheFactory, $timeout, $rootScope, $log, cfpLoadingBar) {
|
||||
|
||||
/**
|
||||
* The total number of requests made
|
||||
*/
|
||||
var reqsTotal = 0;
|
||||
|
||||
/**
|
||||
* The number of requests completed (either successfully or not)
|
||||
*/
|
||||
var reqsCompleted = 0;
|
||||
|
||||
/**
|
||||
* The amount of time spent fetching before showing the loading bar
|
||||
*/
|
||||
var latencyThreshold = cfpLoadingBar.latencyThreshold;
|
||||
|
||||
/**
|
||||
* $timeout handle for latencyThreshold
|
||||
*/
|
||||
var startTimeout;
|
||||
|
||||
|
||||
/**
|
||||
* calls cfpLoadingBar.complete() which removes the
|
||||
* loading bar from the DOM.
|
||||
*/
|
||||
function setComplete() {
|
||||
$timeout.cancel(startTimeout);
|
||||
cfpLoadingBar.complete();
|
||||
reqsCompleted = 0;
|
||||
reqsTotal = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the response has already been cached
|
||||
* @param {Object} config the config option from the request
|
||||
* @return {Boolean} retrns true if cached, otherwise false
|
||||
*/
|
||||
function isCached(config) {
|
||||
var cache;
|
||||
var defaultCache = $cacheFactory.get('$http');
|
||||
var defaults = $httpProvider.defaults;
|
||||
|
||||
// Choose the proper cache source. Borrowed from angular: $http service
|
||||
if ((config.cache || defaults.cache) && config.cache !== false &&
|
||||
(config.method === 'GET' || config.method === 'JSONP')) {
|
||||
cache = angular.isObject(config.cache) ? config.cache
|
||||
: angular.isObject(defaults.cache) ? defaults.cache
|
||||
: defaultCache;
|
||||
}
|
||||
|
||||
var cached = cache !== undefined ?
|
||||
cache.get(config.url) !== undefined : false;
|
||||
|
||||
if (config.cached !== undefined && cached !== config.cached) {
|
||||
return config.cached;
|
||||
}
|
||||
config.cached = cached;
|
||||
return cached;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
'request': function(config) {
|
||||
// Check to make sure this request hasn't already been cached and that
|
||||
// the requester didn't explicitly ask us to ignore this request:
|
||||
if (!config.ignoreLoadingBar && !isCached(config)) {
|
||||
$rootScope.$broadcast('cfpLoadingBar:loading', {url: config.url});
|
||||
if (reqsTotal === 0) {
|
||||
startTimeout = $timeout(function() {
|
||||
cfpLoadingBar.start();
|
||||
}, latencyThreshold);
|
||||
}
|
||||
reqsTotal++;
|
||||
cfpLoadingBar.set(reqsCompleted / reqsTotal);
|
||||
}
|
||||
return config;
|
||||
},
|
||||
|
||||
'response': function(response) {
|
||||
if (!response || !response.config) {
|
||||
$log.error('Broken interceptor detected: Config object not supplied in response:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
|
||||
return response;
|
||||
}
|
||||
|
||||
if (!response.config.ignoreLoadingBar && !isCached(response.config)) {
|
||||
reqsCompleted++;
|
||||
$rootScope.$broadcast('cfpLoadingBar:loaded', {url: response.config.url, result: response});
|
||||
if (reqsCompleted >= reqsTotal) {
|
||||
setComplete();
|
||||
} else {
|
||||
cfpLoadingBar.set(reqsCompleted / reqsTotal);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
||||
'responseError': function(rejection) {
|
||||
if (!rejection || !rejection.config) {
|
||||
$log.error('Broken interceptor detected: Config object not supplied in rejection:\n https://github.com/chieffancypants/angular-loading-bar/pull/50');
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
|
||||
if (!rejection.config.ignoreLoadingBar && !isCached(rejection.config)) {
|
||||
reqsCompleted++;
|
||||
$rootScope.$broadcast('cfpLoadingBar:loaded', {url: rejection.config.url, result: rejection});
|
||||
if (reqsCompleted >= reqsTotal) {
|
||||
setComplete();
|
||||
} else {
|
||||
cfpLoadingBar.set(reqsCompleted / reqsTotal);
|
||||
}
|
||||
}
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
$httpProvider.interceptors.push(interceptor);
|
||||
}]);
|
||||
|
||||
|
||||
/**
|
||||
* Loading Bar
|
||||
*
|
||||
* This service handles adding and removing the actual element in the DOM.
|
||||
* Generally, best practices for DOM manipulation is to take place in a
|
||||
* directive, but because the element itself is injected in the DOM only upon
|
||||
* XHR requests, and it's likely needed on every view, the best option is to
|
||||
* use a service.
|
||||
*/
|
||||
angular.module('cfp.loadingBar', [])
|
||||
.provider('cfpLoadingBar', function() {
|
||||
|
||||
this.autoIncrement = true;
|
||||
this.includeSpinner = true;
|
||||
this.includeBar = true;
|
||||
this.latencyThreshold = 100;
|
||||
this.startSize = 0.02;
|
||||
this.parentSelector = 'body';
|
||||
this.spinnerTemplate = '<div id="loading-bar-spinner"><div class="spinner-icon"></div></div>';
|
||||
this.loadingBarTemplate = '<div id="loading-bar"><div class="bar"><div class="peg"></div></div></div>';
|
||||
|
||||
this.$get = ['$injector', '$document', '$timeout', '$rootScope', function ($injector, $document, $timeout, $rootScope) {
|
||||
var $animate;
|
||||
var $parentSelector = this.parentSelector,
|
||||
loadingBarContainer = angular.element(this.loadingBarTemplate),
|
||||
loadingBar = loadingBarContainer.find('div').eq(0),
|
||||
spinner = angular.element(this.spinnerTemplate);
|
||||
|
||||
var incTimeout,
|
||||
completeTimeout,
|
||||
started = false,
|
||||
status = 0;
|
||||
|
||||
var autoIncrement = this.autoIncrement;
|
||||
var includeSpinner = this.includeSpinner;
|
||||
var includeBar = this.includeBar;
|
||||
var startSize = this.startSize;
|
||||
|
||||
/**
|
||||
* Inserts the loading bar element into the dom, and sets it to 2%
|
||||
*/
|
||||
function _start() {
|
||||
if (!$animate) {
|
||||
$animate = $injector.get('$animate');
|
||||
}
|
||||
|
||||
$timeout.cancel(completeTimeout);
|
||||
|
||||
// do not continually broadcast the started event:
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
var document = $document[0];
|
||||
var parent = document.querySelector ?
|
||||
document.querySelector($parentSelector)
|
||||
: $document.find($parentSelector)[0]
|
||||
;
|
||||
|
||||
if (! parent) {
|
||||
parent = document.getElementsByTagName('body')[0];
|
||||
}
|
||||
|
||||
var $parent = angular.element(parent);
|
||||
var $after = parent.lastChild && angular.element(parent.lastChild);
|
||||
|
||||
$rootScope.$broadcast('cfpLoadingBar:started');
|
||||
started = true;
|
||||
|
||||
if (includeBar) {
|
||||
$animate.enter(loadingBarContainer, $parent, $after);
|
||||
}
|
||||
|
||||
if (includeSpinner) {
|
||||
$animate.enter(spinner, $parent, loadingBarContainer);
|
||||
}
|
||||
|
||||
_set(startSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the loading bar's width to a certain percent.
|
||||
*
|
||||
* @param n any value between 0 and 1
|
||||
*/
|
||||
function _set(n) {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
var pct = (n * 100) + '%';
|
||||
loadingBar.css('width', pct);
|
||||
status = n;
|
||||
|
||||
// increment loadingbar to give the illusion that there is always
|
||||
// progress but make sure to cancel the previous timeouts so we don't
|
||||
// have multiple incs running at the same time.
|
||||
if (autoIncrement) {
|
||||
$timeout.cancel(incTimeout);
|
||||
incTimeout = $timeout(function() {
|
||||
_inc();
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the loading bar by a random amount
|
||||
* but slows down as it progresses
|
||||
*/
|
||||
function _inc() {
|
||||
if (_status() >= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var rnd = 0;
|
||||
|
||||
// TODO: do this mathmatically instead of through conditions
|
||||
|
||||
var stat = _status();
|
||||
if (stat >= 0 && stat < 0.25) {
|
||||
// Start out between 3 - 6% increments
|
||||
rnd = (Math.random() * (5 - 3 + 1) + 3) / 100;
|
||||
} else if (stat >= 0.25 && stat < 0.65) {
|
||||
// increment between 0 - 3%
|
||||
rnd = (Math.random() * 3) / 100;
|
||||
} else if (stat >= 0.65 && stat < 0.9) {
|
||||
// increment between 0 - 2%
|
||||
rnd = (Math.random() * 2) / 100;
|
||||
} else if (stat >= 0.9 && stat < 0.99) {
|
||||
// finally, increment it .5 %
|
||||
rnd = 0.005;
|
||||
} else {
|
||||
// after 99%, don't increment:
|
||||
rnd = 0;
|
||||
}
|
||||
|
||||
var pct = _status() + rnd;
|
||||
_set(pct);
|
||||
}
|
||||
|
||||
function _status() {
|
||||
return status;
|
||||
}
|
||||
|
||||
function _completeAnimation() {
|
||||
status = 0;
|
||||
started = false;
|
||||
}
|
||||
|
||||
function _complete() {
|
||||
if (!$animate) {
|
||||
$animate = $injector.get('$animate');
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('cfpLoadingBar:completed');
|
||||
_set(1);
|
||||
|
||||
$timeout.cancel(completeTimeout);
|
||||
|
||||
// Attempt to aggregate any start/complete calls within 500ms:
|
||||
completeTimeout = $timeout(function() {
|
||||
var promise = $animate.leave(loadingBarContainer, _completeAnimation);
|
||||
if (promise && promise.then) {
|
||||
promise.then(_completeAnimation);
|
||||
}
|
||||
$animate.leave(spinner);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return {
|
||||
start : _start,
|
||||
set : _set,
|
||||
status : _status,
|
||||
inc : _inc,
|
||||
complete : _complete,
|
||||
autoIncrement : this.autoIncrement,
|
||||
includeSpinner : this.includeSpinner,
|
||||
latencyThreshold : this.latencyThreshold,
|
||||
parentSelector : this.parentSelector,
|
||||
startSize : this.startSize
|
||||
};
|
||||
|
||||
|
||||
}]; //
|
||||
}); // wtf javascript. srsly
|
||||
})(); //
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "angular-mocks",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-mocks.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-mocks",
|
||||
"_release": "1.5.8",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.5.8",
|
||||
"commit": "482eefcf6b03057c5fcddb9750e460f458ee3487"
|
||||
},
|
||||
"_source": "https://github.com/angular/bower-angular-mocks.git",
|
||||
"_target": "1.5.8",
|
||||
"_originalSource": "angular-mocks"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Angular
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,63 +0,0 @@
|
|||
# packaged angular-mocks
|
||||
|
||||
This repo is for distribution on `npm` and `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngMock).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
You can install this package either with `npm` or with `bower`.
|
||||
|
||||
### npm
|
||||
|
||||
```shell
|
||||
npm install angular-mocks
|
||||
```
|
||||
|
||||
You can `require` ngMock modules:
|
||||
|
||||
```js
|
||||
var angular = require('angular');
|
||||
angular.module('myMod', [
|
||||
require('angular-animate'),
|
||||
require('angular-mocks/ngMock'),
|
||||
require('angular-mocks/ngAnimateMock')
|
||||
]);
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```shell
|
||||
bower install angular-mocks
|
||||
```
|
||||
|
||||
The mocks are then available at `bower_components/angular-mocks/angular-mocks.js`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](https://docs.angularjs.org/guide/unit-testing).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "angular-mocks",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-mocks.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-mocks');
|
||||
module.exports = 'ngAnimateMock';
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-mocks');
|
||||
module.exports = 'ngMock';
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-mocks');
|
||||
module.exports = 'ngMockE2E';
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "angular-mocks",
|
||||
"version": "1.5.8",
|
||||
"description": "AngularJS mocks for testing",
|
||||
"main": "angular-mocks.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"framework",
|
||||
"browser",
|
||||
"mocks",
|
||||
"testing",
|
||||
"client-side"
|
||||
],
|
||||
"author": "Angular Core Team <angular-core+npm@google.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular.js/issues"
|
||||
},
|
||||
"homepage": "http://angularjs.org",
|
||||
"jspm": {
|
||||
"shim": {
|
||||
"angular-mocks": {
|
||||
"deps": ["angular"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "angular-resource",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-resource.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-resource",
|
||||
"_release": "1.5.8",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.5.8",
|
||||
"commit": "26254617a7f9716d80bc2e32117585f33480a4e3"
|
||||
},
|
||||
"_source": "https://github.com/angular/bower-angular-resource.git",
|
||||
"_target": "1.5.8",
|
||||
"_originalSource": "angular-resource"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Angular
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,68 +0,0 @@
|
|||
# packaged angular-resource
|
||||
|
||||
This repo is for distribution on `npm` and `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngResource).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
You can install this package either with `npm` or with `bower`.
|
||||
|
||||
### npm
|
||||
|
||||
```shell
|
||||
npm install angular-resource
|
||||
```
|
||||
|
||||
Then add `ngResource` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', [require('angular-resource')]);
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```shell
|
||||
bower install angular-resource
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-resource/angular-resource.js"></script>
|
||||
```
|
||||
|
||||
Then add `ngResource` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngResource']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngResource).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,863 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.5.8
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
var $resourceMinErr = angular.$$minErr('$resource');
|
||||
|
||||
// Helper functions and regex to lookup a dotted path on an object
|
||||
// stopping at undefined/null. The path must be composed of ASCII
|
||||
// identifiers (just like $parse)
|
||||
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
|
||||
|
||||
function isValidDottedPath(path) {
|
||||
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
|
||||
MEMBER_NAME_REGEX.test('.' + path));
|
||||
}
|
||||
|
||||
function lookupDottedPath(obj, path) {
|
||||
if (!isValidDottedPath(path)) {
|
||||
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
|
||||
}
|
||||
var keys = path.split('.');
|
||||
for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
|
||||
var key = keys[i];
|
||||
obj = (obj !== null) ? obj[key] : undefined;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a shallow copy of an object and clear other fields from the destination
|
||||
*/
|
||||
function shallowClearAndCopy(src, dst) {
|
||||
dst = dst || {};
|
||||
|
||||
angular.forEach(dst, function(value, key) {
|
||||
delete dst[key];
|
||||
});
|
||||
|
||||
for (var key in src) {
|
||||
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngResource
|
||||
* @description
|
||||
*
|
||||
* # ngResource
|
||||
*
|
||||
* The `ngResource` module provides interaction support with RESTful services
|
||||
* via the $resource service.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngResource"></div>
|
||||
*
|
||||
* See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $resourceProvider
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
|
||||
* service.
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngResource } module to be installed.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $resource
|
||||
* @requires $http
|
||||
* @requires ng.$log
|
||||
* @requires $q
|
||||
* @requires ng.$timeout
|
||||
*
|
||||
* @description
|
||||
* A factory which creates a resource object that lets you interact with
|
||||
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
|
||||
*
|
||||
* The returned resource object has action methods which provide high-level behaviors without
|
||||
* the need to interact with the low level {@link ng.$http $http} service.
|
||||
*
|
||||
* Requires the {@link ngResource `ngResource`} module to be installed.
|
||||
*
|
||||
* By default, trailing slashes will be stripped from the calculated URLs,
|
||||
* which can pose problems with server backends that do not expect that
|
||||
* behavior. This can be disabled by configuring the `$resourceProvider` like
|
||||
* this:
|
||||
*
|
||||
* ```js
|
||||
app.config(['$resourceProvider', function($resourceProvider) {
|
||||
// Don't strip trailing slashes from calculated URLs
|
||||
$resourceProvider.defaults.stripTrailingSlashes = false;
|
||||
}]);
|
||||
* ```
|
||||
*
|
||||
* @param {string} url A parameterized URL template with parameters prefixed by `:` as in
|
||||
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||
* `http://example.com:8080/api`), it will be respected.
|
||||
*
|
||||
* If you are using a url with a suffix, just add the suffix, like this:
|
||||
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
|
||||
* or even `$resource('http://example.com/resource/:resource_id.:format')`
|
||||
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
|
||||
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
|
||||
* can escape it with `/\.`.
|
||||
*
|
||||
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||
* `actions` methods. If a parameter value is a function, it will be called every time
|
||||
* a param value needs to be obtained for a request (unless the param was overridden). The function
|
||||
* will be passed the current data value as an argument.
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
*
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@`, then the value for that parameter will be
|
||||
* extracted from the corresponding property on the `data` object (provided when calling a
|
||||
* "non-GET" action method).
|
||||
* For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
|
||||
* `someParam` will be `data.someProp`.
|
||||
* Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
|
||||
* method that does not accept a request body)
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
* ng.$http#usage $http.config}:
|
||||
*
|
||||
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* action2: {method:?, params:?, isArray:?, headers:?, ...},
|
||||
* ...}
|
||||
*
|
||||
* Where:
|
||||
*
|
||||
* - **`action`** – {string} – The name of action. This name becomes the name of the method on
|
||||
* your resource object.
|
||||
* - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
|
||||
* `DELETE`, `JSONP`, etc).
|
||||
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
|
||||
* the parameter value is a function, it will be called every time when a param value needs to
|
||||
* be obtained for a request (unless the param was overridden). The function will be passed the
|
||||
* current data value as an argument.
|
||||
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
|
||||
* like for the resource-level urls.
|
||||
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
|
||||
* see `returns` section.
|
||||
* - **`transformRequest`** –
|
||||
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* request body and headers and returns its transformed (typically serialized) version.
|
||||
* By default, transformRequest will contain one function that checks if the request data is
|
||||
* an object and serializes to using `angular.toJson`. To prevent this behavior, set
|
||||
* `transformRequest` to an empty array: `transformRequest: []`
|
||||
* - **`transformResponse`** –
|
||||
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body and headers and returns its transformed (typically deserialized) version.
|
||||
* By default, transformResponse will contain one function that checks if the response looks
|
||||
* like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior,
|
||||
* set `transformResponse` to an empty array: `transformResponse: []`
|
||||
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with
|
||||
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||
* caching.
|
||||
* - **`timeout`** – `{number}` – timeout in milliseconds.<br />
|
||||
* **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are
|
||||
* **not** supported in $resource, because the same value would be used for multiple requests.
|
||||
* If you are looking for a way to cancel requests, you should use the `cancellable` option.
|
||||
* - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call
|
||||
* will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's
|
||||
* return value. Calling `$cancelRequest()` for a non-cancellable or an already
|
||||
* completed/cancelled request will have no effect.<br />
|
||||
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||
* XHR object. See
|
||||
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
|
||||
* for more information.
|
||||
* - **`responseType`** - `{string}` - see
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
|
||||
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
|
||||
* with `http response` object. See {@link ng.$http $http interceptors}.
|
||||
*
|
||||
* @param {Object} options Hash with custom settings that should extend the
|
||||
* default `$resourceProvider` behavior. The supported options are:
|
||||
*
|
||||
* - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
|
||||
* slashes from any calculated URL will be stripped. (Defaults to true.)
|
||||
* - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be
|
||||
* cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value.
|
||||
* This can be overwritten per action. (Defaults to false.)
|
||||
*
|
||||
* @returns {Object} A resource "class" object with methods for the default set of resource actions
|
||||
* optionally extended with custom `actions`. The default set contains these actions:
|
||||
* ```js
|
||||
* { 'get': {method:'GET'},
|
||||
* 'save': {method:'POST'},
|
||||
* 'query': {method:'GET', isArray:true},
|
||||
* 'remove': {method:'DELETE'},
|
||||
* 'delete': {method:'DELETE'} };
|
||||
* ```
|
||||
*
|
||||
* Calling these methods invoke an {@link ng.$http} with the specified http method,
|
||||
* destination and parameters. When the data is returned from the server then the object is an
|
||||
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
|
||||
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
|
||||
* read, update, delete) on server-side data like this:
|
||||
* ```js
|
||||
* var User = $resource('/user/:userId', {userId:'@id'});
|
||||
* var user = User.get({userId:123}, function() {
|
||||
* user.abc = true;
|
||||
* user.$save();
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* It is important to realize that invoking a $resource object method immediately returns an
|
||||
* empty reference (object or array depending on `isArray`). Once the data is returned from the
|
||||
* server the existing reference is populated with the actual data. This is a useful trick since
|
||||
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
|
||||
* object results in no rendering, once the data arrives from the server then the object is
|
||||
* populated with the data and the view automatically re-renders itself showing the new data. This
|
||||
* means that in most cases one never has to write a callback function for the action methods.
|
||||
*
|
||||
* The action methods on the class object or instance object can be invoked with the following
|
||||
* parameters:
|
||||
*
|
||||
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
|
||||
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
*
|
||||
* Success callback is called with (value, responseHeaders) arguments, where the value is
|
||||
* the populated resource instance or collection object. The error callback is called
|
||||
* with (httpResponse) argument.
|
||||
*
|
||||
* Class actions return empty instance (with additional properties below).
|
||||
* Instance actions return promise of the action.
|
||||
*
|
||||
* The Resource instances and collections have these additional properties:
|
||||
*
|
||||
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
|
||||
* instance or collection.
|
||||
*
|
||||
* On success, the promise is resolved with the same resource instance or collection object,
|
||||
* updated with data from server. This makes it easy to use in
|
||||
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
|
||||
* rendering until the resource(s) are loaded.
|
||||
*
|
||||
* On failure, the promise is rejected with the {@link ng.$http http response} object, without
|
||||
* the `resource` property.
|
||||
*
|
||||
* If an interceptor object was provided, the promise will instead be resolved with the value
|
||||
* returned by the interceptor.
|
||||
*
|
||||
* - `$resolved`: `true` after first server interaction is completed (either with success or
|
||||
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
|
||||
* data-binding.
|
||||
*
|
||||
* The Resource instances and collections have these additional methods:
|
||||
*
|
||||
* - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
|
||||
* collection, calling this method will abort the request.
|
||||
*
|
||||
* The Resource instances have these additional methods:
|
||||
*
|
||||
* - `toJSON`: It returns a simple object without any of the extra properties added as part of
|
||||
* the Resource API. This object can be serialized through {@link angular.toJson} safely
|
||||
* without attaching Angular-specific fields. Notice that `JSON.stringify` (and
|
||||
* `angular.toJson`) automatically use this method when serializing a Resource instance
|
||||
* (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Credit card resource
|
||||
*
|
||||
* ```js
|
||||
// Define CreditCard class
|
||||
var CreditCard = $resource('/user/:userId/card/:cardId',
|
||||
{userId:123, cardId:'@id'}, {
|
||||
charge: {method:'POST', params:{charge:true}}
|
||||
});
|
||||
|
||||
// We can retrieve a collection from the server
|
||||
var cards = CreditCard.query(function() {
|
||||
// GET: /user/123/card
|
||||
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
|
||||
|
||||
var card = cards[0];
|
||||
// each item is an instance of CreditCard
|
||||
expect(card instanceof CreditCard).toEqual(true);
|
||||
card.name = "J. Smith";
|
||||
// non GET methods are mapped onto the instances
|
||||
card.$save();
|
||||
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
|
||||
// server returns: {id:456, number:'1234', name: 'J. Smith'};
|
||||
|
||||
// our custom method is mapped as well.
|
||||
card.$charge({amount:9.99});
|
||||
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
|
||||
});
|
||||
|
||||
// we can create an instance as well
|
||||
var newCard = new CreditCard({number:'0123'});
|
||||
newCard.name = "Mike Smith";
|
||||
newCard.$save();
|
||||
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
|
||||
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
|
||||
expect(newCard.id).toEqual(789);
|
||||
* ```
|
||||
*
|
||||
* The object returned from this function execution is a resource "class" which has "static" method
|
||||
* for each action in the definition.
|
||||
*
|
||||
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
|
||||
* `headers`.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # User resource
|
||||
*
|
||||
* When the data is returned from the server then the object is an instance of the resource type and
|
||||
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
|
||||
* operations (create, read, update, delete) on server-side data.
|
||||
|
||||
```js
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(user) {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
```
|
||||
*
|
||||
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
|
||||
* in the response that came from the server as well as $http header getter function, so one
|
||||
* could rewrite the above example and get access to http headers as:
|
||||
*
|
||||
```js
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(user, getResponseHeaders){
|
||||
user.abc = true;
|
||||
user.$save(function(user, putResponseHeaders) {
|
||||
//user => saved user object
|
||||
//putResponseHeaders => $http header getter
|
||||
});
|
||||
});
|
||||
```
|
||||
*
|
||||
* You can also access the raw `$http` promise via the `$promise` property on the object returned
|
||||
*
|
||||
```
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123})
|
||||
.$promise.then(function(user) {
|
||||
$scope.user = user;
|
||||
});
|
||||
```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Creating a custom 'PUT' request
|
||||
*
|
||||
* In this example we create a custom method on our resource to make a PUT request
|
||||
* ```js
|
||||
* var app = angular.module('app', ['ngResource', 'ngRoute']);
|
||||
*
|
||||
* // Some APIs expect a PUT request in the format URL/object/ID
|
||||
* // Here we are creating an 'update' method
|
||||
* app.factory('Notes', ['$resource', function($resource) {
|
||||
* return $resource('/notes/:id', null,
|
||||
* {
|
||||
* 'update': { method:'PUT' }
|
||||
* });
|
||||
* }]);
|
||||
*
|
||||
* // In our controller we get the ID from the URL using ngRoute and $routeParams
|
||||
* // We pass in $routeParams and our Notes factory along with $scope
|
||||
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
|
||||
function($scope, $routeParams, Notes) {
|
||||
* // First get a note object from the factory
|
||||
* var note = Notes.get({ id:$routeParams.id });
|
||||
* $id = note.id;
|
||||
*
|
||||
* // Now call update passing in the ID first then the object you are updating
|
||||
* Notes.update({ id:$id }, note);
|
||||
*
|
||||
* // This will PUT /notes/ID with the note object in the request payload
|
||||
* }]);
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Cancelling requests
|
||||
*
|
||||
* If an action's configuration specifies that it is cancellable, you can cancel the request related
|
||||
* to an instance or collection (as long as it is a result of a "non-instance" call):
|
||||
*
|
||||
```js
|
||||
// ...defining the `Hotel` resource...
|
||||
var Hotel = $resource('/api/hotel/:id', {id: '@id'}, {
|
||||
// Let's make the `query()` method cancellable
|
||||
query: {method: 'get', isArray: true, cancellable: true}
|
||||
});
|
||||
|
||||
// ...somewhere in the PlanVacationController...
|
||||
...
|
||||
this.onDestinationChanged = function onDestinationChanged(destination) {
|
||||
// We don't care about any pending request for hotels
|
||||
// in a different destination any more
|
||||
this.availableHotels.$cancelRequest();
|
||||
|
||||
// Let's query for hotels in '<destination>'
|
||||
// (calls: /api/hotel?location=<destination>)
|
||||
this.availableHotels = Hotel.query({location: destination});
|
||||
};
|
||||
```
|
||||
*
|
||||
*/
|
||||
angular.module('ngResource', ['ng']).
|
||||
provider('$resource', function() {
|
||||
var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
|
||||
var provider = this;
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $resourceProvider#defaults
|
||||
* @description
|
||||
* Object containing default options used when creating `$resource` instances.
|
||||
*
|
||||
* The default values satisfy a wide range of usecases, but you may choose to overwrite any of
|
||||
* them to further customize your instances. The available properties are:
|
||||
*
|
||||
* - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
|
||||
* calculated URL will be stripped.<br />
|
||||
* (Defaults to true.)
|
||||
* - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
|
||||
* cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
|
||||
* value. For more details, see {@link ngResource.$resource}. This can be overwritten per
|
||||
* resource class or action.<br />
|
||||
* (Defaults to false.)
|
||||
* - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
|
||||
* high-level methods corresponding to RESTful actions/methods on resources. An action may
|
||||
* specify what HTTP method to use, what URL to hit, if the return value will be a single
|
||||
* object or a collection (array) of objects etc. For more details, see
|
||||
* {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
|
||||
* class.<br />
|
||||
* The default actions are:
|
||||
* ```js
|
||||
* {
|
||||
* get: {method: 'GET'},
|
||||
* save: {method: 'POST'},
|
||||
* query: {method: 'GET', isArray: true},
|
||||
* remove: {method: 'DELETE'},
|
||||
* delete: {method: 'DELETE'}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* #### Example
|
||||
*
|
||||
* For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
|
||||
*
|
||||
* ```js
|
||||
* angular.
|
||||
* module('myApp').
|
||||
* config(['resourceProvider', function ($resourceProvider) {
|
||||
* $resourceProvider.defaults.actions.update = {
|
||||
* method: 'PUT'
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Or you can even overwrite the whole `actions` list and specify your own:
|
||||
*
|
||||
* ```js
|
||||
* angular.
|
||||
* module('myApp').
|
||||
* config(['resourceProvider', function ($resourceProvider) {
|
||||
* $resourceProvider.defaults.actions = {
|
||||
* create: {method: 'POST'}
|
||||
* get: {method: 'GET'},
|
||||
* getAll: {method: 'GET', isArray:true},
|
||||
* update: {method: 'PUT'},
|
||||
* delete: {method: 'DELETE'}
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
this.defaults = {
|
||||
// Strip slashes by default
|
||||
stripTrailingSlashes: true,
|
||||
|
||||
// Make non-instance requests cancellable (via `$cancelRequest()`)
|
||||
cancellable: false,
|
||||
|
||||
// Default actions configuration
|
||||
actions: {
|
||||
'get': {method: 'GET'},
|
||||
'save': {method: 'POST'},
|
||||
'query': {method: 'GET', isArray: true},
|
||||
'remove': {method: 'DELETE'},
|
||||
'delete': {method: 'DELETE'}
|
||||
}
|
||||
};
|
||||
|
||||
this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) {
|
||||
|
||||
var noop = angular.noop,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy,
|
||||
isFunction = angular.isFunction;
|
||||
|
||||
/**
|
||||
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
|
||||
* (pchar) allowed in path segments:
|
||||
* segment = *pchar
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriSegment(val) {
|
||||
return encodeUriQuery(val, true).
|
||||
replace(/%26/gi, '&').
|
||||
replace(/%3D/gi, '=').
|
||||
replace(/%2B/gi, '+');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method is intended for encoding *key* or *value* parts of query component. We need a
|
||||
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
|
||||
* have to be encoded per http://tools.ietf.org/html/rfc3986:
|
||||
* query = *( pchar / "/" / "?" )
|
||||
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
||||
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
|
||||
* pct-encoded = "%" HEXDIG HEXDIG
|
||||
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
|
||||
* / "*" / "+" / "," / ";" / "="
|
||||
*/
|
||||
function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
return encodeURIComponent(val).
|
||||
replace(/%40/gi, '@').
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
function Route(template, defaults) {
|
||||
this.template = template;
|
||||
this.defaults = extend({}, provider.defaults, defaults);
|
||||
this.urlParams = {};
|
||||
}
|
||||
|
||||
Route.prototype = {
|
||||
setUrlParams: function(config, params, actionUrl) {
|
||||
var self = this,
|
||||
url = actionUrl || self.template,
|
||||
val,
|
||||
encodedVal,
|
||||
protocolAndDomain = '';
|
||||
|
||||
var urlParams = self.urlParams = {};
|
||||
forEach(url.split(/\W/), function(param) {
|
||||
if (param === 'hasOwnProperty') {
|
||||
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
|
||||
}
|
||||
if (!(new RegExp("^\\d+$").test(param)) && param &&
|
||||
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
|
||||
urlParams[param] = {
|
||||
isQueryParamValue: (new RegExp("\\?.*=:" + param + "(?:\\W|$)")).test(url)
|
||||
};
|
||||
}
|
||||
});
|
||||
url = url.replace(/\\:/g, ':');
|
||||
url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
|
||||
protocolAndDomain = match;
|
||||
return '';
|
||||
});
|
||||
|
||||
params = params || {};
|
||||
forEach(self.urlParams, function(paramInfo, urlParam) {
|
||||
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
|
||||
if (angular.isDefined(val) && val !== null) {
|
||||
if (paramInfo.isQueryParamValue) {
|
||||
encodedVal = encodeUriQuery(val, true);
|
||||
} else {
|
||||
encodedVal = encodeUriSegment(val);
|
||||
}
|
||||
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
|
||||
return encodedVal + p1;
|
||||
});
|
||||
} else {
|
||||
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
|
||||
leadingSlashes, tail) {
|
||||
if (tail.charAt(0) == '/') {
|
||||
return tail;
|
||||
} else {
|
||||
return leadingSlashes + tail;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// strip trailing slashes and set the url (unless this behavior is specifically disabled)
|
||||
if (self.defaults.stripTrailingSlashes) {
|
||||
url = url.replace(/\/+$/, '') || '/';
|
||||
}
|
||||
|
||||
// then replace collapse `/.` if found in the last URL path segment before the query
|
||||
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
|
||||
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
|
||||
// replace escaped `/\.` with `/.`
|
||||
config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
|
||||
|
||||
|
||||
// set params - delegate param encoding to $http
|
||||
forEach(params, function(value, key) {
|
||||
if (!self.urlParams[key]) {
|
||||
config.params = config.params || {};
|
||||
config.params[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function resourceFactory(url, paramDefaults, actions, options) {
|
||||
var route = new Route(url, options);
|
||||
|
||||
actions = extend({}, provider.defaults.actions, actions);
|
||||
|
||||
function extractParams(data, actionParams) {
|
||||
var ids = {};
|
||||
actionParams = extend({}, paramDefaults, actionParams);
|
||||
forEach(actionParams, function(value, key) {
|
||||
if (isFunction(value)) { value = value(data); }
|
||||
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
|
||||
lookupDottedPath(data, value.substr(1)) : value;
|
||||
});
|
||||
return ids;
|
||||
}
|
||||
|
||||
function defaultResponseInterceptor(response) {
|
||||
return response.resource;
|
||||
}
|
||||
|
||||
function Resource(value) {
|
||||
shallowClearAndCopy(value || {}, this);
|
||||
}
|
||||
|
||||
Resource.prototype.toJSON = function() {
|
||||
var data = extend({}, this);
|
||||
delete data.$promise;
|
||||
delete data.$resolved;
|
||||
return data;
|
||||
};
|
||||
|
||||
forEach(actions, function(action, name) {
|
||||
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
|
||||
var numericTimeout = action.timeout;
|
||||
var cancellable = angular.isDefined(action.cancellable) ? action.cancellable :
|
||||
(options && angular.isDefined(options.cancellable)) ? options.cancellable :
|
||||
provider.defaults.cancellable;
|
||||
|
||||
if (numericTimeout && !angular.isNumber(numericTimeout)) {
|
||||
$log.debug('ngResource:\n' +
|
||||
' Only numeric values are allowed as `timeout`.\n' +
|
||||
' Promises are not supported in $resource, because the same value would ' +
|
||||
'be used for multiple requests. If you are looking for a way to cancel ' +
|
||||
'requests, you should use the `cancellable` option.');
|
||||
delete action.timeout;
|
||||
numericTimeout = null;
|
||||
}
|
||||
|
||||
Resource[name] = function(a1, a2, a3, a4) {
|
||||
var params = {}, data, success, error;
|
||||
|
||||
/* jshint -W086 */ /* (purposefully fall through case statements) */
|
||||
switch (arguments.length) {
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
break;
|
||||
}
|
||||
|
||||
success = a2;
|
||||
error = a3;
|
||||
//fallthrough
|
||||
} else {
|
||||
params = a1;
|
||||
data = a2;
|
||||
success = a3;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
if (isFunction(a1)) success = a1;
|
||||
else if (hasBody) data = a1;
|
||||
else params = a1;
|
||||
break;
|
||||
case 0: break;
|
||||
default:
|
||||
throw $resourceMinErr('badargs',
|
||||
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
|
||||
arguments.length);
|
||||
}
|
||||
/* jshint +W086 */ /* (purposefully fall through case statements) */
|
||||
|
||||
var isInstanceCall = this instanceof Resource;
|
||||
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
|
||||
var httpConfig = {};
|
||||
var responseInterceptor = action.interceptor && action.interceptor.response ||
|
||||
defaultResponseInterceptor;
|
||||
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
|
||||
undefined;
|
||||
var timeoutDeferred;
|
||||
var numericTimeoutPromise;
|
||||
|
||||
forEach(action, function(value, key) {
|
||||
switch (key) {
|
||||
default:
|
||||
httpConfig[key] = copy(value);
|
||||
break;
|
||||
case 'params':
|
||||
case 'isArray':
|
||||
case 'interceptor':
|
||||
case 'cancellable':
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isInstanceCall && cancellable) {
|
||||
timeoutDeferred = $q.defer();
|
||||
httpConfig.timeout = timeoutDeferred.promise;
|
||||
|
||||
if (numericTimeout) {
|
||||
numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasBody) httpConfig.data = data;
|
||||
route.setUrlParams(httpConfig,
|
||||
extend({}, extractParams(data, action.params || {}), params),
|
||||
action.url);
|
||||
|
||||
var promise = $http(httpConfig).then(function(response) {
|
||||
var data = response.data;
|
||||
|
||||
if (data) {
|
||||
// Need to convert action.isArray to boolean in case it is undefined
|
||||
// jshint -W018
|
||||
if (angular.isArray(data) !== (!!action.isArray)) {
|
||||
throw $resourceMinErr('badcfg',
|
||||
'Error in resource configuration for action `{0}`. Expected response to ' +
|
||||
'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
|
||||
angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
|
||||
}
|
||||
// jshint +W018
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
if (typeof item === "object") {
|
||||
value.push(new Resource(item));
|
||||
} else {
|
||||
// Valid JSON values may be string literals, and these should not be converted
|
||||
// into objects. These items will not have access to the Resource prototype
|
||||
// methods, but unfortunately there
|
||||
value.push(item);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
var promise = value.$promise; // Save the promise
|
||||
shallowClearAndCopy(data, value);
|
||||
value.$promise = promise; // Restore the promise
|
||||
}
|
||||
}
|
||||
response.resource = value;
|
||||
|
||||
return response;
|
||||
}, function(response) {
|
||||
(error || noop)(response);
|
||||
return $q.reject(response);
|
||||
});
|
||||
|
||||
promise['finally'](function() {
|
||||
value.$resolved = true;
|
||||
if (!isInstanceCall && cancellable) {
|
||||
value.$cancelRequest = angular.noop;
|
||||
$timeout.cancel(numericTimeoutPromise);
|
||||
timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null;
|
||||
}
|
||||
});
|
||||
|
||||
promise = promise.then(
|
||||
function(response) {
|
||||
var value = responseInterceptor(response);
|
||||
(success || noop)(value, response.headers);
|
||||
return value;
|
||||
},
|
||||
responseErrorInterceptor);
|
||||
|
||||
if (!isInstanceCall) {
|
||||
// we are creating instance / collection
|
||||
// - set the initial promise
|
||||
// - return the instance / collection
|
||||
value.$promise = promise;
|
||||
value.$resolved = false;
|
||||
if (cancellable) value.$cancelRequest = timeoutDeferred.resolve;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// instance call
|
||||
return promise;
|
||||
};
|
||||
|
||||
|
||||
Resource.prototype['$' + name] = function(params, success, error) {
|
||||
if (isFunction(params)) {
|
||||
error = success; success = params; params = {};
|
||||
}
|
||||
var result = Resource[name].call(this, params, this, success, error);
|
||||
return result.$promise || result;
|
||||
};
|
||||
});
|
||||
|
||||
Resource.bind = function(additionalParamDefaults) {
|
||||
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
|
||||
};
|
||||
|
||||
return Resource;
|
||||
}
|
||||
|
||||
return resourceFactory;
|
||||
}];
|
||||
});
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.5.8
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(P,d){'use strict';function G(t,g){g=g||{};d.forEach(g,function(d,q){delete g[q]});for(var q in t)!t.hasOwnProperty(q)||"$"===q.charAt(0)&&"$"===q.charAt(1)||(g[q]=t[q]);return g}var z=d.$$minErr("$resource"),M=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var t=/^https?:\/\/[^\/]*/,g=this;this.defaults={stripTrailingSlashes:!0,cancellable:!1,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},
|
||||
"delete":{method:"DELETE"}}};this.$get=["$http","$log","$q","$timeout",function(q,L,H,I){function A(d,h){return encodeURIComponent(d).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,h?"%20":"+")}function B(d,h){this.template=d;this.defaults=v({},g.defaults,h);this.urlParams={}}function J(e,h,n,k){function b(a,c){var b={};c=v({},h,c);u(c,function(c,h){x(c)&&(c=c(a));var f;if(c&&c.charAt&&"@"==c.charAt(0)){f=a;var l=c.substr(1);if(null==l||""===l||"hasOwnProperty"===
|
||||
l||!M.test("."+l))throw z("badmember",l);for(var l=l.split("."),m=0,k=l.length;m<k&&d.isDefined(f);m++){var r=l[m];f=null!==f?f[r]:void 0}}else f=c;b[h]=f});return b}function N(a){return a.resource}function m(a){G(a||{},this)}var t=new B(e,k);n=v({},g.defaults.actions,n);m.prototype.toJSON=function(){var a=v({},this);delete a.$promise;delete a.$resolved;return a};u(n,function(a,c){var h=/^(POST|PUT|PATCH)$/i.test(a.method),e=a.timeout,E=d.isDefined(a.cancellable)?a.cancellable:k&&d.isDefined(k.cancellable)?
|
||||
k.cancellable:g.defaults.cancellable;e&&!d.isNumber(e)&&(L.debug("ngResource:\n Only numeric values are allowed as `timeout`.\n Promises are not supported in $resource, because the same value would be used for multiple requests. If you are looking for a way to cancel requests, you should use the `cancellable` option."),delete a.timeout,e=null);m[c]=function(f,l,k,g){var r={},n,w,C;switch(arguments.length){case 4:C=g,w=k;case 3:case 2:if(x(l)){if(x(f)){w=f;C=l;break}w=l;C=k}else{r=f;n=l;w=k;break}case 1:x(f)?
|
||||
w=f:h?n=f:r=f;break;case 0:break;default:throw z("badargs",arguments.length);}var D=this instanceof m,p=D?n:a.isArray?[]:new m(n),s={},A=a.interceptor&&a.interceptor.response||N,B=a.interceptor&&a.interceptor.responseError||void 0,y,F;u(a,function(a,c){switch(c){default:s[c]=O(a);case "params":case "isArray":case "interceptor":case "cancellable":}});!D&&E&&(y=H.defer(),s.timeout=y.promise,e&&(F=I(y.resolve,e)));h&&(s.data=n);t.setUrlParams(s,v({},b(n,a.params||{}),r),a.url);r=q(s).then(function(f){var b=
|
||||
f.data;if(b){if(d.isArray(b)!==!!a.isArray)throw z("badcfg",c,a.isArray?"array":"object",d.isArray(b)?"array":"object",s.method,s.url);if(a.isArray)p.length=0,u(b,function(a){"object"===typeof a?p.push(new m(a)):p.push(a)});else{var l=p.$promise;G(b,p);p.$promise=l}}f.resource=p;return f},function(a){(C||K)(a);return H.reject(a)});r["finally"](function(){p.$resolved=!0;!D&&E&&(p.$cancelRequest=d.noop,I.cancel(F),y=F=s.timeout=null)});r=r.then(function(a){var c=A(a);(w||K)(c,a.headers);return c},B);
|
||||
return D?r:(p.$promise=r,p.$resolved=!1,E&&(p.$cancelRequest=y.resolve),p)};m.prototype["$"+c]=function(a,b,d){x(a)&&(d=b,b=a,a={});a=m[c].call(this,a,this,b,d);return a.$promise||a}});m.bind=function(a){return J(e,v({},h,a),n)};return m}var K=d.noop,u=d.forEach,v=d.extend,O=d.copy,x=d.isFunction;B.prototype={setUrlParams:function(e,h,n){var k=this,b=n||k.template,g,m,q="",a=k.urlParams={};u(b.split(/\W/),function(c){if("hasOwnProperty"===c)throw z("badname");!/^\d+$/.test(c)&&c&&(new RegExp("(^|[^\\\\]):"+
|
||||
c+"(\\W|$)")).test(b)&&(a[c]={isQueryParamValue:(new RegExp("\\?.*=:"+c+"(?:\\W|$)")).test(b)})});b=b.replace(/\\:/g,":");b=b.replace(t,function(a){q=a;return""});h=h||{};u(k.urlParams,function(a,e){g=h.hasOwnProperty(e)?h[e]:k.defaults[e];d.isDefined(g)&&null!==g?(m=a.isQueryParamValue?A(g,!0):A(g,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),b=b.replace(new RegExp(":"+e+"(\\W|$)","g"),function(a,c){return m+c})):b=b.replace(new RegExp("(/?):"+e+"(\\W|$)","g"),function(a,c,b){return"/"==
|
||||
b.charAt(0)?b:c+b})});k.defaults.stripTrailingSlashes&&(b=b.replace(/\/+$/,"")||"/");b=b.replace(/\/\.(?=\w+($|\?))/,".");e.url=q+b.replace(/\/\\\./,"/.");u(h,function(a,b){k.urlParams[b]||(e.params=e.params||{},e.params[b]=a)})}};return J}]})})(window,window.angular);
|
||||
//# sourceMappingURL=angular-resource.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "angular-resource",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-resource.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-resource');
|
||||
module.exports = 'ngResource';
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"name": "angular-resource",
|
||||
"version": "1.5.8",
|
||||
"description": "AngularJS module for interacting with RESTful server-side data sources",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"framework",
|
||||
"browser",
|
||||
"rest",
|
||||
"client-side"
|
||||
],
|
||||
"author": "Angular Core Team <angular-core+npm@google.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular.js/issues"
|
||||
},
|
||||
"homepage": "http://angularjs.org",
|
||||
"jspm": {
|
||||
"shim": {
|
||||
"angular-resource": {
|
||||
"deps": ["angular"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "angular-sanitize",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-sanitize.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
},
|
||||
"homepage": "https://github.com/angular/bower-angular-sanitize",
|
||||
"_release": "1.5.8",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.5.8",
|
||||
"commit": "380756a11f0b6b213d0b82c9fdbe56f40d4c0093"
|
||||
},
|
||||
"_source": "https://github.com/angular/bower-angular-sanitize.git",
|
||||
"_target": "1.5.8",
|
||||
"_originalSource": "angular-sanitize"
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Angular
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,68 +0,0 @@
|
|||
# packaged angular-sanitize
|
||||
|
||||
This repo is for distribution on `npm` and `bower`. The source for this module is in the
|
||||
[main AngularJS repo](https://github.com/angular/angular.js/tree/master/src/ngSanitize).
|
||||
Please file issues and pull requests against that repo.
|
||||
|
||||
## Install
|
||||
|
||||
You can install this package either with `npm` or with `bower`.
|
||||
|
||||
### npm
|
||||
|
||||
```shell
|
||||
npm install angular-sanitize
|
||||
```
|
||||
|
||||
Then add `ngSanitize` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', [require('angular-sanitize')]);
|
||||
```
|
||||
|
||||
### bower
|
||||
|
||||
```shell
|
||||
bower install angular-sanitize
|
||||
```
|
||||
|
||||
Add a `<script>` to your `index.html`:
|
||||
|
||||
```html
|
||||
<script src="/bower_components/angular-sanitize/angular-sanitize.js"></script>
|
||||
```
|
||||
|
||||
Then add `ngSanitize` as a dependency for your app:
|
||||
|
||||
```javascript
|
||||
angular.module('myApp', ['ngSanitize']);
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is available on the
|
||||
[AngularJS docs site](http://docs.angularjs.org/api/ngSanitize).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,738 +0,0 @@
|
|||
/**
|
||||
* @license AngularJS v1.5.8
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Any commits to this file should be reviewed with security in mind. *
|
||||
* Changes to this file can potentially create security vulnerabilities. *
|
||||
* An approval from 2 Core members with history of modifying *
|
||||
* this file is required. *
|
||||
* *
|
||||
* Does the change somehow allow for arbitrary javascript to be executed? *
|
||||
* Or allows for someone to change the prototype of built-in objects? *
|
||||
* Or gives undesired access to variables likes document or window? *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
var bind;
|
||||
var extend;
|
||||
var forEach;
|
||||
var isDefined;
|
||||
var lowercase;
|
||||
var noop;
|
||||
var htmlParser;
|
||||
var htmlSanitizeWriter;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngSanitize
|
||||
* @description
|
||||
*
|
||||
* # ngSanitize
|
||||
*
|
||||
* The `ngSanitize` module provides functionality to sanitize HTML.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngSanitize"></div>
|
||||
*
|
||||
* See {@link ngSanitize.$sanitize `$sanitize`} for usage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $sanitize
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Sanitizes an html string by stripping all potentially dangerous tokens.
|
||||
*
|
||||
* The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string.
|
||||
*
|
||||
* The whitelist for URL sanitization of attribute values is configured using the functions
|
||||
* `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider
|
||||
* `$compileProvider`}.
|
||||
*
|
||||
* The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}.
|
||||
*
|
||||
* @param {string} html HTML input.
|
||||
* @returns {string} Sanitized HTML.
|
||||
*
|
||||
* @example
|
||||
<example module="sanitizeExample" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('sanitizeExample', ['ngSanitize'])
|
||||
.controller('ExampleController', ['$scope', '$sce', function($scope, $sce) {
|
||||
$scope.snippet =
|
||||
'<p style="color:blue">an html\n' +
|
||||
'<em onmouseover="this.textContent=\'PWN3D!\'">click here</em>\n' +
|
||||
'snippet</p>';
|
||||
$scope.deliberatelyTrustDangerousSnippet = function() {
|
||||
return $sce.trustAsHtml($scope.snippet);
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
<div ng-controller="ExampleController">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Directive</td>
|
||||
<td>How</td>
|
||||
<td>Source</td>
|
||||
<td>Rendered</td>
|
||||
</tr>
|
||||
<tr id="bind-html-with-sanitize">
|
||||
<td>ng-bind-html</td>
|
||||
<td>Automatically uses $sanitize</td>
|
||||
<td><pre><div ng-bind-html="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind-html="snippet"></div></td>
|
||||
</tr>
|
||||
<tr id="bind-html-with-trust">
|
||||
<td>ng-bind-html</td>
|
||||
<td>Bypass $sanitize by explicitly trusting the dangerous value</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="deliberatelyTrustDangerousSnippet()">
|
||||
</div></pre>
|
||||
</td>
|
||||
<td><div ng-bind-html="deliberatelyTrustDangerousSnippet()"></div></td>
|
||||
</tr>
|
||||
<tr id="bind-default">
|
||||
<td>ng-bind</td>
|
||||
<td>Automatically escapes</td>
|
||||
<td><pre><div ng-bind="snippet"><br/></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should sanitize the html snippet by default', function() {
|
||||
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
|
||||
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
|
||||
});
|
||||
|
||||
it('should inline raw snippet if bound to a trusted value', function() {
|
||||
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should escape snippet without any filter', function() {
|
||||
expect(element(by.css('#bind-default div')).getInnerHtml()).
|
||||
toBe("<p style=\"color:blue\">an html\n" +
|
||||
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
|
||||
"snippet</p>");
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
element(by.model('snippet')).clear();
|
||||
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
|
||||
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
|
||||
toBe('new <b>text</b>');
|
||||
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
|
||||
'new <b onclick="alert(1)">text</b>');
|
||||
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
|
||||
"new <b onclick=\"alert(1)\">text</b>");
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $sanitizeProvider
|
||||
*
|
||||
* @description
|
||||
* Creates and configures {@link $sanitize} instance.
|
||||
*/
|
||||
function $SanitizeProvider() {
|
||||
var svgEnabled = false;
|
||||
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
if (svgEnabled) {
|
||||
extend(validElements, svgElements);
|
||||
}
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
|
||||
return !/^unsafe:/.test($$sanitizeUri(uri, isImage));
|
||||
}));
|
||||
return buf.join('');
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $sanitizeProvider#enableSvg
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Enables a subset of svg to be supported by the sanitizer.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* <p>By enabling this setting without taking other precautions, you might expose your
|
||||
* application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned
|
||||
* outside of the containing element and be rendered over other elements on the page (e.g. a login
|
||||
* link). Such behavior can then result in phishing incidents.</p>
|
||||
*
|
||||
* <p>To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg
|
||||
* tags within the sanitized content:</p>
|
||||
*
|
||||
* <br>
|
||||
*
|
||||
* <pre><code>
|
||||
* .rootOfTheIncludedContent svg {
|
||||
* overflow: hidden !important;
|
||||
* }
|
||||
* </code></pre>
|
||||
* </div>
|
||||
*
|
||||
* @param {boolean=} flag Enable or disable SVG support in the sanitizer.
|
||||
* @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called
|
||||
* without an argument or self for chaining otherwise.
|
||||
*/
|
||||
this.enableSvg = function(enableSvg) {
|
||||
if (isDefined(enableSvg)) {
|
||||
svgEnabled = enableSvg;
|
||||
return this;
|
||||
} else {
|
||||
return svgEnabled;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private stuff
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bind = angular.bind;
|
||||
extend = angular.extend;
|
||||
forEach = angular.forEach;
|
||||
isDefined = angular.isDefined;
|
||||
lowercase = angular.lowercase;
|
||||
noop = angular.noop;
|
||||
|
||||
htmlParser = htmlParserImpl;
|
||||
htmlSanitizeWriter = htmlSanitizeWriterImpl;
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = toMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = toMap("rp,rt"),
|
||||
optionalEndTagElements = extend({},
|
||||
optionalEndTagInlineElements,
|
||||
optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = extend({}, optionalEndTagBlockElements, toMap("address,article," +
|
||||
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
||||
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
|
||||
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
||||
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
// SVG Elements
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
||||
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
|
||||
// They can potentially allow for arbitrary javascript to be executed. See #11290
|
||||
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
|
||||
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
|
||||
"radialGradient,rect,stop,svg,switch,text,title,tspan");
|
||||
|
||||
// Blocked Elements (will be stripped)
|
||||
var blockedElements = toMap("script,style");
|
||||
|
||||
var validElements = extend({},
|
||||
voidElements,
|
||||
blockElements,
|
||||
inlineElements,
|
||||
optionalEndTagElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
|
||||
|
||||
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
||||
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
||||
'valign,value,vspace,width');
|
||||
|
||||
// SVG attributes (without "id" and "name" attributes)
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
||||
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
||||
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
|
||||
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
|
||||
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
|
||||
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
|
||||
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
|
||||
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
|
||||
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
|
||||
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
|
||||
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
|
||||
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
|
||||
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
|
||||
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
|
||||
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
|
||||
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
|
||||
|
||||
var validAttrs = extend({},
|
||||
uriAttrs,
|
||||
svgAttrs,
|
||||
htmlAttrs);
|
||||
|
||||
function toMap(str, lowercaseKeys) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) {
|
||||
obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var inertBodyElement;
|
||||
(function(window) {
|
||||
var doc;
|
||||
if (window.document && window.document.implementation) {
|
||||
doc = window.document.implementation.createHTMLDocument("inert");
|
||||
} else {
|
||||
throw $sanitizeMinErr('noinert', "Can't create an inert html document");
|
||||
}
|
||||
var docElement = doc.documentElement || doc.getDocumentElement();
|
||||
var bodyElements = docElement.getElementsByTagName('body');
|
||||
|
||||
// usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
|
||||
if (bodyElements.length === 1) {
|
||||
inertBodyElement = bodyElements[0];
|
||||
} else {
|
||||
var html = doc.createElement('html');
|
||||
inertBodyElement = doc.createElement('body');
|
||||
html.appendChild(inertBodyElement);
|
||||
doc.appendChild(html);
|
||||
}
|
||||
})(window);
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParserImpl(html, handler) {
|
||||
if (html === null || html === undefined) {
|
||||
html = '';
|
||||
} else if (typeof html !== 'string') {
|
||||
html = '' + html;
|
||||
}
|
||||
inertBodyElement.innerHTML = html;
|
||||
|
||||
//mXSS protection
|
||||
var mXSSAttempts = 5;
|
||||
do {
|
||||
if (mXSSAttempts === 0) {
|
||||
throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
|
||||
}
|
||||
mXSSAttempts--;
|
||||
|
||||
// strip custom-namespaced attributes on IE<=11
|
||||
if (window.document.documentMode) {
|
||||
stripCustomNsAttrs(inertBodyElement);
|
||||
}
|
||||
html = inertBodyElement.innerHTML; //trigger mXSS
|
||||
inertBodyElement.innerHTML = html;
|
||||
} while (html !== inertBodyElement.innerHTML);
|
||||
|
||||
var node = inertBodyElement.firstChild;
|
||||
while (node) {
|
||||
switch (node.nodeType) {
|
||||
case 1: // ELEMENT_NODE
|
||||
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
|
||||
break;
|
||||
case 3: // TEXT NODE
|
||||
handler.chars(node.textContent);
|
||||
break;
|
||||
}
|
||||
|
||||
var nextNode;
|
||||
if (!(nextNode = node.firstChild)) {
|
||||
if (node.nodeType == 1) {
|
||||
handler.end(node.nodeName.toLowerCase());
|
||||
}
|
||||
nextNode = node.nextSibling;
|
||||
if (!nextNode) {
|
||||
while (nextNode == null) {
|
||||
node = node.parentNode;
|
||||
if (node === inertBodyElement) break;
|
||||
nextNode = node.nextSibling;
|
||||
if (node.nodeType == 1) {
|
||||
handler.end(node.nodeName.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
|
||||
while (node = inertBodyElement.firstChild) {
|
||||
inertBodyElement.removeChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
function attrToMap(attrs) {
|
||||
var map = {};
|
||||
for (var i = 0, ii = attrs.length; i < ii; i++) {
|
||||
var attr = attrs[i];
|
||||
map[attr.name] = attr.value;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns {string} escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(SURROGATE_PAIR_REGEXP, function(value) {
|
||||
var hi = value.charCodeAt(0);
|
||||
var low = value.charCodeAt(1);
|
||||
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
|
||||
}).
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.join('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriterImpl(buf, uriValidator) {
|
||||
var ignoreCurrentElement = false;
|
||||
var out = bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs) {
|
||||
tag = lowercase(tag);
|
||||
if (!ignoreCurrentElement && blockedElements[tag]) {
|
||||
ignoreCurrentElement = tag;
|
||||
}
|
||||
if (!ignoreCurrentElement && validElements[tag] === true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
forEach(attrs, function(value, key) {
|
||||
var lkey = lowercase(key);
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out('>');
|
||||
}
|
||||
},
|
||||
end: function(tag) {
|
||||
tag = lowercase(tag);
|
||||
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignoreCurrentElement) {
|
||||
ignoreCurrentElement = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars) {
|
||||
if (!ignoreCurrentElement) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
|
||||
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
|
||||
* to allow any of these custom attributes. This method strips them all.
|
||||
*
|
||||
* @param node Root element to process
|
||||
*/
|
||||
function stripCustomNsAttrs(node) {
|
||||
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
||||
var attrs = node.attributes;
|
||||
for (var i = 0, l = attrs.length; i < l; i++) {
|
||||
var attrNode = attrs[i];
|
||||
var attrName = attrNode.name.toLowerCase();
|
||||
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
|
||||
node.removeAttributeNode(attrNode);
|
||||
i--;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nextNode = node.firstChild;
|
||||
if (nextNode) {
|
||||
stripCustomNsAttrs(nextNode);
|
||||
}
|
||||
|
||||
nextNode = node.nextSibling;
|
||||
if (nextNode) {
|
||||
stripCustomNsAttrs(nextNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
var writer = htmlSanitizeWriter(buf, noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name linky
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports `http/https/ftp/mailto` and
|
||||
* plain email address links.
|
||||
*
|
||||
* Requires the {@link ngSanitize `ngSanitize`} module to be installed.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @param {string} target Window (`_blank|_self|_parent|_top`) or named frame to open links in.
|
||||
* @param {object|function(url)} [attributes] Add custom attributes to the link element.
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* - `object`: A map of attributes
|
||||
* - `function`: Takes the url as a parameter and returns a map of attributes
|
||||
*
|
||||
* If the map of attributes contains a value for `target`, it overrides the value of
|
||||
* the target parameter.
|
||||
*
|
||||
*
|
||||
* @returns {string} Html-linkified and {@link $sanitize sanitized} text.
|
||||
*
|
||||
* @usage
|
||||
<span ng-bind-html="linky_expression | linky"></span>
|
||||
*
|
||||
* @example
|
||||
<example module="linkyExample" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
Snippet: <textarea ng-model="snippet" cols="60" rows="3"></textarea>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Filter</th>
|
||||
<th>Source</th>
|
||||
<th>Rendered</th>
|
||||
</tr>
|
||||
<tr id="linky-filter">
|
||||
<td>linky filter</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippet | linky"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippet | linky"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="linky-target">
|
||||
<td>linky target</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippetWithSingleURL | linky:'_blank'"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippetWithSingleURL | linky:'_blank'"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="linky-custom-attributes">
|
||||
<td>linky custom attributes</td>
|
||||
<td>
|
||||
<pre><div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"><br></div></pre>
|
||||
</td>
|
||||
<td>
|
||||
<div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="escaped-html">
|
||||
<td>no filter</td>
|
||||
<td><pre><div ng-bind="snippet"><br></div></pre></td>
|
||||
<td><div ng-bind="snippet"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('linkyExample', ['ngSanitize'])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.snippet =
|
||||
'Pretty text with some links:\n'+
|
||||
'http://angularjs.org/,\n'+
|
||||
'mailto:us@somewhere.org,\n'+
|
||||
'another@somewhere.org,\n'+
|
||||
'and one more: ftp://127.0.0.1/.';
|
||||
$scope.snippetWithSingleURL = 'http://angularjs.org/';
|
||||
}]);
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should linkify the snippet with urls', function() {
|
||||
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
||||
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
|
||||
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
||||
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
|
||||
});
|
||||
|
||||
it('should not linkify snippet without the linky filter', function() {
|
||||
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
|
||||
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
|
||||
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
|
||||
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should update', function() {
|
||||
element(by.model('snippet')).clear();
|
||||
element(by.model('snippet')).sendKeys('new http://link.');
|
||||
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
|
||||
toBe('new http://link.');
|
||||
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
|
||||
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
|
||||
.toBe('new http://link.');
|
||||
});
|
||||
|
||||
it('should work with the target property', function() {
|
||||
expect(element(by.id('linky-target')).
|
||||
element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()).
|
||||
toBe('http://angularjs.org/');
|
||||
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
|
||||
});
|
||||
|
||||
it('should optionally add custom attributes', function() {
|
||||
expect(element(by.id('linky-custom-attributes')).
|
||||
element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()).
|
||||
toBe('http://angularjs.org/');
|
||||
expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
|
||||
MAILTO_REGEXP = /^mailto:/i;
|
||||
|
||||
var linkyMinErr = angular.$$minErr('linky');
|
||||
var isDefined = angular.isDefined;
|
||||
var isFunction = angular.isFunction;
|
||||
var isObject = angular.isObject;
|
||||
var isString = angular.isString;
|
||||
|
||||
return function(text, target, attributes) {
|
||||
if (text == null || text === '') return text;
|
||||
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
|
||||
|
||||
var attributesFn =
|
||||
isFunction(attributes) ? attributes :
|
||||
isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
||||
function getEmptyAttributesObject() {return {};};
|
||||
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
var url;
|
||||
var i;
|
||||
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/www/mailto then assume mailto
|
||||
if (!match[2] && !match[4]) {
|
||||
url = (match[3] ? 'http://' : 'mailto:') + url;
|
||||
}
|
||||
i = match.index;
|
||||
addText(raw.substr(0, i));
|
||||
addLink(url, match[0].replace(MAILTO_REGEXP, ''));
|
||||
raw = raw.substring(i + match[0].length);
|
||||
}
|
||||
addText(raw);
|
||||
return $sanitize(html.join(''));
|
||||
|
||||
function addText(text) {
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
html.push(sanitizeText(text));
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
var key, linkAttributes = attributesFn(url);
|
||||
html.push('<a ');
|
||||
|
||||
for (key in linkAttributes) {
|
||||
html.push(key + '="' + linkAttributes[key] + '" ');
|
||||
}
|
||||
|
||||
if (isDefined(target) && !('target' in linkAttributes)) {
|
||||
html.push('target="',
|
||||
target,
|
||||
'" ');
|
||||
}
|
||||
html.push('href="',
|
||||
url.replace(/"/g, '"'),
|
||||
'">');
|
||||
addText(text);
|
||||
html.push('</a>');
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
AngularJS v1.5.8
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(s,g){'use strict';function H(g){var l=[];t(l,A).chars(g);return l.join("")}var B=g.$$minErr("$sanitize"),C,l,D,E,q,A,F,t;g.module("ngSanitize",[]).provider("$sanitize",function(){function k(a,e){var b={},c=a.split(","),h;for(h=0;h<c.length;h++)b[e?q(c[h]):c[h]]=!0;return b}function I(a){for(var e={},b=0,c=a.length;b<c;b++){var h=a[b];e[h.name]=h.value}return e}function G(a){return a.replace(/&/g,"&").replace(J,function(a){var b=a.charCodeAt(0);a=a.charCodeAt(1);return"&#"+(1024*(b-55296)+
|
||||
(a-56320)+65536)+";"}).replace(K,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"<").replace(/>/g,">")}function u(a){if(a.nodeType===s.Node.ELEMENT_NODE)for(var e=a.attributes,b=0,c=e.length;b<c;b++){var h=e[b],d=h.name.toLowerCase();if("xmlns:ns1"===d||0===d.lastIndexOf("ns1:",0))a.removeAttributeNode(h),b--,c--}(e=a.firstChild)&&u(e);(e=a.nextSibling)&&u(e)}var v=!1;this.$get=["$$sanitizeUri",function(a){v&&l(w,x);return function(e){var b=[];F(e,t(b,function(b,h){return!/^unsafe:/.test(a(b,
|
||||
h))}));return b.join("")}}];this.enableSvg=function(a){return E(a)?(v=a,this):v};C=g.bind;l=g.extend;D=g.forEach;E=g.isDefined;q=g.lowercase;A=g.noop;F=function(a,e){null===a||void 0===a?a="":"string"!==typeof a&&(a=""+a);f.innerHTML=a;var b=5;do{if(0===b)throw B("uinput");b--;s.document.documentMode&&u(f);a=f.innerHTML;f.innerHTML=a}while(a!==f.innerHTML);for(b=f.firstChild;b;){switch(b.nodeType){case 1:e.start(b.nodeName.toLowerCase(),I(b.attributes));break;case 3:e.chars(b.textContent)}var c;if(!(c=
|
||||
b.firstChild)&&(1==b.nodeType&&e.end(b.nodeName.toLowerCase()),c=b.nextSibling,!c))for(;null==c;){b=b.parentNode;if(b===f)break;c=b.nextSibling;1==b.nodeType&&e.end(b.nodeName.toLowerCase())}b=c}for(;b=f.firstChild;)f.removeChild(b)};t=function(a,e){var b=!1,c=C(a,a.push);return{start:function(a,d){a=q(a);!b&&z[a]&&(b=a);b||!0!==w[a]||(c("<"),c(a),D(d,function(b,d){var f=q(d),g="img"===a&&"src"===f||"background"===f;!0!==m[f]||!0===n[f]&&!e(b,g)||(c(" "),c(d),c('="'),c(G(b)),c('"'))}),c(">"))},end:function(a){a=
|
||||
q(a);b||!0!==w[a]||!0===y[a]||(c("</"),c(a),c(">"));a==b&&(b=!1)},chars:function(a){b||c(G(a))}}};var J=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,K=/([^\#-~ |!])/g,y=k("area,br,col,hr,img,wbr"),d=k("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),r=k("rp,rt"),p=l({},r,d),d=l({},d,k("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),r=l({},r,k("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),
|
||||
x=k("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),z=k("script,style"),w=l({},y,d,r,p),n=k("background,cite,href,longdesc,src,xlink:href"),p=k("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"),
|
||||
r=k("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",
|
||||
!0),m=l({},n,r,p),f;(function(a){if(a.document&&a.document.implementation)a=a.document.implementation.createHTMLDocument("inert");else throw B("noinert");var e=(a.documentElement||a.getDocumentElement()).getElementsByTagName("body");1===e.length?f=e[0]:(e=a.createElement("html"),f=a.createElement("body"),e.appendChild(f),a.appendChild(e))})(s)});g.module("ngSanitize").filter("linky",["$sanitize",function(k){var l=/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i,
|
||||
q=/^mailto:/i,u=g.$$minErr("linky"),v=g.isDefined,s=g.isFunction,t=g.isObject,y=g.isString;return function(d,g,p){function x(a){a&&m.push(H(a))}function z(a,b){var c,d=w(a);m.push("<a ");for(c in d)m.push(c+'="'+d[c]+'" ');!v(g)||"target"in d||m.push('target="',g,'" ');m.push('href="',a.replace(/"/g,"""),'">');x(b);m.push("</a>")}if(null==d||""===d)return d;if(!y(d))throw u("notstring",d);for(var w=s(p)?p:t(p)?function(){return p}:function(){return{}},n=d,m=[],f,a;d=n.match(l);)f=d[0],d[2]||
|
||||
d[4]||(f=(d[3]?"http://":"mailto:")+f),a=d.index,x(n.substr(0,a)),z(f,d[0].replace(q,"")),n=n.substring(a+d[0].length);x(n);return k(m.join(""))}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-sanitize.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"name": "angular-sanitize",
|
||||
"version": "1.5.8",
|
||||
"license": "MIT",
|
||||
"main": "./angular-sanitize.js",
|
||||
"ignore": [],
|
||||
"dependencies": {
|
||||
"angular": "1.5.8"
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require('./angular-sanitize');
|
||||
module.exports = 'ngSanitize';
|
|
@ -1,33 +0,0 @@
|
|||
{
|
||||
"name": "angular-sanitize",
|
||||
"version": "1.5.8",
|
||||
"description": "AngularJS module for sanitizing HTML",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"angular",
|
||||
"framework",
|
||||
"browser",
|
||||
"html",
|
||||
"client-side"
|
||||
],
|
||||
"author": "Angular Core Team <angular-core+npm@google.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/angular/angular.js/issues"
|
||||
},
|
||||
"homepage": "http://angularjs.org",
|
||||
"jspm": {
|
||||
"shim": {
|
||||
"angular-sanitize": {
|
||||
"deps": ["angular"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"name": "angular-ui-router",
|
||||
"version": "0.3.1",
|
||||
"license": "MIT",
|
||||
"main": "./release/angular-ui-router.js",
|
||||
"dependencies": {
|
||||
"angular": "^1.0.8"
|
||||
},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"component.json",
|
||||
"package.json",
|
||||
"lib",
|
||||
"config",
|
||||
"sample",
|
||||
"test",
|
||||
"tests",
|
||||
"ngdoc_assets",
|
||||
"Gruntfile.js",
|
||||
"files.js"
|
||||
],
|
||||
"homepage": "https://github.com/angular-ui/angular-ui-router-bower",
|
||||
"_release": "0.3.1",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "0.3.1",
|
||||
"commit": "6ac1c61991121f5324f99089003314bba3bc6a95"
|
||||
},
|
||||
"_source": "https://github.com/angular-ui/angular-ui-router-bower.git",
|
||||
"_target": "0.3.1",
|
||||
"_originalSource": "angular-ui-router"
|
||||
}
|
|
@ -1,363 +0,0 @@
|
|||
<a name="0.3.1"></a>
|
||||
### 0.3.1 (2016-06-03)
|
||||
|
||||
# obscure BC-BREAK
|
||||
|
||||
If you're using `element.data('$uiView').$animEnter`, switch to `element.data('$uiViewAnim').$animEnter`
|
||||
This was necessary in order to fix #2763
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **state:**
|
||||
* fire $stateChangeError if onEnter/onExit throws. closes #2772 ([a5756c38](https://github.com/angular-ui/ui-router/commit/a5756c38a282bd2556ed5faaf870d6e493722d1b))
|
||||
* fail transition on exceptions in transition handler ([8222fb0e](https://github.com/angular-ui/ui-router/commit/8222fb0e7fd5eaaf6382f36db9ee9077a7bdbc6d))
|
||||
* **uiView:** separate $uiView and $uiViewAnim element.data() ([d3502f3c](https://github.com/angular-ui/ui-router/commit/d3502f3c0cb6a63f4b80aac91428f748b6460396), closes [#2763](https://github.com/angular-ui/ui-router/issues/2763))
|
||||
* Fixes this error: `Cannot read property 'name' of undefined at getUiViewName`
|
||||
|
||||
|
||||
<a name="0.3.0"></a>
|
||||
## 0.3.0 (2016-05-14)
|
||||
|
||||
This is a release of the `legacy` branch, primarily to fix the $scope destroy ordering issues introduced in 0.2.16.
|
||||
|
||||
We recommend all users to try the 1.0.0 alpha and report any issues it causes with your application. Read the [known breaking changes](https://github.com/angular-ui/ui-router/issues/2219) between the legacy and 1.0 branches.
|
||||
|
||||
# BC-BREAK
|
||||
|
||||
In 0.2.16 we delayed the ui-view $scope destroy() until after all animations were completed. This was a mistake, and we're reverting it in 0.3.0.
|
||||
|
||||
The original issue that we tried to address: https://github.com/angular-ui/ui-router/issues/1643
|
||||
|
||||
We are switching back to 0.2.15 behavior. The scope is now destroyed as soon as the view is swapped out. This allows cleanup to happen in response
|
||||
to the $destroy event. If you need to do things after the animation, we've put the promise on the `element.data('$uiView')` in #2562
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **state:** Inject $state at runtime to force initialization ([de3a04a7](https://github.com/angular-ui/ui-router/commit/de3a04a7c676e05b5b868de4f65d03d9c588773c), closes [#2574](https://github.com/angular-ui/ui-router/issues/2574))
|
||||
* **ui-sref:** update ui-sref-active/eq info when params change When ui-state dynamicly changes ([9698ec4d](https://github.com/angular-ui/ui-router/commit/9698ec4d2fbceb463cf11e43b7e74e385eda4beb), closes [#2554](https://github.com/angular-ui/ui-router/issues/2554))
|
||||
* **ui-state:** update ui-sref-active/eq info when ui-state dynamicly changes watchers, make sur ([abb3deba](https://github.com/angular-ui/ui-router/commit/abb3debacb87e1a6c398a13f2d64c53e8b08a233), closes [#2488](https://github.com/angular-ui/ui-router/issues/2488))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **uiView:**
|
||||
* Fire the $onInit hook ([b090ca03](https://github.com/angular-ui/ui-router/commit/b090ca0352eabc13662a8702a2b227b7db606362), closes [#2559](https://github.com/angular-ui/ui-router/issues/2559))
|
||||
* Put $animate promises on element.data('$uiView') closes #2562 closes #2579 ([fde64e18](https://github.com/angular-ui/ui-router/commit/fde64e1897041e59cbc9f8d07b269dcd487abb9c))
|
||||
|
||||
|
||||
<a name="0.2.18"></a>
|
||||
### 0.2.18 (2016-02-07)
|
||||
|
||||
This is a maintenance release which fixes a few known bugs introduced in 0.2.16.
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$urlRouter:** revert BC: resolve clashing of routes This reverts commit b5c57c8ec2e14e17e75104 ([2f1ebefc](https://github.com/angular-ui/ui-router/commit/2f1ebefc242ff48960e0bf63da359296a38f6852), closes [#2501](https://github.com/angular-ui/ui-router/issues/2501))
|
||||
* **uiState:** Corrected typo for 'ref' variable (#2488, #2508) ([b8f3c144](https://github.com/angular-ui/ui-router/commit/b8f3c144b913e620f177b78f3b4f52afa61d41a6))
|
||||
* **$urlMatcherFactory:** Fix to make the YUI Javascript compressor work ([ad9c41d2](https://github.com/angular-ui/ui-router/commit/ad9c41d2e723d50e30dd3452fbd274b7057dc3d9))
|
||||
* **stateBuilder:** fix non-url params on a state without a url. The parameters are now applied when ([d6d8c332](https://github.com/angular-ui/ui-router/commit/d6d8c3322c4dde8bb5b8dde25f9fcda49e9c4c81), closes [#2025](https://github.com/angular-ui/ui-router/issues/2025))
|
||||
* **ui-view:** (ui-view) use static renderer when no animation is present for a ui-view ([2523bbdb](https://github.com/angular-ui/ui-router/commit/2523bbdb5542483a489c22804f1751b8b9f71703), closes [#2485](https://github.com/angular-ui/ui-router/issues/2485)). This allows a ui-view scope to be destroyed when switching states, before the next view is initialized.
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **ui-view:** Add noanimation attribute to specify static renderer. ([2523bbdb](https://github.com/angular-ui/ui-router/commit/2523bbdb5542483a489c22804f1751b8b9f71703), closes [#2485](https://github.com/angular-ui/ui-router/issues/2485)). This allows a ui-view scope to be destroyed before the next ui-view is initialized, when ui-view animation is not present.
|
||||
|
||||
|
||||
<a name="0.2.17"></a>
|
||||
### 0.2.17 (2016-01-25)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **uiSrefActive:** allow multiple classes ([a89114a0](https://github.com/angular-ui/ui-router/commit/a89114a083813c1a7280c48fc18e626caa5a31f4), closes [#2481](https://github.com/angular-ui/ui-router/issues/2481), [#2482](https://github.com/angular-ui/ui-router/issues/2482))
|
||||
|
||||
|
||||
<a name="0.2.16"></a>
|
||||
### 0.2.16 (2016-01-24)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$state:**
|
||||
* statechangeCancel: Avoid infinite digest in .otherwise/redirect case. Don't clobber url if a new transition has started. Closes #222 ([e00aa695](https://github.com/angular-ui/ui-router/commit/e00aa695e41ddc5ebd5d2b226aa0917a751b11aa), closes [#2238](https://github.com/angular-ui/ui-router/issues/2238))
|
||||
* transitionTo: Allow hash (#) value to be read as toParams['#'] in events. Re-add the saved hash before broadcasting $stateChangeStart event. ([8c1bf30d](https://github.com/angular-ui/ui-router/commit/8c1bf30d2a3b78ba40b330f12d854c885d6cc117))
|
||||
* **$stateParams:** Fix for testing: reset service instance between tests ([2aeb0c4b](https://github.com/angular-ui/ui-router/commit/2aeb0c4b205baf6cfa2ef25bb986bb160dc13bf9))
|
||||
* **$urlRouter:**
|
||||
* Sort URL rules by specificity. Potential minor BC if apps were relying on rule registration order. ([b5c57c8e](https://github.com/angular-ui/ui-router/commit/b5c57c8ec2e14e17e75104c1424654f126ea4011))
|
||||
* Use $sniffer for pushstate compat check ([c219e801](https://github.com/angular-ui/ui-router/commit/c219e801797f340ef9c5c919ab890ef003a7a042))
|
||||
* **UrlMatcher:**
|
||||
* Properly encode/decode slashes in parameters Closes #2172 Closes #2250 Closes #1 ([02e98660](https://github.com/angular-ui/ui-router/commit/02e98660a80dfd1ca4b113dd24ee304af91e9f8c), closes [#2339](https://github.com/angular-ui/ui-router/issues/2339))
|
||||
* Array types: Fix default value for array query parameters. Pass empty arrays through in handler. ([20d6e243](https://github.com/angular-ui/ui-router/commit/20d6e243f1745ddbf257217245a1dc22eabe13da), closes [#2222](https://github.com/angular-ui/ui-router/issues/2222))
|
||||
* Remove trailing slash, if parameter is optional and was squashed from URL ([77fa11bf](https://github.com/angular-ui/ui-router/commit/77fa11bf0787d0f6da97ab0003ab29afb7411391), closes [#1902](https://github.com/angular-ui/ui-router/issues/1902))
|
||||
* Allow a parameter declaration to configure the parameter type by name. closes #2294 ([e4010249](https://github.com/angular-ui/ui-router/commit/e40102492d40fe1cf6ba14d955fcc9f345c16458))
|
||||
* include the slash when recognizing squashed params in url ([b5130bb1](https://github.com/angular-ui/ui-router/commit/b5130bb1215e15f832ea6daa670410b9a950c0d4), closes [#2064](https://github.com/angular-ui/ui-router/issues/2064))
|
||||
* Allow url query param names to contain periods ([d31b3337](https://github.com/angular-ui/ui-router/commit/d31b3337cc2ce71d87c92fdded629e46558d0b49))
|
||||
* **reloadOnSearch:** Update `locals.globals.$stateParams` when reloadOnSearch=false ([350d3e87](https://github.com/angular-ui/ui-router/commit/350d3e87783a2263fd7d23913da34f1268c3300b), closes [#2356](https://github.com/angular-ui/ui-router/issues/2356))
|
||||
* **ui-view:**
|
||||
* fix $animate usage for ng 1.4+ ([9b6d9a2d](https://github.com/angular-ui/ui-router/commit/9b6d9a2d0ce4ae08384165cb517bddea59b67892))
|
||||
* change $viewContentLoading to pair with $viewContentLoaded ([f9b43d66](https://github.com/angular-ui/ui-router/commit/f9b43d66833f0e17de41fd8d1cc3b491e3ba4a0e), closes [#685](https://github.com/angular-ui/ui-router/issues/685))
|
||||
* $destroy event is triggered before animation ends ([1be13795](https://github.com/angular-ui/ui-router/commit/1be13795686ab78abb2d5094bc8addcacb928975))
|
||||
* **uiSref:**
|
||||
* Ensure URL once param checks pass ([9dc31c54](https://github.com/angular-ui/ui-router/commit/9dc31c5465328e5666468b0c2319ce205f4b72f8), closes [#2091](https://github.com/angular-ui/ui-router/issues/2091))
|
||||
* uiSrefActive: update the active classes after linking directive ([7c914030](https://github.com/angular-ui/ui-router/commit/7c914030f13e05e45a941c1b723cb785db729890))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$IncludedByStateFilter:** add parameters to $IncludedByStateFilter ([963f6e71](https://github.com/angular-ui/ui-router/commit/963f6e71633b9c3a266f3991d79089b7d14786b4), closes [#1735](https://github.com/angular-ui/ui-router/issues/1735))
|
||||
* **isStateFilter:** Include optional state params. ([71d74699](https://github.com/angular-ui/ui-router/commit/71d7469987ee9ca86a41c8c6393ccd5d8913c3d6))
|
||||
* **$state:** make state data inheritance prototypical ([c4fec8c7](https://github.com/angular-ui/ui-router/commit/c4fec8c7998113902af4152d716c42dada6eb465))
|
||||
* **$stateChangeStart:** Add options to event ([a1f07559](https://github.com/angular-ui/ui-router/commit/a1f07559ec74e10ff80bc4be81f287e3772b8fcb))
|
||||
* **UrlMatcher:** Add param only type names ([6a371f9b](https://github.com/angular-ui/ui-router/commit/6a371f9b70e37a82eb324122879e4473c3f6d526))
|
||||
* **uiSrefActive:**
|
||||
* provide a ng-{class,style} like interface ([a9ff6feb](https://github.com/angular-ui/ui-router/commit/a9ff6febb469e0d5cd49054216c4472df7a6259d))
|
||||
* allow active & active-eq on same element ([d9a676ba](https://github.com/angular-ui/ui-router/commit/d9a676ba2c4d9e954be224c60496bcb38f6074e3))
|
||||
* **uiState:** add ui-state directive ([3831af1d](https://github.com/angular-ui/ui-router/commit/3831af1dc71b601351e6694af0665a77297f8f7f), closes [#395](https://github.com/angular-ui/ui-router/issues/395), [#900](https://github.com/angular-ui/ui-router/issues/900), [#1932](https://github.com/angular-ui/ui-router/issues/1932))
|
||||
* **urlMatcher:** add support for optional spaces in params ([4b7f3046](https://github.com/angular-ui/ui-router/commit/4b7f304617f0b3590b532103b5c2fb526c98a9e4))
|
||||
|
||||
|
||||
<a name="0.2.15"></a>
|
||||
### 0.2.15 (2015-05-19)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$state:** reloadOnSearch should not affect non-search param changes. ([6ca0d770](https://github.com/angular-ui/ui-router/commit/6ca0d7704cf7de9c6e6b7bb64df2f9c68fe081cc), closes [#1079](https://github.com/angular-ui/ui-router/issues/1079))
|
||||
* **urlMatcherFactory:** Revert to 0.2.13 behavior where all string parameters are considered optional fi ([495a02c3](https://github.com/angular-ui/ui-router/commit/495a02c3cbde501c1c149bce137806669209bc29), closes [#1963](https://github.com/angular-ui/ui-router/issues/1963))
|
||||
* **urlRouter:** allow .when() to redirect, even after a successful $state.go() - This partially ([48aeaff6](https://github.com/angular-ui/ui-router/commit/48aeaff645baf3f42f5a8940ebd97563791ad9f8), closes [#1584](https://github.com/angular-ui/ui-router/issues/1584))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$state:** Inject templateProvider with resolved values ([afa20f22](https://github.com/angular-ui/ui-router/commit/afa20f22373b7176b26daa7e1099750c4254a354))
|
||||
|
||||
|
||||
<a name="0.2.14"></a>
|
||||
### 0.2.14 (2015-04-23)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$StateRefDirective:** resolve missing support for svg anchor elements #1667 ([0149a7bb](https://github.com/angular-ui/ui-router/commit/0149a7bb38b7af99388a1ad7cc9909a7b7c4439d))
|
||||
* **$urlMatcherFactory:**
|
||||
* regex params should respect case-sensitivity ([1e10519f](https://github.com/angular-ui/ui-router/commit/1e10519f3be6bbf0cefdcce623cd2ade06e649e5), closes [#1671](https://github.com/angular-ui/ui-router/issues/1671))
|
||||
* unquote all dashes from array params ([06664d33](https://github.com/angular-ui/ui-router/commit/06664d330f882390655dcfa83e10276110d0d0fa))
|
||||
* add Type.$normalize function ([b0c6aa23](https://github.com/angular-ui/ui-router/commit/b0c6aa2350fdd3ce8483144774adc12f5a72b7e9))
|
||||
* make optional params regex grouping optional ([06f73794](https://github.com/angular-ui/ui-router/commit/06f737945e83e668d09cfc3bcffd04a500ff1963), closes [#1576](https://github.com/angular-ui/ui-router/issues/1576))
|
||||
* **$state:** allow about.*.** glob patterns ([e39b27a2](https://github.com/angular-ui/ui-router/commit/e39b27a2cb7d88525c446a041f9fbf1553202010))
|
||||
* **uiSref:**
|
||||
* use Object's toString instead of Window's toString ([2aa7f4d1](https://github.com/angular-ui/ui-router/commit/2aa7f4d139dbd5b9fcc4afdcf2ab6642c87f5671))
|
||||
* add absolute to allowed transition options ([ae1b3c4e](https://github.com/angular-ui/ui-router/commit/ae1b3c4eedc37983400d830895afb50457c63af4))
|
||||
* **uiSrefActive:** Apply active classes on lazy loaded states ([f0ddbe7b](https://github.com/angular-ui/ui-router/commit/f0ddbe7b4a91daf279c3b7d0cee732bb1f3be5b4))
|
||||
* **uiView:** add `$element` to locals for view controller ([db68914c](https://github.com/angular-ui/ui-router/commit/db68914cd6c821e7dec8155bd33142a3a97f5453))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$state:**
|
||||
* support URLs with #fragments ([3da0a170](https://github.com/angular-ui/ui-router/commit/3da0a17069e27598c0f9d9164e104dd5ce05cdc6))
|
||||
* inject resolve params into controllerProvider ([b380c223](https://github.com/angular-ui/ui-router/commit/b380c223fe12e2fde7582c0d6b1ed7b15a23579b), closes [#1131](https://github.com/angular-ui/ui-router/issues/1131))
|
||||
* added 'state' to state reload method (feat no.1612) - modiefied options.reload ([b8f04575](https://github.com/angular-ui/ui-router/commit/b8f04575a8557035c1858c4d5c8dbde3e1855aaa))
|
||||
* broadcast $stateChangeCancel event when event.preventDefault() is called in $sta ([ecefb758](https://github.com/angular-ui/ui-router/commit/ecefb758cb445e41620b62a272aafa3638613d7a))
|
||||
* **$uiViewScroll:** change function to return promise ([c2a9a311](https://github.com/angular-ui/ui-router/commit/c2a9a311388bb212e5a2e820536d1d739f829ccd), closes [#1702](https://github.com/angular-ui/ui-router/issues/1702))
|
||||
* **uiSrefActive:** Added support for multiple nested uiSref directives ([b1844948](https://github.com/angular-ui/ui-router/commit/b18449481d152b50705abfce2493a444eb059fa5))
|
||||
|
||||
|
||||
<a name="0.2.13"></a>
|
||||
### 0.2.13 (2014-11-20)
|
||||
|
||||
This release primarily fixes issues reported against 0.2.12
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$state:** fix $state.includes/.is to apply param types before comparisions fix(uiSref): ma ([19715d15](https://github.com/angular-ui/ui-router/commit/19715d15e3cbfff724519e9febedd05b49c75baa), closes [#1513](https://github.com/angular-ui/ui-router/issues/1513))
|
||||
* Avoid re-synchronizing from url after .transitionTo ([b267ecd3](https://github.com/angular-ui/ui-router/commit/b267ecd348e5c415233573ef95ebdbd051875f52), closes [#1573](https://github.com/angular-ui/ui-router/issues/1573))
|
||||
* **$urlMatcherFactory:**
|
||||
* Built-in date type uses local time zone ([d726bedc](https://github.com/angular-ui/ui-router/commit/d726bedcbb5f70a5660addf43fd52ec730790293))
|
||||
* make date type fn check .is before running ([aa94ce3b](https://github.com/angular-ui/ui-router/commit/aa94ce3b86632ad05301530a2213099da73a3dc0), closes [#1564](https://github.com/angular-ui/ui-router/issues/1564))
|
||||
* early binding of array handler bypasses type resolution ([ada4bc27](https://github.com/angular-ui/ui-router/commit/ada4bc27df5eff3ba3ab0de94a09bd91b0f7a28c))
|
||||
* add 'any' Type for non-encoding non-url params ([3bfd75ab](https://github.com/angular-ui/ui-router/commit/3bfd75ab445ee2f1dd55275465059ed116b10b27), closes [#1562](https://github.com/angular-ui/ui-router/issues/1562))
|
||||
* fix encoding slashes in params ([0c983a08](https://github.com/angular-ui/ui-router/commit/0c983a08e2947f999683571477debd73038e95cf), closes [#1119](https://github.com/angular-ui/ui-router/issues/1119))
|
||||
* fix mixed path/query params ordering problem ([a479fbd0](https://github.com/angular-ui/ui-router/commit/a479fbd0b8eb393a94320973e5b9a62d83912ee2), closes [#1543](https://github.com/angular-ui/ui-router/issues/1543))
|
||||
* **ArrayType:**
|
||||
* specify empty array mapping corner case ([74aa6091](https://github.com/angular-ui/ui-router/commit/74aa60917e996b0b4e27bbb4eb88c3c03832021d), closes [#1511](https://github.com/angular-ui/ui-router/issues/1511))
|
||||
* fix .equals for array types ([5e6783b7](https://github.com/angular-ui/ui-router/commit/5e6783b77af9a90ddff154f990b43dbb17eeda6e), closes [#1538](https://github.com/angular-ui/ui-router/issues/1538))
|
||||
* **Param:** fix default value shorthand declaration ([831d812a](https://github.com/angular-ui/ui-router/commit/831d812a524524c71f0ee1c9afaf0487a5a66230), closes [#1554](https://github.com/angular-ui/ui-router/issues/1554))
|
||||
* **common:** fixed the _.filter clone to not create sparse arrays ([750f5cf5](https://github.com/angular-ui/ui-router/commit/750f5cf5fd91f9ada96f39e50d39aceb2caf22b6), closes [#1563](https://github.com/angular-ui/ui-router/issues/1563))
|
||||
* **ie8:** fix calls to indexOf and filter ([dcb31b84](https://github.com/angular-ui/ui-router/commit/dcb31b843391b3e61dee4de13f368c109541813e), closes [#1556](https://github.com/angular-ui/ui-router/issues/1556))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* add json parameter Type ([027f1fcf](https://github.com/angular-ui/ui-router/commit/027f1fcf9c0916cea651e88981345da6f9ff214a))
|
||||
|
||||
|
||||
<a name="0.2.12"></a>
|
||||
### 0.2.12 (2014-11-13)
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$resolve:** use resolve fn result, not parent resolved value of same name ([67f5e00c](https://github.com/angular-ui/ui-router/commit/67f5e00cc9aa006ce3fe6cde9dff261c28eab70a), closes [#1317], [#1353])
|
||||
* **$state:**
|
||||
* populate default params in .transitionTo. ([3f60fbe6](https://github.com/angular-ui/ui-router/commit/3f60fbe6d65ebeca8d97952c05aa1d269f1b7ba1), closes [#1396])
|
||||
* reload() now reinvokes controllers ([73443420](https://github.com/angular-ui/ui-router/commit/7344342018847902594dc1fc62d30a5c30f01763), closes [#582])
|
||||
* do not emit $viewContentLoading if notify: false ([74255feb](https://github.com/angular-ui/ui-router/commit/74255febdf48ae082a02ca1e735165f2c369a463), closes [#1387](https://github.com/angular-ui/ui-router/issues/1387))
|
||||
* register states at config-time ([4533fe36](https://github.com/angular-ui/ui-router/commit/4533fe36e0ab2f0143edd854a4145deaa013915a))
|
||||
* handle parent.name when parent is obj ([4533fe36](https://github.com/angular-ui/ui-router/commit/4533fe36e0ab2f0143edd854a4145deaa013915a))
|
||||
* **$urlMatcherFactory:**
|
||||
* register types at config ([4533fe36](https://github.com/angular-ui/ui-router/commit/4533fe36e0ab2f0143edd854a4145deaa013915a), closes [#1476])
|
||||
* made path params default value "" for backwards compat ([8f998e71](https://github.com/angular-ui/ui-router/commit/8f998e71e43a0b31293331c981f5db0f0097b8ba))
|
||||
* Pre-replace certain param values for better mapping ([6374a3e2](https://github.com/angular-ui/ui-router/commit/6374a3e29ab932014a7c77d2e1ab884cc841a2e3))
|
||||
* fixed ParamSet.$$keys() ordering ([9136fecb](https://github.com/angular-ui/ui-router/commit/9136fecbc2bfd4fda748a9914f0225a46c933860))
|
||||
* empty string policy now respected in Param.value() ([db12c85c](https://github.com/angular-ui/ui-router/commit/db12c85c16f2d105415f9bbbdeb11863f64728e0))
|
||||
* "string" type now encodes/decodes slashes ([3045e415](https://github.com/angular-ui/ui-router/commit/3045e41577a8b8b8afc6039f42adddf5f3c061ec), closes [#1119])
|
||||
* allow arrays in both path and query params ([fdd2f2c1](https://github.com/angular-ui/ui-router/commit/fdd2f2c191c4a67c874fdb9ec9a34f8dde9ad180), closes [#1073], [#1045], [#1486], [#1394])
|
||||
* typed params in search ([8d4cab69](https://github.com/angular-ui/ui-router/commit/8d4cab69dd67058e1a716892cc37b7d80a57037f), closes [#1488](https://github.com/angular-ui/ui-router/issues/1488))
|
||||
* no longer generate unroutable urls ([cb9fd9d8](https://github.com/angular-ui/ui-router/commit/cb9fd9d8943cb26c7223f6990db29c82ae8740f8), closes [#1487](https://github.com/angular-ui/ui-router/issues/1487))
|
||||
* handle optional parameter followed by required parameter in url format. ([efc72106](https://github.com/angular-ui/ui-router/commit/efc72106ddcc4774b48ea176a505ef9e95193b41))
|
||||
* default to parameter string coersion. ([13a468a7](https://github.com/angular-ui/ui-router/commit/13a468a7d54c2fb0751b94c0c1841d580b71e6dc), closes [#1414](https://github.com/angular-ui/ui-router/issues/1414))
|
||||
* concat respects strictMode/caseInsensitive ([dd72e103](https://github.com/angular-ui/ui-router/commit/dd72e103edb342d9cf802816fe127e1bbd68fd5f), closes [#1395])
|
||||
* **ui-sref:**
|
||||
* Allow sref state options to take a scope object ([b5f7b596](https://github.com/angular-ui/ui-router/commit/b5f7b59692ce4933e2d63eb5df3f50a4ba68ccc0))
|
||||
* replace raw href modification with attrs. ([08c96782](https://github.com/angular-ui/ui-router/commit/08c96782faf881b0c7ab00afc233ee6729548fa0))
|
||||
* nagivate to state when url is "" fix($state.href): generate href for state with ([656b5aab](https://github.com/angular-ui/ui-router/commit/656b5aab906e5749db9b5a080c6a83b95f50fd91), closes [#1363](https://github.com/angular-ui/ui-router/issues/1363))
|
||||
* Check that state is defined in isMatch() ([92aebc75](https://github.com/angular-ui/ui-router/commit/92aebc7520f88babdc6e266536086e07263514c3), closes [#1314](https://github.com/angular-ui/ui-router/issues/1314), [#1332](https://github.com/angular-ui/ui-router/issues/1332))
|
||||
* **uiView:**
|
||||
* allow inteprolated ui-view names ([81f6a19a](https://github.com/angular-ui/ui-router/commit/81f6a19a432dac9198fd33243855bfd3b4fea8c0), closes [#1324](https://github.com/angular-ui/ui-router/issues/1324))
|
||||
* Made anim work with angular 1.3 ([c3bb7ad9](https://github.com/angular-ui/ui-router/commit/c3bb7ad903da1e1f3c91019cfd255be8489ff4ef), closes [#1367](https://github.com/angular-ui/ui-router/issues/1367), [#1345](https://github.com/angular-ui/ui-router/issues/1345))
|
||||
* **urlRouter:** html5Mode accepts an object from angular v1.3.0-rc.3 ([7fea1e9d](https://github.com/angular-ui/ui-router/commit/7fea1e9d0d8c6e09cc6c895ecb93d4221e9adf48))
|
||||
* **stateFilters:** mark state filters as stateful. ([a00b353e](https://github.com/angular-ui/ui-router/commit/a00b353e3036f64a81245c4e7898646ba218f833), closes [#1479])
|
||||
* **ui-router:** re-add IE8 compatibility for map/filter/keys ([8ce69d9f](https://github.com/angular-ui/ui-router/commit/8ce69d9f7c886888ab53eca7e53536f36b428aae), closes [#1518], [#1383])
|
||||
* **package:** point 'main' to a valid filename ([ac903350](https://github.com/angular-ui/ui-router/commit/ac9033501debb63364539d91fbf3a0cba4579f8e))
|
||||
* **travis:** make CI build faster ([0531de05](https://github.com/angular-ui/ui-router/commit/0531de052e414a8d839fbb4e7635e923e94865b3))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
##### Default and Typed params
|
||||
|
||||
This release includes a lot of bug fixes around default/optional and typed parameters. As such, 0.2.12 is the first release where we recommend those features be used.
|
||||
|
||||
* **$state:**
|
||||
* add state params validation ([b1379e6a](https://github.com/angular-ui/ui-router/commit/b1379e6a4d38f7ed7436e05873932d7c279af578), closes [#1433](https://github.com/angular-ui/ui-router/issues/1433))
|
||||
* is/includes/get work on relative stateOrName ([232e94b3](https://github.com/angular-ui/ui-router/commit/232e94b3c2ca2c764bb9510046e4b61690c87852))
|
||||
* .reload() returns state transition promise ([639e0565](https://github.com/angular-ui/ui-router/commit/639e0565dece9d5544cc93b3eee6e11c99bd7373))
|
||||
* **$templateFactory:** request templateURL as text/html ([ccd60769](https://github.com/angular-ui/ui-router/commit/ccd6076904a4b801d77b47f6e2de4c06ce9962f8), closes [#1287])
|
||||
* **$urlMatcherFactory:** Made a Params and ParamSet class ([0cc1e6cc](https://github.com/angular-ui/ui-router/commit/0cc1e6cc461a4640618e2bb594566551c54834e2))
|
||||
|
||||
|
||||
|
||||
<a name="0.2.11"></a>
|
||||
### 0.2.11 (2014-08-26)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$resolve:** Resolves only inherit from immediate parent fixes #702 ([df34e20c](https://github.com/angular-ui/ui-router/commit/df34e20c576299e7a3c8bd4ebc68d42341c0ace9))
|
||||
* **$state:**
|
||||
* change $state.href default options.inherit to true ([deea695f](https://github.com/angular-ui/ui-router/commit/deea695f5cacc55de351ab985144fd233c02a769))
|
||||
* sanity-check state lookups ([456fd5ae](https://github.com/angular-ui/ui-router/commit/456fd5aec9ea507518927bfabd62b4afad4cf714), closes [#980](https://github.com/angular-ui/ui-router/issues/980))
|
||||
* didn't comply to inherit parameter ([09836781](https://github.com/angular-ui/ui-router/commit/09836781f126c1c485b06551eb9cfd4fa0f45c35))
|
||||
* allow view content loading broadcast ([7b78edee](https://github.com/angular-ui/ui-router/commit/7b78edeeb52a74abf4d3f00f79534033d5a08d1a))
|
||||
* **$urlMatcherFactory:**
|
||||
* detect injected functions ([91f75ae6](https://github.com/angular-ui/ui-router/commit/91f75ae66c4d129f6f69e53bd547594e9661f5d5))
|
||||
* syntax ([1ebed370](https://github.com/angular-ui/ui-router/commit/1ebed37069bae8614d41541d56521f5c45f703f3))
|
||||
* **UrlMatcher:**
|
||||
* query param function defaults ([f9c20530](https://github.com/angular-ui/ui-router/commit/f9c205304f10d8a4ebe7efe9025e642016479a51))
|
||||
* don't decode default values ([63607bdb](https://github.com/angular-ui/ui-router/commit/63607bdbbcb432d3fb37856a1cb3da0cd496804e))
|
||||
* **travis:** update Node version to fix build ([d6b95ef2](https://github.com/angular-ui/ui-router/commit/d6b95ef23d9dacb4eba08897f5190a0bcddb3a48))
|
||||
* **uiSref:**
|
||||
* Generate an href for states with a blank url. closes #1293 ([691745b1](https://github.com/angular-ui/ui-router/commit/691745b12fa05d3700dd28f0c8d25f8a105074ad))
|
||||
* should inherit params by default ([b973dad1](https://github.com/angular-ui/ui-router/commit/b973dad155ad09a7975e1476bd096f7b2c758eeb))
|
||||
* cancel transition if preventDefault() has been called ([2e6d9167](https://github.com/angular-ui/ui-router/commit/2e6d9167d3afbfbca6427e53e012f94fb5fb8022))
|
||||
* **uiView:** Fixed infinite loop when is called .go() from a controller. ([e13988b8](https://github.com/angular-ui/ui-router/commit/e13988b8cd6231d75c78876ee9d012cc87f4a8d9), closes [#1194](https://github.com/angular-ui/ui-router/issues/1194))
|
||||
* **docs:**
|
||||
* Fixed link to milestones ([6c0ae500](https://github.com/angular-ui/ui-router/commit/6c0ae500cc238ea9fc95adcc15415c55fc9e1f33))
|
||||
* fix bug in decorator example ([4bd00af5](https://github.com/angular-ui/ui-router/commit/4bd00af50b8b88a49d1545a76290731cb8e0feb1))
|
||||
* Removed an incorrect semi-colon ([af97cef8](https://github.com/angular-ui/ui-router/commit/af97cef8b967f2e32177e539ef41450dca131a7d))
|
||||
* Explain return value of rule as function ([5e887890](https://github.com/angular-ui/ui-router/commit/5e8878900a6ffe59a81aed531a3925e34a297377))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$state:**
|
||||
* allow parameters to pass unharmed ([8939d057](https://github.com/angular-ui/ui-router/commit/8939d0572ab1316e458ef016317ecff53131a822))
|
||||
* **BREAKING CHANGE**: state parameters are no longer automatically coerced to strings, and unspecified parameter values are now set to undefined rather than null.
|
||||
* allow prevent syncUrl on failure ([753060b9](https://github.com/angular-ui/ui-router/commit/753060b910d5d2da600a6fa0757976e401c33172))
|
||||
* **typescript:** Add typescript definitions for component builds ([521ceb3f](https://github.com/angular-ui/ui-router/commit/521ceb3fd7850646422f411921e21ce5e7d82e0f))
|
||||
* **uiSref:** extend syntax for ui-sref ([71cad3d6](https://github.com/angular-ui/ui-router/commit/71cad3d636508b5a9fe004775ad1f1adc0c80c3e))
|
||||
* **uiSrefActive:**
|
||||
* Also activate for child states. ([bf163ad6](https://github.com/angular-ui/ui-router/commit/bf163ad6ce176ce28792696c8302d7cdf5c05a01), closes [#818](https://github.com/angular-ui/ui-router/issues/818))
|
||||
* **BREAKING CHANGE** Since ui-sref-active now activates even when child states are active you may need to swap out your ui-sref-active with ui-sref-active-eq, thought typically we think devs want the auto inheritance.
|
||||
|
||||
* uiSrefActiveEq: new directive with old ui-sref-active behavior
|
||||
* **$urlRouter:**
|
||||
* defer URL change interception ([c72d8ce1](https://github.com/angular-ui/ui-router/commit/c72d8ce11916d0ac22c81b409c9e61d7048554d7))
|
||||
* force URLs to have valid params ([d48505cd](https://github.com/angular-ui/ui-router/commit/d48505cd328d83e39d5706e085ba319715f999a6))
|
||||
* abstract $location handling ([08b4636b](https://github.com/angular-ui/ui-router/commit/08b4636b294611f08db35f00641eb5211686fb50))
|
||||
* **$urlMatcherFactory:**
|
||||
* fail on bad parameters ([d8f124c1](https://github.com/angular-ui/ui-router/commit/d8f124c10d00c7e5dde88c602d966db261aea221))
|
||||
* date type support ([b7f074ff](https://github.com/angular-ui/ui-router/commit/b7f074ff65ca150a3cdbda4d5ad6cb17107300eb))
|
||||
* implement type support ([450b1f0e](https://github.com/angular-ui/ui-router/commit/450b1f0e8e03c738174ff967f688b9a6373290f4))
|
||||
* **UrlMatcher:**
|
||||
* handle query string arrays ([9cf764ef](https://github.com/angular-ui/ui-router/commit/9cf764efab45fa9309368688d535ddf6e96d6449), closes [#373](https://github.com/angular-ui/ui-router/issues/373))
|
||||
* injectable functions as defaults ([00966ecd](https://github.com/angular-ui/ui-router/commit/00966ecd91fb745846039160cab707bfca8b3bec))
|
||||
* default values & type decoding for query params ([a472b301](https://github.com/angular-ui/ui-router/commit/a472b301389fbe84d1c1fa9f24852b492a569d11))
|
||||
* allow shorthand definitions ([5b724304](https://github.com/angular-ui/ui-router/commit/5b7243049793505e44b6608ea09878c37c95b1f5))
|
||||
* validates whole interface ([32b27db1](https://github.com/angular-ui/ui-router/commit/32b27db173722e9194ef1d5c0ea7d93f25a98d11))
|
||||
* implement non-strict matching ([a3e21366](https://github.com/angular-ui/ui-router/commit/a3e21366bee0475c9795a1ec76f70eec41c5b4e3))
|
||||
* add per-param config support ([07b3029f](https://github.com/angular-ui/ui-router/commit/07b3029f4d409cf955780113df92e36401b47580))
|
||||
* **BREAKING CHANGE**: the `params` option in state configurations must now be an object keyed by parameter name.
|
||||
|
||||
### 0.2.10 (2014-03-12)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$state:** use $browser.baseHref() when generating urls with .href() ([cbcc8488](https://github.com/angular-ui/ui-router/commit/cbcc84887d6b6d35258adabb97c714cd9c1e272d))
|
||||
* **bower.json:** JS files should not be ignored ([ccdab193](https://github.com/angular-ui/ui-router/commit/ccdab193315f304eb3be5f5b97c47a926c79263e))
|
||||
* **dev:** karma:background task is missing, can't run grunt:dev. ([d9f7b898](https://github.com/angular-ui/ui-router/commit/d9f7b898e8e3abb8c846b0faa16a382913d7b22b))
|
||||
* **sample:** Contacts menu button not staying active when navigating to detail states. Need t ([2fcb8443](https://github.com/angular-ui/ui-router/commit/2fcb84437cb43ade12682a92b764f13cac77dfe7))
|
||||
* **uiSref:** support mock-clicks/events with no data ([717d3ff7](https://github.com/angular-ui/ui-router/commit/717d3ff7d0ba72d239892dee562b401cdf90e418))
|
||||
* **uiView:**
|
||||
* Do NOT autoscroll when autoscroll attr is missing ([affe5bd7](https://github.com/angular-ui/ui-router/commit/affe5bd785cdc3f02b7a9f64a52e3900386ec3a0), closes [#807](https://github.com/angular-ui/ui-router/issues/807))
|
||||
* Refactoring uiView directive to copy ngView logic ([548fab6a](https://github.com/angular-ui/ui-router/commit/548fab6ab9debc9904c5865c8bc68b4fc3271dd0), closes [#857](https://github.com/angular-ui/ui-router/issues/857), [#552](https://github.com/angular-ui/ui-router/issues/552))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **$state:** includes() allows glob patterns for state matching. ([2d5f6b37](https://github.com/angular-ui/ui-router/commit/2d5f6b37191a3135f4a6d9e8f344c54edcdc065b))
|
||||
* **UrlMatcher:** Add support for case insensitive url matching ([642d5247](https://github.com/angular-ui/ui-router/commit/642d524799f604811e680331002feec7199a1fb5))
|
||||
* **uiSref:** add support for transition options ([2ed7a728](https://github.com/angular-ui/ui-router/commit/2ed7a728cee6854b38501fbc1df6139d3de5b28a))
|
||||
* **uiView:** add controllerAs config with function ([1ee7334a](https://github.com/angular-ui/ui-router/commit/1ee7334a73efeccc9b95340e315cdfd59944762d))
|
||||
|
||||
|
||||
### 0.2.9 (2014-01-17)
|
||||
|
||||
|
||||
This release is identical to 0.2.8. 0.2.8 was re-tagged in git to fix a problem with bower.
|
||||
|
||||
|
||||
### 0.2.8 (2014-01-16)
|
||||
|
||||
|
||||
#### Bug Fixes
|
||||
|
||||
* **$state:** allow null to be passed as 'params' param ([094dc30e](https://github.com/angular-ui/ui-router/commit/094dc30e883e1bd14e50a475553bafeaade3b178))
|
||||
* **$state.go:** param inheritance shouldn't inherit from siblings ([aea872e0](https://github.com/angular-ui/ui-router/commit/aea872e0b983cb433436ce5875df10c838fccedb))
|
||||
* **bower.json:** fixes bower.json ([eed3cc4d](https://github.com/angular-ui/ui-router/commit/eed3cc4d4dfef1d3ef84b9fd063127538ebf59d3))
|
||||
* **uiSrefActive:** annotate controller injection ([85921422](https://github.com/angular-ui/ui-router/commit/85921422ff7fb0effed358136426d616cce3d583), closes [#671](https://github.com/angular-ui/ui-router/issues/671))
|
||||
* **uiView:**
|
||||
* autoscroll tests pass on 1.2.4 & 1.1.5 ([86eacac0](https://github.com/angular-ui/ui-router/commit/86eacac09ca5e9000bd3b9c7ba6e2cc95d883a3a))
|
||||
* don't animate initial load ([83b6634d](https://github.com/angular-ui/ui-router/commit/83b6634d27942ca74766b2b1244a7fc52c5643d9))
|
||||
* test pass against 1.0.8 and 1.2.4 ([a402415a](https://github.com/angular-ui/ui-router/commit/a402415a2a28b360c43b9fe8f4f54c540f6c33de))
|
||||
* it should autoscroll when expr is missing. ([8bb9e27a](https://github.com/angular-ui/ui-router/commit/8bb9e27a2986725f45daf44c4c9f846385095aff))
|
||||
|
||||
|
||||
#### Features
|
||||
|
||||
* **uiSref:** add target attribute behaviour ([c12bf9a5](https://github.com/angular-ui/ui-router/commit/c12bf9a520d30d70294e3d82de7661900f8e394e))
|
||||
* **uiView:**
|
||||
* merge autoscroll expression test. ([b89e0f87](https://github.com/angular-ui/ui-router/commit/b89e0f871d5cc35c10925ede986c10684d5c9252))
|
||||
* cache and test autoscroll expression ([ee262282](https://github.com/angular-ui/ui-router/commit/ee2622828c2ce83807f006a459ac4e11406d9258))
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
# Report an Issue
|
||||
|
||||
Help us make UI-Router better! If you think you might have found a bug, or some other weirdness, start by making sure
|
||||
it hasn't already been reported. You can [search through existing issues](https://github.com/angular-ui/ui-router/search?q=wat%3F&type=Issues)
|
||||
to see if someone's reported one similar to yours.
|
||||
|
||||
If not, then [create a plunkr](http://bit.ly/UIR-Plunk) that demonstrates the problem (try to use as little code
|
||||
as possible: the more minimalist, the faster we can debug it).
|
||||
|
||||
Next, [create a new issue](https://github.com/angular-ui/ui-router/issues/new) that briefly explains the problem,
|
||||
and provides a bit of background as to the circumstances that triggered it. Don't forget to include the link to
|
||||
that plunkr you created!
|
||||
|
||||
**Note**: If you're unsure how a feature is used, or are encountering some unexpected behavior that you aren't sure
|
||||
is a bug, it's best to talk it out on
|
||||
[StackOverflow](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router) before reporting it. This
|
||||
keeps development streamlined, and helps us focus on building great software.
|
||||
|
||||
|
||||
Issues only! |
|
||||
-------------|
|
||||
Please keep in mind that the issue tracker is for *issues*. Please do *not* post an issue if you need help or support. Instead, see one of the above-mentioned forums or [IRC](irc://irc.freenode.net/#angularjs). |
|
||||
|
||||
####Purple Labels
|
||||
A purple label means that **you** need to take some further action.
|
||||
- ![Not Actionable - Need Info](ngdoc_assets/incomplete.png): Your issue is not specific enough, or there is no clear action that we can take. Please clarify and refine your issue.
|
||||
- ![Plunkr Please](ngdoc_assets/example.png): Please [create a plunkr](http://bit.ly/UIR-Plunk)
|
||||
- ![StackOverflow](ngdoc_assets/so.png): We suspect your issue is really a help request, or could be answered by the community. Please ask your question on [StackOverflow](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router). If you determine that is an actual issue, please explain why.
|
||||
|
||||
If your issue gets labeled with purple label, no further action will be taken until you respond to the label appropriately.
|
||||
|
||||
# Contribute
|
||||
|
||||
**(1)** See the **[Developing](#developing)** section below, to get the development version of UI-Router up and running on your local machine.
|
||||
|
||||
**(2)** Check out the [roadmap](https://github.com/angular-ui/ui-router/milestones) to see where the project is headed, and if your feature idea fits with where we're headed.
|
||||
|
||||
**(3)** If you're not sure, [open an RFC](https://github.com/angular-ui/ui-router/issues/new?title=RFC:%20My%20idea) to get some feedback on your idea.
|
||||
|
||||
**(4)** Finally, commit some code and open a pull request. Code & commits should abide by the following rules:
|
||||
|
||||
- *Always* have test coverage for new features (or regression tests for bug fixes), and *never* break existing tests
|
||||
- Commits should represent one logical change each; if a feature goes through multiple iterations, squash your commits down to one
|
||||
- Make sure to follow the [Angular commit message format](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format) so your change will appear in the changelog of the next release.
|
||||
- Changes should always respect the coding style of the project
|
||||
|
||||
|
||||
|
||||
# Developing
|
||||
|
||||
UI-Router uses <code>grunt >= 0.4.x</code>. Make sure to upgrade your environment and read the
|
||||
[Migration Guide](http://gruntjs.com/upgrading-from-0.3-to-0.4).
|
||||
|
||||
Dependencies for building from source and running tests:
|
||||
|
||||
* [grunt-cli](https://github.com/gruntjs/grunt-cli) - run: `$ npm install -g grunt-cli`
|
||||
* Then, install the development dependencies by running `$ npm install` from the project directory
|
||||
|
||||
There are a number of targets in the gruntfile that are used to generating different builds:
|
||||
|
||||
* `grunt`: Perform a normal build, runs jshint and karma tests
|
||||
* `grunt build`: Perform a normal build
|
||||
* `grunt dist`: Perform a clean build and generate documentation
|
||||
* `grunt dev`: Run dev server (sample app) and watch for changes, builds and runs karma tests on changes.
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2013-2015 The AngularUI Team, Karsten Sperling
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,252 +0,0 @@
|
|||
# AngularUI Router [![Build Status](https://travis-ci.org/angular-ui/ui-router.svg?branch=master)](https://travis-ci.org/angular-ui/ui-router)
|
||||
|
||||
#### The de-facto solution to flexible routing with nested views
|
||||
---
|
||||
**[Download 0.2.18](https://cdn.rawgit.com/angular-ui/ui-router/0.2.18/release/angular-ui-router.js)** (or **[Minified](https://cdn.rawgit.com/angular-ui/ui-router/0.2.18/release/angular-ui-router.min.js)**) **|**
|
||||
**[Guide](https://github.com/angular-ui/ui-router/wiki) |**
|
||||
**[API](http://angular-ui.github.io/ui-router/site) |**
|
||||
**[Sample](http://angular-ui.github.com/ui-router/sample/) ([Src](https://github.com/angular-ui/ui-router/tree/gh-pages/sample)) |**
|
||||
**[FAQ](https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions) |**
|
||||
**[Resources](#resources) |**
|
||||
**[Report an Issue](https://github.com/angular-ui/ui-router/blob/master/CONTRIBUTING.md#report-an-issue) |**
|
||||
**[Contribute](https://github.com/angular-ui/ui-router/blob/master/CONTRIBUTING.md#contribute) |**
|
||||
**[Help!](http://stackoverflow.com/questions/ask?tags=angularjs,angular-ui-router) |**
|
||||
**[Discuss](https://groups.google.com/forum/#!categories/angular-ui/router)**
|
||||
|
||||
---
|
||||
|
||||
*_Please help us out by testing the 1.0 alpha release!_*
|
||||
|
||||
[1.0.0alpha0 Announcement](https://github.com/angular-ui/ui-router/releases/tag/1.0.0alpha0) ([Source Code](https://github.com/angular-ui/ui-router/tree/master)) | [Sample App](http://ui-router.github.io/sample-app/) ([Source Code](https://github.com/ui-router/sample-app)) | [Known Issues](https://github.com/angular-ui/ui-router/issues?q=is%3Aissue+is%3Aopen+label%3A1.0)
|
||||
|
||||
|
||||
---
|
||||
|
||||
AngularUI Router is a routing framework for [AngularJS](http://angularjs.org), which allows you to organize the
|
||||
parts of your interface into a [*state machine*](https://en.wikipedia.org/wiki/Finite-state_machine). Unlike the
|
||||
[`$route` service](http://docs.angularjs.org/api/ngRoute.$route) in the Angular ngRoute module, which is organized around URL
|
||||
routes, UI-Router is organized around [*states*](https://github.com/angular-ui/ui-router/wiki),
|
||||
which may optionally have routes, as well as other behavior, attached.
|
||||
|
||||
States are bound to *named*, *nested* and *parallel views*, allowing you to powerfully manage your application's interface.
|
||||
|
||||
Check out the sample app: http://angular-ui.github.io/ui-router/sample/
|
||||
|
||||
-
|
||||
**Note:** *UI-Router is under active development. As such, while this library is well-tested, the API may change. Consider using it in production applications only if you're comfortable following a changelog and updating your usage accordingly.*
|
||||
|
||||
|
||||
## Get Started
|
||||
|
||||
**(1)** Get UI-Router in one of the following ways:
|
||||
- clone & [build](CONTRIBUTING.md#developing) this repository
|
||||
- [download the release](http://angular-ui.github.io/ui-router/release/angular-ui-router.js) (or [minified](http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js))
|
||||
- [link to cdn](http://cdnjs.com/libraries/angular-ui-router)
|
||||
- via **[jspm](http://jspm.io/)**: by running `$ jspm install angular-ui-router` from your console
|
||||
- or via **[npm](https://www.npmjs.org/)**: by running `$ npm install angular-ui-router` from your console
|
||||
- or via **[Bower](http://bower.io/)**: by running `$ bower install angular-ui-router` from your console
|
||||
- or via **[Component](https://github.com/component/component)**: by running `$ component install angular-ui/ui-router` from your console
|
||||
|
||||
**(2)** Include `angular-ui-router.js` (or `angular-ui-router.min.js`) in your `index.html`, after including Angular itself (For Component users: ignore this step)
|
||||
|
||||
**(3)** Add `'ui.router'` to your main module's list of dependencies (For Component users: replace `'ui.router'` with `require('angular-ui-router')`)
|
||||
|
||||
When you're done, your setup should look similar to the following:
|
||||
|
||||
>
|
||||
```html
|
||||
<!doctype html>
|
||||
<html ng-app="myApp">
|
||||
<head>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
|
||||
<script src="js/angular-ui-router.min.js"></script>
|
||||
<script>
|
||||
var myApp = angular.module('myApp', ['ui.router']);
|
||||
// For Component users, it should look like this:
|
||||
// var myApp = angular.module('myApp', [require('angular-ui-router')]);
|
||||
</script>
|
||||
...
|
||||
</head>
|
||||
<body>
|
||||
...
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### [Nested States & Views](http://plnkr.co/edit/u18KQc?p=preview)
|
||||
|
||||
The majority of UI-Router's power is in its ability to nest states & views.
|
||||
|
||||
**(1)** First, follow the [setup](#get-started) instructions detailed above.
|
||||
|
||||
**(2)** Then, add a [`ui-view` directive](https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-view) to the `<body />` of your app.
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<body>
|
||||
<div ui-view></div>
|
||||
<!-- We'll also add some navigation: -->
|
||||
<a ui-sref="state1">State 1</a>
|
||||
<a ui-sref="state2">State 2</a>
|
||||
</body>
|
||||
```
|
||||
|
||||
**(3)** You'll notice we also added some links with [`ui-sref` directives](https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref). In addition to managing state transitions, this directive auto-generates the `href` attribute of the `<a />` element it's attached to, if the corresponding state has a URL. Next we'll add some templates. These will plug into the `ui-view` within `index.html`. Notice that they have their own `ui-view` as well! That is the key to nesting states and views.
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- partials/state1.html -->
|
||||
<h1>State 1</h1>
|
||||
<hr/>
|
||||
<a ui-sref="state1.list">Show List</a>
|
||||
<div ui-view></div>
|
||||
```
|
||||
```html
|
||||
<!-- partials/state2.html -->
|
||||
<h1>State 2</h1>
|
||||
<hr/>
|
||||
<a ui-sref="state2.list">Show List</a>
|
||||
<div ui-view></div>
|
||||
```
|
||||
|
||||
**(4)** Next, we'll add some child templates. *These* will get plugged into the `ui-view` of their parent state templates.
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- partials/state1.list.html -->
|
||||
<h3>List of State 1 Items</h3>
|
||||
<ul>
|
||||
<li ng-repeat="item in items">{{ item }}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
>
|
||||
```html
|
||||
<!-- partials/state2.list.html -->
|
||||
<h3>List of State 2 Things</h3>
|
||||
<ul>
|
||||
<li ng-repeat="thing in things">{{ thing }}</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**(5)** Finally, we'll wire it all up with `$stateProvider`. Set up your states in the module config, as in the following:
|
||||
|
||||
|
||||
>
|
||||
```javascript
|
||||
myApp.config(function($stateProvider, $urlRouterProvider) {
|
||||
//
|
||||
// For any unmatched url, redirect to /state1
|
||||
$urlRouterProvider.otherwise("/state1");
|
||||
//
|
||||
// Now set up the states
|
||||
$stateProvider
|
||||
.state('state1', {
|
||||
url: "/state1",
|
||||
templateUrl: "partials/state1.html"
|
||||
})
|
||||
.state('state1.list', {
|
||||
url: "/list",
|
||||
templateUrl: "partials/state1.list.html",
|
||||
controller: function($scope) {
|
||||
$scope.items = ["A", "List", "Of", "Items"];
|
||||
}
|
||||
})
|
||||
.state('state2', {
|
||||
url: "/state2",
|
||||
templateUrl: "partials/state2.html"
|
||||
})
|
||||
.state('state2.list', {
|
||||
url: "/list",
|
||||
templateUrl: "partials/state2.list.html",
|
||||
controller: function($scope) {
|
||||
$scope.things = ["A", "Set", "Of", "Things"];
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**(6)** See this quick start example in action.
|
||||
>**[Go to Quick Start Plunker for Nested States & Views](http://plnkr.co/edit/u18KQc?p=preview)**
|
||||
|
||||
**(7)** This only scratches the surface
|
||||
>**[Dive Deeper!](https://github.com/angular-ui/ui-router/wiki)**
|
||||
|
||||
|
||||
### [Multiple & Named Views](http://plnkr.co/edit/SDOcGS?p=preview)
|
||||
|
||||
Another great feature is the ability to have multiple `ui-view`s view per template.
|
||||
|
||||
**Pro Tip:** *While multiple parallel views are a powerful feature, you'll often be able to manage your
|
||||
interfaces more effectively by nesting your views, and pairing those views with nested states.*
|
||||
|
||||
**(1)** Follow the [setup](#get-started) instructions detailed above.
|
||||
|
||||
**(2)** Add one or more `ui-view` to your app, give them names.
|
||||
>
|
||||
```html
|
||||
<!-- index.html -->
|
||||
<body>
|
||||
<div ui-view="viewA"></div>
|
||||
<div ui-view="viewB"></div>
|
||||
<!-- Also a way to navigate -->
|
||||
<a ui-sref="route1">Route 1</a>
|
||||
<a ui-sref="route2">Route 2</a>
|
||||
</body>
|
||||
```
|
||||
|
||||
**(3)** Set up your states in the module config:
|
||||
>
|
||||
```javascript
|
||||
myApp.config(function($stateProvider) {
|
||||
$stateProvider
|
||||
.state('index', {
|
||||
url: "",
|
||||
views: {
|
||||
"viewA": { template: "index.viewA" },
|
||||
"viewB": { template: "index.viewB" }
|
||||
}
|
||||
})
|
||||
.state('route1', {
|
||||
url: "/route1",
|
||||
views: {
|
||||
"viewA": { template: "route1.viewA" },
|
||||
"viewB": { template: "route1.viewB" }
|
||||
}
|
||||
})
|
||||
.state('route2', {
|
||||
url: "/route2",
|
||||
views: {
|
||||
"viewA": { template: "route2.viewA" },
|
||||
"viewB": { template: "route2.viewB" }
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
**(4)** See this quick start example in action.
|
||||
>**[Go to Quick Start Plunker for Multiple & Named Views](http://plnkr.co/edit/SDOcGS?p=preview)**
|
||||
|
||||
|
||||
## Resources
|
||||
|
||||
* [In-Depth Guide](https://github.com/angular-ui/ui-router/wiki)
|
||||
* [API Reference](http://angular-ui.github.io/ui-router/site)
|
||||
* [Sample App](http://angular-ui.github.com/ui-router/sample/) ([Source](https://github.com/angular-ui/ui-router/tree/gh-pages/sample))
|
||||
* [FAQ](https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions)
|
||||
* [Slides comparing ngRoute to ui-router](http://slid.es/timkindberg/ui-router#/)
|
||||
* [UI-Router Extras / Addons](http://christopherthielen.github.io/ui-router-extras/#/home) (@christopherthielen)
|
||||
|
||||
### Videos
|
||||
|
||||
* [Introduction Video](https://egghead.io/lessons/angularjs-introduction-ui-router) (egghead.io)
|
||||
* [Tim Kindberg on Angular UI-Router](https://www.youtube.com/watch?v=lBqiZSemrqg)
|
||||
* [Activating States](https://egghead.io/lessons/angularjs-ui-router-activating-states) (egghead.io)
|
||||
* [Learn Angular.js using UI-Router](http://youtu.be/QETUuZ27N0w) (LearnCode.academy)
|
||||
|
||||
|
||||
|
||||
## Reporting issues and Contributing
|
||||
|
||||
Please read our [Contributor guidelines](CONTRIBUTING.md) before reporting an issue or creating a pull request.
|
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
"name": "angular-ui-router",
|
||||
"version": "0.3.1",
|
||||
"license" : "MIT",
|
||||
"main": "./release/angular-ui-router.js",
|
||||
"dependencies": {
|
||||
"angular": "^1.0.8"
|
||||
},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"component.json",
|
||||
"package.json",
|
||||
"lib",
|
||||
"config",
|
||||
"sample",
|
||||
"test",
|
||||
"tests",
|
||||
"ngdoc_assets",
|
||||
"Gruntfile.js",
|
||||
"files.js"
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,293 +0,0 @@
|
|||
/*jshint globalstrict:true*/
|
||||
/*global angular:false*/
|
||||
'use strict';
|
||||
|
||||
var isDefined = angular.isDefined,
|
||||
isFunction = angular.isFunction,
|
||||
isString = angular.isString,
|
||||
isObject = angular.isObject,
|
||||
isArray = angular.isArray,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy,
|
||||
toJson = angular.toJson;
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, { prototype: parent }))(), extra);
|
||||
}
|
||||
|
||||
function merge(dst) {
|
||||
forEach(arguments, function(obj) {
|
||||
if (obj !== dst) {
|
||||
forEach(obj, function(value, key) {
|
||||
if (!dst.hasOwnProperty(key)) dst[key] = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the common ancestor path between two states.
|
||||
*
|
||||
* @param {Object} first The first state.
|
||||
* @param {Object} second The second state.
|
||||
* @return {Array} Returns an array of state names in descending order, not including the root.
|
||||
*/
|
||||
function ancestors(first, second) {
|
||||
var path = [];
|
||||
|
||||
for (var n in first.path) {
|
||||
if (first.path[n] !== second.path[n]) break;
|
||||
path.push(first.path[n]);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* IE8-safe wrapper for `Object.keys()`.
|
||||
*
|
||||
* @param {Object} object A JavaScript object.
|
||||
* @return {Array} Returns the keys of the object as an array.
|
||||
*/
|
||||
function objectKeys(object) {
|
||||
if (Object.keys) {
|
||||
return Object.keys(object);
|
||||
}
|
||||
var result = [];
|
||||
|
||||
forEach(object, function(val, key) {
|
||||
result.push(key);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* IE8-safe wrapper for `Array.prototype.indexOf()`.
|
||||
*
|
||||
* @param {Array} array A JavaScript array.
|
||||
* @param {*} value A value to search the array for.
|
||||
* @return {Number} Returns the array index value of `value`, or `-1` if not present.
|
||||
*/
|
||||
function indexOf(array, value) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return array.indexOf(value, Number(arguments[2]) || 0);
|
||||
}
|
||||
var len = array.length >>> 0, from = Number(arguments[2]) || 0;
|
||||
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
|
||||
|
||||
if (from < 0) from += len;
|
||||
|
||||
for (; from < len; from++) {
|
||||
if (from in array && array[from] === value) return from;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a set of parameters with all parameters inherited between the common parents of the
|
||||
* current state and a given destination state.
|
||||
*
|
||||
* @param {Object} currentParams The value of the current state parameters ($stateParams).
|
||||
* @param {Object} newParams The set of parameters which will be composited with inherited params.
|
||||
* @param {Object} $current Internal definition of object representing the current state.
|
||||
* @param {Object} $to Internal definition of object representing state to transition to.
|
||||
*/
|
||||
function inheritParams(currentParams, newParams, $current, $to) {
|
||||
var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
|
||||
|
||||
for (var i in parents) {
|
||||
if (!parents[i] || !parents[i].params) continue;
|
||||
parentParams = objectKeys(parents[i].params);
|
||||
if (!parentParams.length) continue;
|
||||
|
||||
for (var j in parentParams) {
|
||||
if (indexOf(inheritList, parentParams[j]) >= 0) continue;
|
||||
inheritList.push(parentParams[j]);
|
||||
inherited[parentParams[j]] = currentParams[parentParams[j]];
|
||||
}
|
||||
}
|
||||
return extend({}, inherited, newParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
|
||||
*
|
||||
* @param {Object} a The first object.
|
||||
* @param {Object} b The second object.
|
||||
* @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
|
||||
* it defaults to the list of keys in `a`.
|
||||
* @return {Boolean} Returns `true` if the keys match, otherwise `false`.
|
||||
*/
|
||||
function equalForKeys(a, b, keys) {
|
||||
if (!keys) {
|
||||
keys = [];
|
||||
for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
|
||||
}
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var k = keys[i];
|
||||
if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subset of an object, based on a list of keys.
|
||||
*
|
||||
* @param {Array} keys
|
||||
* @param {Object} values
|
||||
* @return {Boolean} Returns a subset of `values`.
|
||||
*/
|
||||
function filterByKeys(keys, values) {
|
||||
var filtered = {};
|
||||
|
||||
forEach(keys, function (name) {
|
||||
filtered[name] = values[name];
|
||||
});
|
||||
return filtered;
|
||||
}
|
||||
|
||||
// like _.indexBy
|
||||
// when you know that your index values will be unique, or you want last-one-in to win
|
||||
function indexBy(array, propName) {
|
||||
var result = {};
|
||||
forEach(array, function(item) {
|
||||
result[item[propName]] = item;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// extracted from underscore.js
|
||||
// Return a copy of the object only containing the whitelisted properties.
|
||||
function pick(obj) {
|
||||
var copy = {};
|
||||
var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
|
||||
forEach(keys, function(key) {
|
||||
if (key in obj) copy[key] = obj[key];
|
||||
});
|
||||
return copy;
|
||||
}
|
||||
|
||||
// extracted from underscore.js
|
||||
// Return a copy of the object omitting the blacklisted properties.
|
||||
function omit(obj) {
|
||||
var copy = {};
|
||||
var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
|
||||
for (var key in obj) {
|
||||
if (indexOf(keys, key) == -1) copy[key] = obj[key];
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
function pluck(collection, key) {
|
||||
var result = isArray(collection) ? [] : {};
|
||||
|
||||
forEach(collection, function(val, i) {
|
||||
result[i] = isFunction(key) ? key(val) : val[key];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function filter(collection, callback) {
|
||||
var array = isArray(collection);
|
||||
var result = array ? [] : {};
|
||||
forEach(collection, function(val, i) {
|
||||
if (callback(val, i)) {
|
||||
result[array ? result.length : i] = val;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function map(collection, callback) {
|
||||
var result = isArray(collection) ? [] : {};
|
||||
|
||||
forEach(collection, function(val, i) {
|
||||
result[i] = callback(val, i);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ui.router.util
|
||||
*
|
||||
* @description
|
||||
* # ui.router.util sub-module
|
||||
*
|
||||
* This module is a dependency of other sub-modules. Do not include this module as a dependency
|
||||
* in your angular app (use {@link ui.router} module instead).
|
||||
*
|
||||
*/
|
||||
angular.module('ui.router.util', ['ng']);
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ui.router.router
|
||||
*
|
||||
* @requires ui.router.util
|
||||
*
|
||||
* @description
|
||||
* # ui.router.router sub-module
|
||||
*
|
||||
* This module is a dependency of other sub-modules. Do not include this module as a dependency
|
||||
* in your angular app (use {@link ui.router} module instead).
|
||||
*/
|
||||
angular.module('ui.router.router', ['ui.router.util']);
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ui.router.state
|
||||
*
|
||||
* @requires ui.router.router
|
||||
* @requires ui.router.util
|
||||
*
|
||||
* @description
|
||||
* # ui.router.state sub-module
|
||||
*
|
||||
* This module is a dependency of the main ui.router module. Do not include this module as a dependency
|
||||
* in your angular app (use {@link ui.router} module instead).
|
||||
*
|
||||
*/
|
||||
angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
|
||||
|
||||
/**
|
||||
* @ngdoc overview
|
||||
* @name ui.router
|
||||
*
|
||||
* @requires ui.router.state
|
||||
*
|
||||
* @description
|
||||
* # ui.router
|
||||
*
|
||||
* ## The main module for ui.router
|
||||
* There are several sub-modules included with the ui.router module, however only this module is needed
|
||||
* as a dependency within your angular app. The other modules are for organization purposes.
|
||||
*
|
||||
* The modules are:
|
||||
* * ui.router - the main "umbrella" module
|
||||
* * ui.router.router -
|
||||
*
|
||||
* *You'll need to include **only** this module as the dependency within your angular app.*
|
||||
*
|
||||
* <pre>
|
||||
* <!doctype html>
|
||||
* <html ng-app="myApp">
|
||||
* <head>
|
||||
* <script src="js/angular.js"></script>
|
||||
* <!-- Include the ui-router script -->
|
||||
* <script src="js/angular-ui-router.min.js"></script>
|
||||
* <script>
|
||||
* // ...and add 'ui.router' as a dependency
|
||||
* var myApp = angular.module('myApp', ['ui.router']);
|
||||
* </script>
|
||||
* </head>
|
||||
* <body>
|
||||
* </body>
|
||||
* </html>
|
||||
* </pre>
|
||||
*/
|
||||
angular.module('ui.router', ['ui.router.state']);
|
||||
|
||||
angular.module('ui.router.compat', ['ui.router']);
|
|
@ -1,252 +0,0 @@
|
|||
/**
|
||||
* @ngdoc object
|
||||
* @name ui.router.util.$resolve
|
||||
*
|
||||
* @requires $q
|
||||
* @requires $injector
|
||||
*
|
||||
* @description
|
||||
* Manages resolution of (acyclic) graphs of promises.
|
||||
*/
|
||||
$Resolve.$inject = ['$q', '$injector'];
|
||||
function $Resolve( $q, $injector) {
|
||||
|
||||
var VISIT_IN_PROGRESS = 1,
|
||||
VISIT_DONE = 2,
|
||||
NOTHING = {},
|
||||
NO_DEPENDENCIES = [],
|
||||
NO_LOCALS = NOTHING,
|
||||
NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.util.$resolve#study
|
||||
* @methodOf ui.router.util.$resolve
|
||||
*
|
||||
* @description
|
||||
* Studies a set of invocables that are likely to be used multiple times.
|
||||
* <pre>
|
||||
* $resolve.study(invocables)(locals, parent, self)
|
||||
* </pre>
|
||||
* is equivalent to
|
||||
* <pre>
|
||||
* $resolve.resolve(invocables, locals, parent, self)
|
||||
* </pre>
|
||||
* but the former is more efficient (in fact `resolve` just calls `study`
|
||||
* internally).
|
||||
*
|
||||
* @param {object} invocables Invocable objects
|
||||
* @return {function} a function to pass in locals, parent and self
|
||||
*/
|
||||
this.study = function (invocables) {
|
||||
if (!isObject(invocables)) throw new Error("'invocables' must be an object");
|
||||
var invocableKeys = objectKeys(invocables || {});
|
||||
|
||||
// Perform a topological sort of invocables to build an ordered plan
|
||||
var plan = [], cycle = [], visited = {};
|
||||
function visit(value, key) {
|
||||
if (visited[key] === VISIT_DONE) return;
|
||||
|
||||
cycle.push(key);
|
||||
if (visited[key] === VISIT_IN_PROGRESS) {
|
||||
cycle.splice(0, indexOf(cycle, key));
|
||||
throw new Error("Cyclic dependency: " + cycle.join(" -> "));
|
||||
}
|
||||
visited[key] = VISIT_IN_PROGRESS;
|
||||
|
||||
if (isString(value)) {
|
||||
plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
|
||||
} else {
|
||||
var params = $injector.annotate(value);
|
||||
forEach(params, function (param) {
|
||||
if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
|
||||
});
|
||||
plan.push(key, value, params);
|
||||
}
|
||||
|
||||
cycle.pop();
|
||||
visited[key] = VISIT_DONE;
|
||||
}
|
||||
forEach(invocables, visit);
|
||||
invocables = cycle = visited = null; // plan is all that's required
|
||||
|
||||
function isResolve(value) {
|
||||
return isObject(value) && value.then && value.$$promises;
|
||||
}
|
||||
|
||||
return function (locals, parent, self) {
|
||||
if (isResolve(locals) && self === undefined) {
|
||||
self = parent; parent = locals; locals = null;
|
||||
}
|
||||
if (!locals) locals = NO_LOCALS;
|
||||
else if (!isObject(locals)) {
|
||||
throw new Error("'locals' must be an object");
|
||||
}
|
||||
if (!parent) parent = NO_PARENT;
|
||||
else if (!isResolve(parent)) {
|
||||
throw new Error("'parent' must be a promise returned by $resolve.resolve()");
|
||||
}
|
||||
|
||||
// To complete the overall resolution, we have to wait for the parent
|
||||
// promise and for the promise for each invokable in our plan.
|
||||
var resolution = $q.defer(),
|
||||
result = resolution.promise,
|
||||
promises = result.$$promises = {},
|
||||
values = extend({}, locals),
|
||||
wait = 1 + plan.length/3,
|
||||
merged = false;
|
||||
|
||||
function done() {
|
||||
// Merge parent values we haven't got yet and publish our own $$values
|
||||
if (!--wait) {
|
||||
if (!merged) merge(values, parent.$$values);
|
||||
result.$$values = values;
|
||||
result.$$promises = result.$$promises || true; // keep for isResolve()
|
||||
delete result.$$inheritedValues;
|
||||
resolution.resolve(values);
|
||||
}
|
||||
}
|
||||
|
||||
function fail(reason) {
|
||||
result.$$failure = reason;
|
||||
resolution.reject(reason);
|
||||
}
|
||||
|
||||
// Short-circuit if parent has already failed
|
||||
if (isDefined(parent.$$failure)) {
|
||||
fail(parent.$$failure);
|
||||
return result;
|
||||
}
|
||||
|
||||
if (parent.$$inheritedValues) {
|
||||
merge(values, omit(parent.$$inheritedValues, invocableKeys));
|
||||
}
|
||||
|
||||
// Merge parent values if the parent has already resolved, or merge
|
||||
// parent promises and wait if the parent resolve is still in progress.
|
||||
extend(promises, parent.$$promises);
|
||||
if (parent.$$values) {
|
||||
merged = merge(values, omit(parent.$$values, invocableKeys));
|
||||
result.$$inheritedValues = omit(parent.$$values, invocableKeys);
|
||||
done();
|
||||
} else {
|
||||
if (parent.$$inheritedValues) {
|
||||
result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
|
||||
}
|
||||
parent.then(done, fail);
|
||||
}
|
||||
|
||||
// Process each invocable in the plan, but ignore any where a local of the same name exists.
|
||||
for (var i=0, ii=plan.length; i<ii; i+=3) {
|
||||
if (locals.hasOwnProperty(plan[i])) done();
|
||||
else invoke(plan[i], plan[i+1], plan[i+2]);
|
||||
}
|
||||
|
||||
function invoke(key, invocable, params) {
|
||||
// Create a deferred for this invocation. Failures will propagate to the resolution as well.
|
||||
var invocation = $q.defer(), waitParams = 0;
|
||||
function onfailure(reason) {
|
||||
invocation.reject(reason);
|
||||
fail(reason);
|
||||
}
|
||||
// Wait for any parameter that we have a promise for (either from parent or from this
|
||||
// resolve; in that case study() will have made sure it's ordered before us in the plan).
|
||||
forEach(params, function (dep) {
|
||||
if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
|
||||
waitParams++;
|
||||
promises[dep].then(function (result) {
|
||||
values[dep] = result;
|
||||
if (!(--waitParams)) proceed();
|
||||
}, onfailure);
|
||||
}
|
||||
});
|
||||
if (!waitParams) proceed();
|
||||
function proceed() {
|
||||
if (isDefined(result.$$failure)) return;
|
||||
try {
|
||||
invocation.resolve($injector.invoke(invocable, self, values));
|
||||
invocation.promise.then(function (result) {
|
||||
values[key] = result;
|
||||
done();
|
||||
}, onfailure);
|
||||
} catch (e) {
|
||||
onfailure(e);
|
||||
}
|
||||
}
|
||||
// Publish promise synchronously; invocations further down in the plan may depend on it.
|
||||
promises[key] = invocation.promise;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.util.$resolve#resolve
|
||||
* @methodOf ui.router.util.$resolve
|
||||
*
|
||||
* @description
|
||||
* Resolves a set of invocables. An invocable is a function to be invoked via
|
||||
* `$injector.invoke()`, and can have an arbitrary number of dependencies.
|
||||
* An invocable can either return a value directly,
|
||||
* or a `$q` promise. If a promise is returned it will be resolved and the
|
||||
* resulting value will be used instead. Dependencies of invocables are resolved
|
||||
* (in this order of precedence)
|
||||
*
|
||||
* - from the specified `locals`
|
||||
* - from another invocable that is part of this `$resolve` call
|
||||
* - from an invocable that is inherited from a `parent` call to `$resolve`
|
||||
* (or recursively
|
||||
* - from any ancestor `$resolve` of that parent).
|
||||
*
|
||||
* The return value of `$resolve` is a promise for an object that contains
|
||||
* (in this order of precedence)
|
||||
*
|
||||
* - any `locals` (if specified)
|
||||
* - the resolved return values of all injectables
|
||||
* - any values inherited from a `parent` call to `$resolve` (if specified)
|
||||
*
|
||||
* The promise will resolve after the `parent` promise (if any) and all promises
|
||||
* returned by injectables have been resolved. If any invocable
|
||||
* (or `$injector.invoke`) throws an exception, or if a promise returned by an
|
||||
* invocable is rejected, the `$resolve` promise is immediately rejected with the
|
||||
* same error. A rejection of a `parent` promise (if specified) will likewise be
|
||||
* propagated immediately. Once the `$resolve` promise has been rejected, no
|
||||
* further invocables will be called.
|
||||
*
|
||||
* Cyclic dependencies between invocables are not permitted and will cause `$resolve`
|
||||
* to throw an error. As a special case, an injectable can depend on a parameter
|
||||
* with the same name as the injectable, which will be fulfilled from the `parent`
|
||||
* injectable of the same name. This allows inherited values to be decorated.
|
||||
* Note that in this case any other injectable in the same `$resolve` with the same
|
||||
* dependency would see the decorated value, not the inherited value.
|
||||
*
|
||||
* Note that missing dependencies -- unlike cyclic dependencies -- will cause an
|
||||
* (asynchronous) rejection of the `$resolve` promise rather than a (synchronous)
|
||||
* exception.
|
||||
*
|
||||
* Invocables are invoked eagerly as soon as all dependencies are available.
|
||||
* This is true even for dependencies inherited from a `parent` call to `$resolve`.
|
||||
*
|
||||
* As a special case, an invocable can be a string, in which case it is taken to
|
||||
* be a service name to be passed to `$injector.get()`. This is supported primarily
|
||||
* for backwards-compatibility with the `resolve` property of `$routeProvider`
|
||||
* routes.
|
||||
*
|
||||
* @param {object} invocables functions to invoke or
|
||||
* `$injector` services to fetch.
|
||||
* @param {object} locals values to make available to the injectables
|
||||
* @param {object} parent a promise returned by another call to `$resolve`.
|
||||
* @param {object} self the `this` for the invoked methods
|
||||
* @return {object} Promise for an object that contains the resolved return value
|
||||
* of all invocables, as well as any inherited and local values.
|
||||
*/
|
||||
this.resolve = function (invocables, locals, parent, self) {
|
||||
return this.study(invocables)(locals, parent, self);
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.util').service('$resolve', $Resolve);
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,412 +0,0 @@
|
|||
function parseStateRef(ref, current) {
|
||||
var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
|
||||
if (preparsed) ref = current + '(' + preparsed[1] + ')';
|
||||
parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
|
||||
if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
|
||||
return { state: parsed[1], paramExpr: parsed[3] || null };
|
||||
}
|
||||
|
||||
function stateContext(el) {
|
||||
var stateData = el.parent().inheritedData('$uiView');
|
||||
|
||||
if (stateData && stateData.state && stateData.state.name) {
|
||||
return stateData.state;
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeInfo(el) {
|
||||
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
||||
var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]';
|
||||
var isForm = el[0].nodeName === "FORM";
|
||||
|
||||
return {
|
||||
attr: isForm ? "action" : (isSvg ? 'xlink:href' : 'href'),
|
||||
isAnchor: el.prop("tagName").toUpperCase() === "A",
|
||||
clickable: !isForm
|
||||
};
|
||||
}
|
||||
|
||||
function clickHook(el, $state, $timeout, type, current) {
|
||||
return function(e) {
|
||||
var button = e.which || e.button, target = current();
|
||||
|
||||
if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) {
|
||||
// HACK: This is to allow ng-clicks to be processed before the transition is initiated:
|
||||
var transition = $timeout(function() {
|
||||
$state.go(target.state, target.params, target.options);
|
||||
});
|
||||
e.preventDefault();
|
||||
|
||||
// if the state has no URL, ignore one preventDefault from the <a> directive.
|
||||
var ignorePreventDefaultCount = type.isAnchor && !target.href ? 1: 0;
|
||||
|
||||
e.preventDefault = function() {
|
||||
if (ignorePreventDefaultCount-- <= 0) $timeout.cancel(transition);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function defaultOpts(el, $state) {
|
||||
return { relative: stateContext(el) || $state.$current, inherit: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ui.router.state.directive:ui-sref
|
||||
*
|
||||
* @requires ui.router.state.$state
|
||||
* @requires $timeout
|
||||
*
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* A directive that binds a link (`<a>` tag) to a state. If the state has an associated
|
||||
* URL, the directive will automatically generate & update the `href` attribute via
|
||||
* the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
|
||||
* the link will trigger a state transition with optional parameters.
|
||||
*
|
||||
* Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
|
||||
* handled natively by the browser.
|
||||
*
|
||||
* You can also use relative state paths within ui-sref, just like the relative
|
||||
* paths passed to `$state.go()`. You just need to be aware that the path is relative
|
||||
* to the state that the link lives in, in other words the state that loaded the
|
||||
* template containing the link.
|
||||
*
|
||||
* You can specify options to pass to {@link ui.router.state.$state#methods_go $state.go()}
|
||||
* using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
|
||||
* and `reload`.
|
||||
*
|
||||
* @example
|
||||
* Here's an example of how you'd use ui-sref and how it would compile. If you have the
|
||||
* following template:
|
||||
* <pre>
|
||||
* <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
|
||||
*
|
||||
* <ul>
|
||||
* <li ng-repeat="contact in contacts">
|
||||
* <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
|
||||
* <pre>
|
||||
* <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
|
||||
*
|
||||
* <ul>
|
||||
* <li ng-repeat="contact in contacts">
|
||||
* <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
|
||||
* </li>
|
||||
* <li ng-repeat="contact in contacts">
|
||||
* <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
|
||||
* </li>
|
||||
* <li ng-repeat="contact in contacts">
|
||||
* <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
|
||||
* </pre>
|
||||
*
|
||||
* @param {string} ui-sref 'stateName' can be any valid absolute or relative state
|
||||
* @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#methods_go $state.go()}
|
||||
*/
|
||||
$StateRefDirective.$inject = ['$state', '$timeout'];
|
||||
function $StateRefDirective($state, $timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
|
||||
link: function(scope, element, attrs, uiSrefActive) {
|
||||
var ref = parseStateRef(attrs.uiSref, $state.current.name);
|
||||
var def = { state: ref.state, href: null, params: null };
|
||||
var type = getTypeInfo(element);
|
||||
var active = uiSrefActive[1] || uiSrefActive[0];
|
||||
var unlinkInfoFn = null;
|
||||
var hookFn;
|
||||
|
||||
def.options = extend(defaultOpts(element, $state), attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {});
|
||||
|
||||
var update = function(val) {
|
||||
if (val) def.params = angular.copy(val);
|
||||
def.href = $state.href(ref.state, def.params, def.options);
|
||||
|
||||
if (unlinkInfoFn) unlinkInfoFn();
|
||||
if (active) unlinkInfoFn = active.$$addStateInfo(ref.state, def.params);
|
||||
if (def.href !== null) attrs.$set(type.attr, def.href);
|
||||
};
|
||||
|
||||
if (ref.paramExpr) {
|
||||
scope.$watch(ref.paramExpr, function(val) { if (val !== def.params) update(val); }, true);
|
||||
def.params = angular.copy(scope.$eval(ref.paramExpr));
|
||||
}
|
||||
update();
|
||||
|
||||
if (!type.clickable) return;
|
||||
hookFn = clickHook(element, $state, $timeout, type, function() { return def; });
|
||||
element.bind("click", hookFn);
|
||||
scope.$on('$destroy', function() {
|
||||
element.unbind("click", hookFn);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ui.router.state.directive:ui-state
|
||||
*
|
||||
* @requires ui.router.state.uiSref
|
||||
*
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* Much like ui-sref, but will accept named $scope properties to evaluate for a state definition,
|
||||
* params and override options.
|
||||
*
|
||||
* @param {string} ui-state 'stateName' can be any valid absolute or relative state
|
||||
* @param {Object} ui-state-params params to pass to {@link ui.router.state.$state#methods_href $state.href()}
|
||||
* @param {Object} ui-state-opts options to pass to {@link ui.router.state.$state#methods_go $state.go()}
|
||||
*/
|
||||
$StateRefDynamicDirective.$inject = ['$state', '$timeout'];
|
||||
function $StateRefDynamicDirective($state, $timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
|
||||
link: function(scope, element, attrs, uiSrefActive) {
|
||||
var type = getTypeInfo(element);
|
||||
var active = uiSrefActive[1] || uiSrefActive[0];
|
||||
var group = [attrs.uiState, attrs.uiStateParams || null, attrs.uiStateOpts || null];
|
||||
var watch = '[' + group.map(function(val) { return val || 'null'; }).join(', ') + ']';
|
||||
var def = { state: null, params: null, options: null, href: null };
|
||||
var unlinkInfoFn = null;
|
||||
var hookFn;
|
||||
|
||||
function runStateRefLink (group) {
|
||||
def.state = group[0]; def.params = group[1]; def.options = group[2];
|
||||
def.href = $state.href(def.state, def.params, def.options);
|
||||
|
||||
if (unlinkInfoFn) unlinkInfoFn();
|
||||
if (active) unlinkInfoFn = active.$$addStateInfo(def.state, def.params);
|
||||
if (def.href) attrs.$set(type.attr, def.href);
|
||||
}
|
||||
|
||||
scope.$watch(watch, runStateRefLink, true);
|
||||
runStateRefLink(scope.$eval(watch));
|
||||
|
||||
if (!type.clickable) return;
|
||||
hookFn = clickHook(element, $state, $timeout, type, function() { return def; });
|
||||
element.bind("click", hookFn);
|
||||
scope.$on('$destroy', function() {
|
||||
element.unbind("click", hookFn);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ui.router.state.directive:ui-sref-active
|
||||
*
|
||||
* @requires ui.router.state.$state
|
||||
* @requires ui.router.state.$stateParams
|
||||
* @requires $interpolate
|
||||
*
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* A directive working alongside ui-sref to add classes to an element when the
|
||||
* related ui-sref directive's state is active, and removing them when it is inactive.
|
||||
* The primary use-case is to simplify the special appearance of navigation menus
|
||||
* relying on `ui-sref`, by having the "active" state's menu button appear different,
|
||||
* distinguishing it from the inactive menu items.
|
||||
*
|
||||
* ui-sref-active can live on the same element as ui-sref or on a parent element. The first
|
||||
* ui-sref-active found at the same level or above the ui-sref will be used.
|
||||
*
|
||||
* Will activate when the ui-sref's target state or any child state is active. If you
|
||||
* need to activate only when the ui-sref target state is active and *not* any of
|
||||
* it's children, then you will use
|
||||
* {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
|
||||
*
|
||||
* @example
|
||||
* Given the following template:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li ui-sref-active="active" class="item">
|
||||
* <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
|
||||
* the resulting HTML will appear as (note the 'active' class):
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li ui-sref-active="active" class="item active">
|
||||
* <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* The class name is interpolated **once** during the directives link time (any further changes to the
|
||||
* interpolated value are ignored).
|
||||
*
|
||||
* Multiple classes may be specified in a space-separated format:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li ui-sref-active='class1 class2 class3'>
|
||||
* <a ui-sref="app.user">link</a>
|
||||
* </li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* It is also possible to pass ui-sref-active an expression that evaluates
|
||||
* to an object hash, whose keys represent active class names and whose
|
||||
* values represent the respective state names/globs.
|
||||
* ui-sref-active will match if the current active state **includes** any of
|
||||
* the specified state names/globs, even the abstract ones.
|
||||
*
|
||||
* @Example
|
||||
* Given the following template, with "admin" being an abstract state:
|
||||
* <pre>
|
||||
* <div ui-sref-active="{'active': 'admin.*'}">
|
||||
* <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
|
||||
* </div>
|
||||
* </pre>
|
||||
*
|
||||
* When the current state is "admin.roles" the "active" class will be applied
|
||||
* to both the <div> and <a> elements. It is important to note that the state
|
||||
* names/globs passed to ui-sref-active shadow the state provided by ui-sref.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ui.router.state.directive:ui-sref-active-eq
|
||||
*
|
||||
* @requires ui.router.state.$state
|
||||
* @requires ui.router.state.$stateParams
|
||||
* @requires $interpolate
|
||||
*
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
* The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
|
||||
* when the exact target state used in the `ui-sref` is active; no child states.
|
||||
*
|
||||
*/
|
||||
$StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
|
||||
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
|
||||
return {
|
||||
restrict: "A",
|
||||
controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) {
|
||||
var states = [], activeClasses = {}, activeEqClass, uiSrefActive;
|
||||
|
||||
// There probably isn't much point in $observing this
|
||||
// uiSrefActive and uiSrefActiveEq share the same directive object with some
|
||||
// slight difference in logic routing
|
||||
activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
|
||||
|
||||
try {
|
||||
uiSrefActive = $scope.$eval($attrs.uiSrefActive);
|
||||
} catch (e) {
|
||||
// Do nothing. uiSrefActive is not a valid expression.
|
||||
// Fall back to using $interpolate below
|
||||
}
|
||||
uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
|
||||
if (isObject(uiSrefActive)) {
|
||||
forEach(uiSrefActive, function(stateOrName, activeClass) {
|
||||
if (isString(stateOrName)) {
|
||||
var ref = parseStateRef(stateOrName, $state.current.name);
|
||||
addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Allow uiSref to communicate with uiSrefActive[Equals]
|
||||
this.$$addStateInfo = function (newState, newParams) {
|
||||
// we already got an explicit state provided by ui-sref-active, so we
|
||||
// shadow the one that comes from ui-sref
|
||||
if (isObject(uiSrefActive) && states.length > 0) {
|
||||
return;
|
||||
}
|
||||
var deregister = addState(newState, newParams, uiSrefActive);
|
||||
update();
|
||||
return deregister;
|
||||
};
|
||||
|
||||
$scope.$on('$stateChangeSuccess', update);
|
||||
|
||||
function addState(stateName, stateParams, activeClass) {
|
||||
var state = $state.get(stateName, stateContext($element));
|
||||
var stateHash = createStateHash(stateName, stateParams);
|
||||
|
||||
var stateInfo = {
|
||||
state: state || { name: stateName },
|
||||
params: stateParams,
|
||||
hash: stateHash
|
||||
};
|
||||
|
||||
states.push(stateInfo);
|
||||
activeClasses[stateHash] = activeClass;
|
||||
|
||||
return function removeState() {
|
||||
var idx = states.indexOf(stateInfo);
|
||||
if (idx !== -1) states.splice(idx, 1);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} state
|
||||
* @param {Object|string} [params]
|
||||
* @return {string}
|
||||
*/
|
||||
function createStateHash(state, params) {
|
||||
if (!isString(state)) {
|
||||
throw new Error('state should be a string');
|
||||
}
|
||||
if (isObject(params)) {
|
||||
return state + toJson(params);
|
||||
}
|
||||
params = $scope.$eval(params);
|
||||
if (isObject(params)) {
|
||||
return state + toJson(params);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
// Update route state
|
||||
function update() {
|
||||
for (var i = 0; i < states.length; i++) {
|
||||
if (anyMatch(states[i].state, states[i].params)) {
|
||||
addClass($element, activeClasses[states[i].hash]);
|
||||
} else {
|
||||
removeClass($element, activeClasses[states[i].hash]);
|
||||
}
|
||||
|
||||
if (exactMatch(states[i].state, states[i].params)) {
|
||||
addClass($element, activeEqClass);
|
||||
} else {
|
||||
removeClass($element, activeEqClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addClass(el, className) { $timeout(function () { el.addClass(className); }); }
|
||||
function removeClass(el, className) { el.removeClass(className); }
|
||||
function anyMatch(state, params) { return $state.includes(state.name, params); }
|
||||
function exactMatch(state, params) { return $state.is(state.name, params); }
|
||||
|
||||
update();
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.state')
|
||||
.directive('uiSref', $StateRefDirective)
|
||||
.directive('uiSrefActive', $StateRefActiveDirective)
|
||||
.directive('uiSrefActiveEq', $StateRefActiveDirective)
|
||||
.directive('uiState', $StateRefDynamicDirective);
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* @ngdoc filter
|
||||
* @name ui.router.state.filter:isState
|
||||
*
|
||||
* @requires ui.router.state.$state
|
||||
*
|
||||
* @description
|
||||
* Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
|
||||
*/
|
||||
$IsStateFilter.$inject = ['$state'];
|
||||
function $IsStateFilter($state) {
|
||||
var isFilter = function (state, params) {
|
||||
return $state.is(state, params);
|
||||
};
|
||||
isFilter.$stateful = true;
|
||||
return isFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name ui.router.state.filter:includedByState
|
||||
*
|
||||
* @requires ui.router.state.$state
|
||||
*
|
||||
* @description
|
||||
* Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
|
||||
*/
|
||||
$IncludedByStateFilter.$inject = ['$state'];
|
||||
function $IncludedByStateFilter($state) {
|
||||
var includesFilter = function (state, params, options) {
|
||||
return $state.includes(state, params, options);
|
||||
};
|
||||
includesFilter.$stateful = true;
|
||||
return includesFilter;
|
||||
}
|
||||
|
||||
angular.module('ui.router.state')
|
||||
.filter('isState', $IsStateFilter)
|
||||
.filter('includedByState', $IncludedByStateFilter);
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* @ngdoc object
|
||||
* @name ui.router.util.$templateFactory
|
||||
*
|
||||
* @requires $http
|
||||
* @requires $templateCache
|
||||
* @requires $injector
|
||||
*
|
||||
* @description
|
||||
* Service. Manages loading of templates.
|
||||
*/
|
||||
$TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
|
||||
function $TemplateFactory( $http, $templateCache, $injector) {
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.util.$templateFactory#fromConfig
|
||||
* @methodOf ui.router.util.$templateFactory
|
||||
*
|
||||
* @description
|
||||
* Creates a template from a configuration object.
|
||||
*
|
||||
* @param {object} config Configuration object for which to load a template.
|
||||
* The following properties are search in the specified order, and the first one
|
||||
* that is defined is used to create the template:
|
||||
*
|
||||
* @param {string|object} config.template html string template or function to
|
||||
* load via {@link ui.router.util.$templateFactory#fromString fromString}.
|
||||
* @param {string|object} config.templateUrl url to load or a function returning
|
||||
* the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
|
||||
* @param {Function} config.templateProvider function to invoke via
|
||||
* {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
|
||||
* @param {object} params Parameters to pass to the template function.
|
||||
* @param {object} locals Locals to pass to `invoke` if the template is loaded
|
||||
* via a `templateProvider`. Defaults to `{ params: params }`.
|
||||
*
|
||||
* @return {string|object} The template html as a string, or a promise for
|
||||
* that string,or `null` if no template is configured.
|
||||
*/
|
||||
this.fromConfig = function (config, params, locals) {
|
||||
return (
|
||||
isDefined(config.template) ? this.fromString(config.template, params) :
|
||||
isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
|
||||
isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.util.$templateFactory#fromString
|
||||
* @methodOf ui.router.util.$templateFactory
|
||||
*
|
||||
* @description
|
||||
* Creates a template from a string or a function returning a string.
|
||||
*
|
||||
* @param {string|object} template html template as a string or function that
|
||||
* returns an html template as a string.
|
||||
* @param {object} params Parameters to pass to the template function.
|
||||
*
|
||||
* @return {string|object} The template html as a string, or a promise for that
|
||||
* string.
|
||||
*/
|
||||
this.fromString = function (template, params) {
|
||||
return isFunction(template) ? template(params) : template;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.util.$templateFactory#fromUrl
|
||||
* @methodOf ui.router.util.$templateFactory
|
||||
*
|
||||
* @description
|
||||
* Loads a template from the a URL via `$http` and `$templateCache`.
|
||||
*
|
||||
* @param {string|Function} url url of the template to load, or a function
|
||||
* that returns a url.
|
||||
* @param {Object} params Parameters to pass to the url function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise
|
||||
* for that string.
|
||||
*/
|
||||
this.fromUrl = function (url, params) {
|
||||
if (isFunction(url)) url = url(params);
|
||||
if (url == null) return null;
|
||||
else return $http
|
||||
.get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
|
||||
.then(function(response) { return response.data; });
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.util.$templateFactory#fromProvider
|
||||
* @methodOf ui.router.util.$templateFactory
|
||||
*
|
||||
* @description
|
||||
* Creates a template by invoking an injectable provider function.
|
||||
*
|
||||
* @param {Function} provider Function to invoke via `$injector.invoke`
|
||||
* @param {Object} params Parameters for the template.
|
||||
* @param {Object} locals Locals to pass to `invoke`. Defaults to
|
||||
* `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise
|
||||
* for that string.
|
||||
*/
|
||||
this.fromProvider = function (provider, params, locals) {
|
||||
return $injector.invoke(provider, null, locals || { params: params });
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
|
File diff suppressed because it is too large
Load Diff
|
@ -1,431 +0,0 @@
|
|||
/**
|
||||
* @ngdoc object
|
||||
* @name ui.router.router.$urlRouterProvider
|
||||
*
|
||||
* @requires ui.router.util.$urlMatcherFactoryProvider
|
||||
* @requires $locationProvider
|
||||
*
|
||||
* @description
|
||||
* `$urlRouterProvider` has the responsibility of watching `$location`.
|
||||
* When `$location` changes it runs through a list of rules one by one until a
|
||||
* match is found. `$urlRouterProvider` is used behind the scenes anytime you specify
|
||||
* a url in a state configuration. All urls are compiled into a UrlMatcher object.
|
||||
*
|
||||
* There are several methods on `$urlRouterProvider` that make it useful to use directly
|
||||
* in your module config.
|
||||
*/
|
||||
$UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
|
||||
function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
|
||||
var rules = [], otherwise = null, interceptDeferred = false, listener;
|
||||
|
||||
// Returns a string that is a prefix of all strings matching the RegExp
|
||||
function regExpPrefix(re) {
|
||||
var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
|
||||
return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
|
||||
}
|
||||
|
||||
// Interpolates matched values into a String.replace()-style pattern
|
||||
function interpolate(pattern, match) {
|
||||
return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
|
||||
return match[what === '$' ? 0 : Number(what)];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.router.$urlRouterProvider#rule
|
||||
* @methodOf ui.router.router.$urlRouterProvider
|
||||
*
|
||||
* @description
|
||||
* Defines rules that are used by `$urlRouterProvider` to find matches for
|
||||
* specific URLs.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* var app = angular.module('app', ['ui.router.router']);
|
||||
*
|
||||
* app.config(function ($urlRouterProvider) {
|
||||
* // Here's an example of how you might allow case insensitive urls
|
||||
* $urlRouterProvider.rule(function ($injector, $location) {
|
||||
* var path = $location.path(),
|
||||
* normalized = path.toLowerCase();
|
||||
*
|
||||
* if (path !== normalized) {
|
||||
* return normalized;
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {function} rule Handler function that takes `$injector` and `$location`
|
||||
* services as arguments. You can use them to return a valid path as a string.
|
||||
*
|
||||
* @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
|
||||
*/
|
||||
this.rule = function (rule) {
|
||||
if (!isFunction(rule)) throw new Error("'rule' must be a function");
|
||||
rules.push(rule);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ui.router.router.$urlRouterProvider#otherwise
|
||||
* @methodOf ui.router.router.$urlRouterProvider
|
||||
*
|
||||
* @description
|
||||
* Defines a path that is used when an invalid route is requested.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* var app = angular.module('app', ['ui.router.router']);
|
||||
*
|
||||
* app.config(function ($urlRouterProvider) {
|
||||
* // if the path doesn't match any of the urls you configured
|
||||
* // otherwise will take care of routing the user to the
|
||||
* // specified url
|
||||
* $urlRouterProvider.otherwise('/index');
|
||||
*
|
||||
* // Example of using function rule as param
|
||||
* $urlRouterProvider.otherwise(function ($injector, $location) {
|
||||
* return '/a/valid/url';
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {string|function} rule The url path you want to redirect to or a function
|
||||
* rule that returns the url path. The function version is passed two params:
|
||||
* `$injector` and `$location` services, and must return a url string.
|
||||
*
|
||||
* @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
|
||||
*/
|
||||
this.otherwise = function (rule) {
|
||||
if (isString(rule)) {
|
||||
var redirect = rule;
|
||||
rule = function () { return redirect; };
|
||||
}
|
||||
else if (!isFunction(rule)) throw new Error("'rule' must be a function");
|
||||
otherwise = rule;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
function handleIfMatch($injector, handler, match) {
|
||||
if (!match) return false;
|
||||
var result = $injector.invoke(handler, handler, { $match: match });
|
||||
return isDefined(result) ? result : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.router.$urlRouterProvider#when
|
||||
* @methodOf ui.router.router.$urlRouterProvider
|
||||
*
|
||||
* @description
|
||||
* Registers a handler for a given url matching.
|
||||
*
|
||||
* If the handler is a string, it is
|
||||
* treated as a redirect, and is interpolated according to the syntax of match
|
||||
* (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
|
||||
*
|
||||
* If the handler is a function, it is injectable. It gets invoked if `$location`
|
||||
* matches. You have the option of inject the match object as `$match`.
|
||||
*
|
||||
* The handler can return
|
||||
*
|
||||
* - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
|
||||
* will continue trying to find another one that matches.
|
||||
* - **string** which is treated as a redirect and passed to `$location.url()`
|
||||
* - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* var app = angular.module('app', ['ui.router.router']);
|
||||
*
|
||||
* app.config(function ($urlRouterProvider) {
|
||||
* $urlRouterProvider.when($state.url, function ($match, $stateParams) {
|
||||
* if ($state.$current.navigable !== state ||
|
||||
* !equalForKeys($match, $stateParams) {
|
||||
* $state.transitionTo(state, $match, false);
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {string|object} what The incoming path that you want to redirect.
|
||||
* @param {string|function} handler The path you want to redirect your user to.
|
||||
*/
|
||||
this.when = function (what, handler) {
|
||||
var redirect, handlerIsString = isString(handler);
|
||||
if (isString(what)) what = $urlMatcherFactory.compile(what);
|
||||
|
||||
if (!handlerIsString && !isFunction(handler) && !isArray(handler))
|
||||
throw new Error("invalid 'handler' in when()");
|
||||
|
||||
var strategies = {
|
||||
matcher: function (what, handler) {
|
||||
if (handlerIsString) {
|
||||
redirect = $urlMatcherFactory.compile(handler);
|
||||
handler = ['$match', function ($match) { return redirect.format($match); }];
|
||||
}
|
||||
return extend(function ($injector, $location) {
|
||||
return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
|
||||
}, {
|
||||
prefix: isString(what.prefix) ? what.prefix : ''
|
||||
});
|
||||
},
|
||||
regex: function (what, handler) {
|
||||
if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
|
||||
|
||||
if (handlerIsString) {
|
||||
redirect = handler;
|
||||
handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
|
||||
}
|
||||
return extend(function ($injector, $location) {
|
||||
return handleIfMatch($injector, handler, what.exec($location.path()));
|
||||
}, {
|
||||
prefix: regExpPrefix(what)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
|
||||
|
||||
for (var n in check) {
|
||||
if (check[n]) return this.rule(strategies[n](what, handler));
|
||||
}
|
||||
|
||||
throw new Error("invalid 'what' in when()");
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.router.$urlRouterProvider#deferIntercept
|
||||
* @methodOf ui.router.router.$urlRouterProvider
|
||||
*
|
||||
* @description
|
||||
* Disables (or enables) deferring location change interception.
|
||||
*
|
||||
* If you wish to customize the behavior of syncing the URL (for example, if you wish to
|
||||
* defer a transition but maintain the current URL), call this method at configuration time.
|
||||
* Then, at run time, call `$urlRouter.listen()` after you have configured your own
|
||||
* `$locationChangeSuccess` event handler.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* var app = angular.module('app', ['ui.router.router']);
|
||||
*
|
||||
* app.config(function ($urlRouterProvider) {
|
||||
*
|
||||
* // Prevent $urlRouter from automatically intercepting URL changes;
|
||||
* // this allows you to configure custom behavior in between
|
||||
* // location changes and route synchronization:
|
||||
* $urlRouterProvider.deferIntercept();
|
||||
*
|
||||
* }).run(function ($rootScope, $urlRouter, UserService) {
|
||||
*
|
||||
* $rootScope.$on('$locationChangeSuccess', function(e) {
|
||||
* // UserService is an example service for managing user state
|
||||
* if (UserService.isLoggedIn()) return;
|
||||
*
|
||||
* // Prevent $urlRouter's default handler from firing
|
||||
* e.preventDefault();
|
||||
*
|
||||
* UserService.handleLogin().then(function() {
|
||||
* // Once the user has logged in, sync the current URL
|
||||
* // to the router:
|
||||
* $urlRouter.sync();
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* // Configures $urlRouter's listener *after* your custom listener
|
||||
* $urlRouter.listen();
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {boolean} defer Indicates whether to defer location change interception. Passing
|
||||
no parameter is equivalent to `true`.
|
||||
*/
|
||||
this.deferIntercept = function (defer) {
|
||||
if (defer === undefined) defer = true;
|
||||
interceptDeferred = defer;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ui.router.router.$urlRouter
|
||||
*
|
||||
* @requires $location
|
||||
* @requires $rootScope
|
||||
* @requires $injector
|
||||
* @requires $browser
|
||||
*
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
this.$get = $get;
|
||||
$get.$inject = ['$location', '$rootScope', '$injector', '$browser', '$sniffer'];
|
||||
function $get( $location, $rootScope, $injector, $browser, $sniffer) {
|
||||
|
||||
var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
|
||||
|
||||
function appendBasePath(url, isHtml5, absolute) {
|
||||
if (baseHref === '/') return url;
|
||||
if (isHtml5) return baseHref.slice(0, -1) + url;
|
||||
if (absolute) return baseHref.slice(1) + url;
|
||||
return url;
|
||||
}
|
||||
|
||||
// TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
|
||||
function update(evt) {
|
||||
if (evt && evt.defaultPrevented) return;
|
||||
var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
|
||||
lastPushedUrl = undefined;
|
||||
// TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
|
||||
//if (ignoreUpdate) return true;
|
||||
|
||||
function check(rule) {
|
||||
var handled = rule($injector, $location);
|
||||
|
||||
if (!handled) return false;
|
||||
if (isString(handled)) $location.replace().url(handled);
|
||||
return true;
|
||||
}
|
||||
var n = rules.length, i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (check(rules[i])) return;
|
||||
}
|
||||
// always check otherwise last to allow dynamic updates to the set of rules
|
||||
if (otherwise) check(otherwise);
|
||||
}
|
||||
|
||||
function listen() {
|
||||
listener = listener || $rootScope.$on('$locationChangeSuccess', update);
|
||||
return listener;
|
||||
}
|
||||
|
||||
if (!interceptDeferred) listen();
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.router.$urlRouter#sync
|
||||
* @methodOf ui.router.router.$urlRouter
|
||||
*
|
||||
* @description
|
||||
* Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
|
||||
* This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
|
||||
* perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
|
||||
* with the transition by calling `$urlRouter.sync()`.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* angular.module('app', ['ui.router'])
|
||||
* .run(function($rootScope, $urlRouter) {
|
||||
* $rootScope.$on('$locationChangeSuccess', function(evt) {
|
||||
* // Halt state change from even starting
|
||||
* evt.preventDefault();
|
||||
* // Perform custom logic
|
||||
* var meetsRequirement = ...
|
||||
* // Continue with the update and state transition if logic allows
|
||||
* if (meetsRequirement) $urlRouter.sync();
|
||||
* });
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
sync: function() {
|
||||
update();
|
||||
},
|
||||
|
||||
listen: function() {
|
||||
return listen();
|
||||
},
|
||||
|
||||
update: function(read) {
|
||||
if (read) {
|
||||
location = $location.url();
|
||||
return;
|
||||
}
|
||||
if ($location.url() === location) return;
|
||||
|
||||
$location.url(location);
|
||||
$location.replace();
|
||||
},
|
||||
|
||||
push: function(urlMatcher, params, options) {
|
||||
var url = urlMatcher.format(params || {});
|
||||
|
||||
// Handle the special hash param, if needed
|
||||
if (url !== null && params && params['#']) {
|
||||
url += '#' + params['#'];
|
||||
}
|
||||
|
||||
$location.url(url);
|
||||
lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
|
||||
if (options && options.replace) $location.replace();
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.router.$urlRouter#href
|
||||
* @methodOf ui.router.router.$urlRouter
|
||||
*
|
||||
* @description
|
||||
* A URL generation method that returns the compiled URL for a given
|
||||
* {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
|
||||
*
|
||||
* @example
|
||||
* <pre>
|
||||
* $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
|
||||
* person: "bob"
|
||||
* });
|
||||
* // $bob == "/about/bob";
|
||||
* </pre>
|
||||
*
|
||||
* @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
|
||||
* @param {object=} params An object of parameter values to fill the matcher's required parameters.
|
||||
* @param {object=} options Options object. The options are:
|
||||
*
|
||||
* - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
|
||||
*
|
||||
* @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
|
||||
*/
|
||||
href: function(urlMatcher, params, options) {
|
||||
if (!urlMatcher.validates(params)) return null;
|
||||
|
||||
var isHtml5 = $locationProvider.html5Mode();
|
||||
if (angular.isObject(isHtml5)) {
|
||||
isHtml5 = isHtml5.enabled;
|
||||
}
|
||||
|
||||
isHtml5 = isHtml5 && $sniffer.history;
|
||||
|
||||
var url = urlMatcher.format(params);
|
||||
options = options || {};
|
||||
|
||||
if (!isHtml5 && url !== null) {
|
||||
url = "#" + $locationProvider.hashPrefix() + url;
|
||||
}
|
||||
|
||||
// Handle special hash param, if needed
|
||||
if (url !== null && params && params['#']) {
|
||||
url += '#' + params['#'];
|
||||
}
|
||||
|
||||
url = appendBasePath(url, isHtml5, options.absolute);
|
||||
|
||||
if (!options.absolute || !url) {
|
||||
return url;
|
||||
}
|
||||
|
||||
var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
|
||||
port = (port === 80 || port === 443 ? '' : ':' + port);
|
||||
|
||||
return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
|
|
@ -1,45 +0,0 @@
|
|||
|
||||
$ViewProvider.$inject = [];
|
||||
function $ViewProvider() {
|
||||
|
||||
this.$get = $get;
|
||||
/**
|
||||
* @ngdoc object
|
||||
* @name ui.router.state.$view
|
||||
*
|
||||
* @requires ui.router.util.$templateFactory
|
||||
* @requires $rootScope
|
||||
*
|
||||
* @description
|
||||
*
|
||||
*/
|
||||
$get.$inject = ['$rootScope', '$templateFactory'];
|
||||
function $get( $rootScope, $templateFactory) {
|
||||
return {
|
||||
// $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name ui.router.state.$view#load
|
||||
* @methodOf ui.router.state.$view
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* @param {string} name name
|
||||
* @param {object} options option object.
|
||||
*/
|
||||
load: function load(name, options) {
|
||||
var result, defaults = {
|
||||
template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
|
||||
};
|
||||
options = extend(defaults, options);
|
||||
|
||||
if (options.view) {
|
||||
result = $templateFactory.fromConfig(options.view, options.params, options.locals);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.router.state').provider('$view', $ViewProvider);
|
|
@ -1,357 +0,0 @@
|
|||
/**
|
||||
* @ngdoc directive
|
||||
* @name ui.router.state.directive:ui-view
|
||||
*
|
||||
* @requires ui.router.state.$state
|
||||
* @requires $compile
|
||||
* @requires $controller
|
||||
* @requires $injector
|
||||
* @requires ui.router.state.$uiViewScroll
|
||||
* @requires $document
|
||||
*
|
||||
* @restrict ECA
|
||||
*
|
||||
* @description
|
||||
* The ui-view directive tells $state where to place your templates.
|
||||
*
|
||||
* @param {string=} name A view name. The name should be unique amongst the other views in the
|
||||
* same state. You can have views of the same name that live in different states.
|
||||
*
|
||||
* @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
|
||||
* when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
|
||||
* service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
|
||||
* scroll ui-view elements into view when they are populated during a state activation.
|
||||
*
|
||||
* *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
|
||||
* functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
|
||||
*
|
||||
* @param {string=} onload Expression to evaluate whenever the view updates.
|
||||
*
|
||||
* @example
|
||||
* A view can be unnamed or named.
|
||||
* <pre>
|
||||
* <!-- Unnamed -->
|
||||
* <div ui-view></div>
|
||||
*
|
||||
* <!-- Named -->
|
||||
* <div ui-view="viewName"></div>
|
||||
* </pre>
|
||||
*
|
||||
* You can only have one unnamed view within any template (or root html). If you are only using a
|
||||
* single view and it is unnamed then you can populate it like so:
|
||||
* <pre>
|
||||
* <div ui-view></div>
|
||||
* $stateProvider.state("home", {
|
||||
* template: "<h1>HELLO!</h1>"
|
||||
* })
|
||||
* </pre>
|
||||
*
|
||||
* The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#methods_state `views`}
|
||||
* config property, by name, in this case an empty name:
|
||||
* <pre>
|
||||
* $stateProvider.state("home", {
|
||||
* views: {
|
||||
* "": {
|
||||
* template: "<h1>HELLO!</h1>"
|
||||
* }
|
||||
* }
|
||||
* })
|
||||
* </pre>
|
||||
*
|
||||
* But typically you'll only use the views property if you name your view or have more than one view
|
||||
* in the same template. There's not really a compelling reason to name a view if its the only one,
|
||||
* but you could if you wanted, like so:
|
||||
* <pre>
|
||||
* <div ui-view="main"></div>
|
||||
* </pre>
|
||||
* <pre>
|
||||
* $stateProvider.state("home", {
|
||||
* views: {
|
||||
* "main": {
|
||||
* template: "<h1>HELLO!</h1>"
|
||||
* }
|
||||
* }
|
||||
* })
|
||||
* </pre>
|
||||
*
|
||||
* Really though, you'll use views to set up multiple views:
|
||||
* <pre>
|
||||
* <div ui-view></div>
|
||||
* <div ui-view="chart"></div>
|
||||
* <div ui-view="data"></div>
|
||||
* </pre>
|
||||
*
|
||||
* <pre>
|
||||
* $stateProvider.state("home", {
|
||||
* views: {
|
||||
* "": {
|
||||
* template: "<h1>HELLO!</h1>"
|
||||
* },
|
||||
* "chart": {
|
||||
* template: "<chart_thing/>"
|
||||
* },
|
||||
* "data": {
|
||||
* template: "<data_thing/>"
|
||||
* }
|
||||
* }
|
||||
* })
|
||||
* </pre>
|
||||
*
|
||||
* Examples for `autoscroll`:
|
||||
*
|
||||
* <pre>
|
||||
* <!-- If autoscroll present with no expression,
|
||||
* then scroll ui-view into view -->
|
||||
* <ui-view autoscroll/>
|
||||
*
|
||||
* <!-- If autoscroll present with valid expression,
|
||||
* then scroll ui-view into view if expression evaluates to true -->
|
||||
* <ui-view autoscroll='true'/>
|
||||
* <ui-view autoscroll='false'/>
|
||||
* <ui-view autoscroll='scopeVariable'/>
|
||||
* </pre>
|
||||
*
|
||||
* Resolve data:
|
||||
*
|
||||
* The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
|
||||
* can be customized using [[ViewDeclaration.resolveAs]]). This can be then accessed from the template.
|
||||
*
|
||||
* Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
|
||||
* controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
|
||||
* depends on `$resolve` data.
|
||||
*
|
||||
* Example usage of $resolve in a view template
|
||||
* <pre>
|
||||
* $stateProvider.state('home', {
|
||||
* template: '<my-component user="$resolve.user"></my-component>',
|
||||
* resolve: {
|
||||
* user: function(UserService) { return UserService.fetchUser(); }
|
||||
* }
|
||||
* });
|
||||
* </pre>
|
||||
*/
|
||||
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q'];
|
||||
function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) {
|
||||
|
||||
function getService() {
|
||||
return ($injector.has) ? function(service) {
|
||||
return $injector.has(service) ? $injector.get(service) : null;
|
||||
} : function(service) {
|
||||
try {
|
||||
return $injector.get(service);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var service = getService(),
|
||||
$animator = service('$animator'),
|
||||
$animate = service('$animate');
|
||||
|
||||
// Returns a set of DOM manipulation functions based on which Angular version
|
||||
// it should use
|
||||
function getRenderer(attrs, scope) {
|
||||
var statics = function() {
|
||||
return {
|
||||
enter: function (element, target, cb) { target.after(element); cb(); },
|
||||
leave: function (element, cb) { element.remove(); cb(); }
|
||||
};
|
||||
};
|
||||
|
||||
if ($animate) {
|
||||
return {
|
||||
enter: function(element, target, cb) {
|
||||
if (angular.version.minor > 2) {
|
||||
$animate.enter(element, null, target).then(cb);
|
||||
} else {
|
||||
$animate.enter(element, null, target, cb);
|
||||
}
|
||||
},
|
||||
leave: function(element, cb) {
|
||||
if (angular.version.minor > 2) {
|
||||
$animate.leave(element).then(cb);
|
||||
} else {
|
||||
$animate.leave(element, cb);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if ($animator) {
|
||||
var animate = $animator && $animator(scope, attrs);
|
||||
|
||||
return {
|
||||
enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
|
||||
leave: function(element, cb) { animate.leave(element); cb(); }
|
||||
};
|
||||
}
|
||||
|
||||
return statics();
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
priority: 400,
|
||||
transclude: 'element',
|
||||
compile: function (tElement, tAttrs, $transclude) {
|
||||
return function (scope, $element, attrs) {
|
||||
var previousEl, currentEl, currentScope, latestLocals,
|
||||
onloadExp = attrs.onload || '',
|
||||
autoScrollExp = attrs.autoscroll,
|
||||
renderer = getRenderer(attrs, scope),
|
||||
inherited = $element.inheritedData('$uiView');
|
||||
|
||||
scope.$on('$stateChangeSuccess', function() {
|
||||
updateView(false);
|
||||
});
|
||||
|
||||
updateView(true);
|
||||
|
||||
function cleanupLastView() {
|
||||
if (previousEl) {
|
||||
previousEl.remove();
|
||||
previousEl = null;
|
||||
}
|
||||
|
||||
if (currentScope) {
|
||||
currentScope.$destroy();
|
||||
currentScope = null;
|
||||
}
|
||||
|
||||
if (currentEl) {
|
||||
var $uiViewData = currentEl.data('$uiViewAnim');
|
||||
renderer.leave(currentEl, function() {
|
||||
$uiViewData.$$animLeave.resolve();
|
||||
previousEl = null;
|
||||
});
|
||||
|
||||
previousEl = currentEl;
|
||||
currentEl = null;
|
||||
}
|
||||
}
|
||||
|
||||
function updateView(firstTime) {
|
||||
var newScope,
|
||||
name = getUiViewName(scope, attrs, $element, $interpolate),
|
||||
previousLocals = name && $state.$current && $state.$current.locals[name];
|
||||
|
||||
if (!firstTime && previousLocals === latestLocals) return; // nothing to do
|
||||
newScope = scope.$new();
|
||||
latestLocals = $state.$current.locals[name];
|
||||
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ui.router.state.directive:ui-view#$viewContentLoading
|
||||
* @eventOf ui.router.state.directive:ui-view
|
||||
* @eventType emits on ui-view directive scope
|
||||
* @description
|
||||
*
|
||||
* Fired once the view **begins loading**, *before* the DOM is rendered.
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
* @param {string} viewName Name of the view.
|
||||
*/
|
||||
newScope.$emit('$viewContentLoading', name);
|
||||
|
||||
var clone = $transclude(newScope, function(clone) {
|
||||
var animEnter = $q.defer(), animLeave = $q.defer();
|
||||
var viewAnimData = {
|
||||
$animEnter: animEnter.promise,
|
||||
$animLeave: animLeave.promise,
|
||||
$$animLeave: animLeave
|
||||
};
|
||||
|
||||
clone.data('$uiViewAnim', viewAnimData);
|
||||
renderer.enter(clone, $element, function onUiViewEnter() {
|
||||
animEnter.resolve();
|
||||
if(currentScope) {
|
||||
currentScope.$emit('$viewContentAnimationEnded');
|
||||
}
|
||||
|
||||
if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
|
||||
$uiViewScroll(clone);
|
||||
}
|
||||
});
|
||||
cleanupLastView();
|
||||
});
|
||||
|
||||
currentEl = clone;
|
||||
currentScope = newScope;
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ui.router.state.directive:ui-view#$viewContentLoaded
|
||||
* @eventOf ui.router.state.directive:ui-view
|
||||
* @eventType emits on ui-view directive scope
|
||||
* @description
|
||||
* Fired once the view is **loaded**, *after* the DOM is rendered.
|
||||
*
|
||||
* @param {Object} event Event object.
|
||||
* @param {string} viewName Name of the view.
|
||||
*/
|
||||
currentScope.$emit('$viewContentLoaded', name);
|
||||
currentScope.$eval(onloadExp);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
|
||||
function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
priority: -400,
|
||||
compile: function (tElement) {
|
||||
var initial = tElement.html();
|
||||
return function (scope, $element, attrs) {
|
||||
var current = $state.$current,
|
||||
name = getUiViewName(scope, attrs, $element, $interpolate),
|
||||
locals = current && current.locals[name];
|
||||
|
||||
if (! locals) {
|
||||
return;
|
||||
}
|
||||
|
||||
$element.data('$uiView', { name: name, state: locals.$$state });
|
||||
$element.html(locals.$template ? locals.$template : initial);
|
||||
|
||||
var resolveData = angular.extend({}, locals);
|
||||
scope[locals.$$resolveAs] = resolveData;
|
||||
|
||||
var link = $compile($element.contents());
|
||||
|
||||
if (locals.$$controller) {
|
||||
locals.$scope = scope;
|
||||
locals.$element = $element;
|
||||
var controller = $controller(locals.$$controller, locals);
|
||||
if (locals.$$controllerAs) {
|
||||
scope[locals.$$controllerAs] = controller;
|
||||
scope[locals.$$controllerAs][locals.$$resolveAs] = resolveData;
|
||||
}
|
||||
if (isFunction(controller.$onInit)) controller.$onInit();
|
||||
$element.data('$ngControllerController', controller);
|
||||
$element.children().data('$ngControllerController', controller);
|
||||
}
|
||||
|
||||
link(scope);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared ui-view code for both directives:
|
||||
* Given scope, element, and its attributes, return the view's name
|
||||
*/
|
||||
function getUiViewName(scope, attrs, element, $interpolate) {
|
||||
var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
|
||||
var uiViewCreatedBy = element.inheritedData('$uiView');
|
||||
return name.indexOf('@') >= 0 ? name : (name + '@' + (uiViewCreatedBy ? uiViewCreatedBy.state.name : ''));
|
||||
}
|
||||
|
||||
angular.module('ui.router.state').directive('uiView', $ViewDirective);
|
||||
angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue