diff options
author | SerenaFeng <feng.xiaowei@zte.com.cn> | 2017-05-12 01:49:57 +0800 |
---|---|---|
committer | SerenaFeng <feng.xiaowei@zte.com.cn> | 2017-05-12 10:11:57 +0800 |
commit | f562c31e824f573d9a3254a1eacb4981b29290eb (patch) | |
tree | fd5526fc049fae9760da27b64318ad3fe5ce5767 /utils/test/testapi/3rd_party/static/testapi-ui | |
parent | a16b903c9765049bd28102c812b8307090a97e16 (diff) |
add web portal framework for TestAPI
Change-Id: I62cea8b59ffe6a6cde98051c130f4502c07d3557
Signed-off-by: SerenaFeng <feng.xiaowei@zte.com.cn>
Diffstat (limited to 'utils/test/testapi/3rd_party/static/testapi-ui')
336 files changed, 85390 insertions, 0 deletions
diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/app.js b/utils/test/testapi/3rd_party/static/testapi-ui/app.js new file mode 100644 index 000000000..91e53f49a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/app.js @@ -0,0 +1,220 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + /** Main app module where application dependencies are listed. */ + angular + .module('testapiApp', [ + 'ui.router','ui.bootstrap', 'cgBusy', + 'ngResource', 'angular-confirm' + ]); + + angular + .module('testapiApp') + .config(configureRoutes); + + configureRoutes.$inject = ['$stateProvider', '$urlRouterProvider']; + + /** + * Handle application routing. Specific templates and controllers will be + * used based on the URL route. + */ + function configureRoutes($stateProvider, $urlRouterProvider) { + $urlRouterProvider.otherwise('/'); + $stateProvider. + state('home', { + url: '/', + templateUrl: '/swagger/testapi-ui/components/home/home.html' + }). + state('about', { + url: '/about', + templateUrl: '/swagger/testapi-ui/components/about/about.html' + }). + state('guidelines', { + url: '/guidelines', + templateUrl: '/swagger/testapi-ui/components/guidelines/guidelines.html', + controller: 'GuidelinesController as ctrl' + }). + state('communityResults', { + url: '/community_results', + templateUrl: '/swagger/testapi-ui/components/results/results.html', + controller: 'ResultsController as ctrl' + }). + state('userResults', { + url: '/user_results', + templateUrl: '/swagger/testapi-ui/components/results/results.html', + controller: 'ResultsController as ctrl' + }). + state('resultsDetail', { + url: '/results/:testID', + templateUrl: '/swagger/testapi-ui/components/results-report' + + '/resultsReport.html', + controller: 'ResultsReportController as ctrl' + }). + state('profile', { + url: '/profile', + templateUrl: '/swagger/testapi-ui/components/profile/profile.html', + controller: 'ProfileController as ctrl' + }). + state('authFailure', { + url: '/auth_failure', + templateUrl: '/swagger/testapi-ui/components/home/home.html', + controller: 'AuthFailureController as ctrl' + }). + state('logout', { + url: '/logout', + templateUrl: '/swagger/testapi-ui/components/logout/logout.html', + controller: 'LogoutController as ctrl' + }). + state('userVendors', { + url: '/user_vendors', + templateUrl: '/swagger/testapi-ui/components/vendors/vendors.html', + controller: 'VendorsController as ctrl' + }). + state('publicVendors', { + url: '/public_vendors', + templateUrl: '/swagger/testapi-ui/components/vendors/vendors.html', + controller: 'VendorsController as ctrl' + }). + state('vendor', { + url: '/vendor/:vendorID', + templateUrl: '/swagger/testapi-ui/components/vendors/vendor.html', + controller: 'VendorController as ctrl' + }). + state('userProducts', { + url: '/user_products', + templateUrl: '/swagger/testapi-ui/components/products/products.html', + controller: 'ProductsController as ctrl' + }). + state('publicProducts', { + url: '/public_products', + templateUrl: '/swagger/testapi-ui/components/products/products.html', + controller: 'ProductsController as ctrl' + }). + state('cloud', { + url: '/cloud/:id', + templateUrl: '/swagger/testapi-ui/components/products/cloud.html', + controller: 'ProductController as ctrl' + }). + state('distro', { + url: '/distro/:id', + templateUrl: '/swagger/testapi-ui/components/products/distro.html', + controller: 'ProductController as ctrl' + }); + } + + angular + .module('testapiApp') + .config(disableHttpCache); + + disableHttpCache.$inject = ['$httpProvider']; + + /** + * Disable caching in $http requests. This is primarily for IE, as it + * tends to cache Angular IE requests. + */ + function disableHttpCache($httpProvider) { + if (!$httpProvider.defaults.headers.get) { + $httpProvider.defaults.headers.get = {}; + } + $httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache'; + $httpProvider.defaults.headers.get.Pragma = 'no-cache'; + } + + angular + .module('testapiApp') + .run(setup); + + setup.$inject = [ + '$http', '$rootScope', '$window', '$state', 'testapiApiUrl' + ]; + + /** + * Set up the app with injections into $rootscope. This is mainly for auth + * functions. + */ + function setup($http, $rootScope, $window, $state, testapiApiUrl) { + + $rootScope.auth = {}; + $rootScope.auth.doSignIn = doSignIn; + $rootScope.auth.doSignOut = doSignOut; + $rootScope.auth.doSignCheck = doSignCheck; + + var sign_in_url = testapiApiUrl + '/auth/signin'; + var sign_out_url = testapiApiUrl + '/auth/signout'; + var profile_url = testapiApiUrl + '/profile'; + + /** This function initiates a sign in. */ + function doSignIn() { + $window.location.href = sign_in_url; + } + + /** This function will initate a sign out. */ + function doSignOut() { + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = false; + $window.location.href = sign_out_url; + } + + /** + * This function checks to see if a user is logged in and + * authenticated. + */ + function doSignCheck() { + return $http.get(profile_url, {withCredentials: true}). + success(function (data) { + $rootScope.auth.currentUser = data; + $rootScope.auth.isAuthenticated = true; + }). + error(function () { + $rootScope.auth.currentUser = null; + $rootScope.auth.isAuthenticated = false; + }); + } + + $rootScope.auth.doSignCheck(); + } + + angular + .element(document) + .ready(loadConfig); + + /** + * Load config and start up the angular application. + */ + function loadConfig() { + + var $http = angular.injector(['ng']).get('$http'); + + /** + * Store config variables as constants, and start the app. + */ + function startApp(config) { + // Add config options as constants. + angular.forEach(config, function(value, key) { + angular.module('testapiApp').constant(key, value); + }); + + angular.bootstrap(document, ['testapiApp']); + } + + $http.get('swagger/testapi-ui/config.json').success(function (data) { + startApp(data); + }).error(function () { + startApp({}); + }); + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/css/style.css b/utils/test/testapi/3rd_party/static/testapi-ui/assets/css/style.css new file mode 100644 index 000000000..f3480929f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/css/style.css @@ -0,0 +1,228 @@ +body { + background: white; + color: black; + font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif; +} + +a { + text-decoration: none; + cursor: pointer; +} + +a:hover { + text-decoration: underline; +} + +.heading { + font-size: 3em; + font-weight: bold; + margin-bottom: 10px; + margin-top: 10px; +} + +.heading img { + height: 50px; + vertical-align: text-bottom; +} + +form { + margin: 0; + padding: 0; + border: 0; +} + +fieldset { + border: 0; +} + +input.error { + background: #FAFF78; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'Futura-CondensedExtraBold', 'Futura', 'Helvetica', sans-serif; +} + +.footer { + background: none repeat scroll 0% 0% #333; +} + +.required { + color: #1D6503; +} + +.advisory { + color: #9F8501; +} + +.deprecated { + color: #B03838; +} + +.removed { + color: #801601; +} + +.checkbox { + word-spacing: 20px; + background: #F8F8F8; + padding: 10px; +} + +.checkbox-test-list { + word-spacing: normal; + background: none; +} + +.checkbox-test-list .info-hover { + font-size: 12px; + color: #878787; + cursor: help; +} + +.checkbox-verified { + border: 1px solid #A9A9A9; + text-align: center; + width: 150px; +} + +.capabilities { + color: #4B4B4B; +} + +.capabilities .capability-list-item { + border-bottom: 2px solid #AFAFAF; + padding-bottom: .6em; +} + +.capabilities .capability-name { + font-size: 1.3em; + font-weight: bold; + color: black; +} + +#criteria { + color: #4B4B4B; +} + +.criterion-name { + font-size: 1.1em; + font-weight: bold; +} + +.list-inline li:before { + content: '\00BB'; +} + +.program-about { + font-size: .8em; + padding-top: .3em; + float: right; +} + +.jumbotron .left { + width: 70%; +} + +.container .jumbotron { + background: #F6F6F6; + border-top: 2px solid #C9C9C9; + border-bottom: 2px solid #C9C9C9; + border-radius: 0; +} + +.jumbotron .right { + width: 30%; +} + +.jumbotron img { + width: 90%; + height: 70%; +} + +.jumbotron .openstack-intro__logo { + width: 100%; +} + +.result-filters { + padding-bottom: 10px; + border-top: 2px solid #C9C9C9; + border-bottom: 2px solid #C9C9C9; + margin-bottom: 15px; +} + +@media (min-width: 450px) { + .jumbotron .openstack-intro__logo { + width: 30%; + } + .openstack-intro__logo img { + float: right; + } + .openstack-intro__content > *:first-child { + margin-top: 0; + } + .openstack-intro__content > *:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .jumbotron.openstack-intro { + padding: 40px; + } +} + +.yes { + background: #1A911E; + color: white; + padding-left: .5em; + padding-right: .5em; +} + +.no { + background: #BC0505; + color: white; + padding-left: .5em; + padding-right: .5em; +} + +.button-margin { + margin-bottom: 1em; +} + +.tests-modal-content { + overflow: auto; + max-height: calc(100vh - 100px); +} + +.tests-modal-content textarea { + font-size: .9em; + resize: none; +} + +.test-detail { + padding-left: 10px; +} + +.test-detail ul { + padding-left: 20px; +} + +.test-detail-report { + font-size: .9em; +} + +a.glyphicon { + text-decoration: none; +} + +.test-list-dl { + word-spacing: normal; +} + +.test-list-dl:hover { + text-decoration: none; +} + +.modal-body .row { + margin-bottom: 10px; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/OpenStack_Project_Refstack_mascot_90x90.png b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/OpenStack_Project_Refstack_mascot_90x90.png Binary files differnew file mode 100755 index 000000000..4695090dc --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/OpenStack_Project_Refstack_mascot_90x90.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/openstack-logo.png b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/openstack-logo.png Binary files differnew file mode 100644 index 000000000..826bf2e5d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/openstack-logo.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/opnfv-logo.png b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/opnfv-logo.png Binary files differnew file mode 100644 index 000000000..ad6e81543 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/opnfv-logo.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/refstack-logo.png b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/refstack-logo.png Binary files differnew file mode 100755 index 000000000..fc45f3ee0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/refstack-logo.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/testapi-log.png b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/testapi-log.png Binary files differnew file mode 100644 index 000000000..24777a6a7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/img/testapi-log.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/.bower.json new file mode 100644 index 000000000..27598bab2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/.bower.json @@ -0,0 +1,19 @@ +{ + "name": "angular-animate", + "version": "1.3.20", + "main": "./angular-animate.js", + "ignore": [], + "dependencies": { + "angular": "1.3.20" + }, + "homepage": "https://github.com/angular/bower-angular-animate", + "_release": "1.3.20", + "_resolution": { + "type": "version", + "tag": "v1.3.20", + "commit": "a0d4208f770315df80866fcb449eff913efbbbdc" + }, + "_source": "https://github.com/angular/bower-angular-animate.git", + "_target": "~1.3", + "_originalSource": "angular-animate" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/README.md new file mode 100644 index 000000000..8313da67c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/README.md @@ -0,0 +1,68 @@ +# packaged angular-animate + +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/ngAnimate). +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-animate +``` + +Then add `ngAnimate` as a dependency for your app: + +```javascript +angular.module('myApp', [require('angular-animate')]); +``` + +### bower + +```shell +bower install angular-animate +``` + +Then add a `<script>` to your `index.html`: + +```html +<script src="/bower_components/angular-animate/angular-animate.js"></script> +``` + +Then add `ngAnimate` as a dependency for your app: + +```javascript +angular.module('myApp', ['ngAnimate']); +``` + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/api/ngAnimate). + +## 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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.js new file mode 100644 index 000000000..d0a3f54dc --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.js @@ -0,0 +1,2142 @@ +/** + * @license AngularJS v1.3.20 + * (c) 2010-2014 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'use strict'; + +/* jshint maxlen: false */ + +/** + * @ngdoc module + * @name ngAnimate + * @description + * + * The `ngAnimate` module provides support for JavaScript, CSS3 transition and CSS3 keyframe animation hooks within existing core and custom directives. + * + * <div doc-module-components="ngAnimate"></div> + * + * # Usage + * + * To see animations in action, all that is required is to define the appropriate CSS classes + * or to register a JavaScript animation via the `myModule.animation()` function. The directives that support animation automatically are: + * `ngRepeat`, `ngInclude`, `ngIf`, `ngSwitch`, `ngShow`, `ngHide`, `ngView` and `ngClass`. Custom directives can take advantage of animation + * by using the `$animate` service. + * + * Below is a more detailed breakdown of the supported animation events provided by pre-existing ng directives: + * + * | Directive | Supported Animations | + * |----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| + * | {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave and move | + * | {@link ngRoute.directive:ngView#animations ngView} | enter and leave | + * | {@link ng.directive:ngInclude#animations ngInclude} | enter and leave | + * | {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave | + * | {@link ng.directive:ngIf#animations ngIf} | enter and leave | + * | {@link ng.directive:ngClass#animations ngClass} | add and remove (the CSS class(es) present) | + * | {@link ng.directive:ngShow#animations ngShow} & {@link ng.directive:ngHide#animations ngHide} | add and remove (the ng-hide class value) | + * | {@link ng.directive:form#animation-hooks form} & {@link ng.directive:ngModel#animation-hooks ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) | + * | {@link module:ngMessages#animations ngMessages} | add and remove (ng-active & ng-inactive) | + * | {@link module:ngMessages#animations ngMessage} | enter and leave | + * + * You can find out more information about animations upon visiting each directive page. + * + * Below is an example of how to apply animations to a directive that supports animation hooks: + * + * ```html + * <style type="text/css"> + * .slide.ng-enter, .slide.ng-leave { + * -webkit-transition:0.5s linear all; + * transition:0.5s linear all; + * } + * + * .slide.ng-enter { } /* starting animations for enter */ + * .slide.ng-enter.ng-enter-active { } /* terminal animations for enter */ + * .slide.ng-leave { } /* starting animations for leave */ + * .slide.ng-leave.ng-leave-active { } /* terminal animations for leave */ + * </style> + * + * <!-- + * the animate service will automatically add .ng-enter and .ng-leave to the element + * to trigger the CSS transition/animations + * --> + * <ANY class="slide" ng-include="..."></ANY> + * ``` + * + * Keep in mind that, by default, if an animation is running, any child elements cannot be animated + * until the parent element's animation has completed. This blocking feature can be overridden by + * placing the `ng-animate-children` attribute on a parent container tag. + * + * ```html + * <div class="slide-animation" ng-if="on" ng-animate-children> + * <div class="fade-animation" ng-if="on"> + * <div class="explode-animation" ng-if="on"> + * ... + * </div> + * </div> + * </div> + * ``` + * + * When the `on` expression value changes and an animation is triggered then each of the elements within + * will all animate without the block being applied to child elements. + * + * ## Are animations run when the application starts? + * No they are not. When an application is bootstrapped Angular will disable animations from running to avoid + * a frenzy of animations from being triggered as soon as the browser has rendered the screen. For this to work, + * Angular will wait for two digest cycles until enabling animations. From there on, any animation-triggering + * layout changes in the application will trigger animations as normal. + * + * In addition, upon bootstrap, if the routing system or any directives or load remote data (via $http) then Angular + * will automatically extend the wait time to enable animations once **all** of the outbound HTTP requests + * are complete. + * + * ## CSS-defined Animations + * The animate service will automatically apply two CSS classes to the animated element and these two CSS classes + * are designed to contain the start and end CSS styling. Both CSS transitions and keyframe animations are supported + * and can be used to play along with this naming structure. + * + * The following code below demonstrates how to perform animations using **CSS transitions** with Angular: + * + * ```html + * <style type="text/css"> + * /* + * The animate class is apart of the element and the ng-enter class + * is attached to the element once the enter animation event is triggered + * */ + * .reveal-animation.ng-enter { + * -webkit-transition: 1s linear all; /* Safari/Chrome */ + * transition: 1s linear all; /* All other modern browsers and IE10+ */ + * + * /* The animation preparation code */ + * opacity: 0; + * } + * + * /* + * Keep in mind that you want to combine both CSS + * classes together to avoid any CSS-specificity + * conflicts + * */ + * .reveal-animation.ng-enter.ng-enter-active { + * /* The animation code itself */ + * opacity: 1; + * } + * </style> + * + * <div class="view-container"> + * <div ng-view class="reveal-animation"></div> + * </div> + * ``` + * + * The following code below demonstrates how to perform animations using **CSS animations** with Angular: + * + * ```html + * <style type="text/css"> + * .reveal-animation.ng-enter { + * -webkit-animation: enter_sequence 1s linear; /* Safari/Chrome */ + * animation: enter_sequence 1s linear; /* IE10+ and Future Browsers */ + * } + * @-webkit-keyframes enter_sequence { + * from { opacity:0; } + * to { opacity:1; } + * } + * @keyframes enter_sequence { + * from { opacity:0; } + * to { opacity:1; } + * } + * </style> + * + * <div class="view-container"> + * <div ng-view class="reveal-animation"></div> + * </div> + * ``` + * + * Both CSS3 animations and transitions can be used together and the animate service will figure out the correct duration and delay timing. + * + * Upon DOM mutation, the event class is added first (something like `ng-enter`), then the browser prepares itself to add + * the active class (in this case `ng-enter-active`) which then triggers the animation. The animation module will automatically + * detect the CSS code to determine when the animation ends. Once the animation is over then both CSS classes will be + * removed from the DOM. If a browser does not support CSS transitions or CSS animations then the animation will start and end + * immediately resulting in a DOM element that is at its final state. This final state is when the DOM element + * has no CSS transition/animation classes applied to it. + * + * ### Structural transition animations + * + * Structural transitions (such as enter, leave and move) will always apply a `0s none` transition + * value to force the browser into rendering the styles defined in the setup (`.ng-enter`, `.ng-leave` + * or `.ng-move`) class. This means that any active transition animations operating on the element + * will be cut off to make way for the enter, leave or move animation. + * + * ### Class-based transition animations + * + * Class-based transitions refer to transition animations that are triggered when a CSS class is + * added to or removed from the element (via `$animate.addClass`, `$animate.removeClass`, + * `$animate.setClass`, or by directives such as `ngClass`, `ngModel` and `form`). + * They are different when compared to structural animations since they **do not cancel existing + * animations** nor do they **block successive transitions** from rendering on the same element. + * This distinction allows for **multiple class-based transitions** to be performed on the same element. + * + * In addition to ngAnimate supporting the default (natural) functionality of class-based transition + * animations, ngAnimate also decorates the element with starting and ending CSS classes to aid the + * developer in further styling the element throughout the transition animation. Earlier versions + * of ngAnimate may have caused natural CSS transitions to break and not render properly due to + * $animate temporarily blocking transitions using `0s none` in order to allow the setup CSS class + * (the `-add` or `-remove` class) to be applied without triggering an animation. However, as of + * **version 1.3**, this workaround has been removed with ngAnimate and all non-ngAnimate CSS + * class transitions are compatible with ngAnimate. + * + * There is, however, one special case when dealing with class-based transitions in ngAnimate. + * When rendering class-based transitions that make use of the setup and active CSS classes + * (e.g. `.fade-add` and `.fade-add-active` for when `.fade` is added) be sure to define + * the transition value **on the active CSS class** and not the setup class. + * + * ```css + * .fade-add { + * /* remember to place a 0s transition here + * to ensure that the styles are applied instantly + * even if the element already has a transition style */ + * transition:0s linear all; + * + * /* starting CSS styles */ + * opacity:1; + * } + * .fade-add.fade-add-active { + * /* this will be the length of the animation */ + * transition:1s linear all; + * opacity:0; + * } + * ``` + * + * The setup CSS class (in this case `.fade-add`) also has a transition style property, however, it + * has a duration of zero. This may not be required, however, incase the browser is unable to render + * the styling present in this CSS class instantly then it could be that the browser is attempting + * to perform an unnecessary transition. + * + * This workaround, however, does not apply to standard class-based transitions that are rendered + * when a CSS class containing a transition is applied to an element: + * + * ```css + * /* this works as expected */ + * .fade { + * transition:1s linear all; + * opacity:0; + * } + * ``` + * + * Please keep this in mind when coding the CSS markup that will be used within class-based transitions. + * Also, try not to mix the two class-based animation flavors together since the CSS code may become + * overly complex. + * + * + * ### Preventing Collisions With Third Party Libraries + * + * Some third-party frameworks place animation duration defaults across many element or className + * selectors in order to make their code small and reuseable. This can lead to issues with ngAnimate, which + * is expecting actual animations on these elements and has to wait for their completion. + * + * You can prevent this unwanted behavior by using a prefix on all your animation classes: + * + * ```css + * /* prefixed with animate- */ + * .animate-fade-add.animate-fade-add-active { + * transition:1s linear all; + * opacity:0; + * } + * ``` + * + * You then configure `$animate` to enforce this prefix: + * + * ```js + * $animateProvider.classNameFilter(/animate-/); + * ``` + * </div> + * + * ### CSS Staggering Animations + * A Staggering animation is a collection of animations that are issued with a slight delay in between each successive operation resulting in a + * curtain-like effect. The ngAnimate module (versions >=1.2) supports staggering animations and the stagger effect can be + * performed by creating a **ng-EVENT-stagger** CSS class and attaching that class to the base CSS class used for + * the animation. The style property expected within the stagger class can either be a **transition-delay** or an + * **animation-delay** property (or both if your animation contains both transitions and keyframe animations). + * + * ```css + * .my-animation.ng-enter { + * /* standard transition code */ + * -webkit-transition: 1s linear all; + * transition: 1s linear all; + * opacity:0; + * } + * .my-animation.ng-enter-stagger { + * /* this will have a 100ms delay between each successive leave animation */ + * -webkit-transition-delay: 0.1s; + * transition-delay: 0.1s; + * + * /* in case the stagger doesn't work then these two values + * must be set to 0 to avoid an accidental CSS inheritance */ + * -webkit-transition-duration: 0s; + * transition-duration: 0s; + * } + * .my-animation.ng-enter.ng-enter-active { + * /* standard transition styles */ + * opacity:1; + * } + * ``` + * + * Staggering animations work by default in ngRepeat (so long as the CSS class is defined). Outside of ngRepeat, to use staggering animations + * on your own, they can be triggered by firing multiple calls to the same event on $animate. However, the restrictions surrounding this + * are that each of the elements must have the same CSS className value as well as the same parent element. A stagger operation + * will also be reset if more than 10ms has passed after the last animation has been fired. + * + * The following code will issue the **ng-leave-stagger** event on the element provided: + * + * ```js + * var kids = parent.children(); + * + * $animate.leave(kids[0]); //stagger index=0 + * $animate.leave(kids[1]); //stagger index=1 + * $animate.leave(kids[2]); //stagger index=2 + * $animate.leave(kids[3]); //stagger index=3 + * $animate.leave(kids[4]); //stagger index=4 + * + * $timeout(function() { + * //stagger has reset itself + * $animate.leave(kids[5]); //stagger index=0 + * $animate.leave(kids[6]); //stagger index=1 + * }, 100, false); + * ``` + * + * Stagger animations are currently only supported within CSS-defined animations. + * + * ## JavaScript-defined Animations + * In the event that you do not want to use CSS3 transitions or CSS3 animations or if you wish to offer animations on browsers that do not + * yet support CSS transitions/animations, then you can make use of JavaScript animations defined inside of your AngularJS module. + * + * ```js + * //!annotate="YourApp" Your AngularJS Module|Replace this or ngModule with the module that you used to define your application. + * var ngModule = angular.module('YourApp', ['ngAnimate']); + * ngModule.animation('.my-crazy-animation', function() { + * return { + * enter: function(element, done) { + * //run the animation here and call done when the animation is complete + * return function(cancelled) { + * //this (optional) function will be called when the animation + * //completes or when the animation is cancelled (the cancelled + * //flag will be set to true if cancelled). + * }; + * }, + * leave: function(element, done) { }, + * move: function(element, done) { }, + * + * //animation that can be triggered before the class is added + * beforeAddClass: function(element, className, done) { }, + * + * //animation that can be triggered after the class is added + * addClass: function(element, className, done) { }, + * + * //animation that can be triggered before the class is removed + * beforeRemoveClass: function(element, className, done) { }, + * + * //animation that can be triggered after the class is removed + * removeClass: function(element, className, done) { } + * }; + * }); + * ``` + * + * JavaScript-defined animations are created with a CSS-like class selector and a collection of events which are set to run + * a javascript callback function. When an animation is triggered, $animate will look for a matching animation which fits + * the element's CSS class attribute value and then run the matching animation event function (if found). + * In other words, if the CSS classes present on the animated element match any of the JavaScript animations then the callback function will + * be executed. It should be also noted that only simple, single class selectors are allowed (compound class selectors are not supported). + * + * Within a JavaScript animation, an object containing various event callback animation functions is expected to be returned. + * As explained above, these callbacks are triggered based on the animation event. Therefore if an enter animation is run, + * and the JavaScript animation is found, then the enter callback will handle that animation (in addition to the CSS keyframe animation + * or transition code that is defined via a stylesheet). + * + * + * ### Applying Directive-specific Styles to an Animation + * In some cases a directive or service may want to provide `$animate` with extra details that the animation will + * include into its animation. Let's say for example we wanted to render an animation that animates an element + * towards the mouse coordinates as to where the user clicked last. By collecting the X/Y coordinates of the click + * (via the event parameter) we can set the `top` and `left` styles into an object and pass that into our function + * call to `$animate.addClass`. + * + * ```js + * canvas.on('click', function(e) { + * $animate.addClass(element, 'on', { + * to: { + * left : e.client.x + 'px', + * top : e.client.y + 'px' + * } + * }): + * }); + * ``` + * + * Now when the animation runs, and a transition or keyframe animation is picked up, then the animation itself will + * also include and transition the styling of the `left` and `top` properties into its running animation. If we want + * to provide some starting animation values then we can do so by placing the starting animations styles into an object + * called `from` in the same object as the `to` animations. + * + * ```js + * canvas.on('click', function(e) { + * $animate.addClass(element, 'on', { + * from: { + * position: 'absolute', + * left: '0px', + * top: '0px' + * }, + * to: { + * left : e.client.x + 'px', + * top : e.client.y + 'px' + * } + * }): + * }); + * ``` + * + * Once the animation is complete or cancelled then the union of both the before and after styles are applied to the + * element. If `ngAnimate` is not present then the styles will be applied immediately. + * + */ + +angular.module('ngAnimate', ['ng']) + + /** + * @ngdoc provider + * @name $animateProvider + * @description + * + * The `$animateProvider` allows developers to register JavaScript animation event handlers directly inside of a module. + * When an animation is triggered, the $animate service will query the $animate service to find any animations that match + * the provided name value. + * + * Requires the {@link ngAnimate `ngAnimate`} module to be installed. + * + * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. + * + */ + .directive('ngAnimateChildren', function() { + var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; + return function(scope, element, attrs) { + var val = attrs.ngAnimateChildren; + if (angular.isString(val) && val.length === 0) { //empty attribute + element.data(NG_ANIMATE_CHILDREN, true); + } else { + scope.$watch(val, function(value) { + element.data(NG_ANIMATE_CHILDREN, !!value); + }); + } + }; + }) + + //this private service is only used within CSS-enabled animations + //IE8 + IE9 do not support rAF natively, but that is fine since they + //also don't support transitions and keyframes which means that the code + //below will never be used by the two browsers. + .factory('$$animateReflow', ['$$rAF', '$document', function($$rAF, $document) { + var bod = $document[0].body; + return function(fn) { + //the returned function acts as the cancellation function + return $$rAF(function() { + //the line below will force the browser to perform a repaint + //so that all the animated elements within the animation frame + //will be properly updated and drawn on screen. This is + //required to perform multi-class CSS based animations with + //Firefox. DO NOT REMOVE THIS LINE. DO NOT OPTIMIZE THIS LINE. + //THE MINIFIER WILL REMOVE IT OTHERWISE WHICH WILL RESULT IN AN + //UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND WILL + //TAKE YEARS AWAY FROM YOUR LIFE! + fn(bod.offsetWidth); + }); + }; + }]) + + .config(['$provide', '$animateProvider', function($provide, $animateProvider) { + var noop = angular.noop; + var forEach = angular.forEach; + var selectors = $animateProvider.$$selectors; + var isArray = angular.isArray; + var isString = angular.isString; + var isObject = angular.isObject; + + var ELEMENT_NODE = 1; + var NG_ANIMATE_STATE = '$$ngAnimateState'; + var NG_ANIMATE_CHILDREN = '$$ngAnimateChildren'; + var NG_ANIMATE_CLASS_NAME = 'ng-animate'; + var rootAnimateState = {running: true}; + + function extractElementNode(element) { + for (var i = 0; i < element.length; i++) { + var elm = element[i]; + if (elm.nodeType == ELEMENT_NODE) { + return elm; + } + } + } + + function prepareElement(element) { + return element && angular.element(element); + } + + function stripCommentsFromElement(element) { + return angular.element(extractElementNode(element)); + } + + function isMatchingElement(elm1, elm2) { + return extractElementNode(elm1) == extractElementNode(elm2); + } + var $$jqLite; + $provide.decorator('$animate', + ['$delegate', '$$q', '$injector', '$sniffer', '$rootElement', '$$asyncCallback', '$rootScope', '$document', '$templateRequest', '$$jqLite', + function($delegate, $$q, $injector, $sniffer, $rootElement, $$asyncCallback, $rootScope, $document, $templateRequest, $$$jqLite) { + + $$jqLite = $$$jqLite; + $rootElement.data(NG_ANIMATE_STATE, rootAnimateState); + + // Wait until all directive and route-related templates are downloaded and + // compiled. The $templateRequest.totalPendingRequests variable keeps track of + // all of the remote templates being currently downloaded. If there are no + // templates currently downloading then the watcher will still fire anyway. + var deregisterWatch = $rootScope.$watch( + function() { return $templateRequest.totalPendingRequests; }, + function(val, oldVal) { + if (val !== 0) return; + deregisterWatch(); + + // Now that all templates have been downloaded, $animate will wait until + // the post digest queue is empty before enabling animations. By having two + // calls to $postDigest calls we can ensure that the flag is enabled at the + // very end of the post digest queue. Since all of the animations in $animate + // use $postDigest, it's important that the code below executes at the end. + // This basically means that the page is fully downloaded and compiled before + // any animations are triggered. + $rootScope.$$postDigest(function() { + $rootScope.$$postDigest(function() { + rootAnimateState.running = false; + }); + }); + } + ); + + var globalAnimationCounter = 0; + var classNameFilter = $animateProvider.classNameFilter(); + var isAnimatableClassName = !classNameFilter + ? function() { return true; } + : function(className) { + return classNameFilter.test(className); + }; + + function classBasedAnimationsBlocked(element, setter) { + var data = element.data(NG_ANIMATE_STATE) || {}; + if (setter) { + data.running = true; + data.structural = true; + element.data(NG_ANIMATE_STATE, data); + } + return data.disabled || (data.running && data.structural); + } + + function runAnimationPostDigest(fn) { + var cancelFn, defer = $$q.defer(); + defer.promise.$$cancelFn = function() { + cancelFn && cancelFn(); + }; + $rootScope.$$postDigest(function() { + cancelFn = fn(function() { + defer.resolve(); + }); + }); + return defer.promise; + } + + function parseAnimateOptions(options) { + // some plugin code may still be passing in the callback + // function as the last param for the $animate methods so + // it's best to only allow string or array values for now + if (isObject(options)) { + if (options.tempClasses && isString(options.tempClasses)) { + options.tempClasses = options.tempClasses.split(/\s+/); + } + return options; + } + } + + function resolveElementClasses(element, cache, runningAnimations) { + runningAnimations = runningAnimations || {}; + + var lookup = {}; + forEach(runningAnimations, function(data, selector) { + forEach(selector.split(' '), function(s) { + lookup[s]=data; + }); + }); + + var hasClasses = Object.create(null); + forEach((element.attr('class') || '').split(/\s+/), function(className) { + hasClasses[className] = true; + }); + + var toAdd = [], toRemove = []; + forEach((cache && cache.classes) || [], function(status, className) { + var hasClass = hasClasses[className]; + var matchingAnimation = lookup[className] || {}; + + // When addClass and removeClass is called then $animate will check to + // see if addClass and removeClass cancel each other out. When there are + // more calls to removeClass than addClass then the count falls below 0 + // and then the removeClass animation will be allowed. Otherwise if the + // count is above 0 then that means an addClass animation will commence. + // Once an animation is allowed then the code will also check to see if + // there exists any on-going animation that is already adding or remvoing + // the matching CSS class. + if (status === false) { + //does it have the class or will it have the class + if (hasClass || matchingAnimation.event == 'addClass') { + toRemove.push(className); + } + } else if (status === true) { + //is the class missing or will it be removed? + if (!hasClass || matchingAnimation.event == 'removeClass') { + toAdd.push(className); + } + } + }); + + return (toAdd.length + toRemove.length) > 0 && [toAdd.join(' '), toRemove.join(' ')]; + } + + function lookup(name) { + if (name) { + var matches = [], + flagMap = {}, + classes = name.substr(1).split('.'); + + //the empty string value is the default animation + //operation which performs CSS transition and keyframe + //animations sniffing. This is always included for each + //element animation procedure if the browser supports + //transitions and/or keyframe animations. The default + //animation is added to the top of the list to prevent + //any previous animations from affecting the element styling + //prior to the element being animated. + if ($sniffer.transitions || $sniffer.animations) { + matches.push($injector.get(selectors[''])); + } + + for (var i=0; i < classes.length; i++) { + var klass = classes[i], + selectorFactoryName = selectors[klass]; + if (selectorFactoryName && !flagMap[klass]) { + matches.push($injector.get(selectorFactoryName)); + flagMap[klass] = true; + } + } + return matches; + } + } + + function animationRunner(element, animationEvent, className, options) { + //transcluded directives may sometimes fire an animation using only comment nodes + //best to catch this early on to prevent any animation operations from occurring + var node = element[0]; + if (!node) { + return; + } + + if (options) { + options.to = options.to || {}; + options.from = options.from || {}; + } + + var classNameAdd; + var classNameRemove; + if (isArray(className)) { + classNameAdd = className[0]; + classNameRemove = className[1]; + if (!classNameAdd) { + className = classNameRemove; + animationEvent = 'removeClass'; + } else if (!classNameRemove) { + className = classNameAdd; + animationEvent = 'addClass'; + } else { + className = classNameAdd + ' ' + classNameRemove; + } + } + + var isSetClassOperation = animationEvent == 'setClass'; + var isClassBased = isSetClassOperation + || animationEvent == 'addClass' + || animationEvent == 'removeClass' + || animationEvent == 'animate'; + + var currentClassName = element.attr('class'); + var classes = currentClassName + ' ' + className; + if (!isAnimatableClassName(classes)) { + return; + } + + var beforeComplete = noop, + beforeCancel = [], + before = [], + afterComplete = noop, + afterCancel = [], + after = []; + + var animationLookup = (' ' + classes).replace(/\s+/g,'.'); + forEach(lookup(animationLookup), function(animationFactory) { + var created = registerAnimation(animationFactory, animationEvent); + if (!created && isSetClassOperation) { + registerAnimation(animationFactory, 'addClass'); + registerAnimation(animationFactory, 'removeClass'); + } + }); + + function registerAnimation(animationFactory, event) { + var afterFn = animationFactory[event]; + var beforeFn = animationFactory['before' + event.charAt(0).toUpperCase() + event.substr(1)]; + if (afterFn || beforeFn) { + if (event == 'leave') { + beforeFn = afterFn; + //when set as null then animation knows to skip this phase + afterFn = null; + } + after.push({ + event: event, fn: afterFn + }); + before.push({ + event: event, fn: beforeFn + }); + return true; + } + } + + function run(fns, cancellations, allCompleteFn) { + var animations = []; + forEach(fns, function(animation) { + animation.fn && animations.push(animation); + }); + + var count = 0; + function afterAnimationComplete(index) { + if (cancellations) { + (cancellations[index] || noop)(); + if (++count < animations.length) return; + cancellations = null; + } + allCompleteFn(); + } + + //The code below adds directly to the array in order to work with + //both sync and async animations. Sync animations are when the done() + //operation is called right away. DO NOT REFACTOR! + forEach(animations, function(animation, index) { + var progress = function() { + afterAnimationComplete(index); + }; + switch (animation.event) { + case 'setClass': + cancellations.push(animation.fn(element, classNameAdd, classNameRemove, progress, options)); + break; + case 'animate': + cancellations.push(animation.fn(element, className, options.from, options.to, progress)); + break; + case 'addClass': + cancellations.push(animation.fn(element, classNameAdd || className, progress, options)); + break; + case 'removeClass': + cancellations.push(animation.fn(element, classNameRemove || className, progress, options)); + break; + default: + cancellations.push(animation.fn(element, progress, options)); + break; + } + }); + + if (cancellations && cancellations.length === 0) { + allCompleteFn(); + } + } + + return { + node: node, + event: animationEvent, + className: className, + isClassBased: isClassBased, + isSetClassOperation: isSetClassOperation, + applyStyles: function() { + if (options) { + element.css(angular.extend(options.from || {}, options.to || {})); + } + }, + before: function(allCompleteFn) { + beforeComplete = allCompleteFn; + run(before, beforeCancel, function() { + beforeComplete = noop; + allCompleteFn(); + }); + }, + after: function(allCompleteFn) { + afterComplete = allCompleteFn; + run(after, afterCancel, function() { + afterComplete = noop; + allCompleteFn(); + }); + }, + cancel: function() { + if (beforeCancel) { + forEach(beforeCancel, function(cancelFn) { + (cancelFn || noop)(true); + }); + beforeComplete(true); + } + if (afterCancel) { + forEach(afterCancel, function(cancelFn) { + (cancelFn || noop)(true); + }); + afterComplete(true); + } + } + }; + } + + /** + * @ngdoc service + * @name $animate + * @kind object + * + * @description + * The `$animate` service provides animation detection support while performing DOM operations (enter, leave and move) as well as during addClass and removeClass operations. + * When any of these operations are run, the $animate service + * will examine any JavaScript-defined animations (which are defined by using the $animateProvider provider object) + * as well as any CSS-defined animations against the CSS classes present on the element once the DOM operation is run. + * + * The `$animate` service is used behind the scenes with pre-existing directives and animation with these directives + * will work out of the box without any extra configuration. + * + * Requires the {@link ngAnimate `ngAnimate`} module to be installed. + * + * Please visit the {@link ngAnimate `ngAnimate`} module overview page learn more about how to use animations in your application. + * ## Callback Promises + * With AngularJS 1.3, each of the animation methods, on the `$animate` service, return a promise when called. The + * promise itself is then resolved once the animation has completed itself, has been cancelled or has been + * skipped due to animations being disabled. (Note that even if the animation is cancelled it will still + * call the resolve function of the animation.) + * + * ```js + * $animate.enter(element, container).then(function() { + * //...this is called once the animation is complete... + * }); + * ``` + * + * Also note that, due to the nature of the callback promise, if any Angular-specific code (like changing the scope, + * location of the page, etc...) is executed within the callback promise then be sure to wrap the code using + * `$scope.$apply(...)`; + * + * ```js + * $animate.leave(element).then(function() { + * $scope.$apply(function() { + * $location.path('/new-page'); + * }); + * }); + * ``` + * + * An animation can also be cancelled by calling the `$animate.cancel(promise)` method with the provided + * promise that was returned when the animation was started. + * + * ```js + * var promise = $animate.addClass(element, 'super-long-animation'); + * promise.then(function() { + * //this will still be called even if cancelled + * }); + * + * element.on('click', function() { + * //tooo lazy to wait for the animation to end + * $animate.cancel(promise); + * }); + * ``` + * + * (Keep in mind that the promise cancellation is unique to `$animate` since promises in + * general cannot be cancelled.) + * + */ + return { + /** + * @ngdoc method + * @name $animate#animate + * @kind function + * + * @description + * Performs an inline animation on the element which applies the provided `to` and `from` CSS styles to the element. + * If any detected CSS transition, keyframe or JavaScript matches the provided `className` value then the animation + * will take on the provided styles. For example, if a transition animation is set for the given className then the + * provided `from` and `to` styles will be applied alongside the given transition. If a JavaScript animation is + * detected then the provided styles will be given in as function paramters. + * + * ```js + * ngModule.animation('.my-inline-animation', function() { + * return { + * animate : function(element, className, from, to, done) { + * //styles + * } + * } + * }); + * ``` + * + * Below is a breakdown of each step that occurs during the `animate` animation: + * + * | Animation Step | What the element class attribute looks like | + * |-----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------| + * | 1. `$animate.animate(...)` is called | `class="my-animation"` | + * | 2. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` | + * | 3. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` | + * | 4. the `className` class value is added to the element | `class="my-animation ng-animate className"` | + * | 5. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate className"` | + * | 6. `$animate` blocks all CSS transitions on the element to ensure the `.className` class styling is applied right away| `class="my-animation ng-animate className"` | + * | 7. `$animate` applies the provided collection of `from` CSS styles to the element | `class="my-animation ng-animate className"` | + * | 8. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate className"` | + * | 9. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate className"` | + * | 10. the `className-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate className className-active"` | + * | 11. `$animate` applies the collection of `to` CSS styles to the element which are then handled by the transition | `class="my-animation ng-animate className className-active"` | + * | 12. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate className className-active"` | + * | 13. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` | + * | 14. The returned promise is resolved. | `class="my-animation"` | + * + * @param {DOMElement} element the element that will be the focus of the enter animation + * @param {object} from a collection of CSS styles that will be applied to the element at the start of the animation + * @param {object} to a collection of CSS styles that the element will animate towards + * @param {string=} className an optional CSS class that will be added to the element for the duration of the animation (the default class is `ng-inline-animate`) + * @param {object=} options an optional collection of options that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + animate: function(element, from, to, className, options) { + className = className || 'ng-inline-animate'; + options = parseAnimateOptions(options) || {}; + options.from = to ? from : null; + options.to = to ? to : from; + + return runAnimationPostDigest(function(done) { + return performAnimation('animate', className, stripCommentsFromElement(element), null, null, noop, options, done); + }); + }, + + /** + * @ngdoc method + * @name $animate#enter + * @kind function + * + * @description + * Appends the element to the parentElement element that resides in the document and then runs the enter animation. Once + * the animation is started, the following CSS classes will be present on the element for the duration of the animation: + * + * Below is a breakdown of each step that occurs during enter animation: + * + * | Animation Step | What the element class attribute looks like | + * |-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------| + * | 1. `$animate.enter(...)` is called | `class="my-animation"` | + * | 2. element is inserted into the `parentElement` element or beside the `afterElement` element | `class="my-animation"` | + * | 3. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` | + * | 4. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` | + * | 5. the `.ng-enter` class is added to the element | `class="my-animation ng-animate ng-enter"` | + * | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate ng-enter"` | + * | 7. `$animate` blocks all CSS transitions on the element to ensure the `.ng-enter` class styling is applied right away | `class="my-animation ng-animate ng-enter"` | + * | 8. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate ng-enter"` | + * | 9. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate ng-enter"` | + * | 10. the `.ng-enter-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate ng-enter ng-enter-active"` | + * | 11. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate ng-enter ng-enter-active"` | + * | 12. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` | + * | 13. The returned promise is resolved. | `class="my-animation"` | + * + * @param {DOMElement} element the element that will be the focus of the enter animation + * @param {DOMElement} parentElement the parent element of the element that will be the focus of the enter animation + * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the enter animation + * @param {object=} options an optional collection of options that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + enter: function(element, parentElement, afterElement, options) { + options = parseAnimateOptions(options); + element = angular.element(element); + parentElement = prepareElement(parentElement); + afterElement = prepareElement(afterElement); + + classBasedAnimationsBlocked(element, true); + $delegate.enter(element, parentElement, afterElement); + return runAnimationPostDigest(function(done) { + return performAnimation('enter', 'ng-enter', stripCommentsFromElement(element), parentElement, afterElement, noop, options, done); + }); + }, + + /** + * @ngdoc method + * @name $animate#leave + * @kind function + * + * @description + * Runs the leave animation operation and, upon completion, removes the element from the DOM. Once + * the animation is started, the following CSS classes will be added for the duration of the animation: + * + * Below is a breakdown of each step that occurs during leave animation: + * + * | Animation Step | What the element class attribute looks like | + * |-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------| + * | 1. `$animate.leave(...)` is called | `class="my-animation"` | + * | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` | + * | 3. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` | + * | 4. the `.ng-leave` class is added to the element | `class="my-animation ng-animate ng-leave"` | + * | 5. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate ng-leave"` | + * | 6. `$animate` blocks all CSS transitions on the element to ensure the `.ng-leave` class styling is applied right away | `class="my-animation ng-animate ng-leave"` | + * | 7. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate ng-leave"` | + * | 8. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate ng-leave"` | + * | 9. the `.ng-leave-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate ng-leave ng-leave-active"` | + * | 10. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate ng-leave ng-leave-active"` | + * | 11. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` | + * | 12. The element is removed from the DOM | ... | + * | 13. The returned promise is resolved. | ... | + * + * @param {DOMElement} element the element that will be the focus of the leave animation + * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + leave: function(element, options) { + options = parseAnimateOptions(options); + element = angular.element(element); + + cancelChildAnimations(element); + classBasedAnimationsBlocked(element, true); + return runAnimationPostDigest(function(done) { + return performAnimation('leave', 'ng-leave', stripCommentsFromElement(element), null, null, function() { + $delegate.leave(element); + }, options, done); + }); + }, + + /** + * @ngdoc method + * @name $animate#move + * @kind function + * + * @description + * Fires the move DOM operation. Just before the animation starts, the animate service will either append it into the parentElement container or + * add the element directly after the afterElement element if present. Then the move animation will be run. Once + * the animation is started, the following CSS classes will be added for the duration of the animation: + * + * Below is a breakdown of each step that occurs during move animation: + * + * | Animation Step | What the element class attribute looks like | + * |----------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------| + * | 1. `$animate.move(...)` is called | `class="my-animation"` | + * | 2. element is moved into the parentElement element or beside the afterElement element | `class="my-animation"` | + * | 3. `$animate` waits for the next digest to start the animation | `class="my-animation ng-animate"` | + * | 4. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` | + * | 5. the `.ng-move` class is added to the element | `class="my-animation ng-animate ng-move"` | + * | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate ng-move"` | + * | 7. `$animate` blocks all CSS transitions on the element to ensure the `.ng-move` class styling is applied right away | `class="my-animation ng-animate ng-move"` | + * | 8. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate ng-move"` | + * | 9. `$animate` removes the CSS transition block placed on the element | `class="my-animation ng-animate ng-move"` | + * | 10. the `.ng-move-active` class is added (this triggers the CSS transition/animation) | `class="my-animation ng-animate ng-move ng-move-active"` | + * | 11. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate ng-move ng-move-active"` | + * | 12. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` | + * | 13. The returned promise is resolved. | `class="my-animation"` | + * + * @param {DOMElement} element the element that will be the focus of the move animation + * @param {DOMElement} parentElement the parentElement element of the element that will be the focus of the move animation + * @param {DOMElement} afterElement the sibling element (which is the previous element) of the element that will be the focus of the move animation + * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + move: function(element, parentElement, afterElement, options) { + options = parseAnimateOptions(options); + element = angular.element(element); + parentElement = prepareElement(parentElement); + afterElement = prepareElement(afterElement); + + cancelChildAnimations(element); + classBasedAnimationsBlocked(element, true); + $delegate.move(element, parentElement, afterElement); + return runAnimationPostDigest(function(done) { + return performAnimation('move', 'ng-move', stripCommentsFromElement(element), parentElement, afterElement, noop, options, done); + }); + }, + + /** + * @ngdoc method + * @name $animate#addClass + * + * @description + * Triggers a custom animation event based off the className variable and then attaches the className value to the element as a CSS class. + * Unlike the other animation methods, the animate service will suffix the className value with {@type -add} in order to provide + * the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if no CSS transitions + * or keyframes are defined on the -add-active or base CSS class). + * + * Below is a breakdown of each step that occurs during addClass animation: + * + * | Animation Step | What the element class attribute looks like | + * |--------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| + * | 1. `$animate.addClass(element, 'super')` is called | `class="my-animation"` | + * | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate"` | + * | 3. the `.super-add` class is added to the element | `class="my-animation ng-animate super-add"` | + * | 4. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate super-add"` | + * | 5. the `.super` and `.super-add-active` classes are added (this triggers the CSS transition/animation) | `class="my-animation ng-animate super super-add super-add-active"` | + * | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate super super-add super-add-active"` | + * | 7. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate super super-add super-add-active"` | + * | 8. The animation ends and all generated CSS classes are removed from the element | `class="my-animation super"` | + * | 9. The super class is kept on the element | `class="my-animation super"` | + * | 10. The returned promise is resolved. | `class="my-animation super"` | + * + * @param {DOMElement} element the element that will be animated + * @param {string} className the CSS class that will be added to the element and then animated + * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + addClass: function(element, className, options) { + return this.setClass(element, className, [], options); + }, + + /** + * @ngdoc method + * @name $animate#removeClass + * + * @description + * Triggers a custom animation event based off the className variable and then removes the CSS class provided by the className value + * from the element. Unlike the other animation methods, the animate service will suffix the className value with {@type -remove} in + * order to provide the animate service the setup and active CSS classes in order to trigger the animation (this will be skipped if + * no CSS transitions or keyframes are defined on the -remove or base CSS classes). + * + * Below is a breakdown of each step that occurs during removeClass animation: + * + * | Animation Step | What the element class attribute looks like | + * |----------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| + * | 1. `$animate.removeClass(element, 'super')` is called | `class="my-animation super"` | + * | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation super ng-animate"` | + * | 3. the `.super-remove` class is added to the element | `class="my-animation super ng-animate super-remove"` | + * | 4. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation super ng-animate super-remove"` | + * | 5. the `.super-remove-active` classes are added and `.super` is removed (this triggers the CSS transition/animation) | `class="my-animation ng-animate super-remove super-remove-active"` | + * | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate super-remove super-remove-active"` | + * | 7. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate super-remove super-remove-active"` | + * | 8. The animation ends and all generated CSS classes are removed from the element | `class="my-animation"` | + * | 9. The returned promise is resolved. | `class="my-animation"` | + * + * + * @param {DOMElement} element the element that will be animated + * @param {string} className the CSS class that will be animated and then removed from the element + * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + removeClass: function(element, className, options) { + return this.setClass(element, [], className, options); + }, + + /** + * + * @ngdoc method + * @name $animate#setClass + * + * @description Adds and/or removes the given CSS classes to and from the element. + * Once complete, the `done()` callback will be fired (if provided). + * + * | Animation Step | What the element class attribute looks like | + * |----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------| + * | 1. `$animate.setClass(element, 'on', 'off')` is called | `class="my-animation off"` | + * | 2. `$animate` runs the JavaScript-defined animations detected on the element | `class="my-animation ng-animate off"` | + * | 3. the `.on-add` and `.off-remove` classes are added to the element | `class="my-animation ng-animate on-add off-remove off"` | + * | 4. `$animate` waits for a single animation frame (this performs a reflow) | `class="my-animation ng-animate on-add off-remove off"` | + * | 5. the `.on`, `.on-add-active` and `.off-remove-active` classes are added and `.off` is removed (this triggers the CSS transition/animation) | `class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active"` | + * | 6. `$animate` scans the element styles to get the CSS transition/animation duration and delay | `class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active"` | + * | 7. `$animate` waits for the animation to complete (via events and timeout) | `class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active"` | + * | 8. The animation ends and all generated CSS classes are removed from the element | `class="my-animation on"` | + * | 9. The returned promise is resolved. | `class="my-animation on"` | + * + * @param {DOMElement} element the element which will have its CSS classes changed + * removed from it + * @param {string} add the CSS classes which will be added to the element + * @param {string} remove the CSS class which will be removed from the element + * CSS classes have been set on the element + * @param {object=} options an optional collection of styles that will be picked up by the CSS transition/animation + * @return {Promise} the animation callback promise + */ + setClass: function(element, add, remove, options) { + options = parseAnimateOptions(options); + + var STORAGE_KEY = '$$animateClasses'; + element = angular.element(element); + element = stripCommentsFromElement(element); + + if (classBasedAnimationsBlocked(element)) { + return $delegate.$$setClassImmediately(element, add, remove, options); + } + + // we're using a combined array for both the add and remove + // operations since the ORDER OF addClass and removeClass matters + var classes, cache = element.data(STORAGE_KEY); + var hasCache = !!cache; + if (!cache) { + cache = {}; + cache.classes = {}; + } + classes = cache.classes; + + add = isArray(add) ? add : add.split(' '); + forEach(add, function(c) { + if (c && c.length) { + classes[c] = true; + } + }); + + remove = isArray(remove) ? remove : remove.split(' '); + forEach(remove, function(c) { + if (c && c.length) { + classes[c] = false; + } + }); + + if (hasCache) { + if (options && cache.options) { + cache.options = angular.extend(cache.options || {}, options); + } + + //the digest cycle will combine all the animations into one function + return cache.promise; + } else { + element.data(STORAGE_KEY, cache = { + classes: classes, + options: options + }); + } + + return cache.promise = runAnimationPostDigest(function(done) { + var cache, parentNode, parentElement, elementNode = extractElementNode(element); + if (elementNode) { + cache = element.data(STORAGE_KEY); + element.removeData(STORAGE_KEY); + + parentElement = element.parent(); + parentNode = elementNode.parentNode; + } + + // TODO(matsko): move this code into the animationsDisabled() function once #8092 is fixed + if (!parentNode || parentNode['$$NG_REMOVED'] || elementNode['$$NG_REMOVED']) { + done(); + return; + } + + var state = element.data(NG_ANIMATE_STATE) || {}; + var classes = resolveElementClasses(element, cache, state.active); + return !classes + ? done() + : performAnimation('setClass', classes, element, parentElement, null, function() { + if (classes[0]) $delegate.$$addClassImmediately(element, classes[0]); + if (classes[1]) $delegate.$$removeClassImmediately(element, classes[1]); + }, cache.options, done); + }); + }, + + /** + * @ngdoc method + * @name $animate#cancel + * @kind function + * + * @param {Promise} animationPromise The animation promise that is returned when an animation is started. + * + * @description + * Cancels the provided animation. + */ + cancel: function(promise) { + promise.$$cancelFn(); + }, + + /** + * @ngdoc method + * @name $animate#enabled + * @kind function + * + * @param {boolean=} value If provided then set the animation on or off. + * @param {DOMElement=} element If provided then the element will be used to represent the enable/disable operation + * @return {boolean} Current animation state. + * + * @description + * Globally enables/disables animations. + * + */ + enabled: function(value, element) { + switch (arguments.length) { + case 2: + if (value) { + cleanup(element); + } else { + var data = element.data(NG_ANIMATE_STATE) || {}; + data.disabled = true; + element.data(NG_ANIMATE_STATE, data); + } + break; + + case 1: + rootAnimateState.disabled = !value; + break; + + default: + value = !rootAnimateState.disabled; + break; + } + return !!value; + } + }; + + /* + all animations call this shared animation triggering function internally. + The animationEvent variable refers to the JavaScript animation event that will be triggered + and the className value is the name of the animation that will be applied within the + CSS code. Element, `parentElement` and `afterElement` are provided DOM elements for the animation + and the onComplete callback will be fired once the animation is fully complete. + */ + function performAnimation(animationEvent, className, element, parentElement, afterElement, domOperation, options, doneCallback) { + var noopCancel = noop; + var runner = animationRunner(element, animationEvent, className, options); + if (!runner) { + fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); + closeAnimation(); + return noopCancel; + } + + animationEvent = runner.event; + className = runner.className; + var elementEvents = angular.element._data(runner.node); + elementEvents = elementEvents && elementEvents.events; + + if (!parentElement) { + parentElement = afterElement ? afterElement.parent() : element.parent(); + } + + //skip the animation if animations are disabled, a parent is already being animated, + //the element is not currently attached to the document body or then completely close + //the animation if any matching animations are not found at all. + //NOTE: IE8 + IE9 should close properly (run closeAnimation()) in case an animation was found. + if (animationsDisabled(element, parentElement)) { + fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); + closeAnimation(); + return noopCancel; + } + + var ngAnimateState = element.data(NG_ANIMATE_STATE) || {}; + var runningAnimations = ngAnimateState.active || {}; + var totalActiveAnimations = ngAnimateState.totalActive || 0; + var lastAnimation = ngAnimateState.last; + var skipAnimation = false; + + if (totalActiveAnimations > 0) { + var animationsToCancel = []; + if (!runner.isClassBased) { + if (animationEvent == 'leave' && runningAnimations['ng-leave']) { + skipAnimation = true; + } else { + //cancel all animations when a structural animation takes place + for (var klass in runningAnimations) { + animationsToCancel.push(runningAnimations[klass]); + } + ngAnimateState = {}; + cleanup(element, true); + } + } else if (lastAnimation.event == 'setClass') { + animationsToCancel.push(lastAnimation); + cleanup(element, className); + } else if (runningAnimations[className]) { + var current = runningAnimations[className]; + if (current.event == animationEvent) { + skipAnimation = true; + } else { + animationsToCancel.push(current); + cleanup(element, className); + } + } + + if (animationsToCancel.length > 0) { + forEach(animationsToCancel, function(operation) { + operation.cancel(); + }); + } + } + + if (runner.isClassBased + && !runner.isSetClassOperation + && animationEvent != 'animate' + && !skipAnimation) { + skipAnimation = (animationEvent == 'addClass') == element.hasClass(className); //opposite of XOR + } + + if (skipAnimation) { + fireDOMOperation(); + fireBeforeCallbackAsync(); + fireAfterCallbackAsync(); + fireDoneCallbackAsync(); + return noopCancel; + } + + runningAnimations = ngAnimateState.active || {}; + totalActiveAnimations = ngAnimateState.totalActive || 0; + + if (animationEvent == 'leave') { + //there's no need to ever remove the listener since the element + //will be removed (destroyed) after the leave animation ends or + //is cancelled midway + element.one('$destroy', function(e) { + var element = angular.element(this); + var state = element.data(NG_ANIMATE_STATE); + if (state) { + var activeLeaveAnimation = state.active['ng-leave']; + if (activeLeaveAnimation) { + activeLeaveAnimation.cancel(); + cleanup(element, 'ng-leave'); + } + } + }); + } + + //the ng-animate class does nothing, but it's here to allow for + //parent animations to find and cancel child animations when needed + $$jqLite.addClass(element, NG_ANIMATE_CLASS_NAME); + if (options && options.tempClasses) { + forEach(options.tempClasses, function(className) { + $$jqLite.addClass(element, className); + }); + } + + var localAnimationCount = globalAnimationCounter++; + totalActiveAnimations++; + runningAnimations[className] = runner; + + element.data(NG_ANIMATE_STATE, { + last: runner, + active: runningAnimations, + index: localAnimationCount, + totalActive: totalActiveAnimations + }); + + //first we run the before animations and when all of those are complete + //then we perform the DOM operation and run the next set of animations + fireBeforeCallbackAsync(); + runner.before(function(cancelled) { + var data = element.data(NG_ANIMATE_STATE); + cancelled = cancelled || + !data || !data.active[className] || + (runner.isClassBased && data.active[className].event != animationEvent); + + fireDOMOperation(); + if (cancelled === true) { + closeAnimation(); + } else { + fireAfterCallbackAsync(); + runner.after(closeAnimation); + } + }); + + return runner.cancel; + + function fireDOMCallback(animationPhase) { + var eventName = '$animate:' + animationPhase; + if (elementEvents && elementEvents[eventName] && elementEvents[eventName].length > 0) { + $$asyncCallback(function() { + element.triggerHandler(eventName, { + event: animationEvent, + className: className + }); + }); + } + } + + function fireBeforeCallbackAsync() { + fireDOMCallback('before'); + } + + function fireAfterCallbackAsync() { + fireDOMCallback('after'); + } + + function fireDoneCallbackAsync() { + fireDOMCallback('close'); + doneCallback(); + } + + //it is less complicated to use a flag than managing and canceling + //timeouts containing multiple callbacks. + function fireDOMOperation() { + if (!fireDOMOperation.hasBeenRun) { + fireDOMOperation.hasBeenRun = true; + domOperation(); + } + } + + function closeAnimation() { + if (!closeAnimation.hasBeenRun) { + if (runner) { //the runner doesn't exist if it fails to instantiate + runner.applyStyles(); + } + + closeAnimation.hasBeenRun = true; + if (options && options.tempClasses) { + forEach(options.tempClasses, function(className) { + $$jqLite.removeClass(element, className); + }); + } + + var data = element.data(NG_ANIMATE_STATE); + if (data) { + + /* only structural animations wait for reflow before removing an + animation, but class-based animations don't. An example of this + failing would be when a parent HTML tag has a ng-class attribute + causing ALL directives below to skip animations during the digest */ + if (runner && runner.isClassBased) { + cleanup(element, className); + } else { + $$asyncCallback(function() { + var data = element.data(NG_ANIMATE_STATE) || {}; + if (localAnimationCount == data.index) { + cleanup(element, className, animationEvent); + } + }); + element.data(NG_ANIMATE_STATE, data); + } + } + fireDoneCallbackAsync(); + } + } + } + + function cancelChildAnimations(element) { + var node = extractElementNode(element); + if (node) { + var nodes = angular.isFunction(node.getElementsByClassName) ? + node.getElementsByClassName(NG_ANIMATE_CLASS_NAME) : + node.querySelectorAll('.' + NG_ANIMATE_CLASS_NAME); + forEach(nodes, function(element) { + element = angular.element(element); + var data = element.data(NG_ANIMATE_STATE); + if (data && data.active) { + forEach(data.active, function(runner) { + runner.cancel(); + }); + } + }); + } + } + + function cleanup(element, className) { + if (isMatchingElement(element, $rootElement)) { + if (!rootAnimateState.disabled) { + rootAnimateState.running = false; + rootAnimateState.structural = false; + } + } else if (className) { + var data = element.data(NG_ANIMATE_STATE) || {}; + + var removeAnimations = className === true; + if (!removeAnimations && data.active && data.active[className]) { + data.totalActive--; + delete data.active[className]; + } + + if (removeAnimations || !data.totalActive) { + $$jqLite.removeClass(element, NG_ANIMATE_CLASS_NAME); + element.removeData(NG_ANIMATE_STATE); + } + } + } + + function animationsDisabled(element, parentElement) { + if (rootAnimateState.disabled) { + return true; + } + + if (isMatchingElement(element, $rootElement)) { + return rootAnimateState.running; + } + + var allowChildAnimations, parentRunningAnimation, hasParent; + do { + //the element did not reach the root element which means that it + //is not apart of the DOM. Therefore there is no reason to do + //any animations on it + if (parentElement.length === 0) break; + + var isRoot = isMatchingElement(parentElement, $rootElement); + var state = isRoot ? rootAnimateState : (parentElement.data(NG_ANIMATE_STATE) || {}); + if (state.disabled) { + return true; + } + + //no matter what, for an animation to work it must reach the root element + //this implies that the element is attached to the DOM when the animation is run + if (isRoot) { + hasParent = true; + } + + //once a flag is found that is strictly false then everything before + //it will be discarded and all child animations will be restricted + if (allowChildAnimations !== false) { + var animateChildrenFlag = parentElement.data(NG_ANIMATE_CHILDREN); + if (angular.isDefined(animateChildrenFlag)) { + allowChildAnimations = animateChildrenFlag; + } + } + + parentRunningAnimation = parentRunningAnimation || + state.running || + (state.last && !state.last.isClassBased); + } + while (parentElement = parentElement.parent()); + + return !hasParent || (!allowChildAnimations && parentRunningAnimation); + } + }]); + + $animateProvider.register('', ['$window', '$sniffer', '$timeout', '$$animateReflow', + function($window, $sniffer, $timeout, $$animateReflow) { + // Detect proper transitionend/animationend event names. + var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMATIONEND_EVENT; + + // If unprefixed events are not supported but webkit-prefixed are, use the latter. + // Otherwise, just use W3C names, browsers not supporting them at all will just ignore them. + // Note: Chrome implements `window.onwebkitanimationend` and doesn't implement `window.onanimationend` + // but at the same time dispatches the `animationend` event and not `webkitAnimationEnd`. + // Register both events in case `window.onanimationend` is not supported because of that, + // do the same for `transitionend` as Safari is likely to exhibit similar behavior. + // Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit + // therefore there is no reason to test anymore for other vendor prefixes: http://caniuse.com/#search=transition + if (window.ontransitionend === undefined && window.onwebkittransitionend !== undefined) { + CSS_PREFIX = '-webkit-'; + TRANSITION_PROP = 'WebkitTransition'; + TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend'; + } else { + TRANSITION_PROP = 'transition'; + TRANSITIONEND_EVENT = 'transitionend'; + } + + if (window.onanimationend === undefined && window.onwebkitanimationend !== undefined) { + CSS_PREFIX = '-webkit-'; + ANIMATION_PROP = 'WebkitAnimation'; + ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend'; + } else { + ANIMATION_PROP = 'animation'; + ANIMATIONEND_EVENT = 'animationend'; + } + + var DURATION_KEY = 'Duration'; + var PROPERTY_KEY = 'Property'; + var DELAY_KEY = 'Delay'; + var ANIMATION_ITERATION_COUNT_KEY = 'IterationCount'; + var ANIMATION_PLAYSTATE_KEY = 'PlayState'; + var NG_ANIMATE_PARENT_KEY = '$$ngAnimateKey'; + var NG_ANIMATE_CSS_DATA_KEY = '$$ngAnimateCSS3Data'; + var ELAPSED_TIME_MAX_DECIMAL_PLACES = 3; + var CLOSING_TIME_BUFFER = 1.5; + var ONE_SECOND = 1000; + + var lookupCache = {}; + var parentCounter = 0; + var animationReflowQueue = []; + var cancelAnimationReflow; + function clearCacheAfterReflow() { + if (!cancelAnimationReflow) { + cancelAnimationReflow = $$animateReflow(function() { + animationReflowQueue = []; + cancelAnimationReflow = null; + lookupCache = {}; + }); + } + } + + function afterReflow(element, callback) { + if (cancelAnimationReflow) { + cancelAnimationReflow(); + } + animationReflowQueue.push(callback); + cancelAnimationReflow = $$animateReflow(function() { + forEach(animationReflowQueue, function(fn) { + fn(); + }); + + animationReflowQueue = []; + cancelAnimationReflow = null; + lookupCache = {}; + }); + } + + var closingTimer = null; + var closingTimestamp = 0; + var animationElementQueue = []; + function animationCloseHandler(element, totalTime) { + var node = extractElementNode(element); + element = angular.element(node); + + //this item will be garbage collected by the closing + //animation timeout + animationElementQueue.push(element); + + //but it may not need to cancel out the existing timeout + //if the timestamp is less than the previous one + var futureTimestamp = Date.now() + totalTime; + if (futureTimestamp <= closingTimestamp) { + return; + } + + $timeout.cancel(closingTimer); + + closingTimestamp = futureTimestamp; + closingTimer = $timeout(function() { + closeAllAnimations(animationElementQueue); + animationElementQueue = []; + }, totalTime, false); + } + + function closeAllAnimations(elements) { + forEach(elements, function(element) { + var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); + if (elementData) { + forEach(elementData.closeAnimationFns, function(fn) { + fn(); + }); + } + }); + } + + function getElementAnimationDetails(element, cacheKey) { + var data = cacheKey ? lookupCache[cacheKey] : null; + if (!data) { + var transitionDuration = 0; + var transitionDelay = 0; + var animationDuration = 0; + var animationDelay = 0; + + //we want all the styles defined before and after + forEach(element, function(element) { + if (element.nodeType == ELEMENT_NODE) { + var elementStyles = $window.getComputedStyle(element) || {}; + + var transitionDurationStyle = elementStyles[TRANSITION_PROP + DURATION_KEY]; + transitionDuration = Math.max(parseMaxTime(transitionDurationStyle), transitionDuration); + + var transitionDelayStyle = elementStyles[TRANSITION_PROP + DELAY_KEY]; + transitionDelay = Math.max(parseMaxTime(transitionDelayStyle), transitionDelay); + + var animationDelayStyle = elementStyles[ANIMATION_PROP + DELAY_KEY]; + animationDelay = Math.max(parseMaxTime(elementStyles[ANIMATION_PROP + DELAY_KEY]), animationDelay); + + var aDuration = parseMaxTime(elementStyles[ANIMATION_PROP + DURATION_KEY]); + + if (aDuration > 0) { + aDuration *= parseInt(elementStyles[ANIMATION_PROP + ANIMATION_ITERATION_COUNT_KEY], 10) || 1; + } + animationDuration = Math.max(aDuration, animationDuration); + } + }); + data = { + total: 0, + transitionDelay: transitionDelay, + transitionDuration: transitionDuration, + animationDelay: animationDelay, + animationDuration: animationDuration + }; + if (cacheKey) { + lookupCache[cacheKey] = data; + } + } + return data; + } + + function parseMaxTime(str) { + var maxValue = 0; + var values = isString(str) ? + str.split(/\s*,\s*/) : + []; + forEach(values, function(value) { + maxValue = Math.max(parseFloat(value) || 0, maxValue); + }); + return maxValue; + } + + function getCacheKey(element) { + var parentElement = element.parent(); + var parentID = parentElement.data(NG_ANIMATE_PARENT_KEY); + if (!parentID) { + parentElement.data(NG_ANIMATE_PARENT_KEY, ++parentCounter); + parentID = parentCounter; + } + return parentID + '-' + extractElementNode(element).getAttribute('class'); + } + + function animateSetup(animationEvent, element, className, styles) { + var structural = ['ng-enter','ng-leave','ng-move'].indexOf(className) >= 0; + + var cacheKey = getCacheKey(element); + var eventCacheKey = cacheKey + ' ' + className; + var itemIndex = lookupCache[eventCacheKey] ? ++lookupCache[eventCacheKey].total : 0; + + var stagger = {}; + if (itemIndex > 0) { + var staggerClassName = className + '-stagger'; + var staggerCacheKey = cacheKey + ' ' + staggerClassName; + var applyClasses = !lookupCache[staggerCacheKey]; + + applyClasses && $$jqLite.addClass(element, staggerClassName); + + stagger = getElementAnimationDetails(element, staggerCacheKey); + + applyClasses && $$jqLite.removeClass(element, staggerClassName); + } + + $$jqLite.addClass(element, className); + + var formerData = element.data(NG_ANIMATE_CSS_DATA_KEY) || {}; + var timings = getElementAnimationDetails(element, eventCacheKey); + var transitionDuration = timings.transitionDuration; + var animationDuration = timings.animationDuration; + + if (structural && transitionDuration === 0 && animationDuration === 0) { + $$jqLite.removeClass(element, className); + return false; + } + + var blockTransition = styles || (structural && transitionDuration > 0); + var blockAnimation = animationDuration > 0 && + stagger.animationDelay > 0 && + stagger.animationDuration === 0; + + var closeAnimationFns = formerData.closeAnimationFns || []; + element.data(NG_ANIMATE_CSS_DATA_KEY, { + stagger: stagger, + cacheKey: eventCacheKey, + running: formerData.running || 0, + itemIndex: itemIndex, + blockTransition: blockTransition, + closeAnimationFns: closeAnimationFns + }); + + var node = extractElementNode(element); + + if (blockTransition) { + blockTransitions(node, true); + if (styles) { + element.css(styles); + } + } + + if (blockAnimation) { + blockAnimations(node, true); + } + + return true; + } + + function animateRun(animationEvent, element, className, activeAnimationComplete, styles) { + var node = extractElementNode(element); + var elementData = element.data(NG_ANIMATE_CSS_DATA_KEY); + if (node.getAttribute('class').indexOf(className) == -1 || !elementData) { + activeAnimationComplete(); + return; + } + + var activeClassName = ''; + var pendingClassName = ''; + forEach(className.split(' '), function(klass, i) { + var prefix = (i > 0 ? ' ' : '') + klass; + activeClassName += prefix + '-active'; + pendingClassName += prefix + '-pending'; + }); + + var style = ''; + var appliedStyles = []; + var itemIndex = elementData.itemIndex; + var stagger = elementData.stagger; + var staggerTime = 0; + if (itemIndex > 0) { + var transitionStaggerDelay = 0; + if (stagger.transitionDelay > 0 && stagger.transitionDuration === 0) { + transitionStaggerDelay = stagger.transitionDelay * itemIndex; + } + + var animationStaggerDelay = 0; + if (stagger.animationDelay > 0 && stagger.animationDuration === 0) { + animationStaggerDelay = stagger.animationDelay * itemIndex; + appliedStyles.push(CSS_PREFIX + 'animation-play-state'); + } + + staggerTime = Math.round(Math.max(transitionStaggerDelay, animationStaggerDelay) * 100) / 100; + } + + if (!staggerTime) { + $$jqLite.addClass(element, activeClassName); + if (elementData.blockTransition) { + blockTransitions(node, false); + } + } + + var eventCacheKey = elementData.cacheKey + ' ' + activeClassName; + var timings = getElementAnimationDetails(element, eventCacheKey); + var maxDuration = Math.max(timings.transitionDuration, timings.animationDuration); + if (maxDuration === 0) { + $$jqLite.removeClass(element, activeClassName); + animateClose(element, className); + activeAnimationComplete(); + return; + } + + if (!staggerTime && styles && Object.keys(styles).length > 0) { + if (!timings.transitionDuration) { + element.css('transition', timings.animationDuration + 's linear all'); + appliedStyles.push('transition'); + } + element.css(styles); + } + + var maxDelay = Math.max(timings.transitionDelay, timings.animationDelay); + var maxDelayTime = maxDelay * ONE_SECOND; + + if (appliedStyles.length > 0) { + //the element being animated may sometimes contain comment nodes in + //the jqLite object, so we're safe to use a single variable to house + //the styles since there is always only one element being animated + var oldStyle = node.getAttribute('style') || ''; + if (oldStyle.charAt(oldStyle.length - 1) !== ';') { + oldStyle += ';'; + } + node.setAttribute('style', oldStyle + ' ' + style); + } + + var startTime = Date.now(); + var css3AnimationEvents = ANIMATIONEND_EVENT + ' ' + TRANSITIONEND_EVENT; + var animationTime = (maxDelay + maxDuration) * CLOSING_TIME_BUFFER; + var totalTime = (staggerTime + animationTime) * ONE_SECOND; + + var staggerTimeout; + if (staggerTime > 0) { + $$jqLite.addClass(element, pendingClassName); + staggerTimeout = $timeout(function() { + staggerTimeout = null; + + if (timings.transitionDuration > 0) { + blockTransitions(node, false); + } + if (timings.animationDuration > 0) { + blockAnimations(node, false); + } + + $$jqLite.addClass(element, activeClassName); + $$jqLite.removeClass(element, pendingClassName); + + if (styles) { + if (timings.transitionDuration === 0) { + element.css('transition', timings.animationDuration + 's linear all'); + } + element.css(styles); + appliedStyles.push('transition'); + } + }, staggerTime * ONE_SECOND, false); + } + + element.on(css3AnimationEvents, onAnimationProgress); + elementData.closeAnimationFns.push(function() { + onEnd(); + activeAnimationComplete(); + }); + + elementData.running++; + animationCloseHandler(element, totalTime); + return onEnd; + + // This will automatically be called by $animate so + // there is no need to attach this internally to the + // timeout done method. + function onEnd() { + element.off(css3AnimationEvents, onAnimationProgress); + $$jqLite.removeClass(element, activeClassName); + $$jqLite.removeClass(element, pendingClassName); + if (staggerTimeout) { + $timeout.cancel(staggerTimeout); + } + animateClose(element, className); + var node = extractElementNode(element); + for (var i in appliedStyles) { + node.style.removeProperty(appliedStyles[i]); + } + } + + function onAnimationProgress(event) { + event.stopPropagation(); + var ev = event.originalEvent || event; + var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now(); + + /* Firefox (or possibly just Gecko) likes to not round values up + * when a ms measurement is used for the animation */ + var elapsedTime = parseFloat(ev.elapsedTime.toFixed(ELAPSED_TIME_MAX_DECIMAL_PLACES)); + + /* $manualTimeStamp is a mocked timeStamp value which is set + * within browserTrigger(). This is only here so that tests can + * mock animations properly. Real events fallback to event.timeStamp, + * or, if they don't, then a timeStamp is automatically created for them. + * We're checking to see if the timeStamp surpasses the expected delay, + * but we're using elapsedTime instead of the timeStamp on the 2nd + * pre-condition since animations sometimes close off early */ + if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) { + activeAnimationComplete(); + } + } + } + + function blockTransitions(node, bool) { + node.style[TRANSITION_PROP + PROPERTY_KEY] = bool ? 'none' : ''; + } + + function blockAnimations(node, bool) { + node.style[ANIMATION_PROP + ANIMATION_PLAYSTATE_KEY] = bool ? 'paused' : ''; + } + + function animateBefore(animationEvent, element, className, styles) { + if (animateSetup(animationEvent, element, className, styles)) { + return function(cancelled) { + cancelled && animateClose(element, className); + }; + } + } + + function animateAfter(animationEvent, element, className, afterAnimationComplete, styles) { + if (element.data(NG_ANIMATE_CSS_DATA_KEY)) { + return animateRun(animationEvent, element, className, afterAnimationComplete, styles); + } else { + animateClose(element, className); + afterAnimationComplete(); + } + } + + function animate(animationEvent, element, className, animationComplete, options) { + //If the animateSetup function doesn't bother returning a + //cancellation function then it means that there is no animation + //to perform at all + var preReflowCancellation = animateBefore(animationEvent, element, className, options.from); + if (!preReflowCancellation) { + clearCacheAfterReflow(); + animationComplete(); + return; + } + + //There are two cancellation functions: one is before the first + //reflow animation and the second is during the active state + //animation. The first function will take care of removing the + //data from the element which will not make the 2nd animation + //happen in the first place + var cancel = preReflowCancellation; + afterReflow(element, function() { + //once the reflow is complete then we point cancel to + //the new cancellation function which will remove all of the + //animation properties from the active animation + cancel = animateAfter(animationEvent, element, className, animationComplete, options.to); + }); + + return function(cancelled) { + (cancel || noop)(cancelled); + }; + } + + function animateClose(element, className) { + $$jqLite.removeClass(element, className); + var data = element.data(NG_ANIMATE_CSS_DATA_KEY); + if (data) { + if (data.running) { + data.running--; + } + if (!data.running || data.running === 0) { + element.removeData(NG_ANIMATE_CSS_DATA_KEY); + } + } + } + + return { + animate: function(element, className, from, to, animationCompleted, options) { + options = options || {}; + options.from = from; + options.to = to; + return animate('animate', element, className, animationCompleted, options); + }, + + enter: function(element, animationCompleted, options) { + options = options || {}; + return animate('enter', element, 'ng-enter', animationCompleted, options); + }, + + leave: function(element, animationCompleted, options) { + options = options || {}; + return animate('leave', element, 'ng-leave', animationCompleted, options); + }, + + move: function(element, animationCompleted, options) { + options = options || {}; + return animate('move', element, 'ng-move', animationCompleted, options); + }, + + beforeSetClass: function(element, add, remove, animationCompleted, options) { + options = options || {}; + var className = suffixClasses(remove, '-remove') + ' ' + + suffixClasses(add, '-add'); + var cancellationMethod = animateBefore('setClass', element, className, options.from); + if (cancellationMethod) { + afterReflow(element, animationCompleted); + return cancellationMethod; + } + clearCacheAfterReflow(); + animationCompleted(); + }, + + beforeAddClass: function(element, className, animationCompleted, options) { + options = options || {}; + var cancellationMethod = animateBefore('addClass', element, suffixClasses(className, '-add'), options.from); + if (cancellationMethod) { + afterReflow(element, animationCompleted); + return cancellationMethod; + } + clearCacheAfterReflow(); + animationCompleted(); + }, + + beforeRemoveClass: function(element, className, animationCompleted, options) { + options = options || {}; + var cancellationMethod = animateBefore('removeClass', element, suffixClasses(className, '-remove'), options.from); + if (cancellationMethod) { + afterReflow(element, animationCompleted); + return cancellationMethod; + } + clearCacheAfterReflow(); + animationCompleted(); + }, + + setClass: function(element, add, remove, animationCompleted, options) { + options = options || {}; + remove = suffixClasses(remove, '-remove'); + add = suffixClasses(add, '-add'); + var className = remove + ' ' + add; + return animateAfter('setClass', element, className, animationCompleted, options.to); + }, + + addClass: function(element, className, animationCompleted, options) { + options = options || {}; + return animateAfter('addClass', element, suffixClasses(className, '-add'), animationCompleted, options.to); + }, + + removeClass: function(element, className, animationCompleted, options) { + options = options || {}; + return animateAfter('removeClass', element, suffixClasses(className, '-remove'), animationCompleted, options.to); + } + }; + + function suffixClasses(classes, suffix) { + var className = ''; + classes = isArray(classes) ? classes : classes.split(/\s+/); + forEach(classes, function(klass, i) { + if (klass && klass.length > 0) { + className += (i > 0 ? ' ' : '') + klass + suffix; + } + }); + return className; + } + }]); + }]); + + +})(window, window.angular); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.min.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.min.js new file mode 100644 index 000000000..70c25a381 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.min.js @@ -0,0 +1,33 @@ +/* + AngularJS v1.3.20 + (c) 2010-2014 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(N,f,W){'use strict';f.module("ngAnimate",["ng"]).directive("ngAnimateChildren",function(){return function(X,r,g){g=g.ngAnimateChildren;f.isString(g)&&0===g.length?r.data("$$ngAnimateChildren",!0):X.$watch(g,function(f){r.data("$$ngAnimateChildren",!!f)})}}).factory("$$animateReflow",["$$rAF","$document",function(f,r){var g=r[0].body;return function(r){return f(function(){r(g.offsetWidth)})}}]).config(["$provide","$animateProvider",function(X,r){function g(f){for(var n=0;n<f.length;n++){var g= +f[n];if(1==g.nodeType)return g}}function ba(f,n){return g(f)==g(n)}var t=f.noop,n=f.forEach,ca=r.$$selectors,aa=f.isArray,da=f.isString,ga=f.isObject,w={running:!0},u;X.decorator("$animate",["$delegate","$$q","$injector","$sniffer","$rootElement","$$asyncCallback","$rootScope","$document","$templateRequest","$$jqLite",function(O,N,M,Y,y,I,P,W,Z,Q){function R(a,c){var b=a.data("$$ngAnimateState")||{};c&&(b.running=!0,b.structural=!0,a.data("$$ngAnimateState",b));return b.disabled||b.running&&b.structural} +function D(a){var c,b=N.defer();b.promise.$$cancelFn=function(){c&&c()};P.$$postDigest(function(){c=a(function(){b.resolve()})});return b.promise}function J(a){if(ga(a))return a.tempClasses&&da(a.tempClasses)&&(a.tempClasses=a.tempClasses.split(/\s+/)),a}function S(a,c,b){b=b||{};var d={};n(b,function(e,a){n(a.split(" "),function(a){d[a]=e})});var h=Object.create(null);n((a.attr("class")||"").split(/\s+/),function(e){h[e]=!0});var f=[],k=[];n(c&&c.classes||[],function(e,a){var b=h[a],c=d[a]||{};!1=== +e?(b||"addClass"==c.event)&&k.push(a):!0===e&&(b&&"removeClass"!=c.event||f.push(a))});return 0<f.length+k.length&&[f.join(" "),k.join(" ")]}function T(a){if(a){var c=[],b={};a=a.substr(1).split(".");(Y.transitions||Y.animations)&&c.push(M.get(ca[""]));for(var d=0;d<a.length;d++){var f=a[d],l=ca[f];l&&!b[f]&&(c.push(M.get(l)),b[f]=!0)}return c}}function U(a,c,b,d){function h(e,a){var b=e[a],c=e["before"+a.charAt(0).toUpperCase()+a.substr(1)];if(b||c)return"leave"==a&&(c=b,b=null),u.push({event:a, +fn:b}),fa.push({event:a,fn:c}),!0}function l(c,k,x){var E=[];n(c,function(a){a.fn&&E.push(a)});var m=0;n(E,function(c,f){var p=function(){a:{if(k){(k[f]||t)();if(++m<E.length)break a;k=null}x()}};switch(c.event){case "setClass":k.push(c.fn(a,e,A,p,d));break;case "animate":k.push(c.fn(a,b,d.from,d.to,p));break;case "addClass":k.push(c.fn(a,e||b,p,d));break;case "removeClass":k.push(c.fn(a,A||b,p,d));break;default:k.push(c.fn(a,p,d))}});k&&0===k.length&&x()}var k=a[0];if(k){d&&(d.to=d.to||{},d.from= +d.from||{});var e,A;aa(b)&&(e=b[0],A=b[1],e?A?b=e+" "+A:(b=e,c="addClass"):(b=A,c="removeClass"));var x="setClass"==c,E=x||"addClass"==c||"removeClass"==c||"animate"==c,p=a.attr("class")+" "+b;if(B(p)){var G=t,m=[],fa=[],g=t,s=[],u=[],p=(" "+p).replace(/\s+/g,".");n(T(p),function(a){!h(a,c)&&x&&(h(a,"addClass"),h(a,"removeClass"))});return{node:k,event:c,className:b,isClassBased:E,isSetClassOperation:x,applyStyles:function(){d&&a.css(f.extend(d.from||{},d.to||{}))},before:function(a){G=a;l(fa,m,function(){G= +t;a()})},after:function(a){g=a;l(u,s,function(){g=t;a()})},cancel:function(){m&&(n(m,function(a){(a||t)(!0)}),G(!0));s&&(n(s,function(a){(a||t)(!0)}),g(!0))}}}}}function H(a,c,b,d,h,l,k,e){function A(e){var k="$animate:"+e;g&&g[k]&&0<g[k].length&&I(function(){b.triggerHandler(k,{event:a,className:c})})}function x(){A("before")}function E(){A("after")}function p(){p.hasBeenRun||(p.hasBeenRun=!0,l())}function G(){if(!G.hasBeenRun){m&&m.applyStyles();G.hasBeenRun=!0;k&&k.tempClasses&&n(k.tempClasses, +function(a){u.removeClass(b,a)});var x=b.data("$$ngAnimateState");x&&(m&&m.isClassBased?C(b,c):(I(function(){var e=b.data("$$ngAnimateState")||{};ea==e.index&&C(b,c,a)}),b.data("$$ngAnimateState",x)));A("close");e()}}var m=U(b,a,c,k);if(!m)return p(),x(),E(),G(),t;a=m.event;c=m.className;var g=f.element._data(m.node),g=g&&g.events;d||(d=h?h.parent():b.parent());if(z(b,d))return p(),x(),E(),G(),t;d=b.data("$$ngAnimateState")||{};var L=d.active||{},s=d.totalActive||0,q=d.last;h=!1;if(0<s){s=[];if(m.isClassBased)"setClass"== +q.event?(s.push(q),C(b,c)):L[c]&&(v=L[c],v.event==a?h=!0:(s.push(v),C(b,c)));else if("leave"==a&&L["ng-leave"])h=!0;else{for(var v in L)s.push(L[v]);d={};C(b,!0)}0<s.length&&n(s,function(a){a.cancel()})}!m.isClassBased||m.isSetClassOperation||"animate"==a||h||(h="addClass"==a==b.hasClass(c));if(h)return p(),x(),E(),A("close"),e(),t;L=d.active||{};s=d.totalActive||0;if("leave"==a)b.one("$destroy",function(a){a=f.element(this);var e=a.data("$$ngAnimateState");e&&(e=e.active["ng-leave"])&&(e.cancel(), +C(a,"ng-leave"))});u.addClass(b,"ng-animate");k&&k.tempClasses&&n(k.tempClasses,function(a){u.addClass(b,a)});var ea=K++;s++;L[c]=m;b.data("$$ngAnimateState",{last:m,active:L,index:ea,totalActive:s});x();m.before(function(e){var k=b.data("$$ngAnimateState");e=e||!k||!k.active[c]||m.isClassBased&&k.active[c].event!=a;p();!0===e?G():(E(),m.after(G))});return m.cancel}function q(a){if(a=g(a))a=f.isFunction(a.getElementsByClassName)?a.getElementsByClassName("ng-animate"):a.querySelectorAll(".ng-animate"), +n(a,function(a){a=f.element(a);(a=a.data("$$ngAnimateState"))&&a.active&&n(a.active,function(a){a.cancel()})})}function C(a,c){if(ba(a,y))w.disabled||(w.running=!1,w.structural=!1);else if(c){var b=a.data("$$ngAnimateState")||{},d=!0===c;!d&&b.active&&b.active[c]&&(b.totalActive--,delete b.active[c]);if(d||!b.totalActive)u.removeClass(a,"ng-animate"),a.removeData("$$ngAnimateState")}}function z(a,c){if(w.disabled)return!0;if(ba(a,y))return w.running;var b,d,g;do{if(0===c.length)break;var l=ba(c,y), +k=l?w:c.data("$$ngAnimateState")||{};if(k.disabled)return!0;l&&(g=!0);!1!==b&&(l=c.data("$$ngAnimateChildren"),f.isDefined(l)&&(b=l));d=d||k.running||k.last&&!k.last.isClassBased}while(c=c.parent());return!g||!b&&d}u=Q;y.data("$$ngAnimateState",w);var $=P.$watch(function(){return Z.totalPendingRequests},function(a,c){0===a&&($(),P.$$postDigest(function(){P.$$postDigest(function(){w.running=!1})}))}),K=0,V=r.classNameFilter(),B=V?function(a){return V.test(a)}:function(){return!0};return{animate:function(a, +c,b,d,h){d=d||"ng-inline-animate";h=J(h)||{};h.from=b?c:null;h.to=b?b:c;return D(function(b){return H("animate",d,f.element(g(a)),null,null,t,h,b)})},enter:function(a,c,b,d){d=J(d);a=f.element(a);c=c&&f.element(c);b=b&&f.element(b);R(a,!0);O.enter(a,c,b);return D(function(h){return H("enter","ng-enter",f.element(g(a)),c,b,t,d,h)})},leave:function(a,c){c=J(c);a=f.element(a);q(a);R(a,!0);return D(function(b){return H("leave","ng-leave",f.element(g(a)),null,null,function(){O.leave(a)},c,b)})},move:function(a, +c,b,d){d=J(d);a=f.element(a);c=c&&f.element(c);b=b&&f.element(b);q(a);R(a,!0);O.move(a,c,b);return D(function(h){return H("move","ng-move",f.element(g(a)),c,b,t,d,h)})},addClass:function(a,c,b){return this.setClass(a,c,[],b)},removeClass:function(a,c,b){return this.setClass(a,[],c,b)},setClass:function(a,c,b,d){d=J(d);a=f.element(a);a=f.element(g(a));if(R(a))return O.$$setClassImmediately(a,c,b,d);var h,l=a.data("$$animateClasses"),k=!!l;l||(l={classes:{}});h=l.classes;c=aa(c)?c:c.split(" ");n(c, +function(a){a&&a.length&&(h[a]=!0)});b=aa(b)?b:b.split(" ");n(b,function(a){a&&a.length&&(h[a]=!1)});if(k)return d&&l.options&&(l.options=f.extend(l.options||{},d)),l.promise;a.data("$$animateClasses",l={classes:h,options:d});return l.promise=D(function(e){var k,b,c,d=g(a);d&&(k=a.data("$$animateClasses"),a.removeData("$$animateClasses"),c=a.parent(),b=d.parentNode);if(!b||b.$$NG_REMOVED||d.$$NG_REMOVED)e();else{b=a.data("$$ngAnimateState")||{};var f=S(a,k,b.active);return f?H("setClass",f,a,c,null, +function(){f[0]&&O.$$addClassImmediately(a,f[0]);f[1]&&O.$$removeClassImmediately(a,f[1])},k.options,e):e()}})},cancel:function(a){a.$$cancelFn()},enabled:function(a,c){switch(arguments.length){case 2:if(a)C(c);else{var b=c.data("$$ngAnimateState")||{};b.disabled=!0;c.data("$$ngAnimateState",b)}break;case 1:w.disabled=!a;break;default:a=!w.disabled}return!!a}}}]);r.register("",["$window","$sniffer","$timeout","$$animateReflow",function(r,w,M,Y){function y(){b||(b=Y(function(){c=[];b=null;B={}}))} +function I(a,e){b&&b();c.push(e);b=Y(function(){n(c,function(a){a()});c=[];b=null;B={}})}function P(a,e){var b=g(a);a=f.element(b);l.push(a);b=Date.now()+e;b<=h||(M.cancel(d),h=b,d=M(function(){X(l);l=[]},e,!1))}function X(a){n(a,function(a){(a=a.data("$$ngAnimateCSS3Data"))&&n(a.closeAnimationFns,function(a){a()})})}function Z(a,e){var b=e?B[e]:null;if(!b){var c=0,d=0,f=0,g=0;n(a,function(a){if(1==a.nodeType){a=r.getComputedStyle(a)||{};c=Math.max(Q(a[z+"Duration"]),c);d=Math.max(Q(a[z+"Delay"]), +d);g=Math.max(Q(a[K+"Delay"]),g);var e=Q(a[K+"Duration"]);0<e&&(e*=parseInt(a[K+"IterationCount"],10)||1);f=Math.max(e,f)}});b={total:0,transitionDelay:d,transitionDuration:c,animationDelay:g,animationDuration:f};e&&(B[e]=b)}return b}function Q(a){var e=0;a=da(a)?a.split(/\s*,\s*/):[];n(a,function(a){e=Math.max(parseFloat(a)||0,e)});return e}function R(b,e,c,d){b=0<=["ng-enter","ng-leave","ng-move"].indexOf(c);var f,p=e.parent(),h=p.data("$$ngAnimateKey");h||(p.data("$$ngAnimateKey",++a),h=a);f=h+ +"-"+g(e).getAttribute("class");var p=f+" "+c,h=B[p]?++B[p].total:0,m={};if(0<h){var n=c+"-stagger",m=f+" "+n;(f=!B[m])&&u.addClass(e,n);m=Z(e,m);f&&u.removeClass(e,n)}u.addClass(e,c);var n=e.data("$$ngAnimateCSS3Data")||{},l=Z(e,p);f=l.transitionDuration;l=l.animationDuration;if(b&&0===f&&0===l)return u.removeClass(e,c),!1;c=d||b&&0<f;b=0<l&&0<m.animationDelay&&0===m.animationDuration;e.data("$$ngAnimateCSS3Data",{stagger:m,cacheKey:p,running:n.running||0,itemIndex:h,blockTransition:c,closeAnimationFns:n.closeAnimationFns|| +[]});p=g(e);c&&(J(p,!0),d&&e.css(d));b&&(p.style[K+"PlayState"]="paused");return!0}function D(a,e,b,c,d){function f(){e.off(D,h);u.removeClass(e,l);u.removeClass(e,t);z&&M.cancel(z);H(e,b);var a=g(e),c;for(c in s)a.style.removeProperty(s[c])}function h(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||b.timeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));Math.max(a-I,0)>=B&&b>=y&&c()}var m=g(e);a=e.data("$$ngAnimateCSS3Data");if(-1!=m.getAttribute("class").indexOf(b)&& +a){var l="",t="";n(b.split(" "),function(a,b){var e=(0<b?" ":"")+a;l+=e+"-active";t+=e+"-pending"});var s=[],q=a.itemIndex,v=a.stagger,r=0;if(0<q){r=0;0<v.transitionDelay&&0===v.transitionDuration&&(r=v.transitionDelay*q);var w=0;0<v.animationDelay&&0===v.animationDuration&&(w=v.animationDelay*q,s.push(C+"animation-play-state"));r=Math.round(100*Math.max(r,w))/100}r||(u.addClass(e,l),a.blockTransition&&J(m,!1));var F=Z(e,a.cacheKey+" "+l),y=Math.max(F.transitionDuration,F.animationDuration);if(0=== +y)u.removeClass(e,l),H(e,b),c();else{!r&&d&&0<Object.keys(d).length&&(F.transitionDuration||(e.css("transition",F.animationDuration+"s linear all"),s.push("transition")),e.css(d));var q=Math.max(F.transitionDelay,F.animationDelay),B=1E3*q;0<s.length&&(v=m.getAttribute("style")||"",";"!==v.charAt(v.length-1)&&(v+=";"),m.setAttribute("style",v+" "));var I=Date.now(),D=V+" "+$,q=1E3*(r+1.5*(q+y)),z;0<r&&(u.addClass(e,t),z=M(function(){z=null;0<F.transitionDuration&&J(m,!1);0<F.animationDuration&&(m.style[K+ +"PlayState"]="");u.addClass(e,l);u.removeClass(e,t);d&&(0===F.transitionDuration&&e.css("transition",F.animationDuration+"s linear all"),e.css(d),s.push("transition"))},1E3*r,!1));e.on(D,h);a.closeAnimationFns.push(function(){f();c()});a.running++;P(e,q);return f}}else c()}function J(a,b){a.style[z+"Property"]=b?"none":""}function S(a,b,c,d){if(R(a,b,c,d))return function(a){a&&H(b,c)}}function T(a,b,c,d,f){if(b.data("$$ngAnimateCSS3Data"))return D(a,b,c,d,f);H(b,c);d()}function U(a,b,c,d,f){var g= +S(a,b,c,f.from);if(g){var h=g;I(b,function(){h=T(a,b,c,d,f.to)});return function(a){(h||t)(a)}}y();d()}function H(a,b){u.removeClass(a,b);var c=a.data("$$ngAnimateCSS3Data");c&&(c.running&&c.running--,c.running&&0!==c.running||a.removeData("$$ngAnimateCSS3Data"))}function q(a,b){var c="";a=aa(a)?a:a.split(/\s+/);n(a,function(a,d){a&&0<a.length&&(c+=(0<d?" ":"")+a+b)});return c}var C="",z,$,K,V;N.ontransitionend===W&&N.onwebkittransitionend!==W?(C="-webkit-",z="WebkitTransition",$="webkitTransitionEnd transitionend"): +(z="transition",$="transitionend");N.onanimationend===W&&N.onwebkitanimationend!==W?(C="-webkit-",K="WebkitAnimation",V="webkitAnimationEnd animationend"):(K="animation",V="animationend");var B={},a=0,c=[],b,d=null,h=0,l=[];return{animate:function(a,b,c,d,f,g){g=g||{};g.from=c;g.to=d;return U("animate",a,b,f,g)},enter:function(a,b,c){c=c||{};return U("enter",a,"ng-enter",b,c)},leave:function(a,b,c){c=c||{};return U("leave",a,"ng-leave",b,c)},move:function(a,b,c){c=c||{};return U("move",a,"ng-move", +b,c)},beforeSetClass:function(a,b,c,d,f){f=f||{};b=q(c,"-remove")+" "+q(b,"-add");if(f=S("setClass",a,b,f.from))return I(a,d),f;y();d()},beforeAddClass:function(a,b,c,d){d=d||{};if(b=S("addClass",a,q(b,"-add"),d.from))return I(a,c),b;y();c()},beforeRemoveClass:function(a,b,c,d){d=d||{};if(b=S("removeClass",a,q(b,"-remove"),d.from))return I(a,c),b;y();c()},setClass:function(a,b,c,d,f){f=f||{};c=q(c,"-remove");b=q(b,"-add");return T("setClass",a,c+" "+b,d,f.to)},addClass:function(a,b,c,d){d=d||{};return T("addClass", +a,q(b,"-add"),c,d.to)},removeClass:function(a,b,c,d){d=d||{};return T("removeClass",a,q(b,"-remove"),c,d.to)}}}])}])})(window,window.angular); +//# sourceMappingURL=angular-animate.min.js.map diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.min.js.map b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.min.js.map new file mode 100644 index 000000000..b9ada6b41 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/angular-animate.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-animate.min.js", +"lineCount":32, +"mappings":"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", +"sources":["angular-animate.js"], +"names":["window","angular","undefined","module","directive","scope","element","attrs","val","ngAnimateChildren","isString","length","data","NG_ANIMATE_CHILDREN","$watch","value","factory","$$rAF","$document","bod","body","fn","offsetWidth","config","$provide","$animateProvider","extractElementNode","i","elm","ELEMENT_NODE","nodeType","isMatchingElement","elm1","elm2","noop","forEach","selectors","$$selectors","isArray","isObject","rootAnimateState","running","$$jqLite","decorator","$delegate","$$q","$injector","$sniffer","$rootElement","$$asyncCallback","$rootScope","$templateRequest","$$$jqLite","classBasedAnimationsBlocked","setter","NG_ANIMATE_STATE","structural","disabled","runAnimationPostDigest","cancelFn","defer","promise","$$cancelFn","defer.promise.$$cancelFn","$$postDigest","resolve","parseAnimateOptions","options","tempClasses","split","resolveElementClasses","cache","runningAnimations","lookup","selector","s","hasClasses","Object","create","attr","className","toAdd","toRemove","classes","status","hasClass","matchingAnimation","event","push","join","name","matches","flagMap","substr","transitions","animations","get","klass","selectorFactoryName","animationRunner","animationEvent","registerAnimation","animationFactory","afterFn","beforeFn","charAt","toUpperCase","after","before","run","fns","cancellations","allCompleteFn","animation","count","index","progress","classNameAdd","classNameRemove","from","to","node","isSetClassOperation","isClassBased","currentClassName","isAnimatableClassName","beforeComplete","beforeCancel","afterComplete","afterCancel","animationLookup","replace","created","applyStyles","css","extend","cancel","performAnimation","parentElement","afterElement","domOperation","doneCallback","fireDOMCallback","animationPhase","eventName","elementEvents","triggerHandler","fireBeforeCallbackAsync","fireAfterCallbackAsync","fireDOMOperation","hasBeenRun","closeAnimation","runner","removeClass","cleanup","localAnimationCount","_data","events","parent","animationsDisabled","ngAnimateState","active","totalActiveAnimations","totalActive","lastAnimation","last","skipAnimation","animationsToCancel","current","operation","one","e","state","activeLeaveAnimation","addClass","NG_ANIMATE_CLASS_NAME","globalAnimationCounter","cancelled","cancelChildAnimations","nodes","isFunction","getElementsByClassName","querySelectorAll","removeAnimations","removeData","allowChildAnimations","parentRunningAnimation","hasParent","isRoot","animateChildrenFlag","isDefined","deregisterWatch","totalPendingRequests","oldVal","classNameFilter","test","animate","done","enter","leave","move","setClass","add","remove","$$setClassImmediately","STORAGE_KEY","hasCache","c","parentNode","elementNode","$$addClassImmediately","$$removeClassImmediately","enabled","arguments","register","$window","$timeout","$$animateReflow","clearCacheAfterReflow","cancelAnimationReflow","animationReflowQueue","lookupCache","afterReflow","callback","animationCloseHandler","totalTime","animationElementQueue","futureTimestamp","Date","now","closingTimestamp","closingTimer","closeAllAnimations","elements","elementData","NG_ANIMATE_CSS_DATA_KEY","closeAnimationFns","getElementAnimationDetails","cacheKey","transitionDuration","transitionDelay","animationDuration","animationDelay","elementStyles","getComputedStyle","Math","max","parseMaxTime","transitionDurationStyle","TRANSITION_PROP","DURATION_KEY","transitionDelayStyle","DELAY_KEY","ANIMATION_PROP","aDuration","parseInt","ANIMATION_ITERATION_COUNT_KEY","total","str","maxValue","values","parseFloat","animateSetup","styles","indexOf","parentID","NG_ANIMATE_PARENT_KEY","parentCounter","getAttribute","eventCacheKey","itemIndex","stagger","staggerClassName","staggerCacheKey","applyClasses","formerData","timings","blockTransition","blockAnimation","blockTransitions","style","ANIMATION_PLAYSTATE_KEY","animateRun","activeAnimationComplete","onEnd","off","css3AnimationEvents","onAnimationProgress","activeClassName","pendingClassName","staggerTimeout","animateClose","appliedStyles","removeProperty","stopPropagation","ev","originalEvent","timeStamp","$manualTimeStamp","elapsedTime","toFixed","ELAPSED_TIME_MAX_DECIMAL_PLACES","startTime","maxDelayTime","maxDuration","prefix","staggerTime","transitionStaggerDelay","animationStaggerDelay","CSS_PREFIX","round","keys","maxDelay","ONE_SECOND","oldStyle","setAttribute","ANIMATIONEND_EVENT","TRANSITIONEND_EVENT","CLOSING_TIME_BUFFER","on","bool","PROPERTY_KEY","animateBefore","animateAfter","afterAnimationComplete","animationComplete","preReflowCancellation","suffixClasses","suffix","ontransitionend","onwebkittransitionend","onanimationend","onwebkitanimationend","animationCompleted","beforeSetClass","cancellationMethod","beforeAddClass","beforeRemoveClass"] +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/bower.json new file mode 100644 index 000000000..eb11f6ad7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/bower.json @@ -0,0 +1,9 @@ +{ + "name": "angular-animate", + "version": "1.3.20", + "main": "./angular-animate.js", + "ignore": [], + "dependencies": { + "angular": "1.3.20" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/index.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/index.js new file mode 100644 index 000000000..6ec0a3510 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/index.js @@ -0,0 +1,2 @@ +require('./angular-animate'); +module.exports = 'ngAnimate'; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/package.json new file mode 100644 index 000000000..983094d6a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-animate/package.json @@ -0,0 +1,26 @@ +{ + "name": "angular-animate", + "version": "1.3.20", + "description": "AngularJS module for animations", + "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", + "animation", + "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" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.bower.json new file mode 100644 index 000000000..e81c3042d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.bower.json @@ -0,0 +1,31 @@ +{ + "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": "0.14.3", + "main": [ + "./ui-bootstrap-tpls.js" + ], + "dependencies": { + "angular": ">=1.3.0" + }, + "homepage": "https://github.com/angular-ui/bootstrap-bower", + "_release": "0.14.3", + "_resolution": { + "type": "version", + "tag": "0.14.3", + "commit": "306d1a30b4a8e8144741bb9c0126331ac884126a" + }, + "_source": "https://github.com/angular-ui/bootstrap-bower.git", + "_target": "0.14.3", + "_originalSource": "angular-bootstrap" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.gitignore b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.gitignore new file mode 100644 index 000000000..496ee2ca6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.gitignore @@ -0,0 +1 @@ +.DS_Store
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.npmignore b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.npmignore new file mode 100644 index 000000000..d62f9b6e0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/.npmignore @@ -0,0 +1 @@ +bower.json
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/README.md new file mode 100644 index 000000000..9607c65f6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/README.md @@ -0,0 +1,120 @@ +### 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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/bower.json new file mode 100644 index 000000000..03d4eb272 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/bower.json @@ -0,0 +1,19 @@ +{ + "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": "0.14.3", + "main": ["./ui-bootstrap-tpls.js"], + "dependencies": { + "angular": ">=1.3.0" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/index.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/index.js new file mode 100644 index 000000000..a174f26d4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/index.js @@ -0,0 +1,2 @@ +require('./ui-bootstrap-tpls'); +module.exports = 'ui.bootstrap'; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/package.json new file mode 100644 index 000000000..f7800479c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/package.json @@ -0,0 +1,23 @@ +{ + "name": "angular-ui-bootstrap", + "version": "0.14.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.3.x || >= 1.4.0-beta.0 || >= 1.5.0-beta.0" + }, + "license": "MIT" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-csp.css b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-csp.css new file mode 100644 index 000000000..d772f786d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-csp.css @@ -0,0 +1,6 @@ +/* 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 +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.js new file mode 100644 index 000000000..f72df4966 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.js @@ -0,0 +1,8503 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.14.3 - 2015-10-23 + * License: MIT + */ +angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); +angular.module("ui.bootstrap.tpls", ["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-popup.html","template/tooltip/tooltip-popup.html","template/tooltip/tooltip-template-popup.html","template/popover/popover-html.html","template/popover/popover-template.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]); +angular.module('ui.bootstrap.collapse', []) + + .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + function expand() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + addClass: 'in', + easing: 'ease', + to: { height: element[0].scrollHeight + 'px' } + }).start().finally(expandDone); + } else { + $animate.addClass(element, 'in', { + to: { height: element[0].scrollHeight + 'px' } + }).then(expandDone); + } + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse') + .css({height: 'auto'}); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + element + // IMPORTANT: The height must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from height 0 (in + // collapsing class) to the given height here. + .css({height: element[0].scrollHeight + 'px'}) + // initially all panel collapse have the collapse class, this removal + // prevents the animation from jumping to collapsed state + .removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', false) + .attr('aria-hidden', true); + + if ($animateCss) { + $animateCss(element, { + removeClass: 'in', + to: {height: '0'} + }).start().finally(collapseDone); + } else { + $animate.removeClass(element, 'in', { + to: {height: '0'} + }).then(collapseDone); + } + } + + function collapseDone() { + element.css({height: '0'}); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + } + + scope.$watch(attrs.uibCollapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +/* Deprecated collapse below */ + +angular.module('ui.bootstrap.collapse') + + .value('$collapseSuppressWarning', false) + + .directive('collapse', ['$animate', '$injector', '$log', '$collapseSuppressWarning', function($animate, $injector, $log, $collapseSuppressWarning) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + if (!$collapseSuppressWarning) { + $log.warn('collapse is now deprecated. Use uib-collapse instead.'); + } + + function expand() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + easing: 'ease', + to: { height: element[0].scrollHeight + 'px' } + }).start().done(expandDone); + } else { + $animate.animate(element, {}, { + height: element[0].scrollHeight + 'px' + }).then(expandDone); + } + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse in') + .css({height: 'auto'}); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + element + // IMPORTANT: The height must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from height 0 (in + // collapsing class) to the given height here. + .css({height: element[0].scrollHeight + 'px'}) + // initially all panel collapse have the collapse class, this removal + // prevents the animation from jumping to collapsed state + .removeClass('collapse in') + .addClass('collapsing') + .attr('aria-expanded', false) + .attr('aria-hidden', true); + + if ($animateCss) { + $animateCss(element, { + to: {height: '0'} + }).start().done(collapseDone); + } else { + $animate.animate(element, {}, { + height: '0' + }).then(collapseDone); + } + } + + function collapseDone() { + element.css({height: '0'}); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + } + + scope.$watch(attrs.collapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']) + +.constant('uibAccordionConfig', { + closeOthers: true +}) + +.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { + // This array keeps track of the accordion groups + this.groups = []; + + // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to + this.closeOthers = function(openGroup) { + var closeOthers = angular.isDefined($attrs.closeOthers) ? + $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; + if (closeOthers) { + angular.forEach(this.groups, function(group) { + if (group !== openGroup) { + group.isOpen = false; + } + }); + } + }; + + // This is called from the accordion-group directive to add itself to the accordion + this.addGroup = function(groupScope) { + var that = this; + this.groups.push(groupScope); + + groupScope.$on('$destroy', function(event) { + that.removeGroup(groupScope); + }); + }; + + // This is called from the accordion-group directive when to remove itself + this.removeGroup = function(group) { + var index = this.groups.indexOf(group); + if (index !== -1) { + this.groups.splice(index, 1); + } + }; + +}]) + +// The accordion directive simply sets up the directive controller +// and adds an accordion CSS class to itself element. +.directive('uibAccordion', function() { + return { + controller: 'UibAccordionController', + controllerAs: 'accordion', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion.html'; + } + }; +}) + +// The accordion-group directive indicates a block of html that will expand and collapse in an accordion +.directive('uibAccordionGroup', function() { + return { + require: '^uibAccordion', // We need this directive to be inside an accordion + transclude: true, // It transcludes the contents of the directive into the template + replace: true, // The element containing the directive will be replaced with the template + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass; + scope.$watch('isOpen', function(value) { + element.toggleClass(scope.openClass, !!value); + if (value) { + accordionCtrl.closeOthers(scope); + } + }); + + scope.toggleOpen = function($event) { + if (!scope.isDisabled) { + if (!$event || $event.which === 32) { + scope.isOpen = !scope.isOpen; + } + } + }; + } + }; +}) + +// Use accordion-heading below an accordion-group to provide a heading containing HTML +.directive('uibAccordionHeading', function() { + return { + transclude: true, // Grab the contents to be used as the heading + template: '', // In effect remove this element! + replace: true, + require: '^uibAccordionGroup', + link: function(scope, element, attrs, accordionGroupCtrl, transclude) { + // Pass the heading to the accordion-group controller + // so that it can be transcluded into the right place in the template + // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] + accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); + } + }; +}) + +// Use in the accordion-group template to indicate where you want the heading to be transcluded +// You must provide the property on the accordion-group controller that will hold the transcluded element +.directive('uibAccordionTransclude', function() { + return { + require: ['?^uibAccordionGroup', '?^accordionGroup'], + link: function(scope, element, attrs, controller) { + controller = controller[0] ? controller[0] : controller[1]; // Delete after we remove deprecation + scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { + if (heading) { + element.find('span').html(''); + element.find('span').append(heading); + } + }); + } + }; +}); + +/* Deprecated accordion below */ + +angular.module('ui.bootstrap.accordion') + + .value('$accordionSuppressWarning', false) + + .controller('AccordionController', ['$scope', '$attrs', '$controller', '$log', '$accordionSuppressWarning', function($scope, $attrs, $controller, $log, $accordionSuppressWarning) { + if (!$accordionSuppressWarning) { + $log.warn('AccordionController is now deprecated. Use UibAccordionController instead.'); + } + + angular.extend(this, $controller('UibAccordionController', { + $scope: $scope, + $attrs: $attrs + })); + }]) + + .directive('accordion', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + restrict: 'EA', + controller: 'AccordionController', + controllerAs: 'accordion', + transclude: true, + replace: false, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion.html'; + }, + link: function() { + if (!$accordionSuppressWarning) { + $log.warn('accordion is now deprecated. Use uib-accordion instead.'); + } + } + }; + }]) + + .directive('accordionGroup', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + require: '^accordion', // We need this directive to be inside an accordion + restrict: 'EA', + transclude: true, // It transcludes the contents of the directive into the template + replace: true, // The element containing the directive will be replaced with the template + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + if (!$accordionSuppressWarning) { + $log.warn('accordion-group is now deprecated. Use uib-accordion-group instead.'); + } + + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass; + scope.$watch('isOpen', function(value) { + element.toggleClass(scope.openClass, !!value); + if (value) { + accordionCtrl.closeOthers(scope); + } + }); + + scope.toggleOpen = function($event) { + if (!scope.isDisabled) { + if (!$event || $event.which === 32) { + scope.isOpen = !scope.isOpen; + } + } + }; + } + }; + }]) + + .directive('accordionHeading', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + restrict: 'EA', + transclude: true, // Grab the contents to be used as the heading + template: '', // In effect remove this element! + replace: true, + require: '^accordionGroup', + link: function(scope, element, attr, accordionGroupCtrl, transclude) { + if (!$accordionSuppressWarning) { + $log.warn('accordion-heading is now deprecated. Use uib-accordion-heading instead.'); + } + // Pass the heading to the accordion-group controller + // so that it can be transcluded into the right place in the template + // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] + accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); + } + }; + }]) + + .directive('accordionTransclude', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + require: '^accordionGroup', + link: function(scope, element, attr, controller) { + if (!$accordionSuppressWarning) { + $log.warn('accordion-transclude is now deprecated. Use uib-accordion-transclude instead.'); + } + + scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) { + if (heading) { + element.find('span').html(''); + element.find('span').append(heading); + } + }); + } + }; + }]); + + +angular.module('ui.bootstrap.alert', []) + +.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) { + $scope.closeable = !!$attrs.close; + + var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? + $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; + + if (dismissOnTimeout) { + $timeout(function() { + $scope.close(); + }, parseInt(dismissOnTimeout, 10)); + } +}]) + +.directive('uibAlert', function() { + return { + controller: 'UibAlertController', + controllerAs: 'alert', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/alert/alert.html'; + }, + transclude: true, + replace: true, + scope: { + type: '@', + close: '&' + } + }; +}); + +/* Deprecated alert below */ + +angular.module('ui.bootstrap.alert') + + .value('$alertSuppressWarning', false) + + .controller('AlertController', ['$scope', '$attrs', '$controller', '$log', '$alertSuppressWarning', function($scope, $attrs, $controller, $log, $alertSuppressWarning) { + if (!$alertSuppressWarning) { + $log.warn('AlertController is now deprecated. Use UibAlertController instead.'); + } + + angular.extend(this, $controller('UibAlertController', { + $scope: $scope, + $attrs: $attrs + })); + }]) + + .directive('alert', ['$log', '$alertSuppressWarning', function($log, $alertSuppressWarning) { + return { + controller: 'AlertController', + controllerAs: 'alert', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/alert/alert.html'; + }, + transclude: true, + replace: true, + scope: { + type: '@', + close: '&' + }, + link: function() { + if (!$alertSuppressWarning) { + $log.warn('alert is now deprecated. Use uib-alert instead.'); + } + } + }; + }]); + +angular.module('ui.bootstrap.buttons', []) + +.constant('uibButtonConfig', { + activeClass: 'active', + toggleEvent: 'click' +}) + +.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { + this.activeClass = buttonConfig.activeClass || 'active'; + this.toggleEvent = buttonConfig.toggleEvent || 'click'; +}]) + +.directive('uibBtnRadio', function() { + return { + require: ['uibBtnRadio', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + var isActive = element.hasClass(buttonsCtrl.activeClass); + + if (!isActive || angular.isDefined(attrs.uncheckable)) { + scope.$apply(function() { + ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); + ngModelCtrl.$render(); + }); + } + }); + } + }; +}) + +.directive('uibBtnCheckbox', function() { + return { + require: ['uibBtnCheckbox', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'button', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + function getTrueValue() { + return getCheckboxValue(attrs.btnCheckboxTrue, true); + } + + function getFalseValue() { + return getCheckboxValue(attrs.btnCheckboxFalse, false); + } + + function getCheckboxValue(attribute, defaultValue) { + return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; + } + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + } + }; +}); + +/* Deprecated buttons below */ + +angular.module('ui.bootstrap.buttons') + + .value('$buttonsSuppressWarning', false) + + .controller('ButtonsController', ['$controller', '$log', '$buttonsSuppressWarning', function($controller, $log, $buttonsSuppressWarning) { + if (!$buttonsSuppressWarning) { + $log.warn('ButtonsController is now deprecated. Use UibButtonsController instead.'); + } + + angular.extend(this, $controller('UibButtonsController')); + }]) + + .directive('btnRadio', ['$log', '$buttonsSuppressWarning', function($log, $buttonsSuppressWarning) { + return { + require: ['btnRadio', 'ngModel'], + controller: 'ButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + if (!$buttonsSuppressWarning) { + $log.warn('btn-radio is now deprecated. Use uib-btn-radio instead.'); + } + + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio))); + }; + + //ui->model + element.bind(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + var isActive = element.hasClass(buttonsCtrl.activeClass); + + if (!isActive || angular.isDefined(attrs.uncheckable)) { + scope.$apply(function() { + ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio)); + ngModelCtrl.$render(); + }); + } + }); + } + }; + }]) + + .directive('btnCheckbox', ['$document', '$log', '$buttonsSuppressWarning', function($document, $log, $buttonsSuppressWarning) { + return { + require: ['btnCheckbox', 'ngModel'], + controller: 'ButtonsController', + controllerAs: 'button', + link: function(scope, element, attrs, ctrls) { + if (!$buttonsSuppressWarning) { + $log.warn('btn-checkbox is now deprecated. Use uib-btn-checkbox instead.'); + } + + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + function getTrueValue() { + return getCheckboxValue(attrs.btnCheckboxTrue, true); + } + + function getFalseValue() { + return getCheckboxValue(attrs.btnCheckboxFalse, false); + } + + function getCheckboxValue(attributeValue, defaultValue) { + var val = scope.$eval(attributeValue); + return angular.isDefined(val) ? val : defaultValue; + } + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); + }; + + //ui->model + element.bind(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + + //accessibility + element.on('keypress', function(e) { + if (attrs.disabled || e.which !== 32 || $document[0].activeElement !== element[0]) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + } + }; + }]); + + +/** + * @ngdoc overview + * @name ui.bootstrap.carousel + * + * @description + * AngularJS version of an image carousel. + * + */ +angular.module('ui.bootstrap.carousel', []) + +.controller('UibCarouselController', ['$scope', '$element', '$interval', '$animate', function($scope, $element, $interval, $animate) { + var self = this, + slides = self.slides = $scope.slides = [], + NEW_ANIMATE = angular.version.minor >= 4, + NO_TRANSITION = 'uib-noTransition', + SLIDE_DIRECTION = 'uib-slideDirection', + currentIndex = -1, + currentInterval, isPlaying; + self.currentSlide = null; + + var destroyed = false; + /* direction: "prev" or "next" */ + self.select = $scope.select = function(nextSlide, direction) { + var nextIndex = $scope.indexOfSlide(nextSlide); + //Decide direction if it's not given + if (direction === undefined) { + direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; + } + //Prevent this user-triggered transition from occurring if there is already one in progress + if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) { + goNext(nextSlide, nextIndex, direction); + } + }; + + function goNext(slide, index, direction) { + // Scope has been destroyed, stop here. + if (destroyed) { return; } + + angular.extend(slide, {direction: direction, active: true}); + angular.extend(self.currentSlide || {}, {direction: direction, active: false}); + if ($animate.enabled() && !$scope.noTransition && !$scope.$currentTransition && + slide.$element && self.slides.length > 1) { + slide.$element.data(SLIDE_DIRECTION, slide.direction); + if (self.currentSlide && self.currentSlide.$element) { + self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction); + } + + $scope.$currentTransition = true; + if (NEW_ANIMATE) { + $animate.on('addClass', slide.$element, function(element, phase) { + if (phase === 'close') { + $scope.$currentTransition = null; + $animate.off('addClass', element); + } + }); + } else { + slide.$element.one('$animate:close', function closeFn() { + $scope.$currentTransition = null; + }); + } + } + + self.currentSlide = slide; + currentIndex = index; + + //every time you change slides, reset the timer + restartTimer(); + } + + $scope.$on('$destroy', function() { + destroyed = true; + }); + + function getSlideByIndex(index) { + if (angular.isUndefined(slides[index].index)) { + return slides[index]; + } + var i, len = slides.length; + for (i = 0; i < slides.length; ++i) { + if (slides[i].index == index) { + return slides[i]; + } + } + } + + self.getCurrentIndex = function() { + if (self.currentSlide && angular.isDefined(self.currentSlide.index)) { + return +self.currentSlide.index; + } + return currentIndex; + }; + + /* Allow outside people to call indexOf on slides array */ + $scope.indexOfSlide = function(slide) { + return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide); + }; + + $scope.next = function() { + var newIndex = (self.getCurrentIndex() + 1) % slides.length; + + if (newIndex === 0 && $scope.noWrap()) { + $scope.pause(); + return; + } + + return self.select(getSlideByIndex(newIndex), 'next'); + }; + + $scope.prev = function() { + var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; + + if ($scope.noWrap() && newIndex === slides.length - 1) { + $scope.pause(); + return; + } + + return self.select(getSlideByIndex(newIndex), 'prev'); + }; + + $scope.isActive = function(slide) { + return self.currentSlide === slide; + }; + + $scope.$watch('interval', restartTimer); + $scope.$watchCollection('slides', resetTransition); + $scope.$on('$destroy', resetTimer); + + function restartTimer() { + resetTimer(); + var interval = +$scope.interval; + if (!isNaN(interval) && interval > 0) { + currentInterval = $interval(timerFn, interval); + } + } + + function resetTimer() { + if (currentInterval) { + $interval.cancel(currentInterval); + currentInterval = null; + } + } + + function timerFn() { + var interval = +$scope.interval; + if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { + $scope.next(); + } else { + $scope.pause(); + } + } + + function resetTransition(slides) { + if (!slides.length) { + $scope.$currentTransition = null; + } + } + + $scope.play = function() { + if (!isPlaying) { + isPlaying = true; + restartTimer(); + } + }; + $scope.pause = function() { + if (!$scope.noPause) { + isPlaying = false; + resetTimer(); + } + }; + + self.addSlide = function(slide, element) { + slide.$element = element; + slides.push(slide); + //if this is the first slide or the slide is set to active, select it + if (slides.length === 1 || slide.active) { + self.select(slides[slides.length - 1]); + if (slides.length === 1) { + $scope.play(); + } + } else { + slide.active = false; + } + }; + + self.removeSlide = function(slide) { + if (angular.isDefined(slide.index)) { + slides.sort(function(a, b) { + return +a.index > +b.index; + }); + } + //get the index of the slide inside the carousel + var index = slides.indexOf(slide); + slides.splice(index, 1); + if (slides.length > 0 && slide.active) { + if (index >= slides.length) { + self.select(slides[index - 1]); + } else { + self.select(slides[index]); + } + } else if (currentIndex > index) { + currentIndex--; + } + + //clean the currentSlide when no more slide + if (slides.length === 0) { + self.currentSlide = null; + } + }; + + $scope.$watch('noTransition', function(noTransition) { + $element.data(NO_TRANSITION, noTransition); + }); + +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.carousel.directive:carousel + * @restrict EA + * + * @description + * Carousel is the outer container for a set of image 'slides' to showcase. + * + * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide. + * @param {boolean=} noTransition Whether to disable transitions on the carousel. + * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover). + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <uib-carousel> + <uib-slide> + <img src="http://placekitten.com/150/150" style="margin:auto;"> + <div class="carousel-caption"> + <p>Beautiful!</p> + </div> + </uib-slide> + <uib-slide> + <img src="http://placekitten.com/100/150" style="margin:auto;"> + <div class="carousel-caption"> + <p>D'aww!</p> + </div> + </uib-slide> + </uib-carousel> + </file> + <file name="demo.css"> + .carousel-indicators { + top: auto; + bottom: 15px; + } + </file> +</example> + */ +.directive('uibCarousel', [function() { + return { + transclude: true, + replace: true, + controller: 'UibCarouselController', + controllerAs: 'carousel', + require: 'carousel', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/carousel.html'; + }, + scope: { + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + } + }; +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.carousel.directive:slide + * @restrict EA + * + * @description + * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element. + * + * @param {boolean=} active Model binding, whether or not this slide is currently active. + * @param {number=} index The index of the slide. The slides will be sorted by this parameter. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> +<div ng-controller="CarouselDemoCtrl"> + <uib-carousel> + <uib-slide ng-repeat="slide in slides" active="slide.active" index="$index"> + <img ng-src="{{slide.image}}" style="margin:auto;"> + <div class="carousel-caption"> + <h4>Slide {{$index}}</h4> + <p>{{slide.text}}</p> + </div> + </uib-slide> + </uib-carousel> + Interval, in milliseconds: <input type="number" ng-model="myInterval"> + <br />Enter a negative number to stop the interval. +</div> + </file> + <file name="script.js"> +function CarouselDemoCtrl($scope) { + $scope.myInterval = 5000; +} + </file> + <file name="demo.css"> + .carousel-indicators { + top: auto; + bottom: 15px; + } + </file> +</example> +*/ + +.directive('uibSlide', function() { + return { + require: '^uibCarousel', + restrict: 'EA', + transclude: true, + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/slide.html'; + }, + scope: { + active: '=?', + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + carouselCtrl.addSlide(scope, element); + //when the scope is destroyed then remove the slide from the current slides array + scope.$on('$destroy', function() { + carouselCtrl.removeSlide(scope); + }); + + scope.$watch('active', function(active) { + if (active) { + carouselCtrl.select(scope); + } + }); + } + }; +}) + +.animation('.item', [ + '$injector', '$animate', +function ($injector, $animate) { + var NO_TRANSITION = 'uib-noTransition', + SLIDE_DIRECTION = 'uib-slideDirection', + $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + function removeClass(element, className, callback) { + element.removeClass(className); + if (callback) { + callback(); + } + } + + return { + beforeAddClass: function(element, className, done) { + // Due to transclusion, noTransition property is on parent's scope + if (className == 'active' && element.parent() && element.parent().parent() && + !element.parent().parent().data(NO_TRANSITION)) { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction == 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, + directionClass + ' ' + direction, done); + element.addClass(direction); + + if ($animateCss) { + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + } else { + $animate.addClass(element, directionClass).then(function () { + if (!stopped) { + removeClassFn(); + } + done(); + }); + } + + return function () { + stopped = true; + }; + } + done(); + }, + beforeRemoveClass: function (element, className, done) { + // Due to transclusion, noTransition property is on parent's scope + if (className === 'active' && element.parent() && element.parent().parent() && + !element.parent().parent().data(NO_TRANSITION)) { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction == 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, directionClass, done); + + if ($animateCss) { + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + } else { + $animate.addClass(element, directionClass).then(function() { + if (!stopped) { + removeClassFn(); + } + done(); + }); + } + return function() { + stopped = true; + }; + } + done(); + } + }; +}]); + +/* deprecated carousel below */ + +angular.module('ui.bootstrap.carousel') + +.value('$carouselSuppressWarning', false) + +.controller('CarouselController', ['$scope', '$element', '$controller', '$log', '$carouselSuppressWarning', function($scope, $element, $controller, $log, $carouselSuppressWarning) { + if (!$carouselSuppressWarning) { + $log.warn('CarouselController is now deprecated. Use UibCarouselController instead.'); + } + + angular.extend(this, $controller('UibCarouselController', { + $scope: $scope, + $element: $element + })); +}]) + +.directive('carousel', ['$log', '$carouselSuppressWarning', function($log, $carouselSuppressWarning) { + return { + transclude: true, + replace: true, + controller: 'CarouselController', + controllerAs: 'carousel', + require: 'carousel', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/carousel.html'; + }, + scope: { + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + }, + link: function() { + if (!$carouselSuppressWarning) { + $log.warn('carousel is now deprecated. Use uib-carousel instead.'); + } + } + }; +}]) + +.directive('slide', ['$log', '$carouselSuppressWarning', function($log, $carouselSuppressWarning) { + return { + require: '^carousel', + transclude: true, + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/slide.html'; + }, + scope: { + active: '=?', + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + if (!$carouselSuppressWarning) { + $log.warn('slide is now deprecated. Use uib-slide instead.'); + } + + carouselCtrl.addSlide(scope, element); + //when the scope is destroyed then remove the slide from the current slides array + scope.$on('$destroy', function() { + carouselCtrl.removeSlide(scope); + }); + + scope.$watch('active', function(active) { + if (active) { + carouselCtrl.select(scope); + } + }); + } + }; +}]); + +angular.module('ui.bootstrap.dateparser', []) + +.service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) { + // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js + var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + + var localeId; + var formatCodeToRegex; + + this.init = function() { + localeId = $locale.id; + + this.parsers = {}; + + formatCodeToRegex = { + 'yyyy': { + regex: '\\d{4}', + apply: function(value) { this.year = +value; } + }, + 'yy': { + regex: '\\d{2}', + apply: function(value) { this.year = +value + 2000; } + }, + 'y': { + regex: '\\d{1,4}', + apply: function(value) { this.year = +value; } + }, + 'MMMM': { + regex: $locale.DATETIME_FORMATS.MONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); } + }, + 'MMM': { + regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); } + }, + 'MM': { + regex: '0[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; } + }, + 'M': { + regex: '[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; } + }, + 'dd': { + regex: '[0-2][0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; } + }, + 'd': { + regex: '[1-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; } + }, + 'EEEE': { + regex: $locale.DATETIME_FORMATS.DAY.join('|') + }, + 'EEE': { + regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|') + }, + 'HH': { + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'hh': { + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'H': { + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'h': { + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'mm': { + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'm': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'sss': { + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; } + }, + 'ss': { + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 's': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 'a': { + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } + + if (value === 'PM') { + this.hours += 12; + } + } + } + }; + }; + + this.init(); + + function createParser(format) { + var map = [], regex = format.split(''); + + angular.forEach(formatCodeToRegex, function(data, code) { + var index = format.indexOf(code); + + if (index > -1) { + format = format.split(''); + + regex[index] = '(' + data.regex + ')'; + format[index] = '$'; // Custom symbol to define consumed part of format + for (var i = index + 1, n = index + code.length; i < n; i++) { + regex[i] = ''; + format[i] = '$'; + } + format = format.join(''); + + map.push({ index: index, apply: data.apply }); + } + }); + + return { + regex: new RegExp('^' + regex.join('') + '$'), + map: orderByFilter(map, 'index') + }; + } + + this.parse = function(input, format, baseDate) { + if (!angular.isString(input) || !format) { + return input; + } + + format = $locale.DATETIME_FORMATS[format] || format; + format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.parsers[format]) { + this.parsers[format] = createParser(format); + } + + var parser = this.parsers[format], + regex = parser.regex, + map = parser.map, + results = input.match(regex); + + if (results && results.length) { + var fields, dt; + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { + fields = { + year: baseDate.getFullYear(), + month: baseDate.getMonth(), + date: baseDate.getDate(), + hours: baseDate.getHours(), + minutes: baseDate.getMinutes(), + seconds: baseDate.getSeconds(), + milliseconds: baseDate.getMilliseconds() + }; + } else { + if (baseDate) { + $log.warn('dateparser:', 'baseDate is not a valid date'); + } + fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; + } + + for (var i = 1, n = results.length; i < n; i++) { + var mapper = map[i-1]; + if (mapper.apply) { + mapper.apply.call(fields, results[i]); + } + } + + if (isValid(fields.year, fields.month, fields.date)) { + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { + dt = new Date(baseDate); + dt.setFullYear(fields.year, fields.month, fields.date, + fields.hours, fields.minutes, fields.seconds, + fields.milliseconds || 0); + } else { + dt = new Date(fields.year, fields.month, fields.date, + fields.hours, fields.minutes, fields.seconds, + fields.milliseconds || 0); + } + } + + return dt; + } + }; + + // Check if date is valid for specific month (and year for February). + // Month: 0 = Jan, 1 = Feb, etc + function isValid(year, month, date) { + if (date < 1) { + return false; + } + + if (month === 1 && date > 28) { + return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); + } + + if (month === 3 || month === 5 || month === 8 || month === 10) { + return date < 31; + } + + return true; + } +}]); + +/* Deprecated dateparser below */ + +angular.module('ui.bootstrap.dateparser') + +.value('$dateParserSuppressWarning', false) + +.service('dateParser', ['$log', '$dateParserSuppressWarning', 'uibDateParser', function($log, $dateParserSuppressWarning, uibDateParser) { + if (!$dateParserSuppressWarning) { + $log.warn('dateParser is now deprecated. Use uibDateParser instead.'); + } + + angular.extend(this, uibDateParser); +}]); + +angular.module('ui.bootstrap.position', []) + +/** + * A set of utility methods that can be use to retrieve position of DOM elements. + * It is meant to be used where we need to absolute-position DOM elements in + * relation to other, existing elements (this is the case for tooltips, popovers, + * typeahead suggestions etc.). + */ + .factory('$uibPosition', ['$document', '$window', function($document, $window) { + function getStyle(el, cssprop) { + if (el.currentStyle) { //IE + return el.currentStyle[cssprop]; + } else if ($window.getComputedStyle) { + return $window.getComputedStyle(el)[cssprop]; + } + // finally try and get inline style + return el.style[cssprop]; + } + + /** + * Checks if a given element is statically positioned + * @param element - raw DOM element + */ + function isStaticPositioned(element) { + return (getStyle(element, 'position') || 'static' ) === 'static'; + } + + /** + * returns the closest, non-statically positioned parentOffset of a given element + * @param element + */ + var parentOffsetEl = function(element) { + var docDomEl = $document[0]; + var offsetParent = element.offsetParent || docDomEl; + while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || docDomEl; + }; + + return { + /** + * Provides read-only equivalent of jQuery's position function: + * http://api.jquery.com/position/ + */ + position: function(element) { + var elBCR = this.offset(element); + var offsetParentBCR = { top: 0, left: 0 }; + var offsetParentEl = parentOffsetEl(element[0]); + if (offsetParentEl != $document[0]) { + offsetParentBCR = this.offset(angular.element(offsetParentEl)); + offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; + offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; + } + + var boundingClientRect = element[0].getBoundingClientRect(); + return { + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: elBCR.top - offsetParentBCR.top, + left: elBCR.left - offsetParentBCR.left + }; + }, + + /** + * Provides read-only equivalent of jQuery's offset function: + * http://api.jquery.com/offset/ + */ + offset: function(element) { + var boundingClientRect = element[0].getBoundingClientRect(); + return { + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), + left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) + }; + }, + + /** + * Provides coordinates for the targetEl in relation to hostEl + */ + positionElements: function(hostEl, targetEl, positionStr, appendToBody) { + var positionStrParts = positionStr.split('-'); + var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center'; + + var hostElPos, + targetElWidth, + targetElHeight, + targetElPos; + + hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); + + targetElWidth = targetEl.prop('offsetWidth'); + targetElHeight = targetEl.prop('offsetHeight'); + + var shiftWidth = { + center: function() { + return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; + }, + left: function() { + return hostElPos.left; + }, + right: function() { + return hostElPos.left + hostElPos.width; + } + }; + + var shiftHeight = { + center: function() { + return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; + }, + top: function() { + return hostElPos.top; + }, + bottom: function() { + return hostElPos.top + hostElPos.height; + } + }; + + switch (pos0) { + case 'right': + targetElPos = { + top: shiftHeight[pos1](), + left: shiftWidth[pos0]() + }; + break; + case 'left': + targetElPos = { + top: shiftHeight[pos1](), + left: hostElPos.left - targetElWidth + }; + break; + case 'bottom': + targetElPos = { + top: shiftHeight[pos0](), + left: shiftWidth[pos1]() + }; + break; + default: + targetElPos = { + top: hostElPos.top - targetElHeight, + left: shiftWidth[pos1]() + }; + break; + } + + return targetElPos; + } + }; + }]); + +/* Deprecated position below */ + +angular.module('ui.bootstrap.position') + +.value('$positionSuppressWarning', false) + +.service('$position', ['$log', '$positionSuppressWarning', '$uibPosition', function($log, $positionSuppressWarning, $uibPosition) { + if (!$positionSuppressWarning) { + $log.warn('$position is now deprecated. Use $uibPosition instead.'); + } + + angular.extend(this, $uibPosition); +}]); + +angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position']) + +.value('$datepickerSuppressError', false) + +.constant('uibDatepickerConfig', { + formatDay: 'dd', + formatMonth: 'MMMM', + formatYear: 'yyyy', + formatDayHeader: 'EEE', + formatDayTitle: 'MMMM yyyy', + formatMonthTitle: 'yyyy', + datepickerMode: 'day', + minMode: 'day', + maxMode: 'year', + showWeeks: true, + startingDay: 0, + yearRange: 20, + minDate: null, + maxDate: null, + shortcutPropagation: false +}) + +.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl; + + // Modes chain + this.modes = ['day', 'month', 'year']; + + // Configuration attributes + angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', + 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) { + self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key]; + }); + + // Watchable date attributes + angular.forEach(['minDate', 'maxDate'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = value ? new Date(value) : null; + self.refreshView(); + }); + } else { + self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null; + } + }); + + angular.forEach(['minMode', 'maxMode'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = angular.isDefined(value) ? value : $attrs[key]; + $scope[key] = self[key]; + if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) { + $scope.datepickerMode = self[key]; + } + }); + } else { + self[key] = datepickerConfig[key] || null; + $scope[key] = self[key]; + } + }); + + $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode; + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + if (angular.isDefined($attrs.initDate)) { + this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date(); + $scope.$parent.$watch($attrs.initDate, function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = initDate; + self.refreshView(); + } + }); + } else { + this.activeDate = new Date(); + } + + $scope.isActive = function(dateObject) { + if (self.compare(dateObject.date, self.activeDate) === 0) { + $scope.activeDateId = dateObject.uid; + return true; + } + return false; + }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = date; + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + this._refreshView(); + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date))); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + return { + date: date, + label: dateFilter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + current: this.compare(date, new Date()) === 0, + customClass: this.customClass(date) + }; + }; + + this.isDisabled = function(date) { + return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}))); + }; + + this.customClass = function(date) { + return $scope.customClass({date: date, mode: $scope.datepickerMode}); + }; + + // Split array into smaller arrays + this.split = function(arr, size) { + var arrays = []; + while (arr.length > 0) { + arrays.push(arr.splice(0, size)); + } + return arrays; + }; + + $scope.select = function(date) { + if ($scope.datepickerMode === self.minMode) { + var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]; + } + }; + + $scope.move = function(direction) { + var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), + month = self.activeDate.getMonth() + direction * (self.step.months || 0); + self.activeDate.setFullYear(year, month, 1); + self.refreshView(); + }; + + $scope.toggleMode = function(direction) { + direction = direction || 1; + + if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) { + return; + } + + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]; + }; + + // Key event mapper + $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; + + var focusElement = function() { + self.element[0].focus(); + }; + + // Listen for focus requests from popup directive + $scope.$on('uib:datepicker.focus', focusElement); + + $scope.keydown = function(evt) { + var key = $scope.keys[evt.which]; + + if (!key || evt.shiftKey || evt.altKey) { + return; + } + + evt.preventDefault(); + if (!self.shortcutPropagation) { + evt.stopPropagation(); + } + + if (key === 'enter' || key === 'space') { + if (self.isDisabled(self.activeDate)) { + return; // do nothing + } + $scope.select(self.activeDate); + } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { + $scope.toggleMode(key === 'up' ? 1 : -1); + } else { + self.handleKeyDown(key, evt); + self.refreshView(); + } + }; +}]) + +.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + this.step = { months: 1 }; + this.element = $element; + function getDaysInMonth(year, month) { + return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month]; + } + + this.init = function(ctrl) { + angular.extend(ctrl, this); + scope.showWeeks = ctrl.showWeeks; + ctrl.refreshView(); + }; + + this.getDates = function(startDate, n) { + var dates = new Array(n), current = new Date(startDate), i = 0, date; + while (i < n) { + date = new Date(current); + dates[i++] = date; + current.setDate(current.getDate() + 1); + } + return dates; + }; + + this._refreshView = function() { + var year = this.activeDate.getFullYear(), + month = this.activeDate.getMonth(), + firstDayOfMonth = new Date(this.activeDate); + + firstDayOfMonth.setFullYear(year, month, 1); + + var difference = this.startingDay - firstDayOfMonth.getDay(), + numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference, + firstDate = new Date(firstDayOfMonth); + + if (numDisplayedFromPreviousMonth > 0) { + firstDate.setDate(-numDisplayedFromPreviousMonth + 1); + } + + // 42 is the number of days on a six-month calendar + var days = this.getDates(firstDate, 42); + for (var i = 0; i < 42; i ++) { + days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), { + secondary: days[i].getMonth() !== month, + uid: scope.uniqueId + '-' + i + }); + } + + scope.labels = new Array(7); + for (var j = 0; j < 7; j++) { + scope.labels[j] = { + abbr: dateFilter(days[j].date, this.formatDayHeader), + full: dateFilter(days[j].date, 'EEEE') + }; + } + + scope.title = dateFilter(this.activeDate, this.formatDayTitle); + scope.rows = this.split(days, 7); + + if (scope.showWeeks) { + scope.weekNumbers = []; + var thursdayIndex = (4 + 7 - this.startingDay) % 7, + numWeeks = scope.rows.length; + for (var curWeek = 0; curWeek < numWeeks; curWeek++) { + scope.weekNumbers.push( + getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date)); + } + } + }; + + this.compare = function(date1, date2) { + return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate())); + }; + + function getISO8601WeekNumber(date) { + var checkDate = new Date(date); + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday + var time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + } + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getDate(); + + if (key === 'left') { + date = date - 1; // up + } else if (key === 'up') { + date = date - 7; // down + } else if (key === 'right') { + date = date + 1; // down + } else if (key === 'down') { + date = date + 7; + } else if (key === 'pageup' || key === 'pagedown') { + var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setMonth(month, 1); + date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date); + } else if (key === 'home') { + date = 1; + } else if (key === 'end') { + date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()); + } + this.activeDate.setDate(date); + }; +}]) + +.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + this.step = { years: 1 }; + this.element = $element; + + this.init = function(ctrl) { + angular.extend(ctrl, this); + ctrl.refreshView(); + }; + + this._refreshView = function() { + var months = new Array(12), + year = this.activeDate.getFullYear(), + date; + + for (var i = 0; i < 12; i++) { + date = new Date(this.activeDate); + date.setFullYear(year, i, 1); + months[i] = angular.extend(this.createDateObject(date, this.formatMonth), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = dateFilter(this.activeDate, this.formatMonthTitle); + scope.rows = this.split(months, 3); + }; + + this.compare = function(date1, date2) { + return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth()); + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getMonth(); + + if (key === 'left') { + date = date - 1; // up + } else if (key === 'up') { + date = date - 3; // down + } else if (key === 'right') { + date = date + 1; // down + } else if (key === 'down') { + date = date + 3; + } else if (key === 'pageup' || key === 'pagedown') { + var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setFullYear(year); + } else if (key === 'home') { + date = 0; + } else if (key === 'end') { + date = 11; + } + this.activeDate.setMonth(date); + }; +}]) + +.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var range; + this.element = $element; + + function getStartingYear(year) { + return parseInt((year - 1) / range, 10) * range + 1; + } + + this.yearpickerInit = function() { + range = this.yearRange; + this.step = { years: range }; + }; + + this._refreshView = function() { + var years = new Array(range), date; + + for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) { + date = new Date(this.activeDate); + date.setFullYear(start + i, 0, 1); + years[i] = angular.extend(this.createDateObject(date, this.formatYear), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = [years[0].label, years[range - 1].label].join(' - '); + scope.rows = this.split(years, 5); + }; + + this.compare = function(date1, date2) { + return date1.getFullYear() - date2.getFullYear(); + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getFullYear(); + + if (key === 'left') { + date = date - 1; // up + } else if (key === 'up') { + date = date - 5; // down + } else if (key === 'right') { + date = date + 1; // down + } else if (key === 'down') { + date = date + 5; + } else if (key === 'pageup' || key === 'pagedown') { + date += (key === 'pageup' ? - 1 : 1) * this.step.years; + } else if (key === 'home') { + date = getStartingYear(this.activeDate.getFullYear()); + } else if (key === 'end') { + date = getStartingYear(this.activeDate.getFullYear()) + range - 1; + } + this.activeDate.setFullYear(date); + }; +}]) + +.directive('uibDatepicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/datepicker.html'; + }, + scope: { + datepickerMode: '=?', + dateDisabled: '&', + customClass: '&', + shortcutPropagation: '&?' + }, + require: ['uibDatepicker', '^ngModel'], + controller: 'UibDatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}) + +.directive('uibDaypicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/day.html'; + }, + require: ['^?uibDatepicker', 'uibDaypicker', '^?datepicker'], + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0] || ctrls[2], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibMonthpicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/month.html'; + }, + require: ['^?uibDatepicker', 'uibMonthpicker', '^?datepicker'], + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0] || ctrls[2], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibYearpicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/year.html'; + }, + require: ['^?uibDatepicker', 'uibYearpicker', '^?datepicker'], + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + var ctrl = ctrls[0] || ctrls[2]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}) + +.constant('uibDatepickerPopupConfig', { + datepickerPopup: 'yyyy-MM-dd', + datepickerPopupTemplateUrl: 'template/datepicker/popup.html', + datepickerTemplateUrl: 'template/datepicker/datepicker.html', + html5Types: { + date: 'yyyy-MM-dd', + 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', + 'month': 'yyyy-MM' + }, + currentText: 'Today', + clearText: 'Clear', + closeText: 'Done', + closeOnDateSelection: true, + appendToBody: false, + showButtonBar: true, + onOpenFocus: true +}) + +.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', +function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) { + var self = this; + var cache = {}, + isHtml5DateInput = false; + var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, + datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, + ngModel, $popup; + + scope.watchData = {}; + + this.init = function(_ngModel_) { + ngModel = _ngModel_; + closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection; + appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody; + onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus; + datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl; + datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl; + + scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar; + + if (datepickerPopupConfig.html5Types[attrs.type]) { + dateFormat = datepickerPopupConfig.html5Types[attrs.type]; + isHtml5DateInput = true; + } else { + dateFormat = attrs.datepickerPopup || attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup; + attrs.$observe('uibDatepickerPopup', function(value, oldValue) { + var newDateFormat = value || datepickerPopupConfig.datepickerPopup; + // Invalidate the $modelValue to ensure that formatters re-run + // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764 + if (newDateFormat !== dateFormat) { + dateFormat = newDateFormat; + ngModel.$modelValue = null; + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + } + }); + } + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + + if (isHtml5DateInput && attrs.datepickerPopup) { + throw new Error('HTML5 date input types do not support custom formats.'); + } + + // popup element used to display calendar + popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>'); + popupEl.attr({ + 'ng-model': 'date', + 'ng-change': 'dateSelection(date)', + 'template-url': datepickerPopupTemplateUrl + }); + + // datepicker element + datepickerEl = angular.element(popupEl.children()[0]); + datepickerEl.attr('template-url', datepickerTemplateUrl); + + if (isHtml5DateInput) { + if (attrs.type === 'month') { + datepickerEl.attr('datepicker-mode', '"month"'); + datepickerEl.attr('min-mode', 'month'); + } + } + + if (attrs.datepickerOptions) { + var options = scope.$parent.$eval(attrs.datepickerOptions); + if (options && options.initDate) { + scope.initDate = options.initDate; + datepickerEl.attr('init-date', 'initDate'); + delete options.initDate; + } + angular.forEach(options, function(value, option) { + datepickerEl.attr(cameltoDash(option), value); + }); + } + + angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(key) { + if (attrs[key]) { + var getAttribute = $parse(attrs[key]); + scope.$parent.$watch(getAttribute, function(value) { + scope.watchData[key] = value; + if (key === 'minDate' || key === 'maxDate') { + cache[key] = new Date(value); + } + }); + datepickerEl.attr(cameltoDash(key), 'watchData.' + key); + + // Propagate changes from datepicker to outside + if (key === 'datepickerMode') { + var setAttribute = getAttribute.assign; + scope.$watch('watchData.' + key, function(value, oldvalue) { + if (angular.isFunction(setAttribute) && value !== oldvalue) { + setAttribute(scope.$parent, value); + } + }); + } + } + }); + if (attrs.dateDisabled) { + datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })'); + } + + if (attrs.showWeeks) { + datepickerEl.attr('show-weeks', attrs.showWeeks); + } + + if (attrs.customClass) { + datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })'); + } + + if (!isHtml5DateInput) { + // Internal API to maintain the correct ng-invalid-[key] class + ngModel.$$parserName = 'date'; + ngModel.$validators.date = validator; + ngModel.$parsers.unshift(parseDate); + ngModel.$formatters.push(function(value) { + scope.date = value; + return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat); + }); + } else { + ngModel.$formatters.push(function(value) { + scope.date = value; + return value; + }); + } + + // Detect changes in the view from the text box + ngModel.$viewChangeListeners.push(function() { + scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date); + }); + + element.bind('keydown', inputKeydownBind); + + $popup = $compile(popupEl)(scope); + // Prevent jQuery cache memory leak (template is now redundant after linking) + popupEl.remove(); + + if (appendToBody) { + $document.find('body').append($popup); + } else { + element.after($popup); + } + + scope.$on('$destroy', function() { + if (scope.isOpen === true) { + if (!$rootScope.$$phase) { + scope.$apply(function() { + scope.isOpen = false; + }); + } + } + + $popup.remove(); + element.unbind('keydown', inputKeydownBind); + $document.unbind('click', documentClickBind); + }); + }; + + scope.getText = function(key) { + return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; + }; + + scope.isDisabled = function(date) { + if (date === 'today') { + date = new Date(); + } + + return ((scope.watchData.minDate && scope.compare(date, cache.minDate) < 0) || + (scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0)); + }; + + scope.compare = function(date1, date2) { + return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate())); + }; + + // Inner change + scope.dateSelection = function(dt) { + if (angular.isDefined(dt)) { + scope.date = dt; + } + var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function + element.val(date); + ngModel.$setViewValue(date); + + if (closeOnDateSelection) { + scope.isOpen = false; + element[0].focus(); + } + }; + + scope.keydown = function(evt) { + if (evt.which === 27) { + scope.isOpen = false; + element[0].focus(); + } + }; + + scope.select = function(date) { + if (date === 'today') { + var today = new Date(); + if (angular.isDate(scope.date)) { + date = new Date(scope.date); + date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); + } else { + date = new Date(today.setHours(0, 0, 0, 0)); + } + } + scope.dateSelection(date); + }; + + scope.close = function() { + scope.isOpen = false; + element[0].focus(); + }; + + scope.$watch('isOpen', function(value) { + if (value) { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top = scope.position.top + element.prop('offsetHeight'); + + $timeout(function() { + if (onOpenFocus) { + scope.$broadcast('uib:datepicker.focus'); + } + $document.bind('click', documentClickBind); + }, 0, false); + } else { + $document.unbind('click', documentClickBind); + } + }); + + function cameltoDash(string) { + return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); + } + + function parseDate(viewValue) { + if (angular.isNumber(viewValue)) { + // presumably timestamp to date object + viewValue = new Date(viewValue); + } + + if (!viewValue) { + return null; + } else if (angular.isDate(viewValue) && !isNaN(viewValue)) { + return viewValue; + } else if (angular.isString(viewValue)) { + var date = dateParser.parse(viewValue, dateFormat, scope.date); + if (isNaN(date)) { + return undefined; + } else { + return date; + } + } else { + return undefined; + } + } + + function validator(modelValue, viewValue) { + var value = modelValue || viewValue; + + if (!attrs.ngRequired && !value) { + return true; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + if (!value) { + return true; + } else if (angular.isDate(value) && !isNaN(value)) { + return true; + } else if (angular.isString(value)) { + var date = dateParser.parse(value, dateFormat); + return !isNaN(date); + } else { + return false; + } + } + + function documentClickBind(event) { + var popup = $popup[0]; + var dpContainsTarget = element[0].contains(event.target); + // The popup node may not be an element node + // In some browsers (IE) only element nodes have the 'contains' function + var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target); + if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) { + scope.$apply(function() { + scope.isOpen = false; + }); + } + } + + function inputKeydownBind(evt) { + if (evt.which === 27 && scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + scope.$apply(function() { + scope.isOpen = false; + }); + element[0].focus(); + } else if (evt.which === 40 && !scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + scope.$apply(function() { + scope.isOpen = true; + }); + } + } +}]) + +.directive('uibDatepickerPopup', function() { + return { + require: ['ngModel', 'uibDatepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@', + dateDisabled: '&', + customClass: '&' + }, + link: function(scope, element, attrs, ctrls) { + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}) + +.directive('uibDatepickerPopupWrap', function() { + return { + replace: true, + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/popup.html'; + } + }; +}); + +/* Deprecated datepicker below */ + +angular.module('ui.bootstrap.datepicker') + +.value('$datepickerSuppressWarning', false) + +.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', '$datepickerSuppressWarning', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, $datepickerSuppressWarning) { + if (!$datepickerSuppressWarning) { + $log.warn('DatepickerController is now deprecated. Use UibDatepickerController instead.'); + } + + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl; + + this.modes = ['day', 'month', 'year']; + + angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', + 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) { + self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key]; + }); + + angular.forEach(['minDate', 'maxDate'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = value ? new Date(value) : null; + self.refreshView(); + }); + } else { + self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null; + } + }); + + angular.forEach(['minMode', 'maxMode'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = angular.isDefined(value) ? value : $attrs[key]; + $scope[key] = self[key]; + if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) { + $scope.datepickerMode = self[key]; + } + }); + } else { + self[key] = datepickerConfig[key] || null; + $scope[key] = self[key]; + } + }); + + $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode; + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + if (angular.isDefined($attrs.initDate)) { + this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date(); + $scope.$parent.$watch($attrs.initDate, function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = initDate; + self.refreshView(); + } + }); + } else { + this.activeDate = new Date(); + } + + $scope.isActive = function(dateObject) { + if (self.compare(dateObject.date, self.activeDate) === 0) { + $scope.activeDateId = dateObject.uid; + return true; + } + return false; + }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = date; + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + this._refreshView(); + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date))); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + return { + date: date, + label: dateFilter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + current: this.compare(date, new Date()) === 0, + customClass: this.customClass(date) + }; + }; + + this.isDisabled = function(date) { + return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}))); + }; + + this.customClass = function(date) { + return $scope.customClass({date: date, mode: $scope.datepickerMode}); + }; + + // Split array into smaller arrays + this.split = function(arr, size) { + var arrays = []; + while (arr.length > 0) { + arrays.push(arr.splice(0, size)); + } + return arrays; + }; + + this.fixTimeZone = function(date) { + var hours = date.getHours(); + date.setHours(hours === 23 ? hours + 2 : 0); + }; + + $scope.select = function(date) { + if ($scope.datepickerMode === self.minMode) { + var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]; + } + }; + + $scope.move = function(direction) { + var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), + month = self.activeDate.getMonth() + direction * (self.step.months || 0); + self.activeDate.setFullYear(year, month, 1); + self.refreshView(); + }; + + $scope.toggleMode = function(direction) { + direction = direction || 1; + + if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) { + return; + } + + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]; + }; + + // Key event mapper + $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; + + var focusElement = function() { + self.element[0].focus(); + }; + + $scope.$on('uib:datepicker.focus', focusElement); + + $scope.keydown = function(evt) { + var key = $scope.keys[evt.which]; + + if (!key || evt.shiftKey || evt.altKey) { + return; + } + + evt.preventDefault(); + if (!self.shortcutPropagation) { + evt.stopPropagation(); + } + + if (key === 'enter' || key === 'space') { + if (self.isDisabled(self.activeDate)) { + return; // do nothing + } + $scope.select(self.activeDate); + } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { + $scope.toggleMode(key === 'up' ? 1 : -1); + } else { + self.handleKeyDown(key, evt); + self.refreshView(); + } + }; +}]) + +.directive('datepicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/datepicker.html'; + }, + scope: { + datepickerMode: '=?', + dateDisabled: '&', + customClass: '&', + shortcutPropagation: '&?' + }, + require: ['datepicker', '^ngModel'], + controller: 'DatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('datepicker is now deprecated. Use uib-datepicker instead.'); + } + + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}]) + +.directive('daypicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: 'template/datepicker/day.html', + require: ['^datepicker', 'daypicker'], + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('daypicker is now deprecated. Use uib-daypicker instead.'); + } + + var datepickerCtrl = ctrls[0], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}]) + +.directive('monthpicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: 'template/datepicker/month.html', + require: ['^datepicker', 'monthpicker'], + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('monthpicker is now deprecated. Use uib-monthpicker instead.'); + } + + var datepickerCtrl = ctrls[0], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}]) + +.directive('yearpicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: 'template/datepicker/year.html', + require: ['^datepicker', 'yearpicker'], + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('yearpicker is now deprecated. Use uib-yearpicker instead.'); + } + + var ctrl = ctrls[0]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}]) + +.directive('datepickerPopup', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + require: ['ngModel', 'datepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@', + dateDisabled: '&', + customClass: '&' + }, + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('datepicker-popup is now deprecated. Use uib-datepicker-popup instead.'); + } + + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}]) + +.directive('datepickerPopupWrap', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/popup.html'; + }, + link: function() { + if (!$datepickerSuppressWarning) { + $log.warn('datepicker-popup-wrap is now deprecated. Use uib-datepicker-popup-wrap instead.'); + } + } + }; +}]); + +angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) + +.constant('uibDropdownConfig', { + openClass: 'open' +}) + +.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) { + var openScope = null; + + this.open = function(dropdownScope) { + if (!openScope) { + $document.bind('click', closeDropdown); + $document.bind('keydown', keybindFilter); + } + + if (openScope && openScope !== dropdownScope) { + openScope.isOpen = false; + } + + openScope = dropdownScope; + }; + + this.close = function(dropdownScope) { + if (openScope === dropdownScope) { + openScope = null; + $document.unbind('click', closeDropdown); + $document.unbind('keydown', keybindFilter); + } + }; + + var closeDropdown = function(evt) { + // This method may still be called during the same mouse event that + // unbound this event handler. So check openScope before proceeding. + if (!openScope) { return; } + + if (evt && openScope.getAutoClose() === 'disabled') { return ; } + + var toggleElement = openScope.getToggleElement(); + if (evt && toggleElement && toggleElement[0].contains(evt.target)) { + return; + } + + var dropdownElement = openScope.getDropdownElement(); + if (evt && openScope.getAutoClose() === 'outsideClick' && + dropdownElement && dropdownElement[0].contains(evt.target)) { + return; + } + + openScope.isOpen = false; + + if (!$rootScope.$$phase) { + openScope.$apply(); + } + }; + + var keybindFilter = function(evt) { + if (evt.which === 27) { + openScope.focusToggleElement(); + closeDropdown(); + } else if (openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + openScope.focusDropdownEntry(evt.which); + } + }; +}]) + +.controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) { + var self = this, + scope = $scope.$new(), // create a child scope so we are not polluting original one + templateScope, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + appendToBody = false, + keynavEnabled =false, + selectedOption = null; + + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + appendToBody = angular.isDefined($attrs.dropdownAppendToBody); + keynavEnabled = angular.isDefined($attrs.uibKeyboardNav); + + if (appendToBody && self.dropdownMenu) { + $document.find('body').append(self.dropdownMenu); + $element.on('$destroy', function handleDestroyEvent() { + self.dropdownMenu.remove(); + }); + } + }; + + this.toggle = function(open) { + return scope.isOpen = arguments.length ? !!open : !scope.isOpen; + }; + + // Allow other directives to watch status + this.isOpen = function() { + return scope.isOpen; + }; + + scope.getToggleElement = function() { + return self.toggleElement; + }; + + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return $element; + }; + + scope.isKeynavEnabled = function() { + return keynavEnabled; + }; + + scope.focusDropdownEntry = function(keyCode) { + var elems = self.dropdownMenu ? //If append to body is used. + (angular.element(self.dropdownMenu).find('a')) : + (angular.element($element).find('ul').eq(0).find('a')); + + switch (keyCode) { + case (40): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = 0; + } else { + self.selectedOption = (self.selectedOption === elems.length - 1 ? + self.selectedOption : + self.selectedOption + 1); + } + break; + } + case (38): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = elems.length - 1; + } else { + self.selectedOption = self.selectedOption === 0 ? + 0 : self.selectedOption - 1; + } + break; + } + } + elems[self.selectedOption].focus(); + }; + + scope.getDropdownElement = function() { + return self.dropdownMenu; + }; + + scope.focusToggleElement = function() { + if (self.toggleElement) { + self.toggleElement[0].focus(); + } + }; + + scope.$watch('isOpen', function(isOpen, wasOpen) { + if (appendToBody && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true); + var css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + css.right = (window.innerWidth - (pos.left + $element.prop('offsetWidth'))) + 'px'; + } + + self.dropdownMenu.css(css); + } + + $animate[isOpen ? 'addClass' : 'removeClass']($element, openClass).then(function() { + if (angular.isDefined(isOpen) && isOpen !== wasOpen) { + toggleInvoker($scope, { open: !!isOpen }); + } + }); + + if (isOpen) { + if (self.dropdownMenuTemplateUrl) { + $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { + templateScope = scope.$new(); + $compile(tplContent.trim())(templateScope, function(dropdownElement) { + var newEl = dropdownElement; + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + }); + }); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope); + } else { + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element('<ul class="dropdown-menu"></ul>'); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + uibDropdownService.close(scope); + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); + + $scope.$on('$locationChangeSuccess', function() { + if (scope.getAutoClose() !== 'disabled') { + scope.isOpen = false; + } + }); + + var offDestroy = $scope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); +}]) + +.directive('uibDropdown', function() { + return { + controller: 'UibDropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + dropdownCtrl.init(); + } + }; +}) + +.directive('uibDropdownMenu', function() { + return { + restrict: 'AC', + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { + return; + } + + element.addClass('dropdown-menu'); + + var tplUrl = attrs.templateUrl; + if (tplUrl) { + dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; + } + + if (!dropdownCtrl.dropdownMenu) { + dropdownCtrl.dropdownMenu = element; + } + } + }; +}) + +.directive('uibKeyboardNav', function() { + return { + restrict: 'A', + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + element.bind('keydown', function(e) { + if ([38, 40].indexOf(e.which) !== -1) { + e.preventDefault(); + e.stopPropagation(); + + var elems = dropdownCtrl.dropdownMenu.find('a'); + + switch (e.which) { + case (40): { // Down + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = 0; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ? + dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1; + } + break; + } + case (38): { // Up + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = elems.length - 1; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ? + 0 : dropdownCtrl.selectedOption - 1; + } + break; + } + } + elems[dropdownCtrl.selectedOption].focus(); + } + }); + } + }; +}) + +.directive('uibDropdownToggle', function() { + return { + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl) { + return; + } + + element.addClass('dropdown-toggle'); + + dropdownCtrl.toggleElement = element; + + var toggleDropdown = function(event) { + event.preventDefault(); + + if (!element.hasClass('disabled') && !attrs.disabled) { + scope.$apply(function() { + dropdownCtrl.toggle(); + }); + } + }; + + element.bind('click', toggleDropdown); + + // WAI-ARIA + element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); + scope.$watch(dropdownCtrl.isOpen, function(isOpen) { + element.attr('aria-expanded', !!isOpen); + }); + + scope.$on('$destroy', function() { + element.unbind('click', toggleDropdown); + }); + } + }; +}); + +/* Deprecated dropdown below */ + +angular.module('ui.bootstrap.dropdown') + +.value('$dropdownSuppressWarning', false) + +.service('dropdownService', ['$log', '$dropdownSuppressWarning', 'uibDropdownService', function($log, $dropdownSuppressWarning, uibDropdownService) { + if (!$dropdownSuppressWarning) { + $log.warn('dropdownService is now deprecated. Use uibDropdownService instead.'); + } + + angular.extend(this, uibDropdownService); +}]) + +.controller('DropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', '$log', '$dropdownSuppressWarning', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest, $log, $dropdownSuppressWarning) { + if (!$dropdownSuppressWarning) { + $log.warn('DropdownController is now deprecated. Use UibDropdownController instead.'); + } + + var self = this, + scope = $scope.$new(), // create a child scope so we are not polluting original one + templateScope, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + appendToBody = false, + keynavEnabled =false, + selectedOption = null; + + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + appendToBody = angular.isDefined($attrs.dropdownAppendToBody); + keynavEnabled = angular.isDefined($attrs.uibKeyboardNav); + + if (appendToBody && self.dropdownMenu) { + $document.find('body').append(self.dropdownMenu); + $element.on('$destroy', function handleDestroyEvent() { + self.dropdownMenu.remove(); + }); + } + }; + + this.toggle = function(open) { + return scope.isOpen = arguments.length ? !!open : !scope.isOpen; + }; + + // Allow other directives to watch status + this.isOpen = function() { + return scope.isOpen; + }; + + scope.getToggleElement = function() { + return self.toggleElement; + }; + + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return $element; + }; + + scope.isKeynavEnabled = function() { + return keynavEnabled; + }; + + scope.focusDropdownEntry = function(keyCode) { + var elems = self.dropdownMenu ? //If append to body is used. + (angular.element(self.dropdownMenu).find('a')) : + (angular.element($element).find('ul').eq(0).find('a')); + + switch (keyCode) { + case (40): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = 0; + } else { + self.selectedOption = (self.selectedOption === elems.length -1 ? + self.selectedOption : + self.selectedOption + 1); + } + break; + } + case (38): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = elems.length - 1; + } else { + self.selectedOption = self.selectedOption === 0 ? + 0 : self.selectedOption - 1; + } + break; + } + } + elems[self.selectedOption].focus(); + }; + + scope.getDropdownElement = function() { + return self.dropdownMenu; + }; + + scope.focusToggleElement = function() { + if (self.toggleElement) { + self.toggleElement[0].focus(); + } + }; + + scope.$watch('isOpen', function(isOpen, wasOpen) { + if (appendToBody && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true); + var css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + css.right = (window.innerWidth - (pos.left + $element.prop('offsetWidth'))) + 'px'; + } + + self.dropdownMenu.css(css); + } + + $animate[isOpen ? 'addClass' : 'removeClass']($element, openClass).then(function() { + if (angular.isDefined(isOpen) && isOpen !== wasOpen) { + toggleInvoker($scope, { open: !!isOpen }); + } + }); + + if (isOpen) { + if (self.dropdownMenuTemplateUrl) { + $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { + templateScope = scope.$new(); + $compile(tplContent.trim())(templateScope, function(dropdownElement) { + var newEl = dropdownElement; + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + }); + }); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope); + } else { + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element('<ul class="dropdown-menu"></ul>'); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + uibDropdownService.close(scope); + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); + + $scope.$on('$locationChangeSuccess', function() { + if (scope.getAutoClose() !== 'disabled') { + scope.isOpen = false; + } + }); + + var offDestroy = $scope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); +}]) + +.directive('dropdown', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + controller: 'DropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + if (!$dropdownSuppressWarning) { + $log.warn('dropdown is now deprecated. Use uib-dropdown instead.'); + } + + dropdownCtrl.init(); + } + }; +}]) + +.directive('dropdownMenu', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + restrict: 'AC', + require: '?^dropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { + return; + } + + if (!$dropdownSuppressWarning) { + $log.warn('dropdown-menu is now deprecated. Use uib-dropdown-menu instead.'); + } + + element.addClass('dropdown-menu'); + + var tplUrl = attrs.templateUrl; + if (tplUrl) { + dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; + } + + if (!dropdownCtrl.dropdownMenu) { + dropdownCtrl.dropdownMenu = element; + } + } + }; +}]) + +.directive('keyboardNav', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + restrict: 'A', + require: '?^dropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!$dropdownSuppressWarning) { + $log.warn('keyboard-nav is now deprecated. Use uib-keyboard-nav instead.'); + } + + element.bind('keydown', function(e) { + if ([38, 40].indexOf(e.which) !== -1) { + e.preventDefault(); + e.stopPropagation(); + + var elems = dropdownCtrl.dropdownMenu.find('a'); + + switch (e.which) { + case (40): { // Down + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = 0; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ? + dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1; + } + break; + } + case (38): { // Up + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = elems.length - 1; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ? + 0 : dropdownCtrl.selectedOption - 1; + } + break; + } + } + elems[dropdownCtrl.selectedOption].focus(); + } + }); + } + }; +}]) + +.directive('dropdownToggle', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + require: '?^dropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!$dropdownSuppressWarning) { + $log.warn('dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead.'); + } + + if (!dropdownCtrl) { + return; + } + + element.addClass('dropdown-toggle'); + + dropdownCtrl.toggleElement = element; + + var toggleDropdown = function(event) { + event.preventDefault(); + + if (!element.hasClass('disabled') && !attrs.disabled) { + scope.$apply(function() { + dropdownCtrl.toggle(); + }); + } + }; + + element.bind('click', toggleDropdown); + + // WAI-ARIA + element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); + scope.$watch(dropdownCtrl.isOpen, function(isOpen) { + element.attr('aria-expanded', !!isOpen); + }); + + scope.$on('$destroy', function() { + element.unbind('click', toggleDropdown); + }); + } + }; +}]); + +angular.module('ui.bootstrap.stackedMap', []) +/** + * A helper, internal data structure that acts as a map but also allows getting / removing + * elements in the LIFO order + */ + .factory('$$stackedMap', function() { + return { + createNew: function() { + var stack = []; + + return { + add: function(key, value) { + stack.push({ + key: key, + value: value + }); + }, + get: function(key) { + for (var i = 0; i < stack.length; i++) { + if (key == stack[i].key) { + return stack[i]; + } + } + }, + keys: function() { + var keys = []; + for (var i = 0; i < stack.length; i++) { + keys.push(stack[i].key); + } + return keys; + }, + top: function() { + return stack[stack.length - 1]; + }, + remove: function(key) { + var idx = -1; + for (var i = 0; i < stack.length; i++) { + if (key == stack[i].key) { + idx = i; + break; + } + } + return stack.splice(idx, 1)[0]; + }, + removeTop: function() { + return stack.splice(stack.length - 1, 1)[0]; + }, + length: function() { + return stack.length; + } + }; + } + }; + }); +angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) +/** + * A helper, internal data structure that stores all references attached to key + */ + .factory('$$multiMap', function() { + return { + createNew: function() { + var map = {}; + + return { + entries: function() { + return Object.keys(map).map(function(key) { + return { + key: key, + value: map[key] + }; + }); + }, + get: function(key) { + return map[key]; + }, + hasKey: function(key) { + return !!map[key]; + }, + keys: function() { + return Object.keys(map); + }, + put: function(key, value) { + if (!map[key]) { + map[key] = []; + } + + map[key].push(value); + }, + remove: function(key, value) { + var values = map[key]; + + if (!values) { + return; + } + + var idx = values.indexOf(value); + + if (idx !== -1) { + values.splice(idx, 1); + } + + if (!values.length) { + delete map[key]; + } + } + }; + } + }; + }) + +/** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('uibModalBackdrop', [ + '$animate', '$injector', '$uibModalStack', + function($animate , $injector, $modalStack) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + replace: true, + templateUrl: 'template/modal/backdrop.html', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + // Temporary fix for prefixing + element.addClass('modal-backdrop'); + + if (attrs.modalInClass) { + if ($animateCss) { + $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + } + }]) + + .directive('uibModalWindow', [ + '$uibModalStack', '$q', '$animate', '$injector', + function($modalStack , $q , $animate, $injector) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + scope: { + index: '@' + }, + replace: true, + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'template/modal/window.html'; + }, + link: function(scope, element, attrs) { + element.addClass(attrs.windowClass || ''); + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + scope.close = function(evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is render. + var modalRenderDeferObj = $q.defer(); + // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. + // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. + attrs.$observe('modalRender', function(value) { + if (value == 'true') { + modalRenderDeferObj.resolve(); + } + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + if ($animateCss) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + animationPromise = $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + + + $q.when(animationPromise).then(function() { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } + }); + + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + }); + } + }; + }]) + + .directive('uibModalAnimationClass', function() { + return { + compile: function(tElement, tAttrs) { + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.uibModalAnimationClass); + } + } + }; + }) + + .directive('uibModalTransclude', function() { + return { + link: function($scope, $element, $attrs, controller, $transclude) { + $transclude($scope.$parent, function(clone) { + $element.empty(); + $element.append(clone); + }); + } + }; + }) + + .factory('$uibModalStack', [ + '$animate', '$timeout', '$document', '$compile', '$rootScope', + '$q', + '$injector', + '$$multiMap', + '$$stackedMap', + function($animate , $timeout , $document , $compile , $rootScope , + $q, + $injector, + $$multiMap, + $$stackedMap) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + var OPENED_MODAL_CLASS = 'modal-open'; + + var backdropDomEl, backdropScope; + var openedWindows = $$stackedMap.createNew(); + var openedClasses = $$multiMap.createNew(); + var $modalStack = { + NOW_CLOSING_EVENT: 'modal.stack.now-closing' + }; + + //Modal focus behavior + var focusableElementList; + var focusIndex = 0; + var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' + + 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' + + 'iframe, object, embed, *[tabindex], *[contenteditable=true]'; + + function backdropIndex() { + var topBackdropIndex = -1; + var opened = openedWindows.keys(); + for (var i = 0; i < opened.length; i++) { + if (openedWindows.get(opened[i]).value.backdrop) { + topBackdropIndex = i; + } + } + return topBackdropIndex; + } + + $rootScope.$watch(backdropIndex, function(newBackdropIndex) { + if (backdropScope) { + backdropScope.index = newBackdropIndex; + } + }); + + function removeModalWindow(modalInstance, elementToReceiveFocus) { + var body = $document.find('body').eq(0); + var modalWindow = openedWindows.get(modalInstance).value; + + //clean up the stack + openedWindows.remove(modalInstance); + + removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { + var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; + openedClasses.remove(modalBodyClass, modalInstance); + body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass)); + toggleTopWindowClass(true); + }); + checkRemoveBackdrop(); + + //move focus to specified element if available, or else to body + if (elementToReceiveFocus && elementToReceiveFocus.focus) { + elementToReceiveFocus.focus(); + } else { + body.focus(); + } + } + + // Add or remove "windowTopClass" from the top window in the stack + function toggleTopWindowClass(toggleSwitch) { + var modalWindow; + + if (openedWindows.length() > 0) { + modalWindow = openedWindows.top().value; + modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); + } + } + + function checkRemoveBackdrop() { + //remove backdrop if no longer needed + if (backdropDomEl && backdropIndex() == -1) { + var backdropScopeRef = backdropScope; + removeAfterAnimate(backdropDomEl, backdropScope, function() { + backdropScopeRef = null; + }); + backdropDomEl = undefined; + backdropScope = undefined; + } + } + + function removeAfterAnimate(domEl, scope, done) { + var asyncDeferred; + var asyncPromise = null; + var setIsAsync = function() { + if (!asyncDeferred) { + asyncDeferred = $q.defer(); + asyncPromise = asyncDeferred.promise; + } + + return function asyncDone() { + asyncDeferred.resolve(); + }; + }; + scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); + + // Note that it's intentional that asyncPromise might be null. + // That's when setIsAsync has not been called during the + // NOW_CLOSING_EVENT broadcast. + return $q.when(asyncPromise).then(afterAnimating); + + function afterAnimating() { + if (afterAnimating.done) { + return; + } + afterAnimating.done = true; + + if ($animateCss) { + $animateCss(domEl, { + event: 'leave' + }).start().then(function() { + domEl.remove(); + }); + } else { + $animate.leave(domEl); + } + scope.$destroy(); + if (done) { + done(); + } + } + } + + $document.bind('keydown', function(evt) { + if (evt.isDefaultPrevented()) { + return evt; + } + + var modal = openedWindows.top(); + if (modal && modal.value.keyboard) { + switch (evt.which) { + case 27: { + evt.preventDefault(); + $rootScope.$apply(function() { + $modalStack.dismiss(modal.key, 'escape key press'); + }); + break; + } + case 9: { + $modalStack.loadFocusElementList(modal); + var focusChanged = false; + if (evt.shiftKey) { + if ($modalStack.isFocusInFirstItem(evt)) { + focusChanged = $modalStack.focusLastFocusableElement(); + } + } else { + if ($modalStack.isFocusInLastItem(evt)) { + focusChanged = $modalStack.focusFirstFocusableElement(); + } + } + + if (focusChanged) { + evt.preventDefault(); + evt.stopPropagation(); + } + break; + } + } + } + }); + + $modalStack.open = function(modalInstance, modal) { + var modalOpener = $document[0].activeElement, + modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; + + toggleTopWindowClass(false); + + openedWindows.add(modalInstance, { + deferred: modal.deferred, + renderDeferred: modal.renderDeferred, + modalScope: modal.scope, + backdrop: modal.backdrop, + keyboard: modal.keyboard, + openedClass: modal.openedClass, + windowTopClass: modal.windowTopClass + }); + + openedClasses.put(modalBodyClass, modalInstance); + + var body = $document.find('body').eq(0), + currBackdropIndex = backdropIndex(); + + if (currBackdropIndex >= 0 && !backdropDomEl) { + backdropScope = $rootScope.$new(true); + backdropScope.index = currBackdropIndex; + var angularBackgroundDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'); + angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass); + if (modal.animation) { + angularBackgroundDomEl.attr('modal-animation', 'true'); + } + backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope); + body.append(backdropDomEl); + } + + var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>'); + angularDomEl.attr({ + 'template-url': modal.windowTemplateUrl, + 'window-class': modal.windowClass, + 'window-top-class': modal.windowTopClass, + 'size': modal.size, + 'index': openedWindows.length() - 1, + 'animate': 'animate' + }).html(modal.content); + if (modal.animation) { + angularDomEl.attr('modal-animation', 'true'); + } + + var modalDomEl = $compile(angularDomEl)(modal.scope); + openedWindows.top().value.modalDomEl = modalDomEl; + openedWindows.top().value.modalOpener = modalOpener; + body.append(modalDomEl); + body.addClass(modalBodyClass); + + $modalStack.clearFocusListCache(); + }; + + function broadcastClosing(modalWindow, resultOrReason, closing) { + return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; + } + + $modalStack.close = function(modalInstance, result) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow && broadcastClosing(modalWindow, result, true)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.resolve(result); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + return !modalWindow; + }; + + $modalStack.dismiss = function(modalInstance, reason) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow && broadcastClosing(modalWindow, reason, false)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.reject(reason); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + return !modalWindow; + }; + + $modalStack.dismissAll = function(reason) { + var topModal = this.getTop(); + while (topModal && this.dismiss(topModal.key, reason)) { + topModal = this.getTop(); + } + }; + + $modalStack.getTop = function() { + return openedWindows.top(); + }; + + $modalStack.modalRendered = function(modalInstance) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow) { + modalWindow.value.renderDeferred.resolve(); + } + }; + + $modalStack.focusFirstFocusableElement = function() { + if (focusableElementList.length > 0) { + focusableElementList[0].focus(); + return true; + } + return false; + }; + $modalStack.focusLastFocusableElement = function() { + if (focusableElementList.length > 0) { + focusableElementList[focusableElementList.length - 1].focus(); + return true; + } + return false; + }; + + $modalStack.isFocusInFirstItem = function(evt) { + if (focusableElementList.length > 0) { + return (evt.target || evt.srcElement) == focusableElementList[0]; + } + return false; + }; + + $modalStack.isFocusInLastItem = function(evt) { + if (focusableElementList.length > 0) { + return (evt.target || evt.srcElement) == focusableElementList[focusableElementList.length - 1]; + } + return false; + }; + + $modalStack.clearFocusListCache = function() { + focusableElementList = []; + focusIndex = 0; + }; + + $modalStack.loadFocusElementList = function(modalWindow) { + if (focusableElementList === undefined || !focusableElementList.length) { + if (modalWindow) { + var modalDomE1 = modalWindow.value.modalDomEl; + if (modalDomE1 && modalDomE1.length) { + focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector); + } + } + } + }; + + return $modalStack; + }]) + + .provider('$uibModal', function() { + var $modalProvider = { + options: { + animation: true, + backdrop: true, //can also be false or 'static' + keyboard: true + }, + $get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$uibModalStack', '$modalSuppressWarning', '$log', + function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack, $modalSuppressWarning, $log) { + var $modal = {}; + + function getTemplatePromise(options) { + return options.template ? $q.when(options.template) : + $templateRequest(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl); + } + + function getResolvePromises(resolves) { + var promisesArr = []; + angular.forEach(resolves, function(value) { + if (angular.isFunction(value) || angular.isArray(value)) { + promisesArr.push($q.when($injector.invoke(value))); + } else if (angular.isString(value)) { + promisesArr.push($q.when($injector.get(value))); + } else { + promisesArr.push($q.when(value)); + } + }); + return promisesArr; + } + + var promiseChain = null; + $modal.getPromiseChain = function() { + return promiseChain; + }; + + $modal.open = function(modalOptions) { + var modalResultDeferred = $q.defer(); + var modalOpenedDeferred = $q.defer(); + var modalRenderDeferred = $q.defer(); + + //prepare an instance of a modal to be injected into controllers and returned to a caller + var modalInstance = { + result: modalResultDeferred.promise, + opened: modalOpenedDeferred.promise, + rendered: modalRenderDeferred.promise, + close: function (result) { + return $modalStack.close(modalInstance, result); + }, + dismiss: function (reason) { + return $modalStack.dismiss(modalInstance, reason); + } + }; + + //merge and clean up options + modalOptions = angular.extend({}, $modalProvider.options, modalOptions); + modalOptions.resolve = modalOptions.resolve || {}; + + //verify options + if (!modalOptions.template && !modalOptions.templateUrl) { + throw new Error('One of template or templateUrl options is required.'); + } + + var templateAndResolvePromise = + $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve))); + + function resolveWithTemplate() { + return templateAndResolvePromise; + } + + // Wait for the resolution of the existing promise chain. + // Then switch to our own combined promise dependency (regardless of how the previous modal fared). + // Then add to $modalStack and resolve opened. + // Finally clean up the chain variable if no subsequent modal has overwritten it. + var samePromise; + samePromise = promiseChain = $q.all([promiseChain]) + .then(resolveWithTemplate, resolveWithTemplate) + .then(function resolveSuccess(tplAndVars) { + + var modalScope = (modalOptions.scope || $rootScope).$new(); + modalScope.$close = modalInstance.close; + modalScope.$dismiss = modalInstance.dismiss; + + modalScope.$on('$destroy', function() { + if (!modalScope.$$uibDestructionScheduled) { + modalScope.$dismiss('$uibUnscheduledDestruction'); + } + }); + + var ctrlInstance, ctrlLocals = {}; + var resolveIter = 1; + + //controllers + if (modalOptions.controller) { + ctrlLocals.$scope = modalScope; + ctrlLocals.$uibModalInstance = modalInstance; + Object.defineProperty(ctrlLocals, '$modalInstance', { + get: function() { + if (!$modalSuppressWarning) { + $log.warn('$modalInstance is now deprecated. Use $uibModalInstance instead.'); + } + + return modalInstance; + } + }); + angular.forEach(modalOptions.resolve, function(value, key) { + ctrlLocals[key] = tplAndVars[resolveIter++]; + }); + + ctrlInstance = $controller(modalOptions.controller, ctrlLocals); + if (modalOptions.controllerAs) { + if (modalOptions.bindToController) { + angular.extend(ctrlInstance, modalScope); + } + + modalScope[modalOptions.controllerAs] = ctrlInstance; + } + } + + $modalStack.open(modalInstance, { + scope: modalScope, + deferred: modalResultDeferred, + renderDeferred: modalRenderDeferred, + content: tplAndVars[0], + animation: modalOptions.animation, + backdrop: modalOptions.backdrop, + keyboard: modalOptions.keyboard, + backdropClass: modalOptions.backdropClass, + windowTopClass: modalOptions.windowTopClass, + windowClass: modalOptions.windowClass, + windowTemplateUrl: modalOptions.windowTemplateUrl, + size: modalOptions.size, + openedClass: modalOptions.openedClass + }); + modalOpenedDeferred.resolve(true); + + }, function resolveError(reason) { + modalOpenedDeferred.reject(reason); + modalResultDeferred.reject(reason); + }) + .finally(function() { + if (promiseChain === samePromise) { + promiseChain = null; + } + }); + + return modalInstance; + }; + + return $modal; + } + ] + }; + + return $modalProvider; + }); + +/* deprecated modal below */ + +angular.module('ui.bootstrap.modal') + + .value('$modalSuppressWarning', false) + + /** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('modalBackdrop', [ + '$animate', '$injector', '$modalStack', '$log', '$modalSuppressWarning', + function($animate , $injector, $modalStack, $log, $modalSuppressWarning) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + replace: true, + templateUrl: 'template/modal/backdrop.html', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-backdrop is now deprecated. Use uib-modal-backdrop instead.'); + } + element.addClass('modal-backdrop'); + + if (attrs.modalInClass) { + if ($animateCss) { + $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + } + }]) + + .directive('modalWindow', [ + '$modalStack', '$q', '$animate', '$injector', '$log', '$modalSuppressWarning', + function($modalStack , $q , $animate, $injector, $log, $modalSuppressWarning) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + scope: { + index: '@' + }, + replace: true, + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'template/modal/window.html'; + }, + link: function(scope, element, attrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-window is now deprecated. Use uib-modal-window instead.'); + } + element.addClass(attrs.windowClass || ''); + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + scope.close = function(evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is render. + var modalRenderDeferObj = $q.defer(); + // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. + // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. + attrs.$observe('modalRender', function(value) { + if (value == 'true') { + modalRenderDeferObj.resolve(); + } + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + if ($animateCss) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + animationPromise = $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + + + $q.when(animationPromise).then(function() { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } + }); + + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + }); + } + }; + }]) + + .directive('modalAnimationClass', [ + '$log', '$modalSuppressWarning', + function ($log, $modalSuppressWarning) { + return { + compile: function(tElement, tAttrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-animation-class is now deprecated. Use uib-modal-animation-class instead.'); + } + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.modalAnimationClass); + } + } + }; + }]) + + .directive('modalTransclude', [ + '$log', '$modalSuppressWarning', + function ($log, $modalSuppressWarning) { + return { + link: function($scope, $element, $attrs, controller, $transclude) { + if (!$modalSuppressWarning) { + $log.warn('modal-transclude is now deprecated. Use uib-modal-transclude instead.'); + } + $transclude($scope.$parent, function(clone) { + $element.empty(); + $element.append(clone); + }); + } + }; + }]) + + .service('$modalStack', [ + '$animate', '$timeout', '$document', '$compile', '$rootScope', + '$q', + '$injector', + '$$multiMap', + '$$stackedMap', + '$uibModalStack', + '$log', + '$modalSuppressWarning', + function($animate , $timeout , $document , $compile , $rootScope , + $q, + $injector, + $$multiMap, + $$stackedMap, + $uibModalStack, + $log, + $modalSuppressWarning) { + if (!$modalSuppressWarning) { + $log.warn('$modalStack is now deprecated. Use $uibModalStack instead.'); + } + + angular.extend(this, $uibModalStack); + }]) + + .provider('$modal', ['$uibModalProvider', function($uibModalProvider) { + angular.extend(this, $uibModalProvider); + + this.$get = ['$injector', '$log', '$modalSuppressWarning', + function ($injector, $log, $modalSuppressWarning) { + if (!$modalSuppressWarning) { + $log.warn('$modal is now deprecated. Use $uibModal instead.'); + } + + return $injector.invoke($uibModalProvider.$get); + }]; + }]); + +angular.module('ui.bootstrap.pagination', []) +.controller('UibPaginationController', ['$scope', '$attrs', '$parse', function($scope, $attrs, $parse) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + + this.init = function(ngModelCtrl_, config) { + ngModelCtrl = ngModelCtrl_; + this.config = config; + + ngModelCtrl.$render = function() { + self.render(); + }; + + if ($attrs.itemsPerPage) { + $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { + self.itemsPerPage = parseInt(value, 10); + $scope.totalPages = self.calculateTotalPages(); + }); + } else { + this.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function() { + $scope.totalPages = self.calculateTotalPages(); + }); + + $scope.$watch('totalPages', function(value) { + setNumPages($scope.$parent, value); // Readonly variable + + if ( $scope.page > value ) { + $scope.selectPage(value); + } else { + ngModelCtrl.$render(); + } + }); + }; + + this.calculateTotalPages = function() { + var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + this.render = function() { + $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1; + }; + + $scope.selectPage = function(page, evt) { + if (evt) { + evt.preventDefault(); + } + + var clickAllowed = !$scope.ngDisabled || !evt; + if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { + if (evt && evt.target) { + evt.target.blur(); + } + ngModelCtrl.$setViewValue(page); + ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || self.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; +}]) + +.constant('uibPaginationConfig', { + itemsPerPage: 10, + boundaryLinks: false, + directionLinks: true, + firstText: 'First', + previousText: 'Previous', + nextText: 'Next', + lastText: 'Last', + rotate: true +}) + +.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, paginationConfig) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['uibPagination', '?ngModel'], + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pagination.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + // Setup configuration parameters + var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize, + rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate; + scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks; + scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks; + + paginationCtrl.init(ngModelCtrl, paginationConfig); + + if (attrs.maxSize) { + scope.$parent.$watch($parse(attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + paginationCtrl.render(); + }); + } + + // Create page object used in template + function makePage(number, text, isActive) { + return { + number: number, + text: text, + active: isActive + }; + } + + function getPages(currentPage, totalPages) { + var pages = []; + + // Default page limits + var startPage = 1, endPage = totalPages; + var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; + + // recompute if maxSize + if (isMaxSized) { + if (rotate) { + // Current page is displayed in the middle of the visible ones + startPage = Math.max(currentPage - Math.floor(maxSize/2), 1); + endPage = startPage + maxSize - 1; + + // Adjust if limit is exceeded + if (endPage > totalPages) { + endPage = totalPages; + startPage = endPage - maxSize + 1; + } + } else { + // Visible pages are paginated with maxSize + startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1; + + // Adjust last page if limit is exceeded + endPage = Math.min(startPage + maxSize - 1, totalPages); + } + } + + // Add page number links + for (var number = startPage; number <= endPage; number++) { + var page = makePage(number, number, number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && ! rotate) { + if (startPage > 1) { + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + + if (endPage < totalPages) { + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + } + + return pages; + } + + var originalRender = paginationCtrl.render; + paginationCtrl.render = function() { + originalRender(); + if (scope.page > 0 && scope.page <= scope.totalPages) { + scope.pages = getPages(scope.page, scope.totalPages); + } + }; + } + }; +}]) + +.constant('uibPagerConfig', { + itemsPerPage: 10, + previousText: '« Previous', + nextText: 'Next »', + align: true +}) + +.directive('uibPager', ['uibPagerConfig', function(pagerConfig) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['uibPager', '?ngModel'], + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pager.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align; + paginationCtrl.init(ngModelCtrl, pagerConfig); + } + }; +}]); + +/* Deprecated Pagination Below */ + +angular.module('ui.bootstrap.pagination') +.value('$paginationSuppressWarning', false) +.controller('PaginationController', ['$scope', '$attrs', '$parse', '$log', '$paginationSuppressWarning', function($scope, $attrs, $parse, $log, $paginationSuppressWarning) { + if (!$paginationSuppressWarning) { + $log.warn('PaginationController is now deprecated. Use UibPaginationController instead.'); + } + + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + + this.init = function(ngModelCtrl_, config) { + ngModelCtrl = ngModelCtrl_; + this.config = config; + + ngModelCtrl.$render = function() { + self.render(); + }; + + if ($attrs.itemsPerPage) { + $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { + self.itemsPerPage = parseInt(value, 10); + $scope.totalPages = self.calculateTotalPages(); + }); + } else { + this.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function() { + $scope.totalPages = self.calculateTotalPages(); + }); + + $scope.$watch('totalPages', function(value) { + setNumPages($scope.$parent, value); // Readonly variable + + if ( $scope.page > value ) { + $scope.selectPage(value); + } else { + ngModelCtrl.$render(); + } + }); + }; + + this.calculateTotalPages = function() { + var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + this.render = function() { + $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1; + }; + + $scope.selectPage = function(page, evt) { + if (evt) { + evt.preventDefault(); + } + + var clickAllowed = !$scope.ngDisabled || !evt; + if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { + if (evt && evt.target) { + evt.target.blur(); + } + ngModelCtrl.$setViewValue(page); + ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || self.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; +}]) +.directive('pagination', ['$parse', 'uibPaginationConfig', '$log', '$paginationSuppressWarning', function($parse, paginationConfig, $log, $paginationSuppressWarning) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['pagination', '?ngModel'], + controller: 'PaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pagination.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + if (!$paginationSuppressWarning) { + $log.warn('pagination is now deprecated. Use uib-pagination instead.'); + } + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + // Setup configuration parameters + var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize, + rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate; + scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks; + scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks; + + paginationCtrl.init(ngModelCtrl, paginationConfig); + + if (attrs.maxSize) { + scope.$parent.$watch($parse(attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + paginationCtrl.render(); + }); + } + + // Create page object used in template + function makePage(number, text, isActive) { + return { + number: number, + text: text, + active: isActive + }; + } + + function getPages(currentPage, totalPages) { + var pages = []; + + // Default page limits + var startPage = 1, endPage = totalPages; + var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; + + // recompute if maxSize + if (isMaxSized) { + if (rotate) { + // Current page is displayed in the middle of the visible ones + startPage = Math.max(currentPage - Math.floor(maxSize/2), 1); + endPage = startPage + maxSize - 1; + + // Adjust if limit is exceeded + if (endPage > totalPages) { + endPage = totalPages; + startPage = endPage - maxSize + 1; + } + } else { + // Visible pages are paginated with maxSize + startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1; + + // Adjust last page if limit is exceeded + endPage = Math.min(startPage + maxSize - 1, totalPages); + } + } + + // Add page number links + for (var number = startPage; number <= endPage; number++) { + var page = makePage(number, number, number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && ! rotate) { + if (startPage > 1) { + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + + if (endPage < totalPages) { + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + } + + return pages; + } + + var originalRender = paginationCtrl.render; + paginationCtrl.render = function() { + originalRender(); + if (scope.page > 0 && scope.page <= scope.totalPages) { + scope.pages = getPages(scope.page, scope.totalPages); + } + }; + } + }; +}]) + +.directive('pager', ['uibPagerConfig', '$log', '$paginationSuppressWarning', function(pagerConfig, $log, $paginationSuppressWarning) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['pager', '?ngModel'], + controller: 'PaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pager.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + if (!$paginationSuppressWarning) { + $log.warn('pager is now deprecated. Use uib-pager instead.'); + } + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align; + paginationCtrl.init(ngModelCtrl, pagerConfig); + } + }; +}]); + +/** + * The following features are still outstanding: animation as a + * function, placement as a function, inside, support for more triggers than + * just mouse enter/leave, html tooltips, and selector delegation. + */ +angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap']) + +/** + * The $tooltip service creates tooltip- and popover-like directives as well as + * houses global options for them. + */ +.provider('$uibTooltip', function() { + // The default options tooltip and popover. + var defaultOptions = { + placement: 'top', + animation: true, + popupDelay: 0, + popupCloseDelay: 0, + useContentExp: false + }; + + // Default hide triggers for each show trigger + var triggerMap = { + 'mouseenter': 'mouseleave', + 'click': 'click', + 'focus': 'blur', + 'none': '' + }; + + // The options specified to the provider globally. + var globalOptions = {}; + + /** + * `options({})` allows global configuration of all tooltips in the + * application. + * + * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { + * // place tooltips left instead of top by default + * $tooltipProvider.options( { placement: 'left' } ); + * }); + */ + this.options = function(value) { + angular.extend(globalOptions, value); + }; + + /** + * This allows you to extend the set of trigger mappings available. E.g.: + * + * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); + */ + this.setTriggers = function setTriggers(triggers) { + angular.extend(triggerMap, triggers); + }; + + /** + * This is a helper function for translating camel-case to snake-case. + */ + function snake_case(name) { + var regexp = /[A-Z]/g; + var separator = '-'; + return name.replace(regexp, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); + } + + /** + * Returns the actual instance of the $tooltip service. + * TODO support multiple triggers + */ + this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { + var openedTooltips = $$stackedMap.createNew(); + $document.on('keypress', function(e) { + if (e.which === 27) { + var last = openedTooltips.top(); + if (last) { + last.value.close(); + openedTooltips.removeTop(); + last = null; + } + } + }); + + return function $tooltip(ttType, prefix, defaultTriggerShow, options) { + options = angular.extend({}, defaultOptions, globalOptions, options); + + /** + * Returns an object of show and hide triggers. + * + * If a trigger is supplied, + * it is used to show the tooltip; otherwise, it will use the `trigger` + * option passed to the `$tooltipProvider.options` method; else it will + * default to the trigger supplied to this directive factory. + * + * The hide trigger is based on the show trigger. If the `trigger` option + * was passed to the `$tooltipProvider.options` method, it will use the + * mapped trigger from `triggerMap` or the passed trigger if the map is + * undefined; otherwise, it uses the `triggerMap` value of the show + * trigger; else it will just use the show trigger. + */ + function getTriggers(trigger) { + var show = (trigger || options.trigger || defaultTriggerShow).split(' '); + var hide = show.map(function(trigger) { + return triggerMap[trigger] || trigger; + }); + return { + show: show, + hide: hide + }; + } + + var directiveName = snake_case(ttType); + + var startSym = $interpolate.startSymbol(); + var endSym = $interpolate.endSymbol(); + var template = + '<div '+ directiveName + '-popup '+ + 'title="' + startSym + 'title' + endSym + '" '+ + (options.useContentExp ? + 'content-exp="contentExp()" ' : + 'content="' + startSym + 'content' + endSym + '" ') + + 'placement="' + startSym + 'placement' + endSym + '" '+ + 'popup-class="' + startSym + 'popupClass' + endSym + '" '+ + 'animation="animation" ' + + 'is-open="isOpen"' + + 'origin-scope="origScope" ' + + 'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' + + '>' + + '</div>'; + + return { + compile: function(tElem, tAttrs) { + var tooltipLinker = $compile(template); + + return function link(scope, element, attrs, tooltipCtrl) { + var tooltip; + var tooltipLinkedScope; + var transitionTimeout; + var showTimeout; + var hideTimeout; + var positionTimeout; + var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; + var triggers = getTriggers(undefined); + var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); + var ttScope = scope.$new(true); + var repositionScheduled = false; + var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; + var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; + var observers = []; + + var positionTooltip = function() { + // check if tooltip exists and is not empty + if (!tooltip || !tooltip.html()) { return; } + + if (!positionTimeout) { + positionTimeout = $timeout(function() { + // Reset the positioning. + tooltip.css({ top: 0, left: 0 }); + + // Now set the calculated positioning. + var ttCss = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); + ttCss.top += 'px'; + ttCss.left += 'px'; + ttCss.visibility = 'visible'; + tooltip.css(ttCss); + + positionTimeout = null; + }, 0, false); + } + }; + + // Set up the correct scope to allow transclusion later + ttScope.origScope = scope; + + // By default, the tooltip is not open. + // TODO add ability to start tooltip opened + ttScope.isOpen = false; + openedTooltips.add(ttScope, { + close: hide + }); + + function toggleTooltipBind() { + if (!ttScope.isOpen) { + showTooltipBind(); + } else { + hideTooltipBind(); + } + } + + // Show the tooltip with delay if specified, otherwise show it immediately + function showTooltipBind() { + if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { + return; + } + + cancelHide(); + prepareTooltip(); + + if (ttScope.popupDelay) { + // Do nothing if the tooltip was already scheduled to pop-up. + // This happens if show is triggered multiple times before any hide is triggered. + if (!showTimeout) { + showTimeout = $timeout(show, ttScope.popupDelay, false); + } + } else { + show(); + } + } + + function hideTooltipBind() { + cancelShow(); + + if (ttScope.popupCloseDelay) { + if (!hideTimeout) { + hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); + } + } else { + hide(); + } + } + + // Show the tooltip popup element. + function show() { + cancelShow(); + cancelHide(); + + // Don't show empty tooltips. + if (!ttScope.content) { + return angular.noop; + } + + createTooltip(); + + // And show the tooltip. + ttScope.$evalAsync(function() { + ttScope.isOpen = true; + assignIsOpen(true); + positionTooltip(); + }); + } + + function cancelShow() { + if (showTimeout) { + $timeout.cancel(showTimeout); + showTimeout = null; + } + + if (positionTimeout) { + $timeout.cancel(positionTimeout); + positionTimeout = null; + } + } + + // Hide the tooltip popup element. + function hide() { + cancelShow(); + cancelHide(); + + if (!ttScope) { + return; + } + + // First things first: we don't show it anymore. + ttScope.$evalAsync(function() { + ttScope.isOpen = false; + assignIsOpen(false); + // And now we remove it from the DOM. However, if we have animation, we + // need to wait for it to expire beforehand. + // FIXME: this is a placeholder for a port of the transitions library. + // The fade transition in TWBS is 150ms. + if (ttScope.animation) { + if (!transitionTimeout) { + transitionTimeout = $timeout(removeTooltip, 150, false); + } + } else { + removeTooltip(); + } + }); + } + + function cancelHide() { + if (hideTimeout) { + $timeout.cancel(hideTimeout); + hideTimeout = null; + } + if (transitionTimeout) { + $timeout.cancel(transitionTimeout); + transitionTimeout = null; + } + } + + function createTooltip() { + // There can only be one tooltip element per directive shown at once. + if (tooltip) { + return; + } + + tooltipLinkedScope = ttScope.$new(); + tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { + if (appendToBody) { + $document.find('body').append(tooltip); + } else { + element.after(tooltip); + } + }); + + prepObservers(); + } + + function removeTooltip() { + unregisterObservers(); + + transitionTimeout = null; + if (tooltip) { + tooltip.remove(); + tooltip = null; + } + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } + } + + /** + * Set the inital scope values. Once + * the tooltip is created, the observers + * will be added to keep things in synch. + */ + function prepareTooltip() { + ttScope.title = attrs[prefix + 'Title']; + if (contentParse) { + ttScope.content = contentParse(scope); + } else { + ttScope.content = attrs[ttType]; + } + + ttScope.popupClass = attrs[prefix + 'Class']; + ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; + + var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); + var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); + ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; + ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; + } + + function assignIsOpen(isOpen) { + if (isOpenParse && angular.isFunction(isOpenParse.assign)) { + isOpenParse.assign(scope, isOpen); + } + } + + ttScope.contentExp = function() { + return ttScope.content; + }; + + /** + * Observe the relevant attributes. + */ + attrs.$observe('disabled', function(val) { + if (val) { + cancelShow(); + } + + if (val && ttScope.isOpen) { + hide(); + } + }); + + if (isOpenParse) { + scope.$watch(isOpenParse, function(val) { + /*jshint -W018 */ + if (ttScope && !val === ttScope.isOpen) { + toggleTooltipBind(); + } + /*jshint +W018 */ + }); + } + + function prepObservers() { + observers.length = 0; + + if (contentParse) { + observers.push( + scope.$watch(contentParse, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } + }) + ); + + observers.push( + tooltipLinkedScope.$watch(function() { + if (!repositionScheduled) { + repositionScheduled = true; + tooltipLinkedScope.$$postDigest(function() { + repositionScheduled = false; + if (ttScope && ttScope.isOpen) { + positionTooltip(); + } + }); + } + }) + ); + } else { + observers.push( + attrs.$observe(ttType, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } else { + positionTooltip(); + } + }) + ); + } + + observers.push( + attrs.$observe(prefix + 'Title', function(val) { + ttScope.title = val; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + + observers.push( + attrs.$observe(prefix + 'Placement', function(val) { + ttScope.placement = val ? val : options.placement; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + } + + function unregisterObservers() { + if (observers.length) { + angular.forEach(observers, function(observer) { + observer(); + }); + observers.length = 0; + } + } + + var unregisterTriggers = function() { + triggers.show.forEach(function(trigger) { + element.unbind(trigger, showTooltipBind); + }); + triggers.hide.forEach(function(trigger) { + trigger.split(' ').forEach(function(hideTrigger) { + element[0].removeEventListener(hideTrigger, hideTooltipBind); + }); + }); + }; + + function prepTriggers() { + var val = attrs[prefix + 'Trigger']; + unregisterTriggers(); + + triggers = getTriggers(val); + + if (triggers.show !== 'none') { + triggers.show.forEach(function(trigger, idx) { + // Using raw addEventListener due to jqLite/jQuery bug - #4060 + if (trigger === triggers.hide[idx]) { + element[0].addEventListener(trigger, toggleTooltipBind); + } else if (trigger) { + element[0].addEventListener(trigger, showTooltipBind); + triggers.hide[idx].split(' ').forEach(function(trigger) { + element[0].addEventListener(trigger, hideTooltipBind); + }); + } + + element.on('keypress', function(e) { + if (e.which === 27) { + hideTooltipBind(); + } + }); + }); + } + } + + prepTriggers(); + + var animation = scope.$eval(attrs[prefix + 'Animation']); + ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; + + var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']); + appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; + + // if a tooltip is attached to <body> we need to remove it on + // location change as its parent scope will probably not be destroyed + // by the change. + if (appendToBody) { + scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() { + if (ttScope.isOpen) { + hide(); + } + }); + } + + // Make sure tooltip is destroyed and removed. + scope.$on('$destroy', function onDestroyTooltip() { + cancelShow(); + cancelHide(); + unregisterTriggers(); + removeTooltip(); + openedTooltips.remove(ttScope); + ttScope = null; + }); + }; + } + }; + }; + }]; +}) + +// This is mostly ngInclude code but with a custom scope +.directive('uibTooltipTemplateTransclude', [ + '$animate', '$sce', '$compile', '$templateRequest', +function ($animate , $sce , $compile , $templateRequest) { + return { + link: function(scope, elem, attrs) { + var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); + + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) { return; } + var newScope = origScope.$new(); + var template = response; + + var clone = $compile(template)(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, elem); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + } + }); + + scope.$on('$destroy', cleanupLastIncludeContent); + } + }; +}]) + +/** + * Note that it's intentional that these classes are *not* applied through $animate. + * They must not be animated as they're expected to be present on the tooltip on + * initialization. + */ +.directive('uibTooltipClasses', function() { + return { + restrict: 'A', + link: function(scope, element, attrs) { + if (scope.placement) { + element.addClass(scope.placement); + } + + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + + if (scope.animation()) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}) + +.directive('uibTooltipPopup', function() { + return { + replace: true, + scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-popup.html', + link: function(scope, element) { + element.addClass('tooltip'); + } + }; +}) + +.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('uibTooltipTemplatePopup', function() { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/tooltip/tooltip-template-popup.html', + link: function(scope, element) { + element.addClass('tooltip'); + } + }; +}) + +.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('uibTooltipHtmlPopup', function() { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-html-popup.html', + link: function(scope, element) { + element.addClass('tooltip'); + } + }; +}) + +.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]); + +/* Deprecated tooltip below */ + +angular.module('ui.bootstrap.tooltip') + +.value('$tooltipSuppressWarning', false) + +.provider('$tooltip', ['$uibTooltipProvider', function($uibTooltipProvider) { + angular.extend(this, $uibTooltipProvider); + + this.$get = ['$log', '$tooltipSuppressWarning', '$injector', function($log, $tooltipSuppressWarning, $injector) { + if (!$tooltipSuppressWarning) { + $log.warn('$tooltip is now deprecated. Use $uibTooltip instead.'); + } + + return $injector.invoke($uibTooltipProvider.$get); + }]; +}]) + +// This is mostly ngInclude code but with a custom scope +.directive('tooltipTemplateTransclude', [ + '$animate', '$sce', '$compile', '$templateRequest', '$log', '$tooltipSuppressWarning', +function ($animate , $sce , $compile , $templateRequest, $log, $tooltipSuppressWarning) { + return { + link: function(scope, elem, attrs) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-template-transclude is now deprecated. Use uib-tooltip-template-transclude instead.'); + } + + var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); + + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) { return; } + var newScope = origScope.$new(); + var template = response; + + var clone = $compile(template)(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, elem); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + } + }); + + scope.$on('$destroy', cleanupLastIncludeContent); + } + }; +}]) + +.directive('tooltipClasses', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-classes is now deprecated. Use uib-tooltip-classes instead.'); + } + + if (scope.placement) { + element.addClass(scope.placement); + } + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + if (scope.animation()) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}]) + +.directive('tooltipPopup', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + replace: true, + scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-popup.html', + link: function(scope, element) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-popup is now deprecated. Use uib-tooltip-popup instead.'); + } + + element.addClass('tooltip'); + } + }; +}]) + +.directive('tooltip', ['$tooltip', function($tooltip) { + return $tooltip('tooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('tooltipTemplatePopup', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/tooltip/tooltip-template-popup.html', + link: function(scope, element) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-template-popup is now deprecated. Use uib-tooltip-template-popup instead.'); + } + + element.addClass('tooltip'); + } + }; +}]) + +.directive('tooltipTemplate', ['$tooltip', function($tooltip) { + return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('tooltipHtmlPopup', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-html-popup.html', + link: function(scope, element) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-html-popup is now deprecated. Use uib-tooltip-html-popup instead.'); + } + + element.addClass('tooltip'); + } + }; +}]) + +.directive('tooltipHtml', ['$tooltip', function($tooltip) { + return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]); + +/** + * The following features are still outstanding: popup delay, animation as a + * function, placement as a function, inside, support for more triggers than + * just mouse enter/leave, and selector delegatation. + */ +angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) + +.directive('uibPopoverTemplatePopup', function() { + return { + replace: true, + scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/popover/popover-template.html', + link: function(scope, element) { + element.addClass('popover'); + } + }; +}) + +.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverHtmlPopup', function() { + return { + replace: true, + scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover-html.html', + link: function(scope, element) { + element.addClass('popover'); + } + }; +}) + +.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverPopup', function() { + return { + replace: true, + scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover.html', + link: function(scope, element) { + element.addClass('popover'); + } + }; +}) + +.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopover', 'popover', 'click'); +}]); + +/* Deprecated popover below */ + +angular.module('ui.bootstrap.popover') + +.value('$popoverSuppressWarning', false) + +.directive('popoverTemplatePopup', ['$log', '$popoverSuppressWarning', function($log, $popoverSuppressWarning) { + return { + replace: true, + scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/popover/popover-template.html', + link: function(scope, element) { + if (!$popoverSuppressWarning) { + $log.warn('popover-template-popup is now deprecated. Use uib-popover-template-popup instead.'); + } + + element.addClass('popover'); + } + }; +}]) + +.directive('popoverTemplate', ['$tooltip', function($tooltip) { + return $tooltip('popoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('popoverHtmlPopup', ['$log', '$popoverSuppressWarning', function($log, $popoverSuppressWarning) { + return { + replace: true, + scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover-html.html', + link: function(scope, element) { + if (!$popoverSuppressWarning) { + $log.warn('popover-html-popup is now deprecated. Use uib-popover-html-popup instead.'); + } + + element.addClass('popover'); + } + }; +}]) + +.directive('popoverHtml', ['$tooltip', function($tooltip) { + return $tooltip('popoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('popoverPopup', ['$log', '$popoverSuppressWarning', function($log, $popoverSuppressWarning) { + return { + replace: true, + scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover.html', + link: function(scope, element) { + if (!$popoverSuppressWarning) { + $log.warn('popover-popup is now deprecated. Use uib-popover-popup instead.'); + } + + element.addClass('popover'); + } + }; +}]) + +.directive('popover', ['$tooltip', function($tooltip) { + + return $tooltip('popover', 'popover', 'click'); +}]); + +angular.module('ui.bootstrap.progressbar', []) + +.constant('uibProgressConfig', { + animate: true, + max: 100 +}) + +.controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) { + var self = this, + animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; + + this.bars = []; + $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max; + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = $scope.max; + bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; + + bar.$watch('value', function(value) { + bar.recalculatePercentage(); + }); + + bar.recalculatePercentage = function() { + var totalPercentage = self.bars.reduce(function(total, bar) { + bar.percent = +(100 * bar.value / bar.max).toFixed(2); + return total + bar.percent; + }, 0); + + if (totalPercentage > 100) { + bar.percent -= totalPercentage - 100; + } + }; + + bar.$on('$destroy', function() { + element = null; + self.removeBar(bar); + }); + }; + + this.removeBar = function(bar) { + this.bars.splice(this.bars.indexOf(bar), 1); + this.bars.forEach(function (bar) { + bar.recalculatePercentage(); + }); + }; + + $scope.$watch('max', function(max) { + self.bars.forEach(function(bar) { + bar.max = $scope.max; + bar.recalculatePercentage(); + }); + }); +}]) + +.directive('uibProgress', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + require: 'uibProgress', + scope: { + max: '=?' + }, + templateUrl: 'template/progressbar/progress.html' + }; +}) + +.directive('uibBar', function() { + return { + replace: true, + transclude: true, + require: '^uibProgress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'template/progressbar/bar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, element, attrs); + } + }; +}) + +.directive('uibProgressbar', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + scope: { + value: '=', + max: '=?', + type: '@' + }, + templateUrl: 'template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}); + +/* Deprecated progressbar below */ + +angular.module('ui.bootstrap.progressbar') + +.value('$progressSuppressWarning', false) + +.controller('ProgressController', ['$scope', '$attrs', 'uibProgressConfig', '$log', '$progressSuppressWarning', function($scope, $attrs, progressConfig, $log, $progressSuppressWarning) { + if (!$progressSuppressWarning) { + $log.warn('ProgressController is now deprecated. Use UibProgressController instead.'); + } + + var self = this, + animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; + + this.bars = []; + $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max; + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = $scope.max; + bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; + + bar.$watch('value', function(value) { + bar.recalculatePercentage(); + }); + + bar.recalculatePercentage = function() { + bar.percent = +(100 * bar.value / bar.max).toFixed(2); + + var totalPercentage = self.bars.reduce(function(total, bar) { + return total + bar.percent; + }, 0); + + if (totalPercentage > 100) { + bar.percent -= totalPercentage - 100; + } + }; + + bar.$on('$destroy', function() { + element = null; + self.removeBar(bar); + }); + }; + + this.removeBar = function(bar) { + this.bars.splice(this.bars.indexOf(bar), 1); + }; + + $scope.$watch('max', function(max) { + self.bars.forEach(function(bar) { + bar.max = $scope.max; + bar.recalculatePercentage(); + }); + }); +}]) + +.directive('progress', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) { + return { + replace: true, + transclude: true, + controller: 'ProgressController', + require: 'progress', + scope: { + max: '=?', + title: '@?' + }, + templateUrl: 'template/progressbar/progress.html', + link: function() { + if (!$progressSuppressWarning) { + $log.warn('progress is now deprecated. Use uib-progress instead.'); + } + } + }; +}]) + +.directive('bar', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) { + return { + replace: true, + transclude: true, + require: '^progress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'template/progressbar/bar.html', + link: function(scope, element, attrs, progressCtrl) { + if (!$progressSuppressWarning) { + $log.warn('bar is now deprecated. Use uib-bar instead.'); + } + progressCtrl.addBar(scope, element); + } + }; +}]) + +.directive('progressbar', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) { + return { + replace: true, + transclude: true, + controller: 'ProgressController', + scope: { + value: '=', + max: '=?', + type: '@' + }, + templateUrl: 'template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + if (!$progressSuppressWarning) { + $log.warn('progressbar is now deprecated. Use uib-progressbar instead.'); + } + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}]); + +angular.module('ui.bootstrap.rating', []) + +.constant('uibRatingConfig', { + max: 5, + stateOn: null, + stateOff: null, + titles : ['one', 'two', 'three', 'four', 'five'] +}) + +.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { + var ngModelCtrl = { $setViewValue: angular.noop }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.push(function(value) { + if (angular.isNumber(value) && value << 0 !== value) { + value = Math.round(value); + } + return value; + }); + + this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn; + this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff; + var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ; + this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ? + tmpTitles : ratingConfig.titles; + + var ratingStates = angular.isDefined($attrs.ratingStates) ? + $scope.$parent.$eval($attrs.ratingStates) : + new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max); + $scope.range = this.buildTemplateObjects(ratingStates); + }; + + this.buildTemplateObjects = function(states) { + for (var i = 0, n = states.length; i < n; i++) { + states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]); + } + return states; + }; + + this.getTitle = function(index) { + if (index >= this.titles.length) { + return index + 1; + } else { + return this.titles[index]; + } + }; + + $scope.rate = function(value) { + if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { + ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value); + ngModelCtrl.$render(); + } + }; + + $scope.enter = function(value) { + if (!$scope.readonly) { + $scope.value = value; + } + $scope.onHover({value: value}); + }; + + $scope.reset = function() { + $scope.value = ngModelCtrl.$viewValue; + $scope.onLeave(); + }; + + $scope.onKeydown = function(evt) { + if (/(37|38|39|40)/.test(evt.which)) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1)); + } + }; + + this.render = function() { + $scope.value = ngModelCtrl.$viewValue; + }; +}]) + +.directive('uibRating', function() { + return { + require: ['uibRating', 'ngModel'], + scope: { + readonly: '=?', + onHover: '&', + onLeave: '&' + }, + controller: 'UibRatingController', + templateUrl: 'template/rating/rating.html', + replace: true, + link: function(scope, element, attrs, ctrls) { + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}); + +/* Deprecated rating below */ + +angular.module('ui.bootstrap.rating') + +.value('$ratingSuppressWarning', false) + +.controller('RatingController', ['$scope', '$attrs', '$controller', '$log', '$ratingSuppressWarning', function($scope, $attrs, $controller, $log, $ratingSuppressWarning) { + if (!$ratingSuppressWarning) { + $log.warn('RatingController is now deprecated. Use UibRatingController instead.'); + } + + angular.extend(this, $controller('UibRatingController', { + $scope: $scope, + $attrs: $attrs + })); +}]) + +.directive('rating', ['$log', '$ratingSuppressWarning', function($log, $ratingSuppressWarning) { + return { + require: ['rating', 'ngModel'], + scope: { + readonly: '=?', + onHover: '&', + onLeave: '&' + }, + controller: 'RatingController', + templateUrl: 'template/rating/rating.html', + replace: true, + link: function(scope, element, attrs, ctrls) { + if (!$ratingSuppressWarning) { + $log.warn('rating is now deprecated. Use uib-rating instead.'); + } + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}]); + + +/** + * @ngdoc overview + * @name ui.bootstrap.tabs + * + * @description + * AngularJS version of the tabs directive. + */ + +angular.module('ui.bootstrap.tabs', []) + +.controller('UibTabsetController', ['$scope', function ($scope) { + var ctrl = this, + tabs = ctrl.tabs = $scope.tabs = []; + + ctrl.select = function(selectedTab) { + angular.forEach(tabs, function(tab) { + if (tab.active && tab !== selectedTab) { + tab.active = false; + tab.onDeselect(); + selectedTab.selectCalled = false; + } + }); + selectedTab.active = true; + // only call select if it has not already been called + if (!selectedTab.selectCalled) { + selectedTab.onSelect(); + selectedTab.selectCalled = true; + } + }; + + ctrl.addTab = function addTab(tab) { + tabs.push(tab); + // we can't run the select function on the first tab + // since that would select it twice + if (tabs.length === 1 && tab.active !== false) { + tab.active = true; + } else if (tab.active) { + ctrl.select(tab); + } else { + tab.active = false; + } + }; + + ctrl.removeTab = function removeTab(tab) { + var index = tabs.indexOf(tab); + //Select a new tab if the tab to be removed is selected and not destroyed + if (tab.active && tabs.length > 1 && !destroyed) { + //If this is the last tab, select the previous tab. else, the next tab. + var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1; + ctrl.select(tabs[newActiveIndex]); + } + tabs.splice(index, 1); + }; + + var destroyed; + $scope.$on('$destroy', function() { + destroyed = true; + }); +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tabset + * @restrict EA + * + * @description + * Tabset is the outer container for the tabs directive + * + * @param {boolean=} vertical Whether or not to use vertical styling for the tabs. + * @param {boolean=} justified Whether or not to use justified styling for the tabs. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <uib-tabset> + <uib-tab heading="Tab 1"><b>First</b> Content!</uib-tab> + <uib-tab heading="Tab 2"><i>Second</i> Content!</uib-tab> + </uib-tabset> + <hr /> + <uib-tabset vertical="true"> + <uib-tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</uib-tab> + <uib-tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</uib-tab> + </uib-tabset> + <uib-tabset justified="true"> + <uib-tab heading="Justified Tab 1"><b>First</b> Justified Content!</uib-tab> + <uib-tab heading="Justified Tab 2"><i>Second</i> Justified Content!</uib-tab> + </uib-tabset> + </file> +</example> + */ +.directive('uibTabset', function() { + return { + restrict: 'EA', + transclude: true, + replace: true, + scope: { + type: '@' + }, + controller: 'UibTabsetController', + templateUrl: 'template/tabs/tabset.html', + link: function(scope, element, attrs) { + scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; + } + }; +}) + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tab + * @restrict EA + * + * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}. + * @param {string=} select An expression to evaluate when the tab is selected. + * @param {boolean=} active A binding, telling whether or not this tab is selected. + * @param {boolean=} disabled A binding, telling whether or not this tab is disabled. + * + * @description + * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <div ng-controller="TabsDemoCtrl"> + <button class="btn btn-small" ng-click="items[0].active = true"> + Select item 1, using active binding + </button> + <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled"> + Enable/disable item 2, using disabled binding + </button> + <br /> + <uib-tabset> + <uib-tab heading="Tab 1">First Tab</uib-tab> + <uib-tab select="alertMe()"> + <uib-tab-heading><i class="icon-bell"></i> Alert me!</tab-heading> + Second Tab, with alert callback and html heading! + </uib-tab> + <uib-tab ng-repeat="item in items" + heading="{{item.title}}" + disabled="item.disabled" + active="item.active"> + {{item.content}} + </uib-tab> + </uib-tabset> + </div> + </file> + <file name="script.js"> + function TabsDemoCtrl($scope) { + $scope.items = [ + { title:"Dynamic Title 1", content:"Dynamic Item 0" }, + { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true } + ]; + + $scope.alertMe = function() { + setTimeout(function() { + alert("You've selected the alert tab!"); + }); + }; + }; + </file> +</example> + */ + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tabHeading + * @restrict EA + * + * @description + * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <uib-tabset> + <uib-tab> + <uib-tab-heading><b>HTML</b> in my titles?!</tab-heading> + And some content, too! + </uib-tab> + <uib-tab> + <uib-tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading> + That's right. + </uib-tab> + </uib-tabset> + </file> +</example> + */ +.directive('uibTab', ['$parse', function($parse) { + return { + require: '^uibTabset', + restrict: 'EA', + replace: true, + templateUrl: 'template/tabs/tab.html', + transclude: true, + scope: { + active: '=?', + heading: '@', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + scope.$watch('active', function(active) { + if (active) { + tabsetCtrl.select(scope); + } + }); + + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !! value; + }); + } + + scope.select = function() { + if (!scope.disabled) { + scope.active = true; + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + } + }; +}]) + +.directive('uibTabHeadingTransclude', function() { + return { + restrict: 'A', + require: ['?^uibTab', '?^tab'], // TODO: change to '^uibTab' after deprecation removal + link: function(scope, elm) { + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; +}) + +.directive('uibTabContentTransclude', function() { + return { + restrict: 'A', + require: ['?^uibTabset', '?^tabset'], // TODO: change to '^uibTabset' after deprecation removal + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.uibTabContentTransclude); + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } else { + elm.append(node); + } + }); + }); + } + }; + + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('tab-heading') || // TODO: remove after deprecation removal + node.hasAttribute('data-tab-heading') || // TODO: remove after deprecation removal + node.hasAttribute('x-tab-heading') || // TODO: remove after deprecation removal + node.hasAttribute('uib-tab-heading') || + node.hasAttribute('data-uib-tab-heading') || + node.hasAttribute('x-uib-tab-heading') || + node.tagName.toLowerCase() === 'tab-heading' || // TODO: remove after deprecation removal + node.tagName.toLowerCase() === 'data-tab-heading' || // TODO: remove after deprecation removal + node.tagName.toLowerCase() === 'x-tab-heading' || // TODO: remove after deprecation removal + node.tagName.toLowerCase() === 'uib-tab-heading' || + node.tagName.toLowerCase() === 'data-uib-tab-heading' || + node.tagName.toLowerCase() === 'x-uib-tab-heading' + ); + } +}); + +/* deprecated tabs below */ + +angular.module('ui.bootstrap.tabs') + + .value('$tabsSuppressWarning', false) + + .controller('TabsetController', ['$scope', '$controller', '$log', '$tabsSuppressWarning', function($scope, $controller, $log, $tabsSuppressWarning) { + if (!$tabsSuppressWarning) { + $log.warn('TabsetController is now deprecated. Use UibTabsetController instead.'); + } + + angular.extend(this, $controller('UibTabsetController', { + $scope: $scope + })); + }]) + + .directive('tabset', ['$log', '$tabsSuppressWarning', function($log, $tabsSuppressWarning) { + return { + restrict: 'EA', + transclude: true, + replace: true, + scope: { + type: '@' + }, + controller: 'TabsetController', + templateUrl: 'template/tabs/tabset.html', + link: function(scope, element, attrs) { + + if (!$tabsSuppressWarning) { + $log.warn('tabset is now deprecated. Use uib-tabset instead.'); + } + scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; + } + }; + }]) + + .directive('tab', ['$parse', '$log', '$tabsSuppressWarning', function($parse, $log, $tabsSuppressWarning) { + return { + require: '^tabset', + restrict: 'EA', + replace: true, + templateUrl: 'template/tabs/tab.html', + transclude: true, + scope: { + active: '=?', + heading: '@', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + if (!$tabsSuppressWarning) { + $log.warn('tab is now deprecated. Use uib-tab instead.'); + } + + scope.$watch('active', function(active) { + if (active) { + tabsetCtrl.select(scope); + } + }); + + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !!value; + }); + } + + scope.select = function() { + if (!scope.disabled) { + scope.active = true; + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + } + }; + }]) + + .directive('tabHeadingTransclude', ['$log', '$tabsSuppressWarning', function($log, $tabsSuppressWarning) { + return { + restrict: 'A', + require: '^tab', + link: function(scope, elm) { + if (!$tabsSuppressWarning) { + $log.warn('tab-heading-transclude is now deprecated. Use uib-tab-heading-transclude instead.'); + } + + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; + }]) + + .directive('tabContentTransclude', ['$log', '$tabsSuppressWarning', function($log, $tabsSuppressWarning) { + return { + restrict: 'A', + require: '^tabset', + link: function(scope, elm, attrs) { + if (!$tabsSuppressWarning) { + $log.warn('tab-content-transclude is now deprecated. Use uib-tab-content-transclude instead.'); + } + + var tab = scope.$eval(attrs.tabContentTransclude); + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } + else { + elm.append(node); + } + }); + }); + } + }; + + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('tab-heading') || + node.hasAttribute('data-tab-heading') || + node.hasAttribute('x-tab-heading') || + node.tagName.toLowerCase() === 'tab-heading' || + node.tagName.toLowerCase() === 'data-tab-heading' || + node.tagName.toLowerCase() === 'x-tab-heading' + ); + } + }]); + +angular.module('ui.bootstrap.timepicker', []) + +.constant('uibTimepickerConfig', { + hourStep: 1, + minuteStep: 1, + showMeridian: true, + meridians: null, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + showSpinners: true +}) + +.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { + var selected = new Date(), + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS; + + $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; + $element.removeAttr('tabindex'); + + this.init = function(ngModelCtrl_, inputs) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.unshift(function(modelValue) { + return modelValue ? new Date(modelValue) : null; + }); + + var hoursInputEl = inputs.eq(0), + minutesInputEl = inputs.eq(1); + + var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; + if (mousewheel) { + this.setupMousewheelEvents(hoursInputEl, minutesInputEl); + } + + var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; + if (arrowkeys) { + this.setupArrowkeyEvents(hoursInputEl, minutesInputEl); + } + + $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; + this.setupInputEvents(hoursInputEl, minutesInputEl); + }; + + var hourStep = timepickerConfig.hourStep; + if ($attrs.hourStep) { + $scope.$parent.$watch($parse($attrs.hourStep), function(value) { + hourStep = parseInt(value, 10); + }); + } + + var minuteStep = timepickerConfig.minuteStep; + if ($attrs.minuteStep) { + $scope.$parent.$watch($parse($attrs.minuteStep), function(value) { + minuteStep = parseInt(value, 10); + }); + } + + var min; + $scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + }); + + var max; + $scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + }); + + $scope.noIncrementHours = function() { + var incrementedSelected = addMinutes(selected, hourStep * 60); + return incrementedSelected > max || + (incrementedSelected < selected && incrementedSelected < min); + }; + + $scope.noDecrementHours = function() { + var decrementedSelected = addMinutes(selected, -hourStep * 60); + return decrementedSelected < min || + (decrementedSelected > selected && decrementedSelected > max); + }; + + $scope.noIncrementMinutes = function() { + var incrementedSelected = addMinutes(selected, minuteStep); + return incrementedSelected > max || + (incrementedSelected < selected && incrementedSelected < min); + }; + + $scope.noDecrementMinutes = function() { + var decrementedSelected = addMinutes(selected, -minuteStep); + return decrementedSelected < min || + (decrementedSelected > selected && decrementedSelected > max); + }; + + $scope.noToggleMeridian = function() { + if (selected.getHours() < 13) { + return addMinutes(selected, 12 * 60) > max; + } else { + return addMinutes(selected, -12 * 60) < min; + } + }; + + // 12H / 24H mode + $scope.showMeridian = timepickerConfig.showMeridian; + if ($attrs.showMeridian) { + $scope.$parent.$watch($parse($attrs.showMeridian), function(value) { + $scope.showMeridian = !!value; + + if (ngModelCtrl.$error.time) { + // Evaluate from template + var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate(); + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + refresh(); + } + } else { + updateTemplate(); + } + }); + } + + // Get $scope.hours in 24H mode if valid + function getHoursFromTemplate() { + var hours = parseInt($scope.hours, 10); + var valid = $scope.showMeridian ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24); + if (!valid) { + return undefined; + } + + if ($scope.showMeridian) { + if (hours === 12) { + hours = 0; + } + if ($scope.meridian === meridians[1]) { + hours = hours + 12; + } + } + return hours; + } + + function getMinutesFromTemplate() { + var minutes = parseInt($scope.minutes, 10); + return (minutes >= 0 && minutes < 60) ? minutes : undefined; + } + + function pad(value) { + return (angular.isDefined(value) && value.toString().length < 2) ? '0' + value : value.toString(); + } + + // Respond on mousewheel spin + this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl) { + var isScrollingUp = function(e) { + if (e.originalEvent) { + e = e.originalEvent; + } + //pick correct delta variable depending on event + var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY; + return (e.detail || delta > 0); + }; + + hoursInputEl.bind('mousewheel wheel', function(e) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); + e.preventDefault(); + }); + + minutesInputEl.bind('mousewheel wheel', function(e) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); + e.preventDefault(); + }); + + }; + + // Respond on up/down arrowkeys + this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl) { + hoursInputEl.bind('keydown', function(e) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementHours(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementHours(); + $scope.$apply(); + } + }); + + minutesInputEl.bind('keydown', function(e) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementMinutes(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementMinutes(); + $scope.$apply(); + } + }); + }; + + this.setupInputEvents = function(hoursInputEl, minutesInputEl) { + if ($scope.readonlyInput) { + $scope.updateHours = angular.noop; + $scope.updateMinutes = angular.noop; + return; + } + + var invalidate = function(invalidHours, invalidMinutes) { + ngModelCtrl.$setViewValue(null); + ngModelCtrl.$setValidity('time', false); + if (angular.isDefined(invalidHours)) { + $scope.invalidHours = invalidHours; + } + if (angular.isDefined(invalidMinutes)) { + $scope.invalidMinutes = invalidMinutes; + } + }; + + $scope.updateHours = function() { + var hours = getHoursFromTemplate(), + minutes = getMinutesFromTemplate(); + + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + if (selected < min || selected > max) { + invalidate(true); + } else { + refresh('h'); + } + } else { + invalidate(true); + } + }; + + hoursInputEl.bind('blur', function(e) { + if (!$scope.invalidHours && $scope.hours < 10) { + $scope.$apply(function() { + $scope.hours = pad($scope.hours); + }); + } + }); + + $scope.updateMinutes = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(undefined, true); + } else { + refresh('m'); + } + } else { + invalidate(undefined, true); + } + }; + + minutesInputEl.bind('blur', function(e) { + if (!$scope.invalidMinutes && $scope.minutes < 10) { + $scope.$apply(function() { + $scope.minutes = pad($scope.minutes); + }); + } + }); + + }; + + this.render = function() { + var date = ngModelCtrl.$viewValue; + + if (isNaN(date)) { + ngModelCtrl.$setValidity('time', false); + $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } else { + if (date) { + selected = date; + } + + if (selected < min || selected > max) { + ngModelCtrl.$setValidity('time', false); + $scope.invalidHours = true; + $scope.invalidMinutes = true; + } else { + makeValid(); + } + updateTemplate(); + } + }; + + // Call internally when we know that model is valid. + function refresh(keyboardChange) { + makeValid(); + ngModelCtrl.$setViewValue(new Date(selected)); + updateTemplate(keyboardChange); + } + + function makeValid() { + ngModelCtrl.$setValidity('time', true); + $scope.invalidHours = false; + $scope.invalidMinutes = false; + } + + function updateTemplate(keyboardChange) { + var hours = selected.getHours(), minutes = selected.getMinutes(); + + if ($scope.showMeridian) { + hours = (hours === 0 || hours === 12) ? 12 : hours % 12; // Convert 24 to 12 hour system + } + + $scope.hours = keyboardChange === 'h' ? hours : pad(hours); + if (keyboardChange !== 'm') { + $scope.minutes = pad(minutes); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + } + + function addMinutes(date, minutes) { + var dt = new Date(date.getTime() + minutes * 60000); + var newDate = new Date(date); + newDate.setHours(dt.getHours(), dt.getMinutes()); + return newDate; + } + + function addMinutesToSelected(minutes) { + selected = addMinutes(selected, minutes); + refresh(); + } + + $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? + $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; + + $scope.incrementHours = function() { + if (!$scope.noIncrementHours()) { + addMinutesToSelected(hourStep * 60); + } + }; + + $scope.decrementHours = function() { + if (!$scope.noDecrementHours()) { + addMinutesToSelected(-hourStep * 60); + } + }; + + $scope.incrementMinutes = function() { + if (!$scope.noIncrementMinutes()) { + addMinutesToSelected(minuteStep); + } + }; + + $scope.decrementMinutes = function() { + if (!$scope.noDecrementMinutes()) { + addMinutesToSelected(-minuteStep); + } + }; + + $scope.toggleMeridian = function() { + if (!$scope.noToggleMeridian()) { + addMinutesToSelected(12 * 60 * (selected.getHours() < 12 ? 1 : -1)); + } + }; +}]) + +.directive('uibTimepicker', function() { + return { + restrict: 'EA', + require: ['uibTimepicker', '?^ngModel'], + controller: 'UibTimepickerController', + controllerAs: 'timepicker', + replace: true, + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/timepicker/timepicker.html'; + }, + link: function(scope, element, attrs, ctrls) { + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}); + +/* Deprecated timepicker below */ + +angular.module('ui.bootstrap.timepicker') + +.value('$timepickerSuppressWarning', false) + +.controller('TimepickerController', ['$scope', '$element', '$attrs', '$controller', '$log', '$timepickerSuppressWarning', function($scope, $element, $attrs, $controller, $log, $timepickerSuppressWarning) { + if (!$timepickerSuppressWarning) { + $log.warn('TimepickerController is now deprecated. Use UibTimepickerController instead.'); + } + + angular.extend(this, $controller('UibTimepickerController', { + $scope: $scope, + $element: $element, + $attrs: $attrs + })); +}]) + +.directive('timepicker', ['$log', '$timepickerSuppressWarning', function($log, $timepickerSuppressWarning) { + return { + restrict: 'EA', + require: ['timepicker', '?^ngModel'], + controller: 'TimepickerController', + controllerAs: 'timepicker', + replace: true, + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/timepicker/timepicker.html'; + }, + link: function(scope, element, attrs, ctrls) { + if (!$timepickerSuppressWarning) { + $log.warn('timepicker is now deprecated. Use uib-timepicker instead.'); + } + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}]); + +angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) + +/** + * A helper service that can parse typeahead's syntax (string provided by users) + * Extracted to a separate service for ease of unit testing + */ + .factory('uibTypeaheadParser', ['$parse', function($parse) { + // 00000111000000000000022200000000000000003333333333333330000000000044000 + var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/; + return { + parse: function(input) { + var match = input.match(TYPEAHEAD_REGEXP); + if (!match) { + throw new Error( + 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' + + ' but got "' + input + '".'); + } + + return { + itemName: match[3], + source: $parse(match[4]), + viewMapper: $parse(match[2] || match[1]), + modelMapper: $parse(match[1]) + }; + } + }; + }]) + + .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$uibPosition', 'uibTypeaheadParser', + function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) { + var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; + var modelCtrl, ngModelOptions; + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } + + //minimal wait time after last character typed before typeahead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + //should it select highlighted popup value when losing focus? + var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; + + //binding to a variable that indicates if there were no results after the query is completed + var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; + + var appendToElementId = attrs.typeaheadAppendToElementId || false; + + var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var parsedModel = $parse(attrs.ngModel); + var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); + var $setModelValue = function(scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { + return invokeModelSetter(scope, {$$$p: newValue}); + } else { + return parsedModel.assign(scope, newValue); + } + }; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.uibTypeahead); + + var hasFocus; + + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; + + //create a child scope for the typeahead directive so we are not polluting original scope + //with typeahead-specific data (matches, query etc.) + var scope = originalScope.$new(); + var offDestroy = originalScope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); + + // WAI-ARIA + var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + element.attr({ + 'aria-autocomplete': 'list', + 'aria-expanded': false, + 'aria-owns': popupId + }); + + //pop-up element used to display matches + var popUpEl = angular.element('<div uib-typeahead-popup></div>'); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { + popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); + } + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + }; + + var getMatchId = function(index) { + return popupId + '-option-' + index; + }; + + // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. + // This attribute is added or removed automatically when the `activeIdx` changes. + scope.$watch('activeIdx', function(index) { + if (index < 0) { + element.removeAttr('aria-activedescendant'); + } else { + element.attr('aria-activedescendant', getMatchId(index)); + } + }); + + var inputIsExactMatch = function(inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; + + var getMatchesAsync = function(inputValue) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(parserResult.source(originalScope, locals)).then(function(matches) { + //it might happen that several async queries were in progress if a user were typing fast + //but we are interested only in responses that correspond to the current view value + var onCurrentRequest = (inputValue === modelCtrl.$viewValue); + if (onCurrentRequest && hasFocus) { + if (matches && matches.length > 0) { + scope.activeIdx = focusFirst ? 0 : -1; + isNoResultsSetter(originalScope, false); + scope.matches.length = 0; + + //transform labels + for (var i = 0; i < matches.length; i++) { + locals[parserResult.itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: parserResult.viewMapper(scope, locals), + model: matches[i] + }); + } + + scope.query = inputValue; + //position pop-up with matches - we need to re-calculate its position each time we are opening a window + //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page + //due to other elements being rendered + recalculatePosition(); + + element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { + scope.select(0); + } + } else { + resetMatches(); + isNoResultsSetter(originalScope, true); + } + } + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function() { + resetMatches(); + isLoadingSetter(originalScope, false); + isNoResultsSetter(originalScope, true); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).bind('resize', fireRecalculating); + $document.find('body').bind('scroll', fireRecalculating); + } + + // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutEventPromise; + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + // Cancel previous timeout + if (timeoutEventPromise) { + $timeout.cancel(timeoutEventPromise); + } + + // Debounced executing recalculate after events fired + timeoutEventPromise = $timeout(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + } + + // recalculate actual position and set new values to scope + // after digest loop is popup in right position + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top += element.prop('offsetHeight'); + } + + //we need to propagate user's query so we can higlight matches + scope.query = undefined; + + //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutPromise; + + var scheduleSearchWithTimeout = function(inputValue) { + timeoutPromise = $timeout(function() { + getMatchesAsync(inputValue); + }, waitTime); + }; + + var cancelPreviousTimeout = function() { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; + + resetMatches(); + + scope.select = function(activeIdx) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + selected = true; + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals) + }); + + resetMatches(); + + //return focus to the input element if a match was selected via a mouse click event + // use timeout to avoid $rootScope:inprog error + if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { + $timeout(function() { element[0].focus(); }, 0, false); + } + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.bind('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results + if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + + if (evt.which === 40) { + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + } else if (evt.which === 38) { + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + } else if (evt.which === 13 || evt.which === 9) { + scope.$apply(function () { + scope.select(scope.activeIdx); + }); + } else if (evt.which === 27) { + evt.stopPropagation(); + + resetMatches(); + scope.$digest(); + } + }); + + element.bind('blur', function() { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + scope.select(scope.activeIdx); + }); + } + hasFocus = false; + selected = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function(evt) { + // Issue #3973 + // Firefox treats right click as a click on document + if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { + resetMatches(); + if (!$rootScope.$$phase) { + scope.$digest(); + } + } + }; + + $document.bind('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.unbind('click', dismissClickHandler); + if (appendToBody || appendToElementId) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).unbind('resize', fireRecalculating); + $document.find('body').unbind('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendToElementId !== false) { + angular.element($document[0].getElementById(appendToElementId)).append($popup); + } else { + element.after($popup); + } + + this.init = function(_modelCtrl, _ngModelOptions) { + modelCtrl = _modelCtrl; + ngModelOptions = _ngModelOptions; + + //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM + //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue + modelCtrl.$parsers.unshift(function(inputValue) { + hasFocus = true; + + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } else { + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } else { + modelCtrl.$setValidity('editable', false); + return undefined; + } + } + }); + + modelCtrl.$formatters.push(function(modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; + + // The validity may be set to false via $parsers (see above) if + // the model is restricted to selected values. If the model + // is set manually it is considered to be valid. + if (!isEditable) { + modelCtrl.$setValidity('editable', true); + } + + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } else { + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + } + }); + }; + }]) + + .directive('uibTypeahead', function() { + return { + controller: 'UibTypeaheadController', + require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'], + link: function(originalScope, element, attrs, ctrls) { + ctrls[2].init(ctrls[0], ctrls[1]); + } + }; + }) + + .directive('uibTypeaheadPopup', function() { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + return scope.matches.length > 0; + }; + + scope.isActive = function(matchIdx) { + return scope.active == matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx) { + scope.select({activeIdx:activeIdx}); + }; + } + }; + }) + + .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) { + return { + scope: { + index: '=', + match: '=', + query: '=' + }, + link:function(scope, element, attrs) { + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + $compile(tplContent.trim())(scope, function(clonedElement) { + element.replaceWith(clonedElement); + }); + }); + } + }; + }]) + + .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) { + var isSanitizePresent; + isSanitizePresent = $injector.has('$sanitize'); + + function escapeRegexp(queryToEscape) { + // Regex: capture the whole query string and replace it with the string that will be used to match + // the results, for example if the capture is "a" the result will be \a + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); + } + + function containsHtml(matchItem) { + return /<.*>/g.test(matchItem); + } + + return function(matchItem, query) { + if (!isSanitizePresent && containsHtml(matchItem)) { + $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger + } + matchItem = query? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag + if (!isSanitizePresent) { + matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive + } + return matchItem; + }; + }]); + +/* Deprecated typeahead below */ + +angular.module('ui.bootstrap.typeahead') + .value('$typeaheadSuppressWarning', false) + .service('typeaheadParser', ['$parse', 'uibTypeaheadParser', '$log', '$typeaheadSuppressWarning', function($parse, uibTypeaheadParser, $log, $typeaheadSuppressWarning) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeaheadParser is now deprecated. Use uibTypeaheadParser instead.'); + } + + return uibTypeaheadParser; + }]) + + .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$uibPosition', 'typeaheadParser', '$log', '$typeaheadSuppressWarning', + function($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser, $log, $typeaheadSuppressWarning) { + var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; + return { + require: ['ngModel', '^?ngModelOptions'], + link: function(originalScope, element, attrs, ctrls) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeahead is now deprecated. Use uib-typeahead instead.'); + } + var modelCtrl = ctrls[0]; + var ngModelOptions = ctrls[1]; + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } + + //minimal wait time after last character typed before typeahead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + //should it select highlighted popup value when losing focus? + var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; + + //binding to a variable that indicates if there were no results after the query is completed + var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; + + var appendToElementId = attrs.typeaheadAppendToElementId || false; + + var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var parsedModel = $parse(attrs.ngModel); + var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); + var $setModelValue = function(scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { + return invokeModelSetter(scope, {$$$p: newValue}); + } else { + return parsedModel.assign(scope, newValue); + } + }; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.typeahead); + + var hasFocus; + + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; + + //create a child scope for the typeahead directive so we are not polluting original scope + //with typeahead-specific data (matches, query etc.) + var scope = originalScope.$new(); + var offDestroy = originalScope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); + + // WAI-ARIA + var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + element.attr({ + 'aria-autocomplete': 'list', + 'aria-expanded': false, + 'aria-owns': popupId + }); + + //pop-up element used to display matches + var popUpEl = angular.element('<div typeahead-popup></div>'); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { + popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); + } + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + }; + + var getMatchId = function(index) { + return popupId + '-option-' + index; + }; + + // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. + // This attribute is added or removed automatically when the `activeIdx` changes. + scope.$watch('activeIdx', function(index) { + if (index < 0) { + element.removeAttr('aria-activedescendant'); + } else { + element.attr('aria-activedescendant', getMatchId(index)); + } + }); + + var inputIsExactMatch = function(inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; + + var getMatchesAsync = function(inputValue) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(parserResult.source(originalScope, locals)).then(function(matches) { + //it might happen that several async queries were in progress if a user were typing fast + //but we are interested only in responses that correspond to the current view value + var onCurrentRequest = (inputValue === modelCtrl.$viewValue); + if (onCurrentRequest && hasFocus) { + if (matches && matches.length > 0) { + scope.activeIdx = focusFirst ? 0 : -1; + isNoResultsSetter(originalScope, false); + scope.matches.length = 0; + + //transform labels + for (var i = 0; i < matches.length; i++) { + locals[parserResult.itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: parserResult.viewMapper(scope, locals), + model: matches[i] + }); + } + + scope.query = inputValue; + //position pop-up with matches - we need to re-calculate its position each time we are opening a window + //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page + //due to other elements being rendered + recalculatePosition(); + + element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { + scope.select(0); + } + } else { + resetMatches(); + isNoResultsSetter(originalScope, true); + } + } + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function() { + resetMatches(); + isLoadingSetter(originalScope, false); + isNoResultsSetter(originalScope, true); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).bind('resize', fireRecalculating); + $document.find('body').bind('scroll', fireRecalculating); + } + + // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutEventPromise; + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + // Cancel previous timeout + if (timeoutEventPromise) { + $timeout.cancel(timeoutEventPromise); + } + + // Debounced executing recalculate after events fired + timeoutEventPromise = $timeout(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + } + + // recalculate actual position and set new values to scope + // after digest loop is popup in right position + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top += element.prop('offsetHeight'); + } + + resetMatches(); + + //we need to propagate user's query so we can higlight matches + scope.query = undefined; + + //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutPromise; + + var scheduleSearchWithTimeout = function(inputValue) { + timeoutPromise = $timeout(function() { + getMatchesAsync(inputValue); + }, waitTime); + }; + + var cancelPreviousTimeout = function() { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; + + //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM + //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue + modelCtrl.$parsers.unshift(function(inputValue) { + hasFocus = true; + + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } else { + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } else { + modelCtrl.$setValidity('editable', false); + return undefined; + } + } + }); + + modelCtrl.$formatters.push(function(modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; + + // The validity may be set to false via $parsers (see above) if + // the model is restricted to selected values. If the model + // is set manually it is considered to be valid. + if (!isEditable) { + modelCtrl.$setValidity('editable', true); + } + + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } else { + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + } + }); + + scope.select = function(activeIdx) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + selected = true; + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals) + }); + + resetMatches(); + + //return focus to the input element if a match was selected via a mouse click event + // use timeout to avoid $rootScope:inprog error + if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { + $timeout(function() { element[0].focus(); }, 0, false); + } + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.bind('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results + if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + + if (evt.which === 40) { + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + } else if (evt.which === 38) { + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + } else if (evt.which === 13 || evt.which === 9) { + scope.$apply(function () { + scope.select(scope.activeIdx); + }); + } else if (evt.which === 27) { + evt.stopPropagation(); + + resetMatches(); + scope.$digest(); + } + }); + + element.bind('blur', function() { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + scope.select(scope.activeIdx); + }); + } + hasFocus = false; + selected = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function(evt) { + // Issue #3973 + // Firefox treats right click as a click on document + if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { + resetMatches(); + if (!$rootScope.$$phase) { + scope.$digest(); + } + } + }; + + $document.bind('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.unbind('click', dismissClickHandler); + if (appendToBody || appendToElementId) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).unbind('resize', fireRecalculating); + $document.find('body').unbind('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendToElementId !== false) { + angular.element($document[0].getElementById(appendToElementId)).append($popup); + } else { + element.after($popup); + } + } + }; + }]) + + .directive('typeaheadPopup', ['$typeaheadSuppressWarning', '$log', function($typeaheadSuppressWarning, $log) { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + + if (!$typeaheadSuppressWarning) { + $log.warn('typeahead-popup is now deprecated. Use uib-typeahead-popup instead.'); + } + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + return scope.matches.length > 0; + }; + + scope.isActive = function(matchIdx) { + return scope.active == matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx) { + scope.select({activeIdx:activeIdx}); + }; + } + }; + }]) + + .directive('typeaheadMatch', ['$templateRequest', '$compile', '$parse', '$typeaheadSuppressWarning', '$log', function($templateRequest, $compile, $parse, $typeaheadSuppressWarning, $log) { + return { + restrict: 'EA', + scope: { + index: '=', + match: '=', + query: '=' + }, + link:function(scope, element, attrs) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeahead-match is now deprecated. Use uib-typeahead-match instead.'); + } + + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + $compile(tplContent.trim())(scope, function(clonedElement) { + element.replaceWith(clonedElement); + }); + }); + } + }; + }]) + + .filter('typeaheadHighlight', ['$sce', '$injector', '$log', '$typeaheadSuppressWarning', function($sce, $injector, $log, $typeaheadSuppressWarning) { + var isSanitizePresent; + isSanitizePresent = $injector.has('$sanitize'); + + function escapeRegexp(queryToEscape) { + // Regex: capture the whole query string and replace it with the string that will be used to match + // the results, for example if the capture is "a" the result will be \a + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); + } + + function containsHtml(matchItem) { + return /<.*>/g.test(matchItem); + } + + return function(matchItem, query) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeaheadHighlight is now deprecated. Use uibTypeaheadHighlight instead.'); + } + + if (!isSanitizePresent && containsHtml(matchItem)) { + $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger + } + + matchItem = query? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag + if (!isSanitizePresent) { + matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive + } + + return matchItem; + }; + }]); + +angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/accordion/accordion-group.html", + "<div class=\"panel {{panelClass || 'panel-default'}}\">\n" + + " <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" + + " <h4 class=\"panel-title\">\n" + + " <a href tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" + + " </h4>\n" + + " </div>\n" + + " <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" + + " <div class=\"panel-body\" ng-transclude></div>\n" + + " </div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/accordion/accordion.html", + "<div class=\"panel-group\" ng-transclude></div>"); +}]); + +angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/alert/alert.html", + "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" + + " <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" + + " <span aria-hidden=\"true\">×</span>\n" + + " <span class=\"sr-only\">Close</span>\n" + + " </button>\n" + + " <div ng-transclude></div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/carousel/carousel.html", + "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" + + " <div class=\"carousel-inner\" ng-transclude></div>\n" + + " <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" + + " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" + + " <span class=\"sr-only\">previous</span>\n" + + " </a>\n" + + " <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" + + " <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" + + " <span class=\"sr-only\">next</span>\n" + + " </a>\n" + + " <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" + + " <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" + + " <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" + + " </li>\n" + + " </ol>\n" + + "</div>"); +}]); + +angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/carousel/slide.html", + "<div ng-class=\"{\n" + + " 'active': active\n" + + " }\" class=\"item text-center\" ng-transclude></div>\n" + + ""); +}]); + +angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/datepicker/datepicker.html", + "<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" + + " <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" + + " <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" + + " <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" + + "</div>"); +}]); + +angular.module("template/datepicker/day.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/datepicker/day.html", + "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + + " <thead>\n" + + " <tr>\n" + + " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + + " <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + + " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + + " </tr>\n" + + " <tr>\n" + + " <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" + + " <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" + + " </tr>\n" + + " </thead>\n" + + " <tbody>\n" + + " <tr ng-repeat=\"row in rows track by $index\">\n" + + " <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" + + " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" + + " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + + " </td>\n" + + " </tr>\n" + + " </tbody>\n" + + "</table>\n" + + ""); +}]); + +angular.module("template/datepicker/month.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/datepicker/month.html", + "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + + " <thead>\n" + + " <tr>\n" + + " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + + " <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + + " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + + " </tr>\n" + + " </thead>\n" + + " <tbody>\n" + + " <tr ng-repeat=\"row in rows track by $index\">\n" + + " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" + + " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + + " </td>\n" + + " </tr>\n" + + " </tbody>\n" + + "</table>\n" + + ""); +}]); + +angular.module("template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/datepicker/popup.html", + "<ul class=\"dropdown-menu\" dropdown-nested ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" + + " <li ng-transclude></li>\n" + + " <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" + + " <span class=\"btn-group pull-left\">\n" + + " <button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" + + " <button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" + + " </span>\n" + + " <button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText('close') }}</button>\n" + + " </li>\n" + + "</ul>\n" + + ""); +}]); + +angular.module("template/datepicker/year.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/datepicker/year.html", + "<table role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" + + " <thead>\n" + + " <tr>\n" + + " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + + " <th colspan=\"3\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + + " <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + + " </tr>\n" + + " </thead>\n" + + " <tbody>\n" + + " <tr ng-repeat=\"row in rows track by $index\">\n" + + " <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{::dt.uid}}\" ng-class=\"::dt.customClass\">\n" + + " <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" + + " </td>\n" + + " </tr>\n" + + " </tbody>\n" + + "</table>\n" + + ""); +}]); + +angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/modal/backdrop.html", + "<div uib-modal-animation-class=\"fade\"\n" + + " modal-in-class=\"in\"\n" + + " ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" + + "></div>\n" + + ""); +}]); + +angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/modal/window.html", + "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" + + " uib-modal-animation-class=\"fade\"\n" + + " modal-in-class=\"in\"\n" + + " ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" + + " <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/pagination/pager.html", + "<ul class=\"pager\">\n" + + " <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" + + " <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" + + "</ul>\n" + + ""); +}]); + +angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/pagination/pagination.html", + "<ul class=\"pagination\">\n" + + " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" + + " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" + + " <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" + + " <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" + + " <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" + + "</ul>\n" + + ""); +}]); + +angular.module("template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tooltip/tooltip-html-popup.html", + "<div\n" + + " tooltip-animation-class=\"fade\"\n" + + " uib-tooltip-classes\n" + + " ng-class=\"{ in: isOpen() }\">\n" + + " <div class=\"tooltip-arrow\"></div>\n" + + " <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tooltip/tooltip-popup.html", + "<div\n" + + " tooltip-animation-class=\"fade\"\n" + + " uib-tooltip-classes\n" + + " ng-class=\"{ in: isOpen() }\">\n" + + " <div class=\"tooltip-arrow\"></div>\n" + + " <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tooltip/tooltip-template-popup.html", + "<div\n" + + " tooltip-animation-class=\"fade\"\n" + + " uib-tooltip-classes\n" + + " ng-class=\"{ in: isOpen() }\">\n" + + " <div class=\"tooltip-arrow\"></div>\n" + + " <div class=\"tooltip-inner\"\n" + + " uib-tooltip-template-transclude=\"contentExp()\"\n" + + " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/popover/popover-html.html", + "<div tooltip-animation-class=\"fade\"\n" + + " uib-tooltip-classes\n" + + " ng-class=\"{ in: isOpen() }\">\n" + + " <div class=\"arrow\"></div>\n" + + "\n" + + " <div class=\"popover-inner\">\n" + + " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" + + " <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" + + " </div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/popover/popover-template.html", + "<div tooltip-animation-class=\"fade\"\n" + + " uib-tooltip-classes\n" + + " ng-class=\"{ in: isOpen() }\">\n" + + " <div class=\"arrow\"></div>\n" + + "\n" + + " <div class=\"popover-inner\">\n" + + " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" + + " <div class=\"popover-content\"\n" + + " uib-tooltip-template-transclude=\"contentExp()\"\n" + + " tooltip-template-transclude-scope=\"originScope()\"></div>\n" + + " </div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/popover/popover.html", + "<div tooltip-animation-class=\"fade\"\n" + + " uib-tooltip-classes\n" + + " ng-class=\"{ in: isOpen() }\">\n" + + " <div class=\"arrow\"></div>\n" + + "\n" + + " <div class=\"popover-inner\">\n" + + " <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" + + " <div class=\"popover-content\" ng-bind=\"content\"></div>\n" + + " </div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/progressbar/bar.html", + "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" style=\"min-width: 0;\" ng-transclude></div>\n" + + ""); +}]); + +angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/progressbar/progress.html", + "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>"); +}]); + +angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/progressbar/progressbar.html", + "<div class=\"progress\">\n" + + " <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" style=\"min-width: 0;\" ng-transclude></div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/rating/rating.html", + "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" + + " <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" + + " <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\" aria-valuetext=\"{{r.title}}\"></i>\n" + + "</span>\n" + + ""); +}]); + +angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tab.html", + "<li ng-class=\"{active: active, disabled: disabled}\">\n" + + " <a href ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</a>\n" + + "</li>\n" + + ""); +}]); + +angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tabset.html", + "<div>\n" + + " <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" + + " <div class=\"tab-content\">\n" + + " <div class=\"tab-pane\" \n" + + " ng-repeat=\"tab in tabs\" \n" + + " ng-class=\"{active: tab.active}\"\n" + + " uib-tab-content-transclude=\"tab\">\n" + + " </div>\n" + + " </div>\n" + + "</div>\n" + + ""); +}]); + +angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/timepicker/timepicker.html", + "<table>\n" + + " <tbody>\n" + + " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" + + " <td><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" + + " <td> </td>\n" + + " <td><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" + + " <td ng-show=\"showMeridian\"></td>\n" + + " </tr>\n" + + " <tr>\n" + + " <td class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" + + " <input style=\"width:50px;\" type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\">\n" + + " </td>\n" + + " <td>:</td>\n" + + " <td class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" + + " <input style=\"width:50px;\" type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\">\n" + + " </td>\n" + + " <td ng-show=\"showMeridian\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" + + " </tr>\n" + + " <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" + + " <td><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" + + " <td> </td>\n" + + " <td><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" + + " <td ng-show=\"showMeridian\"></td>\n" + + " </tr>\n" + + " </tbody>\n" + + "</table>\n" + + ""); +}]); + +angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/typeahead/typeahead-match.html", + "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" + + ""); +}]); + +angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/typeahead/typeahead-popup.html", + "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" + + " <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{::match.id}}\">\n" + + " <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" + + " </li>\n" + + "</ul>\n" + + ""); +}]); +!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js new file mode 100644 index 000000000..f729eda6e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js @@ -0,0 +1,12 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.14.3 - 2015-10-23 + * License: MIT + */ +angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-popup.html","template/tooltip/tooltip-popup.html","template/tooltip/tooltip-template-popup.html","template/popover/popover-html.html","template/popover/popover-template.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$injector",function(a,b){var c=b.has("$animateCss")?b.get("$animateCss"):null;return{link:function(b,d,e){function f(){d.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),c?c(d,{addClass:"in",easing:"ease",to:{height:d[0].scrollHeight+"px"}}).start()["finally"](g):a.addClass(d,"in",{to:{height:d[0].scrollHeight+"px"}}).then(g)}function g(){d.removeClass("collapsing").addClass("collapse").css({height:"auto"})}function h(){return d.hasClass("collapse")||d.hasClass("in")?(d.css({height:d[0].scrollHeight+"px"}).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),void(c?c(d,{removeClass:"in",to:{height:"0"}}).start()["finally"](i):a.removeClass(d,"in",{to:{height:"0"}}).then(i))):i()}function i(){d.css({height:"0"}),d.removeClass("collapsing").addClass("collapse")}b.$watch(e.uibCollapse,function(a){a?h():f()})}}}]),angular.module("ui.bootstrap.collapse").value("$collapseSuppressWarning",!1).directive("collapse",["$animate","$injector","$log","$collapseSuppressWarning",function(a,b,c,d){var e=b.has("$animateCss")?b.get("$animateCss"):null;return{link:function(b,f,g){function h(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{easing:"ease",to:{height:f[0].scrollHeight+"px"}}).start().done(i):a.animate(f,{},{height:f[0].scrollHeight+"px"}).then(i)}function i(){f.removeClass("collapsing").addClass("collapse in").css({height:"auto"})}function j(){return f.hasClass("collapse")||f.hasClass("in")?(f.css({height:f[0].scrollHeight+"px"}).removeClass("collapse in").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),void(e?e(f,{to:{height:"0"}}).start().done(k):a.animate(f,{},{height:"0"}).then(k))):k()}function k(){f.css({height:"0"}),f.removeClass("collapsing").addClass("collapse")}d||c.warn("collapse is now deprecated. Use uib-collapse instead."),b.$watch(g.collapse,function(a){a?j():h()})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion-group.html"},scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass,a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)}}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){return{require:["?^uibAccordionGroup","?^accordionGroup"],link:function(a,b,c,d){d=d[0]?d[0]:d[1],a.$watch(function(){return d[c.uibAccordionTransclude]},function(a){a&&(b.find("span").html(""),b.find("span").append(a))})}}}),angular.module("ui.bootstrap.accordion").value("$accordionSuppressWarning",!1).controller("AccordionController",["$scope","$attrs","$controller","$log","$accordionSuppressWarning",function(a,b,c,d,e){e||d.warn("AccordionController is now deprecated. Use UibAccordionController instead."),angular.extend(this,c("UibAccordionController",{$scope:a,$attrs:b}))}]).directive("accordion",["$log","$accordionSuppressWarning",function(a,b){return{restrict:"EA",controller:"AccordionController",controllerAs:"accordion",transclude:!0,replace:!1,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion.html"},link:function(){b||a.warn("accordion is now deprecated. Use uib-accordion instead.")}}}]).directive("accordionGroup",["$log","$accordionSuppressWarning",function(a,b){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion-group.html"},scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(c,d,e,f){b||a.warn("accordion-group is now deprecated. Use uib-accordion-group instead."),f.addGroup(c),c.openClass=e.openClass||"panel-open",c.panelClass=e.panelClass,c.$watch("isOpen",function(a){d.toggleClass(c.openClass,!!a),a&&f.closeOthers(c)}),c.toggleOpen=function(a){c.isDisabled||a&&32!==a.which||(c.isOpen=!c.isOpen)}}}}]).directive("accordionHeading",["$log","$accordionSuppressWarning",function(a,b){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(c,d,e,f,g){b||a.warn("accordion-heading is now deprecated. Use uib-accordion-heading instead."),f.setHeading(g(c,angular.noop))}}}]).directive("accordionTransclude",["$log","$accordionSuppressWarning",function(a,b){return{require:"^accordionGroup",link:function(c,d,e,f){b||a.warn("accordion-transclude is now deprecated. Use uib-accordion-transclude instead."),c.$watch(function(){return f[e.accordionTransclude]},function(a){a&&(d.find("span").html(""),d.find("span").append(a))})}}}]),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$attrs","$interpolate","$timeout",function(a,b,c,d){a.closeable=!!b.close;var e=angular.isDefined(b.dismissOnTimeout)?c(b.dismissOnTimeout)(a.$parent):null;e&&d(function(){a.close()},parseInt(e,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",templateUrl:function(a,b){return b.templateUrl||"template/alert/alert.html"},transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}),angular.module("ui.bootstrap.alert").value("$alertSuppressWarning",!1).controller("AlertController",["$scope","$attrs","$controller","$log","$alertSuppressWarning",function(a,b,c,d,e){e||d.warn("AlertController is now deprecated. Use UibAlertController instead."),angular.extend(this,c("UibAlertController",{$scope:a,$attrs:b}))}]).directive("alert",["$log","$alertSuppressWarning",function(a,b){return{controller:"AlertController",controllerAs:"alert",templateUrl:function(a,b){return b.templateUrl||"template/alert/alert.html"},transclude:!0,replace:!0,scope:{type:"@",close:"&"},link:function(){b||a.warn("alert is now deprecated. Use uib-alert instead.")}}}]),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",function(){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(a,b,c,d){var e=d[0],f=d[1];b.find("input").css({display:"none"}),f.$render=function(){b.toggleClass(e.activeClass,angular.equals(f.$modelValue,a.$eval(c.uibBtnRadio)))},b.on(e.toggleEvent,function(){if(!c.disabled){var d=b.hasClass(e.activeClass);(!d||angular.isDefined(c.uncheckable))&&a.$apply(function(){f.$setViewValue(d?null:a.$eval(c.uibBtnRadio)),f.$render()})}})}}}).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.buttons").value("$buttonsSuppressWarning",!1).controller("ButtonsController",["$controller","$log","$buttonsSuppressWarning",function(a,b,c){c||b.warn("ButtonsController is now deprecated. Use UibButtonsController instead."),angular.extend(this,a("UibButtonsController"))}]).directive("btnRadio",["$log","$buttonsSuppressWarning",function(a,b){return{require:["btnRadio","ngModel"],controller:"ButtonsController",controllerAs:"buttons",link:function(c,d,e,f){b||a.warn("btn-radio is now deprecated. Use uib-btn-radio instead.");var g=f[0],h=f[1];d.find("input").css({display:"none"}),h.$render=function(){d.toggleClass(g.activeClass,angular.equals(h.$modelValue,c.$eval(e.btnRadio)))},d.bind(g.toggleEvent,function(){if(!e.disabled){var a=d.hasClass(g.activeClass);(!a||angular.isDefined(e.uncheckable))&&c.$apply(function(){h.$setViewValue(a?null:c.$eval(e.btnRadio)),h.$render()})}})}}}]).directive("btnCheckbox",["$document","$log","$buttonsSuppressWarning",function(a,b,c){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",controllerAs:"button",link:function(d,e,f,g){function h(){return j(f.btnCheckboxTrue,!0)}function i(){return j(f.btnCheckboxFalse,!1)}function j(a,b){var c=d.$eval(a);return angular.isDefined(c)?c:b}c||b.warn("btn-checkbox is now deprecated. Use uib-btn-checkbox instead.");var k=g[0],l=g[1];e.find("input").css({display:"none"}),l.$render=function(){e.toggleClass(k.activeClass,angular.equals(l.$modelValue,h()))},e.bind(k.toggleEvent,function(){f.disabled||d.$apply(function(){l.$setViewValue(e.hasClass(k.activeClass)?i():h()),l.$render()})}),e.on("keypress",function(b){f.disabled||32!==b.which||a[0].activeElement!==e[0]||d.$apply(function(){l.$setViewValue(e.hasClass(k.activeClass)?i():h()),l.$render()})})}}}]),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$animate",function(a,b,c,d){function e(b,c,e){s||(angular.extend(b,{direction:e,active:!0}),angular.extend(m.currentSlide||{},{direction:e,active:!1}),d.enabled()&&!a.noTransition&&!a.$currentTransition&&b.$element&&m.slides.length>1&&(b.$element.data(q,b.direction),m.currentSlide&&m.currentSlide.$element&&m.currentSlide.$element.data(q,b.direction),a.$currentTransition=!0,o?d.on("addClass",b.$element,function(b,c){"close"===c&&(a.$currentTransition=null,d.off("addClass",b))}):b.$element.one("$animate:close",function(){a.$currentTransition=null})),m.currentSlide=b,r=c,g())}function f(a){if(angular.isUndefined(n[a].index))return n[a];var b;n.length;for(b=0;b<n.length;++b)if(n[b].index==a)return n[b]}function g(){h();var b=+a.interval;!isNaN(b)&&b>0&&(k=c(i,b))}function h(){k&&(c.cancel(k),k=null)}function i(){var b=+a.interval;l&&!isNaN(b)&&b>0&&n.length?a.next():a.pause()}function j(b){b.length||(a.$currentTransition=null)}var k,l,m=this,n=m.slides=a.slides=[],o=angular.version.minor>=4,p="uib-noTransition",q="uib-slideDirection",r=-1;m.currentSlide=null;var s=!1;m.select=a.select=function(b,c){var d=a.indexOfSlide(b);void 0===c&&(c=d>m.getCurrentIndex()?"next":"prev"),b&&b!==m.currentSlide&&!a.$currentTransition&&e(b,d,c)},a.$on("$destroy",function(){s=!0}),m.getCurrentIndex=function(){return m.currentSlide&&angular.isDefined(m.currentSlide.index)?+m.currentSlide.index:r},a.indexOfSlide=function(a){return angular.isDefined(a.index)?+a.index:n.indexOf(a)},a.next=function(){var b=(m.getCurrentIndex()+1)%n.length;return 0===b&&a.noWrap()?void a.pause():m.select(f(b),"next")},a.prev=function(){var b=m.getCurrentIndex()-1<0?n.length-1:m.getCurrentIndex()-1;return a.noWrap()&&b===n.length-1?void a.pause():m.select(f(b),"prev")},a.isActive=function(a){return m.currentSlide===a},a.$watch("interval",g),a.$watchCollection("slides",j),a.$on("$destroy",h),a.play=function(){l||(l=!0,g())},a.pause=function(){a.noPause||(l=!1,h())},m.addSlide=function(b,c){b.$element=c,n.push(b),1===n.length||b.active?(m.select(n[n.length-1]),1===n.length&&a.play()):b.active=!1},m.removeSlide=function(a){angular.isDefined(a.index)&&n.sort(function(a,b){return+a.index>+b.index});var b=n.indexOf(a);n.splice(b,1),n.length>0&&a.active?b>=n.length?m.select(n[b-1]):m.select(n[b]):r>b&&r--,0===n.length&&(m.currentSlide=null)},a.$watch("noTransition",function(a){b.data(p,a)})}]).directive("uibCarousel",[function(){return{transclude:!0,replace:!0,controller:"UibCarouselController",controllerAs:"carousel",require:"carousel",templateUrl:function(a,b){return b.templateUrl||"template/carousel/carousel.html"},scope:{interval:"=",noTransition:"=",noPause:"=",noWrap:"&"}}}]).directive("uibSlide",function(){return{require:"^uibCarousel",restrict:"EA",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/carousel/slide.html"},scope:{active:"=?",actual:"=?",index:"=?"},link:function(a,b,c,d){d.addSlide(a,b),a.$on("$destroy",function(){d.removeSlide(a)}),a.$watch("active",function(b){b&&d.select(a)})}}}).animation(".item",["$injector","$animate",function(a,b){function c(a,b,c){a.removeClass(b),c&&c()}var d="uib-noTransition",e="uib-slideDirection",f=null;return a.has("$animateCss")&&(f=a.get("$animateCss")),{beforeAddClass:function(a,g,h){if("active"==g&&a.parent()&&a.parent().parent()&&!a.parent().parent().data(d)){var i=!1,j=a.data(e),k="next"==j?"left":"right",l=c.bind(this,a,k+" "+j,h);return a.addClass(j),f?f(a,{addClass:k}).start().done(l):b.addClass(a,k).then(function(){i||l(),h()}),function(){i=!0}}h()},beforeRemoveClass:function(a,g,h){if("active"===g&&a.parent()&&a.parent().parent()&&!a.parent().parent().data(d)){var i=!1,j=a.data(e),k="next"==j?"left":"right",l=c.bind(this,a,k,h);return f?f(a,{addClass:k}).start().done(l):b.addClass(a,k).then(function(){i||l(),h()}),function(){i=!0}}h()}}}]),angular.module("ui.bootstrap.carousel").value("$carouselSuppressWarning",!1).controller("CarouselController",["$scope","$element","$controller","$log","$carouselSuppressWarning",function(a,b,c,d,e){e||d.warn("CarouselController is now deprecated. Use UibCarouselController instead."),angular.extend(this,c("UibCarouselController",{$scope:a,$element:b}))}]).directive("carousel",["$log","$carouselSuppressWarning",function(a,b){return{transclude:!0,replace:!0,controller:"CarouselController",controllerAs:"carousel",require:"carousel",templateUrl:function(a,b){return b.templateUrl||"template/carousel/carousel.html"},scope:{interval:"=",noTransition:"=",noPause:"=",noWrap:"&"},link:function(){b||a.warn("carousel is now deprecated. Use uib-carousel instead.")}}}]).directive("slide",["$log","$carouselSuppressWarning",function(a,b){return{require:"^carousel",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/carousel/slide.html"},scope:{active:"=?",actual:"=?",index:"=?"},link:function(c,d,e,f){b||a.warn("slide is now deprecated. Use uib-slide instead."),f.addSlide(c,d),c.$on("$destroy",function(){f.removeSlide(c)}),c.$watch("active",function(a){a&&f.select(c)})}}}]),angular.module("ui.bootstrap.dateparser",[]).service("uibDateParser",["$log","$locale","orderByFilter",function(a,b,c){function d(a){var b=[],d=a.split("");return angular.forEach(g,function(c,e){var f=a.indexOf(e);if(f>-1){a=a.split(""),d[f]="("+c.regex+")",a[f]="$";for(var g=f+1,h=f+e.length;h>g;g++)d[g]="",a[g]="$";a=a.join(""),b.push({index:f,apply:c.apply})}}),{regex:new RegExp("^"+d.join("")+"$"),map:c(b,"index")}}function e(a,b,c){return 1>c?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}var f,g,h=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){f=b.id,this.parsers={},g={yyyy:{regex:"\\d{4}",apply:function(a){this.year=+a}},yy:{regex:"\\d{2}",apply:function(a){this.year=+a+2e3}},y:{regex:"\\d{1,4}",apply:function(a){this.year=+a}},MMMM:{regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)}},MMM:{regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)}},MM:{regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1}},M:{regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1}},dd:{regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a}},d:{regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a}},EEEE:{regex:b.DATETIME_FORMATS.DAY.join("|")},EEE:{regex:b.DATETIME_FORMATS.SHORTDAY.join("|")},HH:{regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a}},hh:{regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a}},H:{regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a}},h:{regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a}},mm:{regex:"[0-5][0-9]",apply:function(a){this.minutes=+a}},m:{regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a}},sss:{regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a}},ss:{regex:"[0-5][0-9]",apply:function(a){this.seconds=+a}},s:{regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a}},a:{regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)}}}},this.init(),this.parse=function(c,g,i){if(!angular.isString(c)||!g)return c;g=b.DATETIME_FORMATS[g]||g,g=g.replace(h,"\\$&"),b.id!==f&&this.init(),this.parsers[g]||(this.parsers[g]=d(g));var j=this.parsers[g],k=j.regex,l=j.map,m=c.match(k);if(m&&m.length){var n,o;angular.isDate(i)&&!isNaN(i.getTime())?n={year:i.getFullYear(),month:i.getMonth(),date:i.getDate(),hours:i.getHours(),minutes:i.getMinutes(),seconds:i.getSeconds(),milliseconds:i.getMilliseconds()}:(i&&a.warn("dateparser:","baseDate is not a valid date"),n={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var p=1,q=m.length;q>p;p++){var r=l[p-1];r.apply&&r.apply.call(n,m[p])}return e(n.year,n.month,n.date)&&(angular.isDate(i)&&!isNaN(i.getTime())?(o=new Date(i),o.setFullYear(n.year,n.month,n.date,n.hours,n.minutes,n.seconds,n.milliseconds||0)):o=new Date(n.year,n.month,n.date,n.hours,n.minutes,n.seconds,n.milliseconds||0)),o}}}]),angular.module("ui.bootstrap.dateparser").value("$dateParserSuppressWarning",!1).service("dateParser",["$log","$dateParserSuppressWarning","uibDateParser",function(a,b,c){b||a.warn("dateParser is now deprecated. Use uibDateParser instead."),angular.extend(this,c)}]),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){function c(a,c){return a.currentStyle?a.currentStyle[c]:b.getComputedStyle?b.getComputedStyle(a)[c]:a.style[c]}function d(a){return"static"===(c(a,"position")||"static")}var e=function(b){for(var c=a[0],e=b.offsetParent||c;e&&e!==c&&d(e);)e=e.offsetParent;return e||c};return{position:function(b){var c=this.offset(b),d={top:0,left:0},f=e(b[0]);f!=a[0]&&(d=this.offset(angular.element(f)),d.top+=f.clientTop-f.scrollTop,d.left+=f.clientLeft-f.scrollLeft);var g=b[0].getBoundingClientRect();return{width:g.width||b.prop("offsetWidth"),height:g.height||b.prop("offsetHeight"),top:c.top-d.top,left:c.left-d.left}},offset:function(c){var d=c[0].getBoundingClientRect();return{width:d.width||c.prop("offsetWidth"),height:d.height||c.prop("offsetHeight"),top:d.top+(b.pageYOffset||a[0].documentElement.scrollTop),left:d.left+(b.pageXOffset||a[0].documentElement.scrollLeft)}},positionElements:function(a,b,c,d){var e,f,g,h,i=c.split("-"),j=i[0],k=i[1]||"center";e=d?this.offset(a):this.position(a),f=b.prop("offsetWidth"),g=b.prop("offsetHeight");var l={center:function(){return e.left+e.width/2-f/2},left:function(){return e.left},right:function(){return e.left+e.width}},m={center:function(){return e.top+e.height/2-g/2},top:function(){return e.top},bottom:function(){return e.top+e.height}};switch(j){case"right":h={top:m[k](),left:l[j]()};break;case"left":h={top:m[k](),left:e.left-f};break;case"bottom":h={top:m[j](),left:l[k]()};break;default:h={top:e.top-g,left:l[k]()}}return h}}}]),angular.module("ui.bootstrap.position").value("$positionSuppressWarning",!1).service("$position",["$log","$positionSuppressWarning","$uibPosition",function(a,b,c){b||a.warn("$position is now deprecated. Use $uibPosition instead."),angular.extend(this,c)}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.position"]).value("$datepickerSuppressError",!1).constant("uibDatepickerConfig",{formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",datepickerMode:"day",minMode:"day",maxMode:"year",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null,shortcutPropagation:!1}).controller("UibDatepickerController",["$scope","$attrs","$parse","$interpolate","$log","dateFilter","uibDatepickerConfig","$datepickerSuppressError",function(a,b,c,d,e,f,g,h){var i=this,j={$setViewValue:angular.noop};this.modes=["day","month","year"],angular.forEach(["formatDay","formatMonth","formatYear","formatDayHeader","formatDayTitle","formatMonthTitle","showWeeks","startingDay","yearRange","shortcutPropagation"],function(c,e){i[c]=angular.isDefined(b[c])?6>e?d(b[c])(a.$parent):a.$parent.$eval(b[c]):g[c]}),angular.forEach(["minDate","maxDate"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(a){i[d]=a?new Date(a):null,i.refreshView()}):i[d]=g[d]?new Date(g[d]):null}),angular.forEach(["minMode","maxMode"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(c){i[d]=angular.isDefined(c)?c:b[d],a[d]=i[d],("minMode"==d&&i.modes.indexOf(a.datepickerMode)<i.modes.indexOf(i[d])||"maxMode"==d&&i.modes.indexOf(a.datepickerMode)>i.modes.indexOf(i[d]))&&(a.datepickerMode=i[d])}):(i[d]=g[d]||null,a[d]=i[d])}),a.datepickerMode=a.datepickerMode||g.datepickerMode,a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),angular.isDefined(b.initDate)?(this.activeDate=a.$parent.$eval(b.initDate)||new Date,a.$parent.$watch(b.initDate,function(a){a&&(j.$isEmpty(j.$modelValue)||j.$invalid)&&(i.activeDate=a,i.refreshView())})):this.activeDate=new Date,a.isActive=function(b){return 0===i.compare(b.date,i.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(a){j=a,j.$render=function(){i.render()}},this.render=function(){if(j.$viewValue){var a=new Date(j.$viewValue),b=!isNaN(a);b?this.activeDate=a:h||e.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var a=j.$viewValue?new Date(j.$viewValue):null;j.$setValidity("dateDisabled",!a||this.element&&!this.isDisabled(a))}},this.createDateObject=function(a,b){var c=j.$viewValue?new Date(j.$viewValue):null;return{date:a,label:f(a,b),selected:c&&0===this.compare(a,c),disabled:this.isDisabled(a),current:0===this.compare(a,new Date),customClass:this.customClass(a)}},this.isDisabled=function(c){return this.minDate&&this.compare(c,this.minDate)<0||this.maxDate&&this.compare(c,this.maxDate)>0||b.dateDisabled&&a.dateDisabled({date:c,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===i.minMode){var c=j.$viewValue?new Date(j.$viewValue):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),j.$setViewValue(c),j.$render()}else i.activeDate=b,a.datepickerMode=i.modes[i.modes.indexOf(a.datepickerMode)-1]},a.move=function(a){var b=i.activeDate.getFullYear()+a*(i.step.years||0),c=i.activeDate.getMonth()+a*(i.step.months||0);i.activeDate.setFullYear(b,c,1),i.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===i.maxMode&&1===b||a.datepickerMode===i.minMode&&-1===b||(a.datepickerMode=i.modes[i.modes.indexOf(a.datepickerMode)+b])},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var k=function(){i.element[0].focus()};a.$on("uib:datepicker.focus",k),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey)if(b.preventDefault(),i.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(i.isDisabled(i.activeDate))return;a.select(i.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(i.handleKeyDown(c,b),i.refreshView()):a.toggleMode("up"===c?1:-1)}}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,3)},this.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth())-new Date(b.getFullYear(),b.getMonth())},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=3;else if("right"===a)c+=1;else if("down"===a)c+=3;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/e,10)*e+1}var e;this.element=b,this.yearpickerInit=function(){e=this.yearRange,this.step={years:e}},this._refreshView=function(){for(var b,c=new Array(e),f=0,g=d(this.activeDate.getFullYear());e>f;f++)b=new Date(this.activeDate),b.setFullYear(g+f,0,1),c[f]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+f});a.title=[c[0].label,c[e-1].label].join(" - "),a.rows=this.split(c,5)},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=5:"right"===a?c+=1:"down"===a?c+=5:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*this.step.years:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+e-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/datepicker.html"},scope:{datepickerMode:"=?",dateDisabled:"&",customClass:"&",shortcutPropagation:"&?"},require:["uibDatepicker","^ngModel"],controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/day.html"},require:["^?uibDatepicker","uibDaypicker","^?datepicker"],controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0]||d[2],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/month.html"},require:["^?uibDatepicker","uibMonthpicker","^?datepicker"],controller:"UibMonthpickerController",link:function(a,b,c,d){var e=d[0]||d[2],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/year.html"},require:["^?uibDatepicker","uibYearpicker","^?datepicker"],controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0]||d[2];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}).constant("uibDatepickerPopupConfig",{datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"template/datepicker/popup.html",datepickerTemplateUrl:"template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},currentText:"Today",clearText:"Clear",closeText:"Done",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0,onOpenFocus:!0}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$parse","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout",function(a,b,c,d,e,f,g,h,i,j,k,l){ +function m(a){return a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()})}function n(b){if(angular.isNumber(b)&&(b=new Date(b)),b){if(angular.isDate(b)&&!isNaN(b))return b;if(angular.isString(b)){var c=j.parse(b,r,a.date);return isNaN(c)?void 0:c}return void 0}return null}function o(a,b){var d=a||b;if(!c.ngRequired&&!d)return!0;if(angular.isNumber(d)&&(d=new Date(d)),d){if(angular.isDate(d)&&!isNaN(d))return!0;if(angular.isString(d)){var e=j.parse(d,r);return!isNaN(e)}return!1}return!0}function p(c){var d=A[0],e=b[0].contains(c.target),f=void 0!==d.contains&&d.contains(c.target);!a.isOpen||e||f||a.$apply(function(){a.isOpen=!1})}function q(c){27===c.which&&a.isOpen?(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!1}),b[0].focus()):40!==c.which||a.isOpen||(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!0}))}var r,s,t,u,v,w,x,y,z,A,B={},C=!1;a.watchData={},this.init=function(h){if(z=h,s=angular.isDefined(c.closeOnDateSelection)?a.$parent.$eval(c.closeOnDateSelection):k.closeOnDateSelection,t=angular.isDefined(c.datepickerAppendToBody)?a.$parent.$eval(c.datepickerAppendToBody):k.appendToBody,u=angular.isDefined(c.onOpenFocus)?a.$parent.$eval(c.onOpenFocus):k.onOpenFocus,v=angular.isDefined(c.datepickerPopupTemplateUrl)?c.datepickerPopupTemplateUrl:k.datepickerPopupTemplateUrl,w=angular.isDefined(c.datepickerTemplateUrl)?c.datepickerTemplateUrl:k.datepickerTemplateUrl,a.showButtonBar=angular.isDefined(c.showButtonBar)?a.$parent.$eval(c.showButtonBar):k.showButtonBar,k.html5Types[c.type]?(r=k.html5Types[c.type],C=!0):(r=c.datepickerPopup||c.uibDatepickerPopup||k.datepickerPopup,c.$observe("uibDatepickerPopup",function(a,b){var c=a||k.datepickerPopup;if(c!==r&&(r=c,z.$modelValue=null,!r))throw new Error("uibDatepickerPopup must have a date format specified.")})),!r)throw new Error("uibDatepickerPopup must have a date format specified.");if(C&&c.datepickerPopup)throw new Error("HTML5 date input types do not support custom formats.");if(x=angular.element("<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>"),x.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":v}),y=angular.element(x.children()[0]),y.attr("template-url",w),C&&"month"===c.type&&(y.attr("datepicker-mode",'"month"'),y.attr("min-mode","month")),c.datepickerOptions){var l=a.$parent.$eval(c.datepickerOptions);l&&l.initDate&&(a.initDate=l.initDate,y.attr("init-date","initDate"),delete l.initDate),angular.forEach(l,function(a,b){y.attr(m(b),a)})}angular.forEach(["minMode","maxMode","minDate","maxDate","datepickerMode","initDate","shortcutPropagation"],function(b){if(c[b]){var d=e(c[b]);if(a.$parent.$watch(d,function(c){a.watchData[b]=c,("minDate"===b||"maxDate"===b)&&(B[b]=new Date(c))}),y.attr(m(b),"watchData."+b),"datepickerMode"===b){var f=d.assign;a.$watch("watchData."+b,function(b,c){angular.isFunction(f)&&b!==c&&f(a.$parent,b)})}}}),c.dateDisabled&&y.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),c.showWeeks&&y.attr("show-weeks",c.showWeeks),c.customClass&&y.attr("custom-class","customClass({ date: date, mode: mode })"),C?z.$formatters.push(function(b){return a.date=b,b}):(z.$$parserName="date",z.$validators.date=o,z.$parsers.unshift(n),z.$formatters.push(function(b){return a.date=b,z.$isEmpty(b)?b:i(b,r)})),z.$viewChangeListeners.push(function(){a.date=j.parse(z.$viewValue,r,a.date)}),b.bind("keydown",q),A=d(x)(a),x.remove(),t?f.find("body").append(A):b.after(A),a.$on("$destroy",function(){a.isOpen===!0&&(g.$$phase||a.$apply(function(){a.isOpen=!1})),A.remove(),b.unbind("keydown",q),f.unbind("click",p)})},a.getText=function(b){return a[b+"Text"]||k[b+"Text"]},a.isDisabled=function(b){return"today"===b&&(b=new Date),a.watchData.minDate&&a.compare(b,B.minDate)<0||a.watchData.maxDate&&a.compare(b,B.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){angular.isDefined(c)&&(a.date=c);var d=a.date?i(a.date,r):null;b.val(d),z.$setViewValue(d),s&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(a.isOpen=!1,b[0].focus())},a.select=function(b){if("today"===b){var c=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(c.getFullYear(),c.getMonth(),c.getDate())):b=new Date(c.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(){a.isOpen=!1,b[0].focus()},a.$watch("isOpen",function(c){c?(a.position=t?h.offset(b):h.position(b),a.position.top=a.position.top+b.prop("offsetHeight"),l(function(){u&&a.$broadcast("uib:datepicker.focus"),f.bind("click",p)},0,!1)):f.unbind("click",p)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{isOpen:"=?",currentText:"@",clearText:"@",closeText:"@",dateDisabled:"&",customClass:"&"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/popup.html"}}}),angular.module("ui.bootstrap.datepicker").value("$datepickerSuppressWarning",!1).controller("DatepickerController",["$scope","$attrs","$parse","$interpolate","$log","dateFilter","uibDatepickerConfig","$datepickerSuppressError","$datepickerSuppressWarning",function(a,b,c,d,e,f,g,h,i){i||e.warn("DatepickerController is now deprecated. Use UibDatepickerController instead.");var j=this,k={$setViewValue:angular.noop};this.modes=["day","month","year"],angular.forEach(["formatDay","formatMonth","formatYear","formatDayHeader","formatDayTitle","formatMonthTitle","showWeeks","startingDay","yearRange","shortcutPropagation"],function(c,e){j[c]=angular.isDefined(b[c])?6>e?d(b[c])(a.$parent):a.$parent.$eval(b[c]):g[c]}),angular.forEach(["minDate","maxDate"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(a){j[d]=a?new Date(a):null,j.refreshView()}):j[d]=g[d]?new Date(g[d]):null}),angular.forEach(["minMode","maxMode"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(c){j[d]=angular.isDefined(c)?c:b[d],a[d]=j[d],("minMode"==d&&j.modes.indexOf(a.datepickerMode)<j.modes.indexOf(j[d])||"maxMode"==d&&j.modes.indexOf(a.datepickerMode)>j.modes.indexOf(j[d]))&&(a.datepickerMode=j[d])}):(j[d]=g[d]||null,a[d]=j[d])}),a.datepickerMode=a.datepickerMode||g.datepickerMode,a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),angular.isDefined(b.initDate)?(this.activeDate=a.$parent.$eval(b.initDate)||new Date,a.$parent.$watch(b.initDate,function(a){a&&(k.$isEmpty(k.$modelValue)||k.$invalid)&&(j.activeDate=a,j.refreshView())})):this.activeDate=new Date,a.isActive=function(b){return 0===j.compare(b.date,j.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(a){k=a,k.$render=function(){j.render()}},this.render=function(){if(k.$viewValue){var a=new Date(k.$viewValue),b=!isNaN(a);b?this.activeDate=a:h||e.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var a=k.$viewValue?new Date(k.$viewValue):null;k.$setValidity("dateDisabled",!a||this.element&&!this.isDisabled(a))}},this.createDateObject=function(a,b){var c=k.$viewValue?new Date(k.$viewValue):null;return{date:a,label:f(a,b),selected:c&&0===this.compare(a,c),disabled:this.isDisabled(a),current:0===this.compare(a,new Date),customClass:this.customClass(a)}},this.isDisabled=function(c){return this.minDate&&this.compare(c,this.minDate)<0||this.maxDate&&this.compare(c,this.maxDate)>0||b.dateDisabled&&a.dateDisabled({date:c,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},this.fixTimeZone=function(a){var b=a.getHours();a.setHours(23===b?b+2:0)},a.select=function(b){if(a.datepickerMode===j.minMode){var c=k.$viewValue?new Date(k.$viewValue):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),k.$setViewValue(c),k.$render()}else j.activeDate=b,a.datepickerMode=j.modes[j.modes.indexOf(a.datepickerMode)-1]},a.move=function(a){var b=j.activeDate.getFullYear()+a*(j.step.years||0),c=j.activeDate.getMonth()+a*(j.step.months||0);j.activeDate.setFullYear(b,c,1),j.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===j.maxMode&&1===b||a.datepickerMode===j.minMode&&-1===b||(a.datepickerMode=j.modes[j.modes.indexOf(a.datepickerMode)+b])},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var l=function(){j.element[0].focus()};a.$on("uib:datepicker.focus",l),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey)if(b.preventDefault(),j.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(j.isDisabled(j.activeDate))return;a.select(j.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(j.handleKeyDown(c,b),j.refreshView()):a.toggleMode("up"===c?1:-1)}}]).directive("datepicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/datepicker.html"},scope:{datepickerMode:"=?",dateDisabled:"&",customClass:"&",shortcutPropagation:"&?"},require:["datepicker","^ngModel"],controller:"DatepickerController",controllerAs:"datepicker",link:function(c,d,e,f){b||a.warn("datepicker is now deprecated. Use uib-datepicker instead.");var g=f[0],h=f[1];g.init(h)}}}]).directive("daypicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:"template/datepicker/day.html",require:["^datepicker","daypicker"],controller:"UibDaypickerController",link:function(c,d,e,f){b||a.warn("daypicker is now deprecated. Use uib-daypicker instead.");var g=f[0],h=f[1];h.init(g)}}}]).directive("monthpicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:"template/datepicker/month.html",require:["^datepicker","monthpicker"],controller:"UibMonthpickerController",link:function(c,d,e,f){b||a.warn("monthpicker is now deprecated. Use uib-monthpicker instead.");var g=f[0],h=f[1];h.init(g)}}}]).directive("yearpicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:"template/datepicker/year.html",require:["^datepicker","yearpicker"],controller:"UibYearpickerController",link:function(c,d,e,f){b||a.warn("yearpicker is now deprecated. Use uib-yearpicker instead.");var g=f[0];angular.extend(g,f[1]),g.yearpickerInit(),g.refreshView()}}}]).directive("datepickerPopup",["$log","$datepickerSuppressWarning",function(a,b){return{require:["ngModel","datepickerPopup"],controller:"UibDatepickerPopupController",scope:{isOpen:"=?",currentText:"@",clearText:"@",closeText:"@",dateDisabled:"&",customClass:"&"},link:function(c,d,e,f){b||a.warn("datepicker-popup is now deprecated. Use uib-datepicker-popup instead.");var g=f[0],h=f[1];h.init(g)}}}]).directive("datepickerPopupWrap",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/popup.html"},link:function(){b||a.warn("datepicker-popup-wrap is now deprecated. Use uib-datepicker-popup-wrap instead.")}}}]),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.position"]).constant("uibDropdownConfig",{openClass:"open"}).service("uibDropdownService",["$document","$rootScope",function(a,b){var c=null;this.open=function(b){c||(a.bind("click",d),a.bind("keydown",e)),c&&c!==b&&(c.isOpen=!1),c=b},this.close=function(b){c===b&&(c=null,a.unbind("click",d),a.unbind("keydown",e))};var d=function(a){if(c&&(!a||"disabled"!==c.getAutoClose())){var d=c.getToggleElement();if(!(a&&d&&d[0].contains(a.target))){var e=c.getDropdownElement();a&&"outsideClick"===c.getAutoClose()&&e&&e[0].contains(a.target)||(c.isOpen=!1,b.$$phase||c.$apply())}}},e=function(a){27===a.which?(c.focusToggleElement(),d()):c.isKeynavEnabled()&&/(38|40)/.test(a.which)&&c.isOpen&&(a.preventDefault(),a.stopPropagation(),c.focusDropdownEntry(a.which))}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){var l,m,n=this,o=a.$new(),p=e.openClass,q=angular.noop,r=c.onToggle?d(c.onToggle):angular.noop,s=!1,t=!1;b.addClass("dropdown"),this.init=function(){c.isOpen&&(m=d(c.isOpen),q=m.assign,a.$watch(m,function(a){o.isOpen=!!a})),s=angular.isDefined(c.dropdownAppendToBody),t=angular.isDefined(c.uibKeyboardNav),s&&n.dropdownMenu&&(i.find("body").append(n.dropdownMenu),b.on("$destroy",function(){n.dropdownMenu.remove()}))},this.toggle=function(a){return o.isOpen=arguments.length?!!a:!o.isOpen},this.isOpen=function(){return o.isOpen},o.getToggleElement=function(){return n.toggleElement},o.getAutoClose=function(){return c.autoClose||"always"},o.getElement=function(){return b},o.isKeynavEnabled=function(){return t},o.focusDropdownEntry=function(a){var c=n.dropdownMenu?angular.element(n.dropdownMenu).find("a"):angular.element(b).find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(n.selectedOption)?n.selectedOption=n.selectedOption===c.length-1?n.selectedOption:n.selectedOption+1:n.selectedOption=0;break;case 38:angular.isNumber(n.selectedOption)?n.selectedOption=0===n.selectedOption?0:n.selectedOption-1:n.selectedOption=c.length-1}c[n.selectedOption].focus()},o.getDropdownElement=function(){return n.dropdownMenu},o.focusToggleElement=function(){n.toggleElement&&n.toggleElement[0].focus()},o.$watch("isOpen",function(c,d){if(s&&n.dropdownMenu){var e=h.positionElements(b,n.dropdownMenu,"bottom-left",!0),i={top:e.top+"px",display:c?"block":"none"},m=n.dropdownMenu.hasClass("dropdown-menu-right");m?(i.left="auto",i.right=window.innerWidth-(e.left+b.prop("offsetWidth"))+"px"):(i.left=e.left+"px",i.right="auto"),n.dropdownMenu.css(i)}if(g[c?"addClass":"removeClass"](b,p).then(function(){angular.isDefined(c)&&c!==d&&r(a,{open:!!c})}),c)n.dropdownMenuTemplateUrl&&k(n.dropdownMenuTemplateUrl).then(function(a){l=o.$new(),j(a.trim())(l,function(a){var b=a;n.dropdownMenu.replaceWith(b),n.dropdownMenu=b})}),o.focusToggleElement(),f.open(o);else{if(n.dropdownMenuTemplateUrl){l&&l.$destroy();var t=angular.element('<ul class="dropdown-menu"></ul>');n.dropdownMenu.replaceWith(t),n.dropdownMenu=t}f.close(o),n.selectedOption=null}angular.isFunction(q)&&q(a,c)}),a.$on("$locationChangeSuccess",function(){"disabled"!==o.getAutoClose()&&(o.isOpen=!1)});var u=a.$on("$destroy",function(){o.$destroy()});o.$on("$destroy",u)}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"AC",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibKeyboardNav",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){b.bind("keydown",function(a){if(-1!==[38,40].indexOf(a.which)){a.preventDefault(),a.stopPropagation();var b=d.dropdownMenu.find("a");switch(a.which){case 40:angular.isNumber(d.selectedOption)?d.selectedOption=d.selectedOption===b.length-1?d.selectedOption:d.selectedOption+1:d.selectedOption=0;break;case 38:angular.isNumber(d.selectedOption)?d.selectedOption=0===d.selectedOption?0:d.selectedOption-1:d.selectedOption=b.length-1}b[d.selectedOption].focus()}})}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.bind("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.unbind("click",e)})}}}}),angular.module("ui.bootstrap.dropdown").value("$dropdownSuppressWarning",!1).service("dropdownService",["$log","$dropdownSuppressWarning","uibDropdownService",function(a,b,c){b||a.warn("dropdownService is now deprecated. Use uibDropdownService instead."),angular.extend(this,c)}]).controller("DropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest","$log","$dropdownSuppressWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m){m||l.warn("DropdownController is now deprecated. Use UibDropdownController instead.");var n,o,p=this,q=a.$new(),r=e.openClass,s=angular.noop,t=c.onToggle?d(c.onToggle):angular.noop,u=!1,v=!1;b.addClass("dropdown"),this.init=function(){c.isOpen&&(o=d(c.isOpen),s=o.assign,a.$watch(o,function(a){q.isOpen=!!a})),u=angular.isDefined(c.dropdownAppendToBody),v=angular.isDefined(c.uibKeyboardNav),u&&p.dropdownMenu&&(i.find("body").append(p.dropdownMenu),b.on("$destroy",function(){p.dropdownMenu.remove()}))},this.toggle=function(a){return q.isOpen=arguments.length?!!a:!q.isOpen},this.isOpen=function(){return q.isOpen},q.getToggleElement=function(){return p.toggleElement},q.getAutoClose=function(){return c.autoClose||"always"},q.getElement=function(){return b},q.isKeynavEnabled=function(){return v},q.focusDropdownEntry=function(a){var c=p.dropdownMenu?angular.element(p.dropdownMenu).find("a"):angular.element(b).find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(p.selectedOption)?p.selectedOption=p.selectedOption===c.length-1?p.selectedOption:p.selectedOption+1:p.selectedOption=0;break;case 38:angular.isNumber(p.selectedOption)?p.selectedOption=0===p.selectedOption?0:p.selectedOption-1:p.selectedOption=c.length-1}c[p.selectedOption].focus()},q.getDropdownElement=function(){return p.dropdownMenu},q.focusToggleElement=function(){p.toggleElement&&p.toggleElement[0].focus()},q.$watch("isOpen",function(c,d){if(u&&p.dropdownMenu){var e=h.positionElements(b,p.dropdownMenu,"bottom-left",!0),i={top:e.top+"px",display:c?"block":"none"},l=p.dropdownMenu.hasClass("dropdown-menu-right");l?(i.left="auto",i.right=window.innerWidth-(e.left+b.prop("offsetWidth"))+"px"):(i.left=e.left+"px",i.right="auto"),p.dropdownMenu.css(i)}if(g[c?"addClass":"removeClass"](b,r).then(function(){angular.isDefined(c)&&c!==d&&t(a,{open:!!c})}),c)p.dropdownMenuTemplateUrl&&k(p.dropdownMenuTemplateUrl).then(function(a){n=q.$new(),j(a.trim())(n,function(a){var b=a;p.dropdownMenu.replaceWith(b),p.dropdownMenu=b})}),q.focusToggleElement(),f.open(q);else{if(p.dropdownMenuTemplateUrl){n&&n.$destroy();var m=angular.element('<ul class="dropdown-menu"></ul>');p.dropdownMenu.replaceWith(m),p.dropdownMenu=m}f.close(q),p.selectedOption=null}angular.isFunction(s)&&s(a,c)}),a.$on("$locationChangeSuccess",function(){"disabled"!==q.getAutoClose()&&(q.isOpen=!1)});var w=a.$on("$destroy",function(){q.$destroy()});q.$on("$destroy",w)}]).directive("dropdown",["$log","$dropdownSuppressWarning",function(a,b){return{controller:"DropdownController",link:function(c,d,e,f){b||a.warn("dropdown is now deprecated. Use uib-dropdown instead."),f.init()}}}]).directive("dropdownMenu",["$log","$dropdownSuppressWarning",function(a,b){return{restrict:"AC",require:"?^dropdown",link:function(c,d,e,f){if(f&&!angular.isDefined(e.dropdownNested)){b||a.warn("dropdown-menu is now deprecated. Use uib-dropdown-menu instead."),d.addClass("dropdown-menu");var g=e.templateUrl;g&&(f.dropdownMenuTemplateUrl=g),f.dropdownMenu||(f.dropdownMenu=d)}}}}]).directive("keyboardNav",["$log","$dropdownSuppressWarning",function(a,b){return{restrict:"A",require:"?^dropdown",link:function(c,d,e,f){b||a.warn("keyboard-nav is now deprecated. Use uib-keyboard-nav instead."),d.bind("keydown",function(a){if(-1!==[38,40].indexOf(a.which)){a.preventDefault(),a.stopPropagation();var b=f.dropdownMenu.find("a");switch(a.which){case 40:angular.isNumber(f.selectedOption)?f.selectedOption=f.selectedOption===b.length-1?f.selectedOption:f.selectedOption+1:f.selectedOption=0;break;case 38:angular.isNumber(f.selectedOption)?f.selectedOption=0===f.selectedOption?0:f.selectedOption-1:f.selectedOption=b.length-1}b[f.selectedOption].focus()}})}}}]).directive("dropdownToggle",["$log","$dropdownSuppressWarning",function(a,b){return{require:"?^dropdown",link:function(c,d,e,f){if(b||a.warn("dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead."),f){d.addClass("dropdown-toggle"),f.toggleElement=d;var g=function(a){a.preventDefault(),d.hasClass("disabled")||e.disabled||c.$apply(function(){f.toggle()})};d.bind("click",g),d.attr({"aria-haspopup":!0,"aria-expanded":!1}),c.$watch(f.isOpen,function(a){d.attr("aria-expanded",!!a)}),c.$on("$destroy",function(){d.unbind("click",g)})}}}}]),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b==a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b==a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.splice(a.length-1,1)[0]},length:function(){return a.length}}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.stackedMap"]).factory("$$multiMap",function(){return{createNew:function(){var a={};return{entries:function(){return Object.keys(a).map(function(b){return{key:b,value:a[b]}})},get:function(b){return a[b]},hasKey:function(b){return!!a[b]},keys:function(){return Object.keys(a)},put:function(b,c){a[b]||(a[b]=[]),a[b].push(c)},remove:function(b,c){var d=a[b];if(d){var e=d.indexOf(c);-1!==e&&d.splice(e,1),d.length||delete a[b]}}}}}}).directive("uibModalBackdrop",["$animate","$injector","$uibModalStack",function(a,b,c){function d(b,d,f){d.addClass("modal-backdrop"),f.modalInClass&&(e?e(d,{addClass:f.modalInClass}).start():a.addClass(d,f.modalInClass),b.$on(c.NOW_CLOSING_EVENT,function(b,c){var g=c();e?e(d,{removeClass:f.modalInClass}).start().then(g):a.removeClass(d,f.modalInClass).then(g)}))}var e=null;return b.has("$animateCss")&&(e=b.get("$animateCss")),{replace:!0,templateUrl:"template/modal/backdrop.html",compile:function(a,b){return a.addClass(b.backdropClass),d}}}]).directive("uibModalWindow",["$uibModalStack","$q","$animate","$injector",function(a,b,c,d){var e=null;return d.has("$animateCss")&&(e=d.get("$animateCss")),{scope:{index:"@"},replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/modal/window.html"},link:function(d,f,g){f.addClass(g.windowClass||""),f.addClass(g.windowTopClass||""),d.size=g.size,d.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!==c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))},f.on("click",d.close),d.$isRendered=!0;var h=b.defer();g.$observe("modalRender",function(a){"true"==a&&h.resolve()}),h.promise.then(function(){var h=null;g.modalInClass&&(h=e?e(f,{addClass:g.modalInClass}).start():c.addClass(f,g.modalInClass),d.$on(a.NOW_CLOSING_EVENT,function(a,b){var d=b();e?e(f,{removeClass:g.modalInClass}).start().then(d):c.removeClass(f,g.modalInClass).then(d)})),b.when(h).then(function(){var a=f[0].querySelector("[autofocus]");a?a.focus():f[0].focus()});var i=a.getTop();i&&a.modalRendered(i.key)})}}}]).directive("uibModalAnimationClass",function(){return{compile:function(a,b){b.modalAnimation&&a.addClass(b.uibModalAnimationClass)}}}).directive("uibModalTransclude",function(){return{link:function(a,b,c,d,e){e(a.$parent,function(a){b.empty(),b.append(a)})}}}).factory("$uibModalStack",["$animate","$timeout","$document","$compile","$rootScope","$q","$injector","$$multiMap","$$stackedMap",function(a,b,c,d,e,f,g,h,i){function j(){for(var a=-1,b=u.keys(),c=0;c<b.length;c++)u.get(b[c]).value.backdrop&&(a=c);return a}function k(a,b){var d=c.find("body").eq(0),e=u.get(a).value;u.remove(a),n(e.modalDomEl,e.modalScope,function(){var b=e.openedClass||t;v.remove(b,a),d.toggleClass(b,v.hasKey(b)),l(!0)}),m(),b&&b.focus?b.focus():d.focus()}function l(a){var b;u.length()>0&&(b=u.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function m(){if(q&&-1==j()){var a=r;n(q,r,function(){a=null}),q=void 0,r=void 0}}function n(b,c,d){function e(){e.done||(e.done=!0,p?p(b,{event:"leave"}).start().then(function(){b.remove()}):a.leave(b),c.$destroy(),d&&d())}var g,h=null,i=function(){return g||(g=f.defer(),h=g.promise),function(){g.resolve()}};return c.$broadcast(w.NOW_CLOSING_EVENT,i),f.when(h).then(e)}function o(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}var p=null;g.has("$animateCss")&&(p=g.get("$animateCss"));var q,r,s,t="modal-open",u=i.createNew(),v=h.createNew(),w={NOW_CLOSING_EVENT:"modal.stack.now-closing"},x=0,y="a[href], area[href], input:not([disabled]), button:not([disabled]),select:not([disabled]), textarea:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable=true]";return e.$watch(j,function(a){r&&(r.index=a)}),c.bind("keydown",function(a){if(a.isDefaultPrevented())return a;var b=u.top();if(b&&b.value.keyboard)switch(a.which){case 27:a.preventDefault(),e.$apply(function(){w.dismiss(b.key,"escape key press")});break;case 9:w.loadFocusElementList(b);var c=!1;a.shiftKey?w.isFocusInFirstItem(a)&&(c=w.focusLastFocusableElement()):w.isFocusInLastItem(a)&&(c=w.focusFirstFocusableElement()),c&&(a.preventDefault(),a.stopPropagation())}}),w.open=function(a,b){var f=c[0].activeElement,g=b.openedClass||t;l(!1),u.add(a,{deferred:b.deferred,renderDeferred:b.renderDeferred,modalScope:b.scope,backdrop:b.backdrop,keyboard:b.keyboard,openedClass:b.openedClass,windowTopClass:b.windowTopClass}),v.put(g,a);var h=c.find("body").eq(0),i=j();if(i>=0&&!q){r=e.$new(!0),r.index=i;var k=angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');k.attr("backdrop-class",b.backdropClass),b.animation&&k.attr("modal-animation","true"),q=d(k)(r),h.append(q)}var m=angular.element('<div uib-modal-window="modal-window"></div>');m.attr({"template-url":b.windowTemplateUrl,"window-class":b.windowClass,"window-top-class":b.windowTopClass,size:b.size,index:u.length()-1,animate:"animate"}).html(b.content),b.animation&&m.attr("modal-animation","true");var n=d(m)(b.scope);u.top().value.modalDomEl=n,u.top().value.modalOpener=f,h.append(n),h.addClass(g),w.clearFocusListCache()},w.close=function(a,b){var c=u.get(a);return c&&o(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),k(a,c.value.modalOpener),!0):!c},w.dismiss=function(a,b){var c=u.get(a);return c&&o(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),k(a,c.value.modalOpener),!0):!c},w.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},w.getTop=function(){return u.top()},w.modalRendered=function(a){var b=u.get(a);b&&b.value.renderDeferred.resolve()},w.focusFirstFocusableElement=function(){return s.length>0?(s[0].focus(),!0):!1},w.focusLastFocusableElement=function(){return s.length>0?(s[s.length-1].focus(),!0):!1},w.isFocusInFirstItem=function(a){return s.length>0?(a.target||a.srcElement)==s[0]:!1},w.isFocusInLastItem=function(a){return s.length>0?(a.target||a.srcElement)==s[s.length-1]:!1},w.clearFocusListCache=function(){s=[],x=0},w.loadFocusElementList=function(a){if((void 0===s||!s.length)&&a){var b=a.value.modalDomEl;b&&b.length&&(s=b[0].querySelectorAll(y))}},w}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$templateRequest","$controller","$uibModalStack","$modalSuppressWarning","$log",function(b,c,d,e,f,g,h,i){function j(a){return a.template?d.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}function k(a){var c=[];return angular.forEach(a,function(a){angular.isFunction(a)||angular.isArray(a)?c.push(d.when(b.invoke(a))):angular.isString(a)?c.push(d.when(b.get(a))):c.push(d.when(a))}),c}var l={},m=null;return l.getPromiseChain=function(){return m},l.open=function(b){function e(){return r}var l=d.defer(),n=d.defer(),o=d.defer(),p={result:l.promise,opened:n.promise,rendered:o.promise,close:function(a){return g.close(p,a)},dismiss:function(a){return g.dismiss(p,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var q,r=d.all([j(b)].concat(k(b.resolve)));return q=m=d.all([m]).then(e,e).then(function(a){var d=(b.scope||c).$new();d.$close=p.close,d.$dismiss=p.dismiss,d.$on("$destroy",function(){d.$$uibDestructionScheduled||d.$dismiss("$uibUnscheduledDestruction")});var e,j={},k=1;b.controller&&(j.$scope=d,j.$uibModalInstance=p,Object.defineProperty(j,"$modalInstance",{get:function(){return h||i.warn("$modalInstance is now deprecated. Use $uibModalInstance instead."),p}}),angular.forEach(b.resolve,function(b,c){j[c]=a[k++]}),e=f(b.controller,j),b.controllerAs&&(b.bindToController&&angular.extend(e,d),d[b.controllerAs]=e)),g.open(p,{scope:d,deferred:l,renderDeferred:o,content:a[0],animation:b.animation,backdrop:b.backdrop,keyboard:b.keyboard,backdropClass:b.backdropClass,windowTopClass:b.windowTopClass,windowClass:b.windowClass,windowTemplateUrl:b.windowTemplateUrl,size:b.size,openedClass:b.openedClass}),n.resolve(!0)},function(a){n.reject(a),l.reject(a)})["finally"](function(){m===q&&(m=null)}),p},l}]};return a}),angular.module("ui.bootstrap.modal").value("$modalSuppressWarning",!1).directive("modalBackdrop",["$animate","$injector","$modalStack","$log","$modalSuppressWarning",function(a,b,c,d,e){function f(b,f,h){e||d.warn("modal-backdrop is now deprecated. Use uib-modal-backdrop instead."),f.addClass("modal-backdrop"),h.modalInClass&&(g?g(f,{addClass:h.modalInClass}).start():a.addClass(f,h.modalInClass),b.$on(c.NOW_CLOSING_EVENT,function(b,c){var d=c();g?g(f,{removeClass:h.modalInClass}).start().then(d):a.removeClass(f,h.modalInClass).then(d)}))}var g=null;return b.has("$animateCss")&&(g=b.get("$animateCss")),{replace:!0,templateUrl:"template/modal/backdrop.html",compile:function(a,b){return a.addClass(b.backdropClass),f}}}]).directive("modalWindow",["$modalStack","$q","$animate","$injector","$log","$modalSuppressWarning",function(a,b,c,d,e,f){var g=null;return d.has("$animateCss")&&(g=d.get("$animateCss")),{scope:{index:"@"},replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/modal/window.html"},link:function(d,h,i){f||e.warn("modal-window is now deprecated. Use uib-modal-window instead."),h.addClass(i.windowClass||""),h.addClass(i.windowTopClass||""),d.size=i.size,d.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!==c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))},h.on("click",d.close),d.$isRendered=!0;var j=b.defer();i.$observe("modalRender",function(a){"true"==a&&j.resolve()}),j.promise.then(function(){var e=null;i.modalInClass&&(e=g?g(h,{addClass:i.modalInClass}).start():c.addClass(h,i.modalInClass),d.$on(a.NOW_CLOSING_EVENT,function(a,b){var d=b();g?g(h,{removeClass:i.modalInClass}).start().then(d):c.removeClass(h,i.modalInClass).then(d)})),b.when(e).then(function(){var a=h[0].querySelector("[autofocus]");a?a.focus():h[0].focus()});var f=a.getTop();f&&a.modalRendered(f.key)})}}}]).directive("modalAnimationClass",["$log","$modalSuppressWarning",function(a,b){return{compile:function(c,d){b||a.warn("modal-animation-class is now deprecated. Use uib-modal-animation-class instead."),d.modalAnimation&&c.addClass(d.modalAnimationClass)}}}]).directive("modalTransclude",["$log","$modalSuppressWarning",function(a,b){return{link:function(c,d,e,f,g){ +b||a.warn("modal-transclude is now deprecated. Use uib-modal-transclude instead."),g(c.$parent,function(a){d.empty(),d.append(a)})}}}]).service("$modalStack",["$animate","$timeout","$document","$compile","$rootScope","$q","$injector","$$multiMap","$$stackedMap","$uibModalStack","$log","$modalSuppressWarning",function(a,b,c,d,e,f,g,h,i,j,k,l){l||k.warn("$modalStack is now deprecated. Use $uibModalStack instead."),angular.extend(this,j)}]).provider("$modal",["$uibModalProvider",function(a){angular.extend(this,a),this.$get=["$injector","$log","$modalSuppressWarning",function(b,c,d){return d||c.warn("$modal is now deprecated. Use $uibModal instead."),b.invoke(a.$get)}]}]),angular.module("ui.bootstrap.pagination",[]).controller("UibPaginationController",["$scope","$attrs","$parse",function(a,b,c){var d=this,e={$setViewValue:angular.noop},f=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(g,h){e=g,this.config=h,e.$render=function(){d.render()},b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){d.itemsPerPage=parseInt(b,10),a.totalPages=d.calculateTotalPages()}):this.itemsPerPage=h.itemsPerPage,a.$watch("totalItems",function(){a.totalPages=d.calculateTotalPages()}),a.$watch("totalPages",function(b){f(a.$parent,b),a.page>b?a.selectPage(b):e.$render()})},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.render=function(){a.page=parseInt(e.$viewValue,10)||1},a.selectPage=function(b,c){c&&c.preventDefault();var d=!a.ngDisabled||!c;d&&a.page!==b&&b>0&&b<=a.totalPages&&(c&&c.target&&c.target.blur(),e.$setViewValue(b),e.$render())},a.getText=function(b){return a[b+"Text"]||d.config[b+"Text"]},a.noPrevious=function(){return 1===a.page},a.noNext=function(){return a.page===a.totalPages}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pagination.html"},replace:!0,link:function(c,d,e,f){function g(a,b,c){return{number:a,text:b,active:c}}function h(a,b){var c=[],d=1,e=b,f=angular.isDefined(k)&&b>k;f&&(l?(d=Math.max(a-Math.floor(k/2),1),e=d+k-1,e>b&&(e=b,d=e-k+1)):(d=(Math.ceil(a/k)-1)*k+1,e=Math.min(d+k-1,b)));for(var h=d;e>=h;h++){var i=g(h,h,h===a);c.push(i)}if(f&&!l){if(d>1){var j=g(d-1,"...",!1);c.unshift(j)}if(b>e){var m=g(e+1,"...",!1);c.push(m)}}return c}var i=f[0],j=f[1];if(j){var k=angular.isDefined(e.maxSize)?c.$parent.$eval(e.maxSize):b.maxSize,l=angular.isDefined(e.rotate)?c.$parent.$eval(e.rotate):b.rotate;c.boundaryLinks=angular.isDefined(e.boundaryLinks)?c.$parent.$eval(e.boundaryLinks):b.boundaryLinks,c.directionLinks=angular.isDefined(e.directionLinks)?c.$parent.$eval(e.directionLinks):b.directionLinks,i.init(j,b),e.maxSize&&c.$parent.$watch(a(e.maxSize),function(a){k=parseInt(a,10),i.render()});var m=i.render;i.render=function(){m(),c.page>0&&c.page<=c.totalPages&&(c.pages=h(c.page,c.totalPages))}}}}}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{restrict:"EA",scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pager.html"},replace:!0,link:function(b,c,d,e){var f=e[0],g=e[1];g&&(b.align=angular.isDefined(d.align)?b.$parent.$eval(d.align):a.align,f.init(g,a))}}}]),angular.module("ui.bootstrap.pagination").value("$paginationSuppressWarning",!1).controller("PaginationController",["$scope","$attrs","$parse","$log","$paginationSuppressWarning",function(a,b,c,d,e){e||d.warn("PaginationController is now deprecated. Use UibPaginationController instead.");var f=this,g={$setViewValue:angular.noop},h=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(d,e){g=d,this.config=e,g.$render=function(){f.render()},b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){f.itemsPerPage=parseInt(b,10),a.totalPages=f.calculateTotalPages()}):this.itemsPerPage=e.itemsPerPage,a.$watch("totalItems",function(){a.totalPages=f.calculateTotalPages()}),a.$watch("totalPages",function(b){h(a.$parent,b),a.page>b?a.selectPage(b):g.$render()})},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.render=function(){a.page=parseInt(g.$viewValue,10)||1},a.selectPage=function(b,c){c&&c.preventDefault();var d=!a.ngDisabled||!c;d&&a.page!==b&&b>0&&b<=a.totalPages&&(c&&c.target&&c.target.blur(),g.$setViewValue(b),g.$render())},a.getText=function(b){return a[b+"Text"]||f.config[b+"Text"]},a.noPrevious=function(){return 1===a.page},a.noNext=function(){return a.page===a.totalPages}}]).directive("pagination",["$parse","uibPaginationConfig","$log","$paginationSuppressWarning",function(a,b,c,d){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["pagination","?ngModel"],controller:"PaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pagination.html"},replace:!0,link:function(e,f,g,h){function i(a,b,c){return{number:a,text:b,active:c}}function j(a,b){var c=[],d=1,e=b,f=angular.isDefined(m)&&b>m;f&&(n?(d=Math.max(a-Math.floor(m/2),1),e=d+m-1,e>b&&(e=b,d=e-m+1)):(d=(Math.ceil(a/m)-1)*m+1,e=Math.min(d+m-1,b)));for(var g=d;e>=g;g++){var h=i(g,g,g===a);c.push(h)}if(f&&!n){if(d>1){var j=i(d-1,"...",!1);c.unshift(j)}if(b>e){var k=i(e+1,"...",!1);c.push(k)}}return c}d||c.warn("pagination is now deprecated. Use uib-pagination instead.");var k=h[0],l=h[1];if(l){var m=angular.isDefined(g.maxSize)?e.$parent.$eval(g.maxSize):b.maxSize,n=angular.isDefined(g.rotate)?e.$parent.$eval(g.rotate):b.rotate;e.boundaryLinks=angular.isDefined(g.boundaryLinks)?e.$parent.$eval(g.boundaryLinks):b.boundaryLinks,e.directionLinks=angular.isDefined(g.directionLinks)?e.$parent.$eval(g.directionLinks):b.directionLinks,k.init(l,b),g.maxSize&&e.$parent.$watch(a(g.maxSize),function(a){m=parseInt(a,10),k.render()});var o=k.render;k.render=function(){o(),e.page>0&&e.page<=e.totalPages&&(e.pages=j(e.page,e.totalPages))}}}}}]).directive("pager",["uibPagerConfig","$log","$paginationSuppressWarning",function(a,b,c){return{restrict:"EA",scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["pager","?ngModel"],controller:"PaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pager.html"},replace:!0,link:function(d,e,f,g){c||b.warn("pager is now deprecated. Use uib-pager instead.");var h=g[0],i=g[1];i&&(d.align=angular.isDefined(f.align)?d.$parent.$eval(f.align):a.align,h.init(i,a))}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){var n=m.createNew();return h.on("keypress",function(a){if(27===a.which){var b=n.top();b&&(b.value.close(),n.removeTop(),b=null)}}),function(e,k,m,o){function p(a){var b=(a||o.trigger||m).split(" "),d=b.map(function(a){return c[a]||a});return{show:b,hide:d}}o=angular.extend({},b,d,o);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="<div "+q+'-popup title="'+r+"title"+s+'" '+(o.useContentExp?'content-exp="contentExp()" ':'content="'+r+"content"+s+'" ')+'placement="'+r+"placement"+s+'" popup-class="'+r+"popupClass"+s+'" animation="animation" is-open="isOpen"origin-scope="origScope" style="visibility: hidden; display: block; top: -9999px; left: -9999px;"></div>';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){L.isOpen?q():m()}function m(){(!K||a.$eval(d[k+"Enable"]))&&(u(),x(),L.popupDelay?F||(F=g(r,L.popupDelay,!1)):r())}function q(){s(),L.popupCloseDelay?G||(G=g(t,L.popupCloseDelay,!1)):t()}function r(){return s(),u(),L.content?(v(),void L.$evalAsync(function(){L.isOpen=!0,y(!0),Q()})):angular.noop}function s(){F&&(g.cancel(F),F=null),H&&(g.cancel(H),H=null)}function t(){s(),u(),L&&L.$evalAsync(function(){L.isOpen=!1,y(!1),L.animation?E||(E=g(w,150,!1)):w()})}function u(){G&&(g.cancel(G),G=null),E&&(g.cancel(E),E=null)}function v(){C||(D=L.$new(),C=c(D,function(a){I?h.find("body").append(a):b.after(a)}),z())}function w(){A(),E=null,C&&(C.remove(),C=null),D&&(D.$destroy(),D=null)}function x(){L.title=d[k+"Title"],O?L.content=O(a):L.content=d[e],L.popupClass=d[k+"Class"],L.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:o.placement;var b=parseInt(d[k+"PopupDelay"],10),c=parseInt(d[k+"PopupCloseDelay"],10);L.popupDelay=isNaN(b)?o.popupDelay:b,L.popupCloseDelay=isNaN(c)?o.popupCloseDelay:c}function y(b){N&&angular.isFunction(N.assign)&&N.assign(a,b)}function z(){P.length=0,O?(P.push(a.$watch(O,function(a){L.content=a,!a&&L.isOpen&&t()})),P.push(D.$watch(function(){M||(M=!0,D.$$postDigest(function(){M=!1,L&&L.isOpen&&Q()}))}))):P.push(d.$observe(e,function(a){L.content=a,!a&&L.isOpen?t():Q()})),P.push(d.$observe(k+"Title",function(a){L.title=a,L.isOpen&&Q()})),P.push(d.$observe(k+"Placement",function(a){L.placement=a?a:o.placement,L.isOpen&&Q()}))}function A(){P.length&&(angular.forEach(P,function(a){a()}),P.length=0)}function B(){var a=d[k+"Trigger"];R(),J=p(a),"none"!==J.show&&J.show.forEach(function(a,c){a===J.hide[c]?b[0].addEventListener(a,j):a&&(b[0].addEventListener(a,m),J.hide[c].split(" ").forEach(function(a){b[0].addEventListener(a,q)})),b.on("keypress",function(a){27===a.which&&q()})})}var C,D,E,F,G,H,I=angular.isDefined(o.appendToBody)?o.appendToBody:!1,J=p(void 0),K=angular.isDefined(d[k+"Enable"]),L=a.$new(!0),M=!1,N=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,O=o.useContentExp?l(d[e]):!1,P=[],Q=function(){C&&C.html()&&(H||(H=g(function(){C.css({top:0,left:0});var a=i.positionElements(b,C,L.placement,I);a.top+="px",a.left+="px",a.visibility="visible",C.css(a),H=null},0,!1)))};L.origScope=a,L.isOpen=!1,n.add(L,{close:t}),L.contentExp=function(){return L.content},d.$observe("disabled",function(a){a&&s(),a&&L.isOpen&&t()}),N&&a.$watch(N,function(a){L&&!a===L.isOpen&&j()});var R=function(){J.show.forEach(function(a){b.unbind(a,m)}),J.hide.forEach(function(a){a.split(" ").forEach(function(a){b[0].removeEventListener(a,q)})})};B();var S=a.$eval(d[k+"Animation"]);L.animation=angular.isDefined(S)?!!S:o.animation;var T=a.$eval(d[k+"AppendToBody"]);I=angular.isDefined(T)?T:I,I&&a.$on("$locationChangeSuccess",function(){L.isOpen&&t()}),a.$on("$destroy",function(){s(),u(),R(),w(),n.remove(L),L=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",function(){return{restrict:"A",link:function(a,b,c){a.placement&&b.addClass(a.placement),a.popupClass&&b.addClass(a.popupClass),a.animation()&&b.addClass(c.tooltipAnimationClass)}}}).directive("uibTooltipPopup",function(){return{replace:!0,scope:{content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html",link:function(a,b){b.addClass("tooltip")}}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/tooltip/tooltip-template-popup.html",link:function(a,b){b.addClass("tooltip")}}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-popup.html",link:function(a,b){b.addClass("tooltip")}}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.tooltip").value("$tooltipSuppressWarning",!1).provider("$tooltip",["$uibTooltipProvider",function(a){angular.extend(this,a),this.$get=["$log","$tooltipSuppressWarning","$injector",function(b,c,d){return c||b.warn("$tooltip is now deprecated. Use $uibTooltip instead."),d.invoke(a.$get)}]}]).directive("tooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest","$log","$tooltipSuppressWarning",function(a,b,c,d,e,f){return{link:function(g,h,i){f||e.warn("tooltip-template-transclude is now deprecated. Use uib-tooltip-template-transclude instead.");var j,k,l,m=g.$eval(i.tooltipTemplateTranscludeScope),n=0,o=function(){k&&(k.remove(),k=null),j&&(j.$destroy(),j=null),l&&(a.leave(l).then(function(){k=null}),k=l,l=null)};g.$watch(b.parseAsResourceUrl(i.tooltipTemplateTransclude),function(b){var e=++n;b?(d(b,!0).then(function(d){if(e===n){var f=m.$new(),g=d,i=c(g)(f,function(b){o(),a.enter(b,h)});j=f,l=i,j.$emit("$includeContentLoaded",b)}},function(){e===n&&(o(),g.$emit("$includeContentError",b))}),g.$emit("$includeContentRequested",b)):o()}),g.$on("$destroy",o)}}}]).directive("tooltipClasses",["$log","$tooltipSuppressWarning",function(a,b){return{restrict:"A",link:function(c,d,e){b||a.warn("tooltip-classes is now deprecated. Use uib-tooltip-classes instead."),c.placement&&d.addClass(c.placement),c.popupClass&&d.addClass(c.popupClass),c.animation()&&d.addClass(e.tooltipAnimationClass)}}}]).directive("tooltipPopup",["$log","$tooltipSuppressWarning",function(a,b){return{replace:!0,scope:{content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html",link:function(c,d){b||a.warn("tooltip-popup is now deprecated. Use uib-tooltip-popup instead."),d.addClass("tooltip")}}}]).directive("tooltip",["$tooltip",function(a){return a("tooltip","tooltip","mouseenter")}]).directive("tooltipTemplatePopup",["$log","$tooltipSuppressWarning",function(a,b){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/tooltip/tooltip-template-popup.html",link:function(c,d){b||a.warn("tooltip-template-popup is now deprecated. Use uib-tooltip-template-popup instead."),d.addClass("tooltip")}}}]).directive("tooltipTemplate",["$tooltip",function(a){return a("tooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("tooltipHtmlPopup",["$log","$tooltipSuppressWarning",function(a,b){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-popup.html",link:function(c,d){b||a.warn("tooltip-html-popup is now deprecated. Use uib-tooltip-html-popup instead."),d.addClass("tooltip")}}}]).directive("tooltipHtml",["$tooltip",function(a){return a("tooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{replace:!0,scope:{title:"@",contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/popover/popover-template.html",link:function(a,b){b.addClass("popover")}}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",title:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover-html.html",link:function(a,b){b.addClass("popover")}}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{replace:!0,scope:{title:"@",content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html",link:function(a,b){b.addClass("popover")}}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.popover").value("$popoverSuppressWarning",!1).directive("popoverTemplatePopup",["$log","$popoverSuppressWarning",function(a,b){return{replace:!0,scope:{title:"@",contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/popover/popover-template.html",link:function(c,d){b||a.warn("popover-template-popup is now deprecated. Use uib-popover-template-popup instead."),d.addClass("popover")}}}]).directive("popoverTemplate",["$tooltip",function(a){return a("popoverTemplate","popover","click",{useContentExp:!0})}]).directive("popoverHtmlPopup",["$log","$popoverSuppressWarning",function(a,b){return{replace:!0,scope:{contentExp:"&",title:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover-html.html",link:function(c,d){b||a.warn("popover-html-popup is now deprecated. Use uib-popover-html-popup instead."),d.addClass("popover")}}}]).directive("popoverHtml",["$tooltip",function(a){return a("popoverHtml","popover","click",{useContentExp:!0})}]).directive("popoverPopup",["$log","$popoverSuppressWarning",function(a,b){return{replace:!0,scope:{title:"@",content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html",link:function(c,d){b||a.warn("popover-popup is now deprecated. Use uib-popover-popup instead."),d.addClass("popover")}}}]).directive("popover",["$tooltip",function(a){return a("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){var d=this,e=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=angular.isDefined(a.max)?a.max:c.max,this.addBar=function(b,c,f){e||c.css({transition:"none"}),this.bars.push(b),b.max=a.max,b.title=f&&angular.isDefined(f.title)?f.title:"progressbar",b.$watch("value",function(a){b.recalculatePercentage()}),b.recalculatePercentage=function(){var a=d.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);a>100&&(b.percent-=a-100)},b.$on("$destroy",function(){c=null,d.removeBar(b)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEach(function(a){a.recalculatePercentage()})},a.$watch("max",function(b){d.bars.forEach(function(b){b.max=a.max,b.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{max:"=?"},templateUrl:"template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",max:"=?",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.progressbar").value("$progressSuppressWarning",!1).controller("ProgressController",["$scope","$attrs","uibProgressConfig","$log","$progressSuppressWarning",function(a,b,c,d,e){e||d.warn("ProgressController is now deprecated. Use UibProgressController instead.");var f=this,g=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=angular.isDefined(a.max)?a.max:c.max,this.addBar=function(b,c,d){g||c.css({transition:"none"}),this.bars.push(b),b.max=a.max,b.title=d&&angular.isDefined(d.title)?d.title:"progressbar",b.$watch("value",function(a){b.recalculatePercentage()}),b.recalculatePercentage=function(){b.percent=+(100*b.value/b.max).toFixed(2);var a=f.bars.reduce(function(a,b){return a+b.percent},0);a>100&&(b.percent-=a-100)},b.$on("$destroy",function(){c=null,f.removeBar(b)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1)},a.$watch("max",function(b){f.bars.forEach(function(b){b.max=a.max,b.recalculatePercentage()})})}]).directive("progress",["$log","$progressSuppressWarning",function(a,b){return{replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{max:"=?",title:"@?"},templateUrl:"template/progressbar/progress.html",link:function(){b||a.warn("progress is now deprecated. Use uib-progress instead.")}}}]).directive("bar",["$log","$progressSuppressWarning",function(a,b){return{replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(c,d,e,f){b||a.warn("bar is now deprecated. Use uib-bar instead."),f.addBar(c,d)}}}]).directive("progressbar",["$log","$progressSuppressWarning",function(a,b){return{replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",max:"=?",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(c,d,e,f){b||a.warn("progressbar is now deprecated. Use uib-progressbar instead."),f.addBar(c,angular.element(d.children()[0]),{title:e.title})}}}]),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop};this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){!a.readonly&&b>=0&&b<=a.range.length&&(d.$setViewValue(d.$viewValue===b?0:b),d.$render())},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],scope:{readonly:"=?",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"template/rating/rating.html",replace:!0,link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.rating").value("$ratingSuppressWarning",!1).controller("RatingController",["$scope","$attrs","$controller","$log","$ratingSuppressWarning",function(a,b,c,d,e){e||d.warn("RatingController is now deprecated. Use UibRatingController instead."),angular.extend(this,c("UibRatingController",{$scope:a,$attrs:b}))}]).directive("rating",["$log","$ratingSuppressWarning",function(a,b){return{require:["rating","ngModel"],scope:{readonly:"=?",onHover:"&",onLeave:"&"},controller:"RatingController",templateUrl:"template/rating/rating.html",replace:!0,link:function(c,d,e,f){b||a.warn("rating is now deprecated. Use uib-rating instead.");var g=f[0],h=f[1];g.init(h)}}}]),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){var b=this,c=b.tabs=a.tabs=[];b.select=function(a){angular.forEach(c,function(b){b.active&&b!==a&&(b.active=!1,b.onDeselect(),a.selectCalled=!1)}),a.active=!0,a.selectCalled||(a.onSelect(),a.selectCalled=!0)},b.addTab=function(a){c.push(a),1===c.length&&a.active!==!1?a.active=!0:a.active?b.select(a):a.active=!1},b.removeTab=function(a){var e=c.indexOf(a);if(a.active&&c.length>1&&!d){var f=e==c.length-1?e-1:e+1;b.select(c[f])}c.splice(e,1)};var d;a.$on("$destroy",function(){d=!0})}]).directive("uibTabset",function(){return{restrict:"EA",transclude:!0,replace:!0,scope:{type:"@"},controller:"UibTabsetController",templateUrl:"template/tabs/tabset.html",link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1}}}).directive("uibTab",["$parse",function(a){return{require:"^uibTabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{active:"=?",heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},link:function(b,c,d,e,f){b.$watch("active",function(a){a&&e.select(b)}),b.disabled=!1,d.disable&&b.$parent.$watch(a(d.disable),function(a){b.disabled=!!a}),b.select=function(){b.disabled||(b.active=!0)},e.addTab(b),b.$on("$destroy",function(){e.removeTab(b)}),b.$transcludeFn=f}}}]).directive("uibTabHeadingTransclude",function(){return{restrict:"A",require:["?^uibTab","?^tab"],link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}).directive("uibTabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||a.hasAttribute("x-tab-heading")||a.hasAttribute("uib-tab-heading")||a.hasAttribute("data-uib-tab-heading")||a.hasAttribute("x-uib-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase()||"x-tab-heading"===a.tagName.toLowerCase()||"uib-tab-heading"===a.tagName.toLowerCase()||"data-uib-tab-heading"===a.tagName.toLowerCase()||"x-uib-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:["?^uibTabset","?^tabset"],link:function(b,c,d){var e=b.$eval(d.uibTabContentTransclude);e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("ui.bootstrap.tabs").value("$tabsSuppressWarning",!1).controller("TabsetController",["$scope","$controller","$log","$tabsSuppressWarning",function(a,b,c,d){d||c.warn("TabsetController is now deprecated. Use UibTabsetController instead."),angular.extend(this,b("UibTabsetController",{$scope:a}))}]).directive("tabset",["$log","$tabsSuppressWarning",function(a,b){return{restrict:"EA",transclude:!0,replace:!0,scope:{type:"@"},controller:"TabsetController",templateUrl:"template/tabs/tabset.html",link:function(c,d,e){b||a.warn("tabset is now deprecated. Use uib-tabset instead."),c.vertical=angular.isDefined(e.vertical)?c.$parent.$eval(e.vertical):!1,c.justified=angular.isDefined(e.justified)?c.$parent.$eval(e.justified):!1}}}]).directive("tab",["$parse","$log","$tabsSuppressWarning",function(a,b,c){return{require:"^tabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{active:"=?",heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},link:function(d,e,f,g,h){c||b.warn("tab is now deprecated. Use uib-tab instead."),d.$watch("active",function(a){a&&g.select(d)}),d.disabled=!1,f.disable&&d.$parent.$watch(a(f.disable),function(a){d.disabled=!!a}),d.select=function(){d.disabled||(d.active=!0)},g.addTab(d),d.$on("$destroy",function(){g.removeTab(d)}),d.$transcludeFn=h}}}]).directive("tabHeadingTransclude",["$log","$tabsSuppressWarning",function(a,b){return{restrict:"A",require:"^tab",link:function(c,d){b||a.warn("tab-heading-transclude is now deprecated. Use uib-tab-heading-transclude instead."),c.$watch("headingElement",function(a){a&&(d.html(""),d.append(a))})}}}]).directive("tabContentTransclude",["$log","$tabsSuppressWarning",function(a,b){function c(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||a.hasAttribute("x-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase()||"x-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^tabset",link:function(d,e,f){b||a.warn("tab-content-transclude is now deprecated. Use uib-tab-content-transclude instead.");var g=d.$eval(f.tabContentTransclude);g.$transcludeFn(g.$parent,function(a){angular.forEach(a,function(a){c(a)?g.headingElement=a:e.append(a)})})}}}]),angular.module("ui.bootstrap.timepicker",[]).constant("uibTimepickerConfig",{hourStep:1,minuteStep:1,showMeridian:!0,meridians:null,readonlyInput:!1,mousewheel:!0,arrowkeys:!0,showSpinners:!0}).controller("UibTimepickerController",["$scope","$element","$attrs","$parse","$log","$locale","uibTimepickerConfig",function(a,b,c,d,e,f,g){function h(){var b=parseInt(a.hours,10),c=a.showMeridian?b>0&&13>b:b>=0&&24>b;return c?(a.showMeridian&&(12===b&&(b=0),a.meridian===r[1]&&(b+=12)),b):void 0}function i(){var b=parseInt(a.minutes,10);return b>=0&&60>b?b:void 0}function j(a){return angular.isDefined(a)&&a.toString().length<2?"0"+a:a.toString()}function k(a){l(),q.$setViewValue(new Date(p)),m(a)}function l(){q.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1}function m(b){var c=p.getHours(),d=p.getMinutes();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:j(c),"m"!==b&&(a.minutes=j(d)),a.meridian=p.getHours()<12?r[0]:r[1]}function n(a,b){var c=new Date(a.getTime()+6e4*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes()),d}function o(a){p=n(p,a),k()}var p=new Date,q={$setViewValue:angular.noop},r=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){q=b,q.$render=this.render,q.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;h&&this.setupMousewheelEvents(e,f);var i=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;i&&this.setupArrowkeyEvents(e,f),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f)};var s=g.hourStep;c.hourStep&&a.$parent.$watch(d(c.hourStep),function(a){s=parseInt(a,10)});var t=g.minuteStep;c.minuteStep&&a.$parent.$watch(d(c.minuteStep),function(a){t=parseInt(a,10)});var u;a.$parent.$watch(d(c.min),function(a){var b=new Date(a);u=isNaN(b)?void 0:b});var v;a.$parent.$watch(d(c.max),function(a){var b=new Date(a);v=isNaN(b)?void 0:b}),a.noIncrementHours=function(){var a=n(p,60*s); +return a>v||p>a&&u>a},a.noDecrementHours=function(){var a=n(p,60*-s);return u>a||a>p&&a>v},a.noIncrementMinutes=function(){var a=n(p,t);return a>v||p>a&&u>a},a.noDecrementMinutes=function(){var a=n(p,-t);return u>a||a>p&&a>v},a.noToggleMeridian=function(){return p.getHours()<13?n(p,720)>v:n(p,-720)<u},a.showMeridian=g.showMeridian,c.showMeridian&&a.$parent.$watch(d(c.showMeridian),function(b){if(a.showMeridian=!!b,q.$error.time){var c=h(),d=i();angular.isDefined(c)&&angular.isDefined(d)&&(p.setHours(c),k())}else m()}),this.setupMousewheelEvents=function(b,c){var d=function(a){a.originalEvent&&(a=a.originalEvent);var b=a.wheelDelta?a.wheelDelta:-a.deltaY;return a.detail||b>0};b.bind("mousewheel wheel",function(b){a.$apply(d(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.bind("mousewheel wheel",function(b){a.$apply(d(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c){b.bind("keydown",function(b){38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementHours(),a.$apply())}),c.bind("keydown",function(b){38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply())})},this.setupInputEvents=function(b,c){if(a.readonlyInput)return a.updateHours=angular.noop,void(a.updateMinutes=angular.noop);var d=function(b,c){q.$setViewValue(null),q.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b),angular.isDefined(c)&&(a.invalidMinutes=c)};a.updateHours=function(){var a=h(),b=i();angular.isDefined(a)&&angular.isDefined(b)?(p.setHours(a),u>p||p>v?d(!0):k("h")):d(!0)},b.bind("blur",function(b){!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=j(a.hours)})}),a.updateMinutes=function(){var a=i(),b=h();angular.isDefined(a)&&angular.isDefined(b)?(p.setMinutes(a),u>p||p>v?d(void 0,!0):k("m")):d(void 0,!0)},c.bind("blur",function(b){!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=j(a.minutes)})})},this.render=function(){var b=q.$viewValue;isNaN(b)?(q.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(p=b),u>p||p>v?(q.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):l(),m())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*s)},a.decrementHours=function(){a.noDecrementHours()||o(60*-s)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(t)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(-t)},a.toggleMeridian=function(){a.noToggleMeridian()||o(720*(p.getHours()<12?1:-1))}}]).directive("uibTimepicker",function(){return{restrict:"EA",require:["uibTimepicker","?^ngModel"],controller:"UibTimepickerController",controllerAs:"timepicker",replace:!0,scope:{},templateUrl:function(a,b){return b.templateUrl||"template/timepicker/timepicker.html"},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}),angular.module("ui.bootstrap.timepicker").value("$timepickerSuppressWarning",!1).controller("TimepickerController",["$scope","$element","$attrs","$controller","$log","$timepickerSuppressWarning",function(a,b,c,d,e,f){f||e.warn("TimepickerController is now deprecated. Use UibTimepickerController instead."),angular.extend(this,d("UibTimepickerController",{$scope:a,$element:b,$attrs:c}))}]).directive("timepicker",["$log","$timepickerSuppressWarning",function(a,b){return{restrict:"EA",require:["timepicker","?^ngModel"],controller:"TimepickerController",controllerAs:"timepicker",replace:!0,scope:{},templateUrl:function(a,b){return b.templateUrl||"template/timepicker/timepicker.html"},link:function(c,d,e,f){b||a.warn("timepicker is now deprecated. Use uib-timepicker instead.");var g=f[0],h=f[1];h&&g.init(h,d.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(){K.moveInProgress||(K.moveInProgress=!0,K.$digest()),S&&g.cancel(S),S=g(function(){K.matches.length&&n(),K.moveInProgress=!1},r)}function n(){K.position=C?k.offset(b):k.position(b),K.position.top+=b.prop("offsetHeight")}var o,p,q=[9,13,27,38,40],r=200,s=a.$eval(c.typeaheadMinLength);s||0===s||(s=1);var t,u,v=a.$eval(c.typeaheadWaitMs)||0,w=a.$eval(c.typeaheadEditable)!==!1,x=e(c.typeaheadLoading).assign||angular.noop,y=e(c.typeaheadOnSelect),z=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,A=e(c.typeaheadNoResults).assign||angular.noop,B=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,C=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,D=c.typeaheadAppendToElementId||!1,E=a.$eval(c.typeaheadFocusFirst)!==!1,F=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,G=e(c.ngModel),H=e(c.ngModel+"($$$p)"),I=function(b,c){return angular.isFunction(G(a))&&p&&p.$options&&p.$options.getterSetter?H(b,{$$$p:c}):G.assign(b,c)},J=l.parse(c.uibTypeahead),K=a.$new(),L=a.$on("$destroy",function(){K.$destroy()});K.$on("$destroy",L);var M="typeahead-"+K.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":M});var N=angular.element("<div uib-typeahead-popup></div>");N.attr({id:M,matches:"matches",active:"activeIdx",select:"select(activeIdx)","move-in-progress":"moveInProgress",query:"query",position:"position"}),angular.isDefined(c.typeaheadTemplateUrl)&&N.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&N.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var O=function(){K.matches=[],K.activeIdx=-1,b.attr("aria-expanded",!1)},P=function(a){return M+"-option-"+a};K.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",P(a))});var Q=function(a,b){return K.matches.length>b&&a?a.toUpperCase()===K.matches[b].label.toUpperCase():!1},R=function(c){var d={$viewValue:c};x(a,!0),A(a,!1),f.when(J.source(a,d)).then(function(e){var f=c===o.$viewValue;if(f&&t)if(e&&e.length>0){K.activeIdx=E?0:-1,A(a,!1),K.matches.length=0;for(var g=0;g<e.length;g++)d[J.itemName]=e[g],K.matches.push({id:P(g),label:J.viewMapper(K,d),model:e[g]});K.query=c,n(),b.attr("aria-expanded",!0),F&&1===K.matches.length&&Q(c,0)&&K.select(0)}else O(),A(a,!0);f&&x(a,!1)},function(){O(),x(a,!1),A(a,!0)})};C&&(angular.element(i).bind("resize",m),h.find("body").bind("scroll",m));var S;K.moveInProgress=!1,K.query=void 0;var T,U=function(a){T=g(function(){R(a)},v)},V=function(){T&&g.cancel(T)};O(),K.select=function(d){var e,f,h={};u=!0,h[J.itemName]=f=K.matches[d].model,e=J.modelMapper(a,h),I(a,e),o.$setValidity("editable",!0),o.$setValidity("parse",!0),y(a,{$item:f,$model:e,$label:J.viewMapper(a,h)}),O(),K.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)},b.bind("keydown",function(a){if(0!==K.matches.length&&-1!==q.indexOf(a.which)){if(-1===K.activeIdx&&(9===a.which||13===a.which))return O(),void K.$digest();a.preventDefault(),40===a.which?(K.activeIdx=(K.activeIdx+1)%K.matches.length,K.$digest()):38===a.which?(K.activeIdx=(K.activeIdx>0?K.activeIdx:K.matches.length)-1,K.$digest()):13===a.which||9===a.which?K.$apply(function(){K.select(K.activeIdx)}):27===a.which&&(a.stopPropagation(),O(),K.$digest())}}),b.bind("blur",function(){z&&K.matches.length&&-1!==K.activeIdx&&!u&&(u=!0,K.$apply(function(){K.select(K.activeIdx)})),t=!1,u=!1});var W=function(a){b[0]!==a.target&&3!==a.which&&0!==K.matches.length&&(O(),j.$$phase||K.$digest())};h.bind("click",W),a.$on("$destroy",function(){h.unbind("click",W),(C||D)&&X.remove(),C&&(angular.element(i).unbind("resize",m),h.find("body").unbind("scroll",m)),N.remove()});var X=d(N)(K);C?h.find("body").append(X):D!==!1?angular.element(h[0].getElementById(D)).append(X):b.after(X),this.init=function(b,c){o=b,p=c,o.$parsers.unshift(function(b){return t=!0,0===s||b&&b.length>=s?v>0?(V(),U(b)):R(b):(x(a,!1),V(),O()),w?b:b?void o.$setValidity("editable",!1):(o.$setValidity("editable",!0),null)}),o.$formatters.push(function(b){var c,d,e={};return w||o.$setValidity("editable",!0),B?(e.$model=b,B(a,e)):(e[J.itemName]=b,c=J.viewMapper(a,e),e[J.itemName]=void 0,d=J.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","^?ngModelOptions","uibTypeahead"],link:function(a,b,c,d){d[2].init(d[0],d[1])}}}).directive("uibTypeaheadPopup",function(){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"template/typeahead/typeahead-popup.html"},link:function(a,b,c){a.templateUrl=c.templateUrl,a.isOpen=function(){return a.matches.length>0},a.isActive=function(b){return a.active==b},a.selectActive=function(b){a.active=b},a.selectMatch=function(b){a.select({activeIdx:b})}}}}).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"template/typeahead/typeahead-match.html";a(g).then(function(a){b(a.trim())(d,function(a){e.replaceWith(a)})})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"<strong>$&</strong>"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("ui.bootstrap.typeahead").value("$typeaheadSuppressWarning",!1).service("typeaheadParser",["$parse","uibTypeaheadParser","$log","$typeaheadSuppressWarning",function(a,b,c,d){return d||c.warn("typeaheadParser is now deprecated. Use uibTypeaheadParser instead."),b}]).directive("typeahead",["$compile","$parse","$q","$timeout","$document","$window","$rootScope","$uibPosition","typeaheadParser","$log","$typeaheadSuppressWarning",function(a,b,c,d,e,f,g,h,i,j,k){var l=[9,13,27,38,40],m=200;return{require:["ngModel","^?ngModelOptions"],link:function(n,o,p,q){function r(){N.moveInProgress||(N.moveInProgress=!0,N.$digest()),V&&d.cancel(V),V=d(function(){N.matches.length&&s(),N.moveInProgress=!1},m)}function s(){N.position=F?h.offset(o):h.position(o),N.position.top+=o.prop("offsetHeight")}k||j.warn("typeahead is now deprecated. Use uib-typeahead instead.");var t=q[0],u=q[1],v=n.$eval(p.typeaheadMinLength);v||0===v||(v=1);var w,x,y=n.$eval(p.typeaheadWaitMs)||0,z=n.$eval(p.typeaheadEditable)!==!1,A=b(p.typeaheadLoading).assign||angular.noop,B=b(p.typeaheadOnSelect),C=angular.isDefined(p.typeaheadSelectOnBlur)?n.$eval(p.typeaheadSelectOnBlur):!1,D=b(p.typeaheadNoResults).assign||angular.noop,E=p.typeaheadInputFormatter?b(p.typeaheadInputFormatter):void 0,F=p.typeaheadAppendToBody?n.$eval(p.typeaheadAppendToBody):!1,G=p.typeaheadAppendToElementId||!1,H=n.$eval(p.typeaheadFocusFirst)!==!1,I=p.typeaheadSelectOnExact?n.$eval(p.typeaheadSelectOnExact):!1,J=b(p.ngModel),K=b(p.ngModel+"($$$p)"),L=function(a,b){return angular.isFunction(J(n))&&u&&u.$options&&u.$options.getterSetter?K(a,{$$$p:b}):J.assign(a,b)},M=i.parse(p.typeahead),N=n.$new(),O=n.$on("$destroy",function(){N.$destroy()});N.$on("$destroy",O);var P="typeahead-"+N.$id+"-"+Math.floor(1e4*Math.random());o.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":P});var Q=angular.element("<div typeahead-popup></div>");Q.attr({id:P,matches:"matches",active:"activeIdx",select:"select(activeIdx)","move-in-progress":"moveInProgress",query:"query",position:"position"}),angular.isDefined(p.typeaheadTemplateUrl)&&Q.attr("template-url",p.typeaheadTemplateUrl),angular.isDefined(p.typeaheadPopupTemplateUrl)&&Q.attr("popup-template-url",p.typeaheadPopupTemplateUrl);var R=function(){N.matches=[],N.activeIdx=-1,o.attr("aria-expanded",!1)},S=function(a){return P+"-option-"+a};N.$watch("activeIdx",function(a){0>a?o.removeAttr("aria-activedescendant"):o.attr("aria-activedescendant",S(a))});var T=function(a,b){return N.matches.length>b&&a?a.toUpperCase()===N.matches[b].label.toUpperCase():!1},U=function(a){var b={$viewValue:a};A(n,!0),D(n,!1),c.when(M.source(n,b)).then(function(c){var d=a===t.$viewValue;if(d&&w)if(c&&c.length>0){N.activeIdx=H?0:-1,D(n,!1),N.matches.length=0;for(var e=0;e<c.length;e++)b[M.itemName]=c[e],N.matches.push({id:S(e),label:M.viewMapper(N,b),model:c[e]});N.query=a,s(),o.attr("aria-expanded",!0),I&&1===N.matches.length&&T(a,0)&&N.select(0)}else R(),D(n,!0);d&&A(n,!1)},function(){R(),A(n,!1),D(n,!0)})};F&&(angular.element(f).bind("resize",r),e.find("body").bind("scroll",r));var V;N.moveInProgress=!1,R(),N.query=void 0;var W,X=function(a){W=d(function(){U(a)},y)},Y=function(){W&&d.cancel(W)};t.$parsers.unshift(function(a){return w=!0,0===v||a&&a.length>=v?y>0?(Y(),X(a)):U(a):(A(n,!1),Y(),R()),z?a:a?void t.$setValidity("editable",!1):(t.$setValidity("editable",!0),null)}),t.$formatters.push(function(a){var b,c,d={};return z||t.$setValidity("editable",!0),E?(d.$model=a,E(n,d)):(d[M.itemName]=a,b=M.viewMapper(n,d),d[M.itemName]=void 0,c=M.viewMapper(n,d),b!==c?b:a)}),N.select=function(a){var b,c,e={};x=!0,e[M.itemName]=c=N.matches[a].model,b=M.modelMapper(n,e),L(n,b),t.$setValidity("editable",!0),t.$setValidity("parse",!0),B(n,{$item:c,$model:b,$label:M.viewMapper(n,e)}),R(),N.$eval(p.typeaheadFocusOnSelect)!==!1&&d(function(){o[0].focus()},0,!1)},o.bind("keydown",function(a){if(0!==N.matches.length&&-1!==l.indexOf(a.which)){if(-1===N.activeIdx&&(9===a.which||13===a.which))return R(),void N.$digest();a.preventDefault(),40===a.which?(N.activeIdx=(N.activeIdx+1)%N.matches.length,N.$digest()):38===a.which?(N.activeIdx=(N.activeIdx>0?N.activeIdx:N.matches.length)-1,N.$digest()):13===a.which||9===a.which?N.$apply(function(){N.select(N.activeIdx)}):27===a.which&&(a.stopPropagation(),R(),N.$digest())}}),o.bind("blur",function(){C&&N.matches.length&&-1!==N.activeIdx&&!x&&(x=!0,N.$apply(function(){N.select(N.activeIdx)})),w=!1,x=!1});var Z=function(a){o[0]!==a.target&&3!==a.which&&0!==N.matches.length&&(R(),g.$$phase||N.$digest())};e.bind("click",Z),n.$on("$destroy",function(){e.unbind("click",Z),(F||G)&&$.remove(),F&&(angular.element(f).unbind("resize",r),e.find("body").unbind("scroll",r)),Q.remove()});var $=a(Q)(N);F?e.find("body").append($):G!==!1?angular.element(e[0].getElementById(G)).append($):o.after($)}}}]).directive("typeaheadPopup",["$typeaheadSuppressWarning","$log",function(a,b){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"template/typeahead/typeahead-popup.html"},link:function(c,d,e){a||b.warn("typeahead-popup is now deprecated. Use uib-typeahead-popup instead."),c.templateUrl=e.templateUrl,c.isOpen=function(){return c.matches.length>0},c.isActive=function(a){return c.active==a},c.selectActive=function(a){c.active=a},c.selectMatch=function(a){c.select({activeIdx:a})}}}}]).directive("typeaheadMatch",["$templateRequest","$compile","$parse","$typeaheadSuppressWarning","$log",function(a,b,c,d,e){return{restrict:"EA",scope:{index:"=",match:"=",query:"="},link:function(f,g,h){d||e.warn("typeahead-match is now deprecated. Use uib-typeahead-match instead.");var i=c(h.templateUrl)(f.$parent)||"template/typeahead/typeahead-match.html";a(i).then(function(a){b(a.trim())(f,function(a){g.replaceWith(a)})})}}}]).filter("typeaheadHighlight",["$sce","$injector","$log","$typeaheadSuppressWarning",function(a,b,c,d){function e(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function f(a){return/<.*>/g.test(a)}var g;return g=b.has("$sanitize"),function(b,h){return d||c.warn("typeaheadHighlight is now deprecated. Use uibTypeaheadHighlight instead."),!g&&f(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=h?(""+b).replace(new RegExp(e(h),"gi"),"<strong>$&</strong>"):b,g||(b=a.trustAsHtml(b)),b}}]),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion-group.html",'<div class="panel {{panelClass || \'panel-default\'}}">\n <div class="panel-heading" ng-keypress="toggleOpen($event)">\n <h4 class="panel-title">\n <a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading"><span ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n </h4>\n </div>\n <div class="panel-collapse collapse" uib-collapse="!isOpen">\n <div class="panel-body" ng-transclude></div>\n </div>\n</div>\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion.html",'<div class="panel-group" ng-transclude></div>')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("template/alert/alert.html",'<div class="alert" ng-class="[\'alert-\' + (type || \'warning\'), closeable ? \'alert-dismissible\' : null]" role="alert">\n <button ng-show="closeable" type="button" class="close" ng-click="close({$event: $event})">\n <span aria-hidden="true">×</span>\n <span class="sr-only">Close</span>\n </button>\n <div ng-transclude></div>\n</div>\n')}]),angular.module("template/carousel/carousel.html",[]).run(["$templateCache",function(a){a.put("template/carousel/carousel.html",'<div ng-mouseenter="pause()" ng-mouseleave="play()" class="carousel" ng-swipe-right="prev()" ng-swipe-left="next()">\n <div class="carousel-inner" ng-transclude></div>\n <a role="button" href class="left carousel-control" ng-click="prev()" ng-show="slides.length > 1">\n <span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>\n <span class="sr-only">previous</span>\n </a>\n <a role="button" href class="right carousel-control" ng-click="next()" ng-show="slides.length > 1">\n <span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>\n <span class="sr-only">next</span>\n </a>\n <ol class="carousel-indicators" ng-show="slides.length > 1">\n <li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">\n <span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>\n </li>\n </ol>\n</div>')}]),angular.module("template/carousel/slide.html",[]).run(["$templateCache",function(a){a.put("template/carousel/slide.html",'<div ng-class="{\n \'active\': active\n }" class="item text-center" ng-transclude></div>\n')}]),angular.module("template/datepicker/datepicker.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/datepicker.html",'<div ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">\n <uib-daypicker ng-switch-when="day" tabindex="0"></uib-daypicker>\n <uib-monthpicker ng-switch-when="month" tabindex="0"></uib-monthpicker>\n <uib-yearpicker ng-switch-when="year" tabindex="0"></uib-yearpicker>\n</div>')}]),angular.module("template/datepicker/day.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/day.html",'<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n <th colspan="{{::5 + showWeeks}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n </tr>\n <tr>\n <th ng-if="showWeeks" class="text-center"></th>\n <th ng-repeat="label in ::labels track by $index" class="text-center"><small aria-label="{{::label.full}}">{{::label.abbr}}</small></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in rows track by $index">\n <td ng-if="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>\n <td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{::dt.uid}}" ng-class="::dt.customClass">\n <button type="button" style="min-width:100%;" class="btn btn-default btn-sm" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="::{\'text-muted\': dt.secondary, \'text-info\': dt.current}">{{::dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/datepicker/month.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/month.html",'<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n <th><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in rows track by $index">\n <td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{::dt.uid}}" ng-class="::dt.customClass">\n <button type="button" style="min-width:100%;" class="btn btn-default" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="::{\'text-info\': dt.current}">{{::dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/datepicker/popup.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/popup.html",'<ul class="dropdown-menu" dropdown-nested ng-if="isOpen" style="display: block" ng-style="{top: position.top+\'px\', left: position.left+\'px\'}" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">\n <li ng-transclude></li>\n <li ng-if="showButtonBar" style="padding:10px 9px 2px">\n <span class="btn-group pull-left">\n <button type="button" class="btn btn-sm btn-info" ng-click="select(\'today\')" ng-disabled="isDisabled(\'today\')">{{ getText(\'current\') }}</button>\n <button type="button" class="btn btn-sm btn-danger" ng-click="select(null)">{{ getText(\'clear\') }}</button>\n </span>\n <button type="button" class="btn btn-sm btn-success pull-right" ng-click="close()">{{ getText(\'close\') }}</button>\n </li>\n</ul>\n')}]),angular.module("template/datepicker/year.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/year.html",'<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n <th colspan="3"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in rows track by $index">\n <td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{::dt.uid}}" ng-class="::dt.customClass">\n <button type="button" style="min-width:100%;" class="btn btn-default" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="::{\'text-info\': dt.current}">{{::dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("template/modal/backdrop.html",'<div uib-modal-animation-class="fade"\n modal-in-class="in"\n ng-style="{\'z-index\': 1040 + (index && 1 || 0) + index*10}"\n></div>\n')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(a){a.put("template/modal/window.html",'<div modal-render="{{$isRendered}}" tabindex="-1" role="dialog" class="modal"\n uib-modal-animation-class="fade"\n modal-in-class="in"\n ng-style="{\'z-index\': 1050 + index*10, display: \'block\'}">\n <div class="modal-dialog" ng-class="size ? \'modal-\' + size : \'\'"><div class="modal-content" uib-modal-transclude></div></div>\n</div>\n')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pager.html",'<ul class="pager">\n <li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)">{{::getText(\'previous\')}}</a></li>\n <li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)">{{::getText(\'next\')}}</a></li>\n</ul>\n')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pagination.html",'<ul class="pagination">\n <li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)">{{::getText(\'first\')}}</a></li>\n <li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)">{{::getText(\'previous\')}}</a></li>\n <li ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)">{{page.text}}</a></li>\n <li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)">{{::getText(\'next\')}}</a></li>\n <li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)">{{::getText(\'last\')}}</a></li>\n</ul>\n')}]),angular.module("template/tooltip/tooltip-html-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-html-popup.html",'<div\n tooltip-animation-class="fade"\n uib-tooltip-classes\n ng-class="{ in: isOpen() }">\n <div class="tooltip-arrow"></div>\n <div class="tooltip-inner" ng-bind-html="contentExp()"></div>\n</div>\n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-popup.html",'<div\n tooltip-animation-class="fade"\n uib-tooltip-classes\n ng-class="{ in: isOpen() }">\n <div class="tooltip-arrow"></div>\n <div class="tooltip-inner" ng-bind="content"></div>\n</div>\n')}]),angular.module("template/tooltip/tooltip-template-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-template-popup.html",'<div\n tooltip-animation-class="fade"\n uib-tooltip-classes\n ng-class="{ in: isOpen() }">\n <div class="tooltip-arrow"></div>\n <div class="tooltip-inner"\n uib-tooltip-template-transclude="contentExp()"\n tooltip-template-transclude-scope="originScope()"></div>\n</div>\n')}]),angular.module("template/popover/popover-html.html",[]).run(["$templateCache",function(a){a.put("template/popover/popover-html.html",'<div tooltip-animation-class="fade"\n uib-tooltip-classes\n ng-class="{ in: isOpen() }">\n <div class="arrow"></div>\n\n <div class="popover-inner">\n <h3 class="popover-title" ng-bind="title" ng-if="title"></h3>\n <div class="popover-content" ng-bind-html="contentExp()"></div>\n </div>\n</div>\n')}]),angular.module("template/popover/popover-template.html",[]).run(["$templateCache",function(a){a.put("template/popover/popover-template.html",'<div tooltip-animation-class="fade"\n uib-tooltip-classes\n ng-class="{ in: isOpen() }">\n <div class="arrow"></div>\n\n <div class="popover-inner">\n <h3 class="popover-title" ng-bind="title" ng-if="title"></h3>\n <div class="popover-content"\n uib-tooltip-template-transclude="contentExp()"\n tooltip-template-transclude-scope="originScope()"></div>\n </div>\n</div>\n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("template/popover/popover.html",'<div tooltip-animation-class="fade"\n uib-tooltip-classes\n ng-class="{ in: isOpen() }">\n <div class="arrow"></div>\n\n <div class="popover-inner">\n <h3 class="popover-title" ng-bind="title" ng-if="title"></h3>\n <div class="popover-content" ng-bind="content"></div>\n </div>\n</div>\n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/bar.html",'<div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: (percent < 100 ? percent : 100) + \'%\'}" aria-valuetext="{{percent | number:0}}%" aria-labelledby="{{::title}}" style="min-width: 0;" ng-transclude></div>\n')}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progress.html",'<div class="progress" ng-transclude aria-labelledby="{{::title}}"></div>')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progressbar.html",'<div class="progress">\n <div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: (percent < 100 ? percent : 100) + \'%\'}" aria-valuetext="{{percent | number:0}}%" aria-labelledby="{{::title}}" style="min-width: 0;" ng-transclude></div>\n</div>\n')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("template/rating/rating.html",'<span ng-mouseleave="reset()" ng-keydown="onKeydown($event)" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="{{range.length}}" aria-valuenow="{{value}}">\n <span ng-repeat-start="r in range track by $index" class="sr-only">({{ $index < value ? \'*\' : \' \' }})</span>\n <i ng-repeat-end ng-mouseenter="enter($index + 1)" ng-click="rate($index + 1)" class="glyphicon" ng-class="$index < value && (r.stateOn || \'glyphicon-star\') || (r.stateOff || \'glyphicon-star-empty\')" ng-attr-title="{{r.title}}" aria-valuetext="{{r.title}}"></i>\n</span>\n'); +}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tab.html",'<li ng-class="{active: active, disabled: disabled}">\n <a href ng-click="select()" uib-tab-heading-transclude>{{heading}}</a>\n</li>\n')}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset.html",'<div>\n <ul class="nav nav-{{type || \'tabs\'}}" ng-class="{\'nav-stacked\': vertical, \'nav-justified\': justified}" ng-transclude></ul>\n <div class="tab-content">\n <div class="tab-pane" \n ng-repeat="tab in tabs" \n ng-class="{active: tab.active}"\n uib-tab-content-transclude="tab">\n </div>\n </div>\n</div>\n')}]),angular.module("template/timepicker/timepicker.html",[]).run(["$templateCache",function(a){a.put("template/timepicker/timepicker.html",'<table>\n <tbody>\n <tr class="text-center" ng-show="::showSpinners">\n <td><a ng-click="incrementHours()" ng-class="{disabled: noIncrementHours()}" class="btn btn-link" ng-disabled="noIncrementHours()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td> </td>\n <td><a ng-click="incrementMinutes()" ng-class="{disabled: noIncrementMinutes()}" class="btn btn-link" ng-disabled="noIncrementMinutes()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td ng-show="showMeridian"></td>\n </tr>\n <tr>\n <td class="form-group" ng-class="{\'has-error\': invalidHours}">\n <input style="width:50px;" type="text" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}">\n </td>\n <td>:</td>\n <td class="form-group" ng-class="{\'has-error\': invalidMinutes}">\n <input style="width:50px;" type="text" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="::readonlyInput" maxlength="2" tabindex="{{::tabindex}}">\n </td>\n <td ng-show="showMeridian"><button type="button" ng-class="{disabled: noToggleMeridian()}" class="btn btn-default text-center" ng-click="toggleMeridian()" ng-disabled="noToggleMeridian()" tabindex="{{::tabindex}}">{{meridian}}</button></td>\n </tr>\n <tr class="text-center" ng-show="::showSpinners">\n <td><a ng-click="decrementHours()" ng-class="{disabled: noDecrementHours()}" class="btn btn-link" ng-disabled="noDecrementHours()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td> </td>\n <td><a ng-click="decrementMinutes()" ng-class="{disabled: noDecrementMinutes()}" class="btn btn-link" ng-disabled="noDecrementMinutes()" tabindex="{{::tabindex}}"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td ng-show="showMeridian"></td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-match.html",'<a href tabindex="-1" ng-bind-html="match.label | uibTypeaheadHighlight:query"></a>\n')}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-popup.html",'<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+\'px\', left: position().left+\'px\'}" style="display: block;" role="listbox" aria-hidden="{{!isOpen()}}">\n <li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{::match.id}}">\n <div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>\n </li>\n</ul>\n')}]),!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap.js new file mode 100644 index 000000000..9287bb546 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap.js @@ -0,0 +1,8126 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.14.3 - 2015-10-23 + * License: MIT + */ +angular.module("ui.bootstrap", ["ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]); +angular.module('ui.bootstrap.collapse', []) + + .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + function expand() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + addClass: 'in', + easing: 'ease', + to: { height: element[0].scrollHeight + 'px' } + }).start().finally(expandDone); + } else { + $animate.addClass(element, 'in', { + to: { height: element[0].scrollHeight + 'px' } + }).then(expandDone); + } + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse') + .css({height: 'auto'}); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + element + // IMPORTANT: The height must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from height 0 (in + // collapsing class) to the given height here. + .css({height: element[0].scrollHeight + 'px'}) + // initially all panel collapse have the collapse class, this removal + // prevents the animation from jumping to collapsed state + .removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', false) + .attr('aria-hidden', true); + + if ($animateCss) { + $animateCss(element, { + removeClass: 'in', + to: {height: '0'} + }).start().finally(collapseDone); + } else { + $animate.removeClass(element, 'in', { + to: {height: '0'} + }).then(collapseDone); + } + } + + function collapseDone() { + element.css({height: '0'}); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + } + + scope.$watch(attrs.uibCollapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +/* Deprecated collapse below */ + +angular.module('ui.bootstrap.collapse') + + .value('$collapseSuppressWarning', false) + + .directive('collapse', ['$animate', '$injector', '$log', '$collapseSuppressWarning', function($animate, $injector, $log, $collapseSuppressWarning) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + if (!$collapseSuppressWarning) { + $log.warn('collapse is now deprecated. Use uib-collapse instead.'); + } + + function expand() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + easing: 'ease', + to: { height: element[0].scrollHeight + 'px' } + }).start().done(expandDone); + } else { + $animate.animate(element, {}, { + height: element[0].scrollHeight + 'px' + }).then(expandDone); + } + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse in') + .css({height: 'auto'}); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + element + // IMPORTANT: The height must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from height 0 (in + // collapsing class) to the given height here. + .css({height: element[0].scrollHeight + 'px'}) + // initially all panel collapse have the collapse class, this removal + // prevents the animation from jumping to collapsed state + .removeClass('collapse in') + .addClass('collapsing') + .attr('aria-expanded', false) + .attr('aria-hidden', true); + + if ($animateCss) { + $animateCss(element, { + to: {height: '0'} + }).start().done(collapseDone); + } else { + $animate.animate(element, {}, { + height: '0' + }).then(collapseDone); + } + } + + function collapseDone() { + element.css({height: '0'}); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + } + + scope.$watch(attrs.collapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse']) + +.constant('uibAccordionConfig', { + closeOthers: true +}) + +.controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) { + // This array keeps track of the accordion groups + this.groups = []; + + // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to + this.closeOthers = function(openGroup) { + var closeOthers = angular.isDefined($attrs.closeOthers) ? + $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers; + if (closeOthers) { + angular.forEach(this.groups, function(group) { + if (group !== openGroup) { + group.isOpen = false; + } + }); + } + }; + + // This is called from the accordion-group directive to add itself to the accordion + this.addGroup = function(groupScope) { + var that = this; + this.groups.push(groupScope); + + groupScope.$on('$destroy', function(event) { + that.removeGroup(groupScope); + }); + }; + + // This is called from the accordion-group directive when to remove itself + this.removeGroup = function(group) { + var index = this.groups.indexOf(group); + if (index !== -1) { + this.groups.splice(index, 1); + } + }; + +}]) + +// The accordion directive simply sets up the directive controller +// and adds an accordion CSS class to itself element. +.directive('uibAccordion', function() { + return { + controller: 'UibAccordionController', + controllerAs: 'accordion', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion.html'; + } + }; +}) + +// The accordion-group directive indicates a block of html that will expand and collapse in an accordion +.directive('uibAccordionGroup', function() { + return { + require: '^uibAccordion', // We need this directive to be inside an accordion + transclude: true, // It transcludes the contents of the directive into the template + replace: true, // The element containing the directive will be replaced with the template + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass; + scope.$watch('isOpen', function(value) { + element.toggleClass(scope.openClass, !!value); + if (value) { + accordionCtrl.closeOthers(scope); + } + }); + + scope.toggleOpen = function($event) { + if (!scope.isDisabled) { + if (!$event || $event.which === 32) { + scope.isOpen = !scope.isOpen; + } + } + }; + } + }; +}) + +// Use accordion-heading below an accordion-group to provide a heading containing HTML +.directive('uibAccordionHeading', function() { + return { + transclude: true, // Grab the contents to be used as the heading + template: '', // In effect remove this element! + replace: true, + require: '^uibAccordionGroup', + link: function(scope, element, attrs, accordionGroupCtrl, transclude) { + // Pass the heading to the accordion-group controller + // so that it can be transcluded into the right place in the template + // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] + accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); + } + }; +}) + +// Use in the accordion-group template to indicate where you want the heading to be transcluded +// You must provide the property on the accordion-group controller that will hold the transcluded element +.directive('uibAccordionTransclude', function() { + return { + require: ['?^uibAccordionGroup', '?^accordionGroup'], + link: function(scope, element, attrs, controller) { + controller = controller[0] ? controller[0] : controller[1]; // Delete after we remove deprecation + scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { + if (heading) { + element.find('span').html(''); + element.find('span').append(heading); + } + }); + } + }; +}); + +/* Deprecated accordion below */ + +angular.module('ui.bootstrap.accordion') + + .value('$accordionSuppressWarning', false) + + .controller('AccordionController', ['$scope', '$attrs', '$controller', '$log', '$accordionSuppressWarning', function($scope, $attrs, $controller, $log, $accordionSuppressWarning) { + if (!$accordionSuppressWarning) { + $log.warn('AccordionController is now deprecated. Use UibAccordionController instead.'); + } + + angular.extend(this, $controller('UibAccordionController', { + $scope: $scope, + $attrs: $attrs + })); + }]) + + .directive('accordion', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + restrict: 'EA', + controller: 'AccordionController', + controllerAs: 'accordion', + transclude: true, + replace: false, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion.html'; + }, + link: function() { + if (!$accordionSuppressWarning) { + $log.warn('accordion is now deprecated. Use uib-accordion instead.'); + } + } + }; + }]) + + .directive('accordionGroup', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + require: '^accordion', // We need this directive to be inside an accordion + restrict: 'EA', + transclude: true, // It transcludes the contents of the directive into the template + replace: true, // The element containing the directive will be replaced with the template + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + if (!$accordionSuppressWarning) { + $log.warn('accordion-group is now deprecated. Use uib-accordion-group instead.'); + } + + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass; + scope.$watch('isOpen', function(value) { + element.toggleClass(scope.openClass, !!value); + if (value) { + accordionCtrl.closeOthers(scope); + } + }); + + scope.toggleOpen = function($event) { + if (!scope.isDisabled) { + if (!$event || $event.which === 32) { + scope.isOpen = !scope.isOpen; + } + } + }; + } + }; + }]) + + .directive('accordionHeading', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + restrict: 'EA', + transclude: true, // Grab the contents to be used as the heading + template: '', // In effect remove this element! + replace: true, + require: '^accordionGroup', + link: function(scope, element, attr, accordionGroupCtrl, transclude) { + if (!$accordionSuppressWarning) { + $log.warn('accordion-heading is now deprecated. Use uib-accordion-heading instead.'); + } + // Pass the heading to the accordion-group controller + // so that it can be transcluded into the right place in the template + // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat] + accordionGroupCtrl.setHeading(transclude(scope, angular.noop)); + } + }; + }]) + + .directive('accordionTransclude', ['$log', '$accordionSuppressWarning', function($log, $accordionSuppressWarning) { + return { + require: '^accordionGroup', + link: function(scope, element, attr, controller) { + if (!$accordionSuppressWarning) { + $log.warn('accordion-transclude is now deprecated. Use uib-accordion-transclude instead.'); + } + + scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) { + if (heading) { + element.find('span').html(''); + element.find('span').append(heading); + } + }); + } + }; + }]); + + +angular.module('ui.bootstrap.alert', []) + +.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) { + $scope.closeable = !!$attrs.close; + + var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ? + $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null; + + if (dismissOnTimeout) { + $timeout(function() { + $scope.close(); + }, parseInt(dismissOnTimeout, 10)); + } +}]) + +.directive('uibAlert', function() { + return { + controller: 'UibAlertController', + controllerAs: 'alert', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/alert/alert.html'; + }, + transclude: true, + replace: true, + scope: { + type: '@', + close: '&' + } + }; +}); + +/* Deprecated alert below */ + +angular.module('ui.bootstrap.alert') + + .value('$alertSuppressWarning', false) + + .controller('AlertController', ['$scope', '$attrs', '$controller', '$log', '$alertSuppressWarning', function($scope, $attrs, $controller, $log, $alertSuppressWarning) { + if (!$alertSuppressWarning) { + $log.warn('AlertController is now deprecated. Use UibAlertController instead.'); + } + + angular.extend(this, $controller('UibAlertController', { + $scope: $scope, + $attrs: $attrs + })); + }]) + + .directive('alert', ['$log', '$alertSuppressWarning', function($log, $alertSuppressWarning) { + return { + controller: 'AlertController', + controllerAs: 'alert', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/alert/alert.html'; + }, + transclude: true, + replace: true, + scope: { + type: '@', + close: '&' + }, + link: function() { + if (!$alertSuppressWarning) { + $log.warn('alert is now deprecated. Use uib-alert instead.'); + } + } + }; + }]); + +angular.module('ui.bootstrap.buttons', []) + +.constant('uibButtonConfig', { + activeClass: 'active', + toggleEvent: 'click' +}) + +.controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) { + this.activeClass = buttonConfig.activeClass || 'active'; + this.toggleEvent = buttonConfig.toggleEvent || 'click'; +}]) + +.directive('uibBtnRadio', function() { + return { + require: ['uibBtnRadio', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio))); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + var isActive = element.hasClass(buttonsCtrl.activeClass); + + if (!isActive || angular.isDefined(attrs.uncheckable)) { + scope.$apply(function() { + ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio)); + ngModelCtrl.$render(); + }); + } + }); + } + }; +}) + +.directive('uibBtnCheckbox', function() { + return { + require: ['uibBtnCheckbox', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'button', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + function getTrueValue() { + return getCheckboxValue(attrs.btnCheckboxTrue, true); + } + + function getFalseValue() { + return getCheckboxValue(attrs.btnCheckboxFalse, false); + } + + function getCheckboxValue(attribute, defaultValue) { + return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue; + } + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); + }; + + //ui->model + element.on(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + } + }; +}); + +/* Deprecated buttons below */ + +angular.module('ui.bootstrap.buttons') + + .value('$buttonsSuppressWarning', false) + + .controller('ButtonsController', ['$controller', '$log', '$buttonsSuppressWarning', function($controller, $log, $buttonsSuppressWarning) { + if (!$buttonsSuppressWarning) { + $log.warn('ButtonsController is now deprecated. Use UibButtonsController instead.'); + } + + angular.extend(this, $controller('UibButtonsController')); + }]) + + .directive('btnRadio', ['$log', '$buttonsSuppressWarning', function($log, $buttonsSuppressWarning) { + return { + require: ['btnRadio', 'ngModel'], + controller: 'ButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + if (!$buttonsSuppressWarning) { + $log.warn('btn-radio is now deprecated. Use uib-btn-radio instead.'); + } + + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio))); + }; + + //ui->model + element.bind(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + var isActive = element.hasClass(buttonsCtrl.activeClass); + + if (!isActive || angular.isDefined(attrs.uncheckable)) { + scope.$apply(function() { + ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.btnRadio)); + ngModelCtrl.$render(); + }); + } + }); + } + }; + }]) + + .directive('btnCheckbox', ['$document', '$log', '$buttonsSuppressWarning', function($document, $log, $buttonsSuppressWarning) { + return { + require: ['btnCheckbox', 'ngModel'], + controller: 'ButtonsController', + controllerAs: 'button', + link: function(scope, element, attrs, ctrls) { + if (!$buttonsSuppressWarning) { + $log.warn('btn-checkbox is now deprecated. Use uib-btn-checkbox instead.'); + } + + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + element.find('input').css({display: 'none'}); + + function getTrueValue() { + return getCheckboxValue(attrs.btnCheckboxTrue, true); + } + + function getFalseValue() { + return getCheckboxValue(attrs.btnCheckboxFalse, false); + } + + function getCheckboxValue(attributeValue, defaultValue) { + var val = scope.$eval(attributeValue); + return angular.isDefined(val) ? val : defaultValue; + } + + //model -> UI + ngModelCtrl.$render = function() { + element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue())); + }; + + //ui->model + element.bind(buttonsCtrl.toggleEvent, function() { + if (attrs.disabled) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + + //accessibility + element.on('keypress', function(e) { + if (attrs.disabled || e.which !== 32 || $document[0].activeElement !== element[0]) { + return; + } + + scope.$apply(function() { + ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue()); + ngModelCtrl.$render(); + }); + }); + } + }; + }]); + + +/** + * @ngdoc overview + * @name ui.bootstrap.carousel + * + * @description + * AngularJS version of an image carousel. + * + */ +angular.module('ui.bootstrap.carousel', []) + +.controller('UibCarouselController', ['$scope', '$element', '$interval', '$animate', function($scope, $element, $interval, $animate) { + var self = this, + slides = self.slides = $scope.slides = [], + NEW_ANIMATE = angular.version.minor >= 4, + NO_TRANSITION = 'uib-noTransition', + SLIDE_DIRECTION = 'uib-slideDirection', + currentIndex = -1, + currentInterval, isPlaying; + self.currentSlide = null; + + var destroyed = false; + /* direction: "prev" or "next" */ + self.select = $scope.select = function(nextSlide, direction) { + var nextIndex = $scope.indexOfSlide(nextSlide); + //Decide direction if it's not given + if (direction === undefined) { + direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev'; + } + //Prevent this user-triggered transition from occurring if there is already one in progress + if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) { + goNext(nextSlide, nextIndex, direction); + } + }; + + function goNext(slide, index, direction) { + // Scope has been destroyed, stop here. + if (destroyed) { return; } + + angular.extend(slide, {direction: direction, active: true}); + angular.extend(self.currentSlide || {}, {direction: direction, active: false}); + if ($animate.enabled() && !$scope.noTransition && !$scope.$currentTransition && + slide.$element && self.slides.length > 1) { + slide.$element.data(SLIDE_DIRECTION, slide.direction); + if (self.currentSlide && self.currentSlide.$element) { + self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction); + } + + $scope.$currentTransition = true; + if (NEW_ANIMATE) { + $animate.on('addClass', slide.$element, function(element, phase) { + if (phase === 'close') { + $scope.$currentTransition = null; + $animate.off('addClass', element); + } + }); + } else { + slide.$element.one('$animate:close', function closeFn() { + $scope.$currentTransition = null; + }); + } + } + + self.currentSlide = slide; + currentIndex = index; + + //every time you change slides, reset the timer + restartTimer(); + } + + $scope.$on('$destroy', function() { + destroyed = true; + }); + + function getSlideByIndex(index) { + if (angular.isUndefined(slides[index].index)) { + return slides[index]; + } + var i, len = slides.length; + for (i = 0; i < slides.length; ++i) { + if (slides[i].index == index) { + return slides[i]; + } + } + } + + self.getCurrentIndex = function() { + if (self.currentSlide && angular.isDefined(self.currentSlide.index)) { + return +self.currentSlide.index; + } + return currentIndex; + }; + + /* Allow outside people to call indexOf on slides array */ + $scope.indexOfSlide = function(slide) { + return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide); + }; + + $scope.next = function() { + var newIndex = (self.getCurrentIndex() + 1) % slides.length; + + if (newIndex === 0 && $scope.noWrap()) { + $scope.pause(); + return; + } + + return self.select(getSlideByIndex(newIndex), 'next'); + }; + + $scope.prev = function() { + var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1; + + if ($scope.noWrap() && newIndex === slides.length - 1) { + $scope.pause(); + return; + } + + return self.select(getSlideByIndex(newIndex), 'prev'); + }; + + $scope.isActive = function(slide) { + return self.currentSlide === slide; + }; + + $scope.$watch('interval', restartTimer); + $scope.$watchCollection('slides', resetTransition); + $scope.$on('$destroy', resetTimer); + + function restartTimer() { + resetTimer(); + var interval = +$scope.interval; + if (!isNaN(interval) && interval > 0) { + currentInterval = $interval(timerFn, interval); + } + } + + function resetTimer() { + if (currentInterval) { + $interval.cancel(currentInterval); + currentInterval = null; + } + } + + function timerFn() { + var interval = +$scope.interval; + if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { + $scope.next(); + } else { + $scope.pause(); + } + } + + function resetTransition(slides) { + if (!slides.length) { + $scope.$currentTransition = null; + } + } + + $scope.play = function() { + if (!isPlaying) { + isPlaying = true; + restartTimer(); + } + }; + $scope.pause = function() { + if (!$scope.noPause) { + isPlaying = false; + resetTimer(); + } + }; + + self.addSlide = function(slide, element) { + slide.$element = element; + slides.push(slide); + //if this is the first slide or the slide is set to active, select it + if (slides.length === 1 || slide.active) { + self.select(slides[slides.length - 1]); + if (slides.length === 1) { + $scope.play(); + } + } else { + slide.active = false; + } + }; + + self.removeSlide = function(slide) { + if (angular.isDefined(slide.index)) { + slides.sort(function(a, b) { + return +a.index > +b.index; + }); + } + //get the index of the slide inside the carousel + var index = slides.indexOf(slide); + slides.splice(index, 1); + if (slides.length > 0 && slide.active) { + if (index >= slides.length) { + self.select(slides[index - 1]); + } else { + self.select(slides[index]); + } + } else if (currentIndex > index) { + currentIndex--; + } + + //clean the currentSlide when no more slide + if (slides.length === 0) { + self.currentSlide = null; + } + }; + + $scope.$watch('noTransition', function(noTransition) { + $element.data(NO_TRANSITION, noTransition); + }); + +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.carousel.directive:carousel + * @restrict EA + * + * @description + * Carousel is the outer container for a set of image 'slides' to showcase. + * + * @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide. + * @param {boolean=} noTransition Whether to disable transitions on the carousel. + * @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover). + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <uib-carousel> + <uib-slide> + <img src="http://placekitten.com/150/150" style="margin:auto;"> + <div class="carousel-caption"> + <p>Beautiful!</p> + </div> + </uib-slide> + <uib-slide> + <img src="http://placekitten.com/100/150" style="margin:auto;"> + <div class="carousel-caption"> + <p>D'aww!</p> + </div> + </uib-slide> + </uib-carousel> + </file> + <file name="demo.css"> + .carousel-indicators { + top: auto; + bottom: 15px; + } + </file> +</example> + */ +.directive('uibCarousel', [function() { + return { + transclude: true, + replace: true, + controller: 'UibCarouselController', + controllerAs: 'carousel', + require: 'carousel', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/carousel.html'; + }, + scope: { + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + } + }; +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.carousel.directive:slide + * @restrict EA + * + * @description + * Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}. Must be placed as a child of a carousel element. + * + * @param {boolean=} active Model binding, whether or not this slide is currently active. + * @param {number=} index The index of the slide. The slides will be sorted by this parameter. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> +<div ng-controller="CarouselDemoCtrl"> + <uib-carousel> + <uib-slide ng-repeat="slide in slides" active="slide.active" index="$index"> + <img ng-src="{{slide.image}}" style="margin:auto;"> + <div class="carousel-caption"> + <h4>Slide {{$index}}</h4> + <p>{{slide.text}}</p> + </div> + </uib-slide> + </uib-carousel> + Interval, in milliseconds: <input type="number" ng-model="myInterval"> + <br />Enter a negative number to stop the interval. +</div> + </file> + <file name="script.js"> +function CarouselDemoCtrl($scope) { + $scope.myInterval = 5000; +} + </file> + <file name="demo.css"> + .carousel-indicators { + top: auto; + bottom: 15px; + } + </file> +</example> +*/ + +.directive('uibSlide', function() { + return { + require: '^uibCarousel', + restrict: 'EA', + transclude: true, + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/slide.html'; + }, + scope: { + active: '=?', + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + carouselCtrl.addSlide(scope, element); + //when the scope is destroyed then remove the slide from the current slides array + scope.$on('$destroy', function() { + carouselCtrl.removeSlide(scope); + }); + + scope.$watch('active', function(active) { + if (active) { + carouselCtrl.select(scope); + } + }); + } + }; +}) + +.animation('.item', [ + '$injector', '$animate', +function ($injector, $animate) { + var NO_TRANSITION = 'uib-noTransition', + SLIDE_DIRECTION = 'uib-slideDirection', + $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + function removeClass(element, className, callback) { + element.removeClass(className); + if (callback) { + callback(); + } + } + + return { + beforeAddClass: function(element, className, done) { + // Due to transclusion, noTransition property is on parent's scope + if (className == 'active' && element.parent() && element.parent().parent() && + !element.parent().parent().data(NO_TRANSITION)) { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction == 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, + directionClass + ' ' + direction, done); + element.addClass(direction); + + if ($animateCss) { + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + } else { + $animate.addClass(element, directionClass).then(function () { + if (!stopped) { + removeClassFn(); + } + done(); + }); + } + + return function () { + stopped = true; + }; + } + done(); + }, + beforeRemoveClass: function (element, className, done) { + // Due to transclusion, noTransition property is on parent's scope + if (className === 'active' && element.parent() && element.parent().parent() && + !element.parent().parent().data(NO_TRANSITION)) { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction == 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, directionClass, done); + + if ($animateCss) { + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + } else { + $animate.addClass(element, directionClass).then(function() { + if (!stopped) { + removeClassFn(); + } + done(); + }); + } + return function() { + stopped = true; + }; + } + done(); + } + }; +}]); + +/* deprecated carousel below */ + +angular.module('ui.bootstrap.carousel') + +.value('$carouselSuppressWarning', false) + +.controller('CarouselController', ['$scope', '$element', '$controller', '$log', '$carouselSuppressWarning', function($scope, $element, $controller, $log, $carouselSuppressWarning) { + if (!$carouselSuppressWarning) { + $log.warn('CarouselController is now deprecated. Use UibCarouselController instead.'); + } + + angular.extend(this, $controller('UibCarouselController', { + $scope: $scope, + $element: $element + })); +}]) + +.directive('carousel', ['$log', '$carouselSuppressWarning', function($log, $carouselSuppressWarning) { + return { + transclude: true, + replace: true, + controller: 'CarouselController', + controllerAs: 'carousel', + require: 'carousel', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/carousel.html'; + }, + scope: { + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + }, + link: function() { + if (!$carouselSuppressWarning) { + $log.warn('carousel is now deprecated. Use uib-carousel instead.'); + } + } + }; +}]) + +.directive('slide', ['$log', '$carouselSuppressWarning', function($log, $carouselSuppressWarning) { + return { + require: '^carousel', + transclude: true, + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/carousel/slide.html'; + }, + scope: { + active: '=?', + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + if (!$carouselSuppressWarning) { + $log.warn('slide is now deprecated. Use uib-slide instead.'); + } + + carouselCtrl.addSlide(scope, element); + //when the scope is destroyed then remove the slide from the current slides array + scope.$on('$destroy', function() { + carouselCtrl.removeSlide(scope); + }); + + scope.$watch('active', function(active) { + if (active) { + carouselCtrl.select(scope); + } + }); + } + }; +}]); + +angular.module('ui.bootstrap.dateparser', []) + +.service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) { + // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js + var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + + var localeId; + var formatCodeToRegex; + + this.init = function() { + localeId = $locale.id; + + this.parsers = {}; + + formatCodeToRegex = { + 'yyyy': { + regex: '\\d{4}', + apply: function(value) { this.year = +value; } + }, + 'yy': { + regex: '\\d{2}', + apply: function(value) { this.year = +value + 2000; } + }, + 'y': { + regex: '\\d{1,4}', + apply: function(value) { this.year = +value; } + }, + 'MMMM': { + regex: $locale.DATETIME_FORMATS.MONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); } + }, + 'MMM': { + regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); } + }, + 'MM': { + regex: '0[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; } + }, + 'M': { + regex: '[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; } + }, + 'dd': { + regex: '[0-2][0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; } + }, + 'd': { + regex: '[1-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; } + }, + 'EEEE': { + regex: $locale.DATETIME_FORMATS.DAY.join('|') + }, + 'EEE': { + regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|') + }, + 'HH': { + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'hh': { + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'H': { + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; } + }, + 'h': { + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; } + }, + 'mm': { + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'm': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; } + }, + 'sss': { + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; } + }, + 'ss': { + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 's': { + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; } + }, + 'a': { + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } + + if (value === 'PM') { + this.hours += 12; + } + } + } + }; + }; + + this.init(); + + function createParser(format) { + var map = [], regex = format.split(''); + + angular.forEach(formatCodeToRegex, function(data, code) { + var index = format.indexOf(code); + + if (index > -1) { + format = format.split(''); + + regex[index] = '(' + data.regex + ')'; + format[index] = '$'; // Custom symbol to define consumed part of format + for (var i = index + 1, n = index + code.length; i < n; i++) { + regex[i] = ''; + format[i] = '$'; + } + format = format.join(''); + + map.push({ index: index, apply: data.apply }); + } + }); + + return { + regex: new RegExp('^' + regex.join('') + '$'), + map: orderByFilter(map, 'index') + }; + } + + this.parse = function(input, format, baseDate) { + if (!angular.isString(input) || !format) { + return input; + } + + format = $locale.DATETIME_FORMATS[format] || format; + format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&'); + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.parsers[format]) { + this.parsers[format] = createParser(format); + } + + var parser = this.parsers[format], + regex = parser.regex, + map = parser.map, + results = input.match(regex); + + if (results && results.length) { + var fields, dt; + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { + fields = { + year: baseDate.getFullYear(), + month: baseDate.getMonth(), + date: baseDate.getDate(), + hours: baseDate.getHours(), + minutes: baseDate.getMinutes(), + seconds: baseDate.getSeconds(), + milliseconds: baseDate.getMilliseconds() + }; + } else { + if (baseDate) { + $log.warn('dateparser:', 'baseDate is not a valid date'); + } + fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }; + } + + for (var i = 1, n = results.length; i < n; i++) { + var mapper = map[i-1]; + if (mapper.apply) { + mapper.apply.call(fields, results[i]); + } + } + + if (isValid(fields.year, fields.month, fields.date)) { + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) { + dt = new Date(baseDate); + dt.setFullYear(fields.year, fields.month, fields.date, + fields.hours, fields.minutes, fields.seconds, + fields.milliseconds || 0); + } else { + dt = new Date(fields.year, fields.month, fields.date, + fields.hours, fields.minutes, fields.seconds, + fields.milliseconds || 0); + } + } + + return dt; + } + }; + + // Check if date is valid for specific month (and year for February). + // Month: 0 = Jan, 1 = Feb, etc + function isValid(year, month, date) { + if (date < 1) { + return false; + } + + if (month === 1 && date > 28) { + return date === 29 && ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); + } + + if (month === 3 || month === 5 || month === 8 || month === 10) { + return date < 31; + } + + return true; + } +}]); + +/* Deprecated dateparser below */ + +angular.module('ui.bootstrap.dateparser') + +.value('$dateParserSuppressWarning', false) + +.service('dateParser', ['$log', '$dateParserSuppressWarning', 'uibDateParser', function($log, $dateParserSuppressWarning, uibDateParser) { + if (!$dateParserSuppressWarning) { + $log.warn('dateParser is now deprecated. Use uibDateParser instead.'); + } + + angular.extend(this, uibDateParser); +}]); + +angular.module('ui.bootstrap.position', []) + +/** + * A set of utility methods that can be use to retrieve position of DOM elements. + * It is meant to be used where we need to absolute-position DOM elements in + * relation to other, existing elements (this is the case for tooltips, popovers, + * typeahead suggestions etc.). + */ + .factory('$uibPosition', ['$document', '$window', function($document, $window) { + function getStyle(el, cssprop) { + if (el.currentStyle) { //IE + return el.currentStyle[cssprop]; + } else if ($window.getComputedStyle) { + return $window.getComputedStyle(el)[cssprop]; + } + // finally try and get inline style + return el.style[cssprop]; + } + + /** + * Checks if a given element is statically positioned + * @param element - raw DOM element + */ + function isStaticPositioned(element) { + return (getStyle(element, 'position') || 'static' ) === 'static'; + } + + /** + * returns the closest, non-statically positioned parentOffset of a given element + * @param element + */ + var parentOffsetEl = function(element) { + var docDomEl = $document[0]; + var offsetParent = element.offsetParent || docDomEl; + while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || docDomEl; + }; + + return { + /** + * Provides read-only equivalent of jQuery's position function: + * http://api.jquery.com/position/ + */ + position: function(element) { + var elBCR = this.offset(element); + var offsetParentBCR = { top: 0, left: 0 }; + var offsetParentEl = parentOffsetEl(element[0]); + if (offsetParentEl != $document[0]) { + offsetParentBCR = this.offset(angular.element(offsetParentEl)); + offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop; + offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft; + } + + var boundingClientRect = element[0].getBoundingClientRect(); + return { + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: elBCR.top - offsetParentBCR.top, + left: elBCR.left - offsetParentBCR.left + }; + }, + + /** + * Provides read-only equivalent of jQuery's offset function: + * http://api.jquery.com/offset/ + */ + offset: function(element) { + var boundingClientRect = element[0].getBoundingClientRect(); + return { + width: boundingClientRect.width || element.prop('offsetWidth'), + height: boundingClientRect.height || element.prop('offsetHeight'), + top: boundingClientRect.top + ($window.pageYOffset || $document[0].documentElement.scrollTop), + left: boundingClientRect.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft) + }; + }, + + /** + * Provides coordinates for the targetEl in relation to hostEl + */ + positionElements: function(hostEl, targetEl, positionStr, appendToBody) { + var positionStrParts = positionStr.split('-'); + var pos0 = positionStrParts[0], pos1 = positionStrParts[1] || 'center'; + + var hostElPos, + targetElWidth, + targetElHeight, + targetElPos; + + hostElPos = appendToBody ? this.offset(hostEl) : this.position(hostEl); + + targetElWidth = targetEl.prop('offsetWidth'); + targetElHeight = targetEl.prop('offsetHeight'); + + var shiftWidth = { + center: function() { + return hostElPos.left + hostElPos.width / 2 - targetElWidth / 2; + }, + left: function() { + return hostElPos.left; + }, + right: function() { + return hostElPos.left + hostElPos.width; + } + }; + + var shiftHeight = { + center: function() { + return hostElPos.top + hostElPos.height / 2 - targetElHeight / 2; + }, + top: function() { + return hostElPos.top; + }, + bottom: function() { + return hostElPos.top + hostElPos.height; + } + }; + + switch (pos0) { + case 'right': + targetElPos = { + top: shiftHeight[pos1](), + left: shiftWidth[pos0]() + }; + break; + case 'left': + targetElPos = { + top: shiftHeight[pos1](), + left: hostElPos.left - targetElWidth + }; + break; + case 'bottom': + targetElPos = { + top: shiftHeight[pos0](), + left: shiftWidth[pos1]() + }; + break; + default: + targetElPos = { + top: hostElPos.top - targetElHeight, + left: shiftWidth[pos1]() + }; + break; + } + + return targetElPos; + } + }; + }]); + +/* Deprecated position below */ + +angular.module('ui.bootstrap.position') + +.value('$positionSuppressWarning', false) + +.service('$position', ['$log', '$positionSuppressWarning', '$uibPosition', function($log, $positionSuppressWarning, $uibPosition) { + if (!$positionSuppressWarning) { + $log.warn('$position is now deprecated. Use $uibPosition instead.'); + } + + angular.extend(this, $uibPosition); +}]); + +angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.position']) + +.value('$datepickerSuppressError', false) + +.constant('uibDatepickerConfig', { + formatDay: 'dd', + formatMonth: 'MMMM', + formatYear: 'yyyy', + formatDayHeader: 'EEE', + formatDayTitle: 'MMMM yyyy', + formatMonthTitle: 'yyyy', + datepickerMode: 'day', + minMode: 'day', + maxMode: 'year', + showWeeks: true, + startingDay: 0, + yearRange: 20, + minDate: null, + maxDate: null, + shortcutPropagation: false +}) + +.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl; + + // Modes chain + this.modes = ['day', 'month', 'year']; + + // Configuration attributes + angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', + 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) { + self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key]; + }); + + // Watchable date attributes + angular.forEach(['minDate', 'maxDate'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = value ? new Date(value) : null; + self.refreshView(); + }); + } else { + self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null; + } + }); + + angular.forEach(['minMode', 'maxMode'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = angular.isDefined(value) ? value : $attrs[key]; + $scope[key] = self[key]; + if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) { + $scope.datepickerMode = self[key]; + } + }); + } else { + self[key] = datepickerConfig[key] || null; + $scope[key] = self[key]; + } + }); + + $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode; + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + if (angular.isDefined($attrs.initDate)) { + this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date(); + $scope.$parent.$watch($attrs.initDate, function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = initDate; + self.refreshView(); + } + }); + } else { + this.activeDate = new Date(); + } + + $scope.isActive = function(dateObject) { + if (self.compare(dateObject.date, self.activeDate) === 0) { + $scope.activeDateId = dateObject.uid; + return true; + } + return false; + }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = date; + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + this._refreshView(); + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date))); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + return { + date: date, + label: dateFilter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + current: this.compare(date, new Date()) === 0, + customClass: this.customClass(date) + }; + }; + + this.isDisabled = function(date) { + return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}))); + }; + + this.customClass = function(date) { + return $scope.customClass({date: date, mode: $scope.datepickerMode}); + }; + + // Split array into smaller arrays + this.split = function(arr, size) { + var arrays = []; + while (arr.length > 0) { + arrays.push(arr.splice(0, size)); + } + return arrays; + }; + + $scope.select = function(date) { + if ($scope.datepickerMode === self.minMode) { + var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]; + } + }; + + $scope.move = function(direction) { + var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), + month = self.activeDate.getMonth() + direction * (self.step.months || 0); + self.activeDate.setFullYear(year, month, 1); + self.refreshView(); + }; + + $scope.toggleMode = function(direction) { + direction = direction || 1; + + if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) { + return; + } + + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]; + }; + + // Key event mapper + $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; + + var focusElement = function() { + self.element[0].focus(); + }; + + // Listen for focus requests from popup directive + $scope.$on('uib:datepicker.focus', focusElement); + + $scope.keydown = function(evt) { + var key = $scope.keys[evt.which]; + + if (!key || evt.shiftKey || evt.altKey) { + return; + } + + evt.preventDefault(); + if (!self.shortcutPropagation) { + evt.stopPropagation(); + } + + if (key === 'enter' || key === 'space') { + if (self.isDisabled(self.activeDate)) { + return; // do nothing + } + $scope.select(self.activeDate); + } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { + $scope.toggleMode(key === 'up' ? 1 : -1); + } else { + self.handleKeyDown(key, evt); + self.refreshView(); + } + }; +}]) + +.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + this.step = { months: 1 }; + this.element = $element; + function getDaysInMonth(year, month) { + return ((month === 1) && (year % 4 === 0) && ((year % 100 !== 0) || (year % 400 === 0))) ? 29 : DAYS_IN_MONTH[month]; + } + + this.init = function(ctrl) { + angular.extend(ctrl, this); + scope.showWeeks = ctrl.showWeeks; + ctrl.refreshView(); + }; + + this.getDates = function(startDate, n) { + var dates = new Array(n), current = new Date(startDate), i = 0, date; + while (i < n) { + date = new Date(current); + dates[i++] = date; + current.setDate(current.getDate() + 1); + } + return dates; + }; + + this._refreshView = function() { + var year = this.activeDate.getFullYear(), + month = this.activeDate.getMonth(), + firstDayOfMonth = new Date(this.activeDate); + + firstDayOfMonth.setFullYear(year, month, 1); + + var difference = this.startingDay - firstDayOfMonth.getDay(), + numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference, + firstDate = new Date(firstDayOfMonth); + + if (numDisplayedFromPreviousMonth > 0) { + firstDate.setDate(-numDisplayedFromPreviousMonth + 1); + } + + // 42 is the number of days on a six-month calendar + var days = this.getDates(firstDate, 42); + for (var i = 0; i < 42; i ++) { + days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), { + secondary: days[i].getMonth() !== month, + uid: scope.uniqueId + '-' + i + }); + } + + scope.labels = new Array(7); + for (var j = 0; j < 7; j++) { + scope.labels[j] = { + abbr: dateFilter(days[j].date, this.formatDayHeader), + full: dateFilter(days[j].date, 'EEEE') + }; + } + + scope.title = dateFilter(this.activeDate, this.formatDayTitle); + scope.rows = this.split(days, 7); + + if (scope.showWeeks) { + scope.weekNumbers = []; + var thursdayIndex = (4 + 7 - this.startingDay) % 7, + numWeeks = scope.rows.length; + for (var curWeek = 0; curWeek < numWeeks; curWeek++) { + scope.weekNumbers.push( + getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date)); + } + } + }; + + this.compare = function(date1, date2) { + return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate())); + }; + + function getISO8601WeekNumber(date) { + var checkDate = new Date(date); + checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday + var time = checkDate.getTime(); + checkDate.setMonth(0); // Compare with Jan 1 + checkDate.setDate(1); + return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1; + } + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getDate(); + + if (key === 'left') { + date = date - 1; // up + } else if (key === 'up') { + date = date - 7; // down + } else if (key === 'right') { + date = date + 1; // down + } else if (key === 'down') { + date = date + 7; + } else if (key === 'pageup' || key === 'pagedown') { + var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setMonth(month, 1); + date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date); + } else if (key === 'home') { + date = 1; + } else if (key === 'end') { + date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()); + } + this.activeDate.setDate(date); + }; +}]) + +.controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + this.step = { years: 1 }; + this.element = $element; + + this.init = function(ctrl) { + angular.extend(ctrl, this); + ctrl.refreshView(); + }; + + this._refreshView = function() { + var months = new Array(12), + year = this.activeDate.getFullYear(), + date; + + for (var i = 0; i < 12; i++) { + date = new Date(this.activeDate); + date.setFullYear(year, i, 1); + months[i] = angular.extend(this.createDateObject(date, this.formatMonth), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = dateFilter(this.activeDate, this.formatMonthTitle); + scope.rows = this.split(months, 3); + }; + + this.compare = function(date1, date2) { + return new Date(date1.getFullYear(), date1.getMonth()) - new Date(date2.getFullYear(), date2.getMonth()); + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getMonth(); + + if (key === 'left') { + date = date - 1; // up + } else if (key === 'up') { + date = date - 3; // down + } else if (key === 'right') { + date = date + 1; // down + } else if (key === 'down') { + date = date + 3; + } else if (key === 'pageup' || key === 'pagedown') { + var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1); + this.activeDate.setFullYear(year); + } else if (key === 'home') { + date = 0; + } else if (key === 'end') { + date = 11; + } + this.activeDate.setMonth(date); + }; +}]) + +.controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) { + var range; + this.element = $element; + + function getStartingYear(year) { + return parseInt((year - 1) / range, 10) * range + 1; + } + + this.yearpickerInit = function() { + range = this.yearRange; + this.step = { years: range }; + }; + + this._refreshView = function() { + var years = new Array(range), date; + + for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) { + date = new Date(this.activeDate); + date.setFullYear(start + i, 0, 1); + years[i] = angular.extend(this.createDateObject(date, this.formatYear), { + uid: scope.uniqueId + '-' + i + }); + } + + scope.title = [years[0].label, years[range - 1].label].join(' - '); + scope.rows = this.split(years, 5); + }; + + this.compare = function(date1, date2) { + return date1.getFullYear() - date2.getFullYear(); + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getFullYear(); + + if (key === 'left') { + date = date - 1; // up + } else if (key === 'up') { + date = date - 5; // down + } else if (key === 'right') { + date = date + 1; // down + } else if (key === 'down') { + date = date + 5; + } else if (key === 'pageup' || key === 'pagedown') { + date += (key === 'pageup' ? - 1 : 1) * this.step.years; + } else if (key === 'home') { + date = getStartingYear(this.activeDate.getFullYear()); + } else if (key === 'end') { + date = getStartingYear(this.activeDate.getFullYear()) + range - 1; + } + this.activeDate.setFullYear(date); + }; +}]) + +.directive('uibDatepicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/datepicker.html'; + }, + scope: { + datepickerMode: '=?', + dateDisabled: '&', + customClass: '&', + shortcutPropagation: '&?' + }, + require: ['uibDatepicker', '^ngModel'], + controller: 'UibDatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}) + +.directive('uibDaypicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/day.html'; + }, + require: ['^?uibDatepicker', 'uibDaypicker', '^?datepicker'], + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0] || ctrls[2], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibMonthpicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/month.html'; + }, + require: ['^?uibDatepicker', 'uibMonthpicker', '^?datepicker'], + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0] || ctrls[2], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibYearpicker', function() { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/year.html'; + }, + require: ['^?uibDatepicker', 'uibYearpicker', '^?datepicker'], + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + var ctrl = ctrls[0] || ctrls[2]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}) + +.constant('uibDatepickerPopupConfig', { + datepickerPopup: 'yyyy-MM-dd', + datepickerPopupTemplateUrl: 'template/datepicker/popup.html', + datepickerTemplateUrl: 'template/datepicker/datepicker.html', + html5Types: { + date: 'yyyy-MM-dd', + 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', + 'month': 'yyyy-MM' + }, + currentText: 'Today', + clearText: 'Clear', + closeText: 'Done', + closeOnDateSelection: true, + appendToBody: false, + showButtonBar: true, + onOpenFocus: true +}) + +.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', +function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout) { + var self = this; + var cache = {}, + isHtml5DateInput = false; + var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, + datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, + ngModel, $popup; + + scope.watchData = {}; + + this.init = function(_ngModel_) { + ngModel = _ngModel_; + closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection; + appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody; + onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus; + datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl; + datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl; + + scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar; + + if (datepickerPopupConfig.html5Types[attrs.type]) { + dateFormat = datepickerPopupConfig.html5Types[attrs.type]; + isHtml5DateInput = true; + } else { + dateFormat = attrs.datepickerPopup || attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup; + attrs.$observe('uibDatepickerPopup', function(value, oldValue) { + var newDateFormat = value || datepickerPopupConfig.datepickerPopup; + // Invalidate the $modelValue to ensure that formatters re-run + // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764 + if (newDateFormat !== dateFormat) { + dateFormat = newDateFormat; + ngModel.$modelValue = null; + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + } + }); + } + + if (!dateFormat) { + throw new Error('uibDatepickerPopup must have a date format specified.'); + } + + if (isHtml5DateInput && attrs.datepickerPopup) { + throw new Error('HTML5 date input types do not support custom formats.'); + } + + // popup element used to display calendar + popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>'); + popupEl.attr({ + 'ng-model': 'date', + 'ng-change': 'dateSelection(date)', + 'template-url': datepickerPopupTemplateUrl + }); + + // datepicker element + datepickerEl = angular.element(popupEl.children()[0]); + datepickerEl.attr('template-url', datepickerTemplateUrl); + + if (isHtml5DateInput) { + if (attrs.type === 'month') { + datepickerEl.attr('datepicker-mode', '"month"'); + datepickerEl.attr('min-mode', 'month'); + } + } + + if (attrs.datepickerOptions) { + var options = scope.$parent.$eval(attrs.datepickerOptions); + if (options && options.initDate) { + scope.initDate = options.initDate; + datepickerEl.attr('init-date', 'initDate'); + delete options.initDate; + } + angular.forEach(options, function(value, option) { + datepickerEl.attr(cameltoDash(option), value); + }); + } + + angular.forEach(['minMode', 'maxMode', 'minDate', 'maxDate', 'datepickerMode', 'initDate', 'shortcutPropagation'], function(key) { + if (attrs[key]) { + var getAttribute = $parse(attrs[key]); + scope.$parent.$watch(getAttribute, function(value) { + scope.watchData[key] = value; + if (key === 'minDate' || key === 'maxDate') { + cache[key] = new Date(value); + } + }); + datepickerEl.attr(cameltoDash(key), 'watchData.' + key); + + // Propagate changes from datepicker to outside + if (key === 'datepickerMode') { + var setAttribute = getAttribute.assign; + scope.$watch('watchData.' + key, function(value, oldvalue) { + if (angular.isFunction(setAttribute) && value !== oldvalue) { + setAttribute(scope.$parent, value); + } + }); + } + } + }); + if (attrs.dateDisabled) { + datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })'); + } + + if (attrs.showWeeks) { + datepickerEl.attr('show-weeks', attrs.showWeeks); + } + + if (attrs.customClass) { + datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })'); + } + + if (!isHtml5DateInput) { + // Internal API to maintain the correct ng-invalid-[key] class + ngModel.$$parserName = 'date'; + ngModel.$validators.date = validator; + ngModel.$parsers.unshift(parseDate); + ngModel.$formatters.push(function(value) { + scope.date = value; + return ngModel.$isEmpty(value) ? value : dateFilter(value, dateFormat); + }); + } else { + ngModel.$formatters.push(function(value) { + scope.date = value; + return value; + }); + } + + // Detect changes in the view from the text box + ngModel.$viewChangeListeners.push(function() { + scope.date = dateParser.parse(ngModel.$viewValue, dateFormat, scope.date); + }); + + element.bind('keydown', inputKeydownBind); + + $popup = $compile(popupEl)(scope); + // Prevent jQuery cache memory leak (template is now redundant after linking) + popupEl.remove(); + + if (appendToBody) { + $document.find('body').append($popup); + } else { + element.after($popup); + } + + scope.$on('$destroy', function() { + if (scope.isOpen === true) { + if (!$rootScope.$$phase) { + scope.$apply(function() { + scope.isOpen = false; + }); + } + } + + $popup.remove(); + element.unbind('keydown', inputKeydownBind); + $document.unbind('click', documentClickBind); + }); + }; + + scope.getText = function(key) { + return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; + }; + + scope.isDisabled = function(date) { + if (date === 'today') { + date = new Date(); + } + + return ((scope.watchData.minDate && scope.compare(date, cache.minDate) < 0) || + (scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0)); + }; + + scope.compare = function(date1, date2) { + return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate())); + }; + + // Inner change + scope.dateSelection = function(dt) { + if (angular.isDefined(dt)) { + scope.date = dt; + } + var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function + element.val(date); + ngModel.$setViewValue(date); + + if (closeOnDateSelection) { + scope.isOpen = false; + element[0].focus(); + } + }; + + scope.keydown = function(evt) { + if (evt.which === 27) { + scope.isOpen = false; + element[0].focus(); + } + }; + + scope.select = function(date) { + if (date === 'today') { + var today = new Date(); + if (angular.isDate(scope.date)) { + date = new Date(scope.date); + date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate()); + } else { + date = new Date(today.setHours(0, 0, 0, 0)); + } + } + scope.dateSelection(date); + }; + + scope.close = function() { + scope.isOpen = false; + element[0].focus(); + }; + + scope.$watch('isOpen', function(value) { + if (value) { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top = scope.position.top + element.prop('offsetHeight'); + + $timeout(function() { + if (onOpenFocus) { + scope.$broadcast('uib:datepicker.focus'); + } + $document.bind('click', documentClickBind); + }, 0, false); + } else { + $document.unbind('click', documentClickBind); + } + }); + + function cameltoDash(string) { + return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); + } + + function parseDate(viewValue) { + if (angular.isNumber(viewValue)) { + // presumably timestamp to date object + viewValue = new Date(viewValue); + } + + if (!viewValue) { + return null; + } else if (angular.isDate(viewValue) && !isNaN(viewValue)) { + return viewValue; + } else if (angular.isString(viewValue)) { + var date = dateParser.parse(viewValue, dateFormat, scope.date); + if (isNaN(date)) { + return undefined; + } else { + return date; + } + } else { + return undefined; + } + } + + function validator(modelValue, viewValue) { + var value = modelValue || viewValue; + + if (!attrs.ngRequired && !value) { + return true; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + if (!value) { + return true; + } else if (angular.isDate(value) && !isNaN(value)) { + return true; + } else if (angular.isString(value)) { + var date = dateParser.parse(value, dateFormat); + return !isNaN(date); + } else { + return false; + } + } + + function documentClickBind(event) { + var popup = $popup[0]; + var dpContainsTarget = element[0].contains(event.target); + // The popup node may not be an element node + // In some browsers (IE) only element nodes have the 'contains' function + var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target); + if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) { + scope.$apply(function() { + scope.isOpen = false; + }); + } + } + + function inputKeydownBind(evt) { + if (evt.which === 27 && scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + scope.$apply(function() { + scope.isOpen = false; + }); + element[0].focus(); + } else if (evt.which === 40 && !scope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + scope.$apply(function() { + scope.isOpen = true; + }); + } + } +}]) + +.directive('uibDatepickerPopup', function() { + return { + require: ['ngModel', 'uibDatepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@', + dateDisabled: '&', + customClass: '&' + }, + link: function(scope, element, attrs, ctrls) { + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}) + +.directive('uibDatepickerPopupWrap', function() { + return { + replace: true, + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/popup.html'; + } + }; +}); + +/* Deprecated datepicker below */ + +angular.module('ui.bootstrap.datepicker') + +.value('$datepickerSuppressWarning', false) + +.controller('DatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', '$datepickerSuppressWarning', function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, $datepickerSuppressWarning) { + if (!$datepickerSuppressWarning) { + $log.warn('DatepickerController is now deprecated. Use UibDatepickerController instead.'); + } + + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl; + + this.modes = ['day', 'month', 'year']; + + angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', + 'showWeeks', 'startingDay', 'yearRange', 'shortcutPropagation'], function(key, index) { + self[key] = angular.isDefined($attrs[key]) ? (index < 6 ? $interpolate($attrs[key])($scope.$parent) : $scope.$parent.$eval($attrs[key])) : datepickerConfig[key]; + }); + + angular.forEach(['minDate', 'maxDate'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = value ? new Date(value) : null; + self.refreshView(); + }); + } else { + self[key] = datepickerConfig[key] ? new Date(datepickerConfig[key]) : null; + } + }); + + angular.forEach(['minMode', 'maxMode'], function(key) { + if ($attrs[key]) { + $scope.$parent.$watch($parse($attrs[key]), function(value) { + self[key] = angular.isDefined(value) ? value : $attrs[key]; + $scope[key] = self[key]; + if ((key == 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key])) || (key == 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key]))) { + $scope.datepickerMode = self[key]; + } + }); + } else { + self[key] = datepickerConfig[key] || null; + $scope[key] = self[key]; + } + }); + + $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode; + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + if (angular.isDefined($attrs.initDate)) { + this.activeDate = $scope.$parent.$eval($attrs.initDate) || new Date(); + $scope.$parent.$watch($attrs.initDate, function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = initDate; + self.refreshView(); + } + }); + } else { + this.activeDate = new Date(); + } + + $scope.isActive = function(dateObject) { + if (self.compare(dateObject.date, self.activeDate) === 0) { + $scope.activeDateId = dateObject.uid; + return true; + } + return false; + }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = date; + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + this._refreshView(); + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + ngModelCtrl.$setValidity('dateDisabled', !date || (this.element && !this.isDisabled(date))); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + return { + date: date, + label: dateFilter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + current: this.compare(date, new Date()) === 0, + customClass: this.customClass(date) + }; + }; + + this.isDisabled = function(date) { + return ((this.minDate && this.compare(date, this.minDate) < 0) || (this.maxDate && this.compare(date, this.maxDate) > 0) || ($attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode}))); + }; + + this.customClass = function(date) { + return $scope.customClass({date: date, mode: $scope.datepickerMode}); + }; + + // Split array into smaller arrays + this.split = function(arr, size) { + var arrays = []; + while (arr.length > 0) { + arrays.push(arr.splice(0, size)); + } + return arrays; + }; + + this.fixTimeZone = function(date) { + var hours = date.getHours(); + date.setHours(hours === 23 ? hours + 2 : 0); + }; + + $scope.select = function(date) { + if ($scope.datepickerMode === self.minMode) { + var dt = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]; + } + }; + + $scope.move = function(direction) { + var year = self.activeDate.getFullYear() + direction * (self.step.years || 0), + month = self.activeDate.getMonth() + direction * (self.step.months || 0); + self.activeDate.setFullYear(year, month, 1); + self.refreshView(); + }; + + $scope.toggleMode = function(direction) { + direction = direction || 1; + + if (($scope.datepickerMode === self.maxMode && direction === 1) || ($scope.datepickerMode === self.minMode && direction === -1)) { + return; + } + + $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]; + }; + + // Key event mapper + $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' }; + + var focusElement = function() { + self.element[0].focus(); + }; + + $scope.$on('uib:datepicker.focus', focusElement); + + $scope.keydown = function(evt) { + var key = $scope.keys[evt.which]; + + if (!key || evt.shiftKey || evt.altKey) { + return; + } + + evt.preventDefault(); + if (!self.shortcutPropagation) { + evt.stopPropagation(); + } + + if (key === 'enter' || key === 'space') { + if (self.isDisabled(self.activeDate)) { + return; // do nothing + } + $scope.select(self.activeDate); + } else if (evt.ctrlKey && (key === 'up' || key === 'down')) { + $scope.toggleMode(key === 'up' ? 1 : -1); + } else { + self.handleKeyDown(key, evt); + self.refreshView(); + } + }; +}]) + +.directive('datepicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/datepicker.html'; + }, + scope: { + datepickerMode: '=?', + dateDisabled: '&', + customClass: '&', + shortcutPropagation: '&?' + }, + require: ['datepicker', '^ngModel'], + controller: 'DatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('datepicker is now deprecated. Use uib-datepicker instead.'); + } + + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}]) + +.directive('daypicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: 'template/datepicker/day.html', + require: ['^datepicker', 'daypicker'], + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('daypicker is now deprecated. Use uib-daypicker instead.'); + } + + var datepickerCtrl = ctrls[0], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}]) + +.directive('monthpicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: 'template/datepicker/month.html', + require: ['^datepicker', 'monthpicker'], + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('monthpicker is now deprecated. Use uib-monthpicker instead.'); + } + + var datepickerCtrl = ctrls[0], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}]) + +.directive('yearpicker', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + templateUrl: 'template/datepicker/year.html', + require: ['^datepicker', 'yearpicker'], + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('yearpicker is now deprecated. Use uib-yearpicker instead.'); + } + + var ctrl = ctrls[0]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}]) + +.directive('datepickerPopup', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + require: ['ngModel', 'datepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@', + dateDisabled: '&', + customClass: '&' + }, + link: function(scope, element, attrs, ctrls) { + if (!$datepickerSuppressWarning) { + $log.warn('datepicker-popup is now deprecated. Use uib-datepicker-popup instead.'); + } + + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}]) + +.directive('datepickerPopupWrap', ['$log', '$datepickerSuppressWarning', function($log, $datepickerSuppressWarning) { + return { + replace: true, + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/datepicker/popup.html'; + }, + link: function() { + if (!$datepickerSuppressWarning) { + $log.warn('datepicker-popup-wrap is now deprecated. Use uib-datepicker-popup-wrap instead.'); + } + } + }; +}]); + +angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position']) + +.constant('uibDropdownConfig', { + openClass: 'open' +}) + +.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) { + var openScope = null; + + this.open = function(dropdownScope) { + if (!openScope) { + $document.bind('click', closeDropdown); + $document.bind('keydown', keybindFilter); + } + + if (openScope && openScope !== dropdownScope) { + openScope.isOpen = false; + } + + openScope = dropdownScope; + }; + + this.close = function(dropdownScope) { + if (openScope === dropdownScope) { + openScope = null; + $document.unbind('click', closeDropdown); + $document.unbind('keydown', keybindFilter); + } + }; + + var closeDropdown = function(evt) { + // This method may still be called during the same mouse event that + // unbound this event handler. So check openScope before proceeding. + if (!openScope) { return; } + + if (evt && openScope.getAutoClose() === 'disabled') { return ; } + + var toggleElement = openScope.getToggleElement(); + if (evt && toggleElement && toggleElement[0].contains(evt.target)) { + return; + } + + var dropdownElement = openScope.getDropdownElement(); + if (evt && openScope.getAutoClose() === 'outsideClick' && + dropdownElement && dropdownElement[0].contains(evt.target)) { + return; + } + + openScope.isOpen = false; + + if (!$rootScope.$$phase) { + openScope.$apply(); + } + }; + + var keybindFilter = function(evt) { + if (evt.which === 27) { + openScope.focusToggleElement(); + closeDropdown(); + } else if (openScope.isKeynavEnabled() && /(38|40)/.test(evt.which) && openScope.isOpen) { + evt.preventDefault(); + evt.stopPropagation(); + openScope.focusDropdownEntry(evt.which); + } + }; +}]) + +.controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) { + var self = this, + scope = $scope.$new(), // create a child scope so we are not polluting original one + templateScope, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + appendToBody = false, + keynavEnabled =false, + selectedOption = null; + + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + appendToBody = angular.isDefined($attrs.dropdownAppendToBody); + keynavEnabled = angular.isDefined($attrs.uibKeyboardNav); + + if (appendToBody && self.dropdownMenu) { + $document.find('body').append(self.dropdownMenu); + $element.on('$destroy', function handleDestroyEvent() { + self.dropdownMenu.remove(); + }); + } + }; + + this.toggle = function(open) { + return scope.isOpen = arguments.length ? !!open : !scope.isOpen; + }; + + // Allow other directives to watch status + this.isOpen = function() { + return scope.isOpen; + }; + + scope.getToggleElement = function() { + return self.toggleElement; + }; + + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return $element; + }; + + scope.isKeynavEnabled = function() { + return keynavEnabled; + }; + + scope.focusDropdownEntry = function(keyCode) { + var elems = self.dropdownMenu ? //If append to body is used. + (angular.element(self.dropdownMenu).find('a')) : + (angular.element($element).find('ul').eq(0).find('a')); + + switch (keyCode) { + case (40): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = 0; + } else { + self.selectedOption = (self.selectedOption === elems.length - 1 ? + self.selectedOption : + self.selectedOption + 1); + } + break; + } + case (38): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = elems.length - 1; + } else { + self.selectedOption = self.selectedOption === 0 ? + 0 : self.selectedOption - 1; + } + break; + } + } + elems[self.selectedOption].focus(); + }; + + scope.getDropdownElement = function() { + return self.dropdownMenu; + }; + + scope.focusToggleElement = function() { + if (self.toggleElement) { + self.toggleElement[0].focus(); + } + }; + + scope.$watch('isOpen', function(isOpen, wasOpen) { + if (appendToBody && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true); + var css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + css.right = (window.innerWidth - (pos.left + $element.prop('offsetWidth'))) + 'px'; + } + + self.dropdownMenu.css(css); + } + + $animate[isOpen ? 'addClass' : 'removeClass']($element, openClass).then(function() { + if (angular.isDefined(isOpen) && isOpen !== wasOpen) { + toggleInvoker($scope, { open: !!isOpen }); + } + }); + + if (isOpen) { + if (self.dropdownMenuTemplateUrl) { + $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { + templateScope = scope.$new(); + $compile(tplContent.trim())(templateScope, function(dropdownElement) { + var newEl = dropdownElement; + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + }); + }); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope); + } else { + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element('<ul class="dropdown-menu"></ul>'); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + uibDropdownService.close(scope); + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); + + $scope.$on('$locationChangeSuccess', function() { + if (scope.getAutoClose() !== 'disabled') { + scope.isOpen = false; + } + }); + + var offDestroy = $scope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); +}]) + +.directive('uibDropdown', function() { + return { + controller: 'UibDropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + dropdownCtrl.init(); + } + }; +}) + +.directive('uibDropdownMenu', function() { + return { + restrict: 'AC', + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { + return; + } + + element.addClass('dropdown-menu'); + + var tplUrl = attrs.templateUrl; + if (tplUrl) { + dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; + } + + if (!dropdownCtrl.dropdownMenu) { + dropdownCtrl.dropdownMenu = element; + } + } + }; +}) + +.directive('uibKeyboardNav', function() { + return { + restrict: 'A', + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + element.bind('keydown', function(e) { + if ([38, 40].indexOf(e.which) !== -1) { + e.preventDefault(); + e.stopPropagation(); + + var elems = dropdownCtrl.dropdownMenu.find('a'); + + switch (e.which) { + case (40): { // Down + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = 0; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ? + dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1; + } + break; + } + case (38): { // Up + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = elems.length - 1; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ? + 0 : dropdownCtrl.selectedOption - 1; + } + break; + } + } + elems[dropdownCtrl.selectedOption].focus(); + } + }); + } + }; +}) + +.directive('uibDropdownToggle', function() { + return { + require: '?^uibDropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl) { + return; + } + + element.addClass('dropdown-toggle'); + + dropdownCtrl.toggleElement = element; + + var toggleDropdown = function(event) { + event.preventDefault(); + + if (!element.hasClass('disabled') && !attrs.disabled) { + scope.$apply(function() { + dropdownCtrl.toggle(); + }); + } + }; + + element.bind('click', toggleDropdown); + + // WAI-ARIA + element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); + scope.$watch(dropdownCtrl.isOpen, function(isOpen) { + element.attr('aria-expanded', !!isOpen); + }); + + scope.$on('$destroy', function() { + element.unbind('click', toggleDropdown); + }); + } + }; +}); + +/* Deprecated dropdown below */ + +angular.module('ui.bootstrap.dropdown') + +.value('$dropdownSuppressWarning', false) + +.service('dropdownService', ['$log', '$dropdownSuppressWarning', 'uibDropdownService', function($log, $dropdownSuppressWarning, uibDropdownService) { + if (!$dropdownSuppressWarning) { + $log.warn('dropdownService is now deprecated. Use uibDropdownService instead.'); + } + + angular.extend(this, uibDropdownService); +}]) + +.controller('DropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', '$log', '$dropdownSuppressWarning', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest, $log, $dropdownSuppressWarning) { + if (!$dropdownSuppressWarning) { + $log.warn('DropdownController is now deprecated. Use UibDropdownController instead.'); + } + + var self = this, + scope = $scope.$new(), // create a child scope so we are not polluting original one + templateScope, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + appendToBody = false, + keynavEnabled =false, + selectedOption = null; + + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + appendToBody = angular.isDefined($attrs.dropdownAppendToBody); + keynavEnabled = angular.isDefined($attrs.uibKeyboardNav); + + if (appendToBody && self.dropdownMenu) { + $document.find('body').append(self.dropdownMenu); + $element.on('$destroy', function handleDestroyEvent() { + self.dropdownMenu.remove(); + }); + } + }; + + this.toggle = function(open) { + return scope.isOpen = arguments.length ? !!open : !scope.isOpen; + }; + + // Allow other directives to watch status + this.isOpen = function() { + return scope.isOpen; + }; + + scope.getToggleElement = function() { + return self.toggleElement; + }; + + scope.getAutoClose = function() { + return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled' + }; + + scope.getElement = function() { + return $element; + }; + + scope.isKeynavEnabled = function() { + return keynavEnabled; + }; + + scope.focusDropdownEntry = function(keyCode) { + var elems = self.dropdownMenu ? //If append to body is used. + (angular.element(self.dropdownMenu).find('a')) : + (angular.element($element).find('ul').eq(0).find('a')); + + switch (keyCode) { + case (40): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = 0; + } else { + self.selectedOption = (self.selectedOption === elems.length -1 ? + self.selectedOption : + self.selectedOption + 1); + } + break; + } + case (38): { + if (!angular.isNumber(self.selectedOption)) { + self.selectedOption = elems.length - 1; + } else { + self.selectedOption = self.selectedOption === 0 ? + 0 : self.selectedOption - 1; + } + break; + } + } + elems[self.selectedOption].focus(); + }; + + scope.getDropdownElement = function() { + return self.dropdownMenu; + }; + + scope.focusToggleElement = function() { + if (self.toggleElement) { + self.toggleElement[0].focus(); + } + }; + + scope.$watch('isOpen', function(isOpen, wasOpen) { + if (appendToBody && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true); + var css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + var rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + css.right = (window.innerWidth - (pos.left + $element.prop('offsetWidth'))) + 'px'; + } + + self.dropdownMenu.css(css); + } + + $animate[isOpen ? 'addClass' : 'removeClass']($element, openClass).then(function() { + if (angular.isDefined(isOpen) && isOpen !== wasOpen) { + toggleInvoker($scope, { open: !!isOpen }); + } + }); + + if (isOpen) { + if (self.dropdownMenuTemplateUrl) { + $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) { + templateScope = scope.$new(); + $compile(tplContent.trim())(templateScope, function(dropdownElement) { + var newEl = dropdownElement; + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + }); + }); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope); + } else { + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element('<ul class="dropdown-menu"></ul>'); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + uibDropdownService.close(scope); + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); + + $scope.$on('$locationChangeSuccess', function() { + if (scope.getAutoClose() !== 'disabled') { + scope.isOpen = false; + } + }); + + var offDestroy = $scope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); +}]) + +.directive('dropdown', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + controller: 'DropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + if (!$dropdownSuppressWarning) { + $log.warn('dropdown is now deprecated. Use uib-dropdown instead.'); + } + + dropdownCtrl.init(); + } + }; +}]) + +.directive('dropdownMenu', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + restrict: 'AC', + require: '?^dropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) { + return; + } + + if (!$dropdownSuppressWarning) { + $log.warn('dropdown-menu is now deprecated. Use uib-dropdown-menu instead.'); + } + + element.addClass('dropdown-menu'); + + var tplUrl = attrs.templateUrl; + if (tplUrl) { + dropdownCtrl.dropdownMenuTemplateUrl = tplUrl; + } + + if (!dropdownCtrl.dropdownMenu) { + dropdownCtrl.dropdownMenu = element; + } + } + }; +}]) + +.directive('keyboardNav', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + restrict: 'A', + require: '?^dropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!$dropdownSuppressWarning) { + $log.warn('keyboard-nav is now deprecated. Use uib-keyboard-nav instead.'); + } + + element.bind('keydown', function(e) { + if ([38, 40].indexOf(e.which) !== -1) { + e.preventDefault(); + e.stopPropagation(); + + var elems = dropdownCtrl.dropdownMenu.find('a'); + + switch (e.which) { + case (40): { // Down + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = 0; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === elems.length -1 ? + dropdownCtrl.selectedOption : dropdownCtrl.selectedOption + 1; + } + break; + } + case (38): { // Up + if (!angular.isNumber(dropdownCtrl.selectedOption)) { + dropdownCtrl.selectedOption = elems.length - 1; + } else { + dropdownCtrl.selectedOption = dropdownCtrl.selectedOption === 0 ? + 0 : dropdownCtrl.selectedOption - 1; + } + break; + } + } + elems[dropdownCtrl.selectedOption].focus(); + } + }); + } + }; +}]) + +.directive('dropdownToggle', ['$log', '$dropdownSuppressWarning', function($log, $dropdownSuppressWarning) { + return { + require: '?^dropdown', + link: function(scope, element, attrs, dropdownCtrl) { + if (!$dropdownSuppressWarning) { + $log.warn('dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead.'); + } + + if (!dropdownCtrl) { + return; + } + + element.addClass('dropdown-toggle'); + + dropdownCtrl.toggleElement = element; + + var toggleDropdown = function(event) { + event.preventDefault(); + + if (!element.hasClass('disabled') && !attrs.disabled) { + scope.$apply(function() { + dropdownCtrl.toggle(); + }); + } + }; + + element.bind('click', toggleDropdown); + + // WAI-ARIA + element.attr({ 'aria-haspopup': true, 'aria-expanded': false }); + scope.$watch(dropdownCtrl.isOpen, function(isOpen) { + element.attr('aria-expanded', !!isOpen); + }); + + scope.$on('$destroy', function() { + element.unbind('click', toggleDropdown); + }); + } + }; +}]); + +angular.module('ui.bootstrap.stackedMap', []) +/** + * A helper, internal data structure that acts as a map but also allows getting / removing + * elements in the LIFO order + */ + .factory('$$stackedMap', function() { + return { + createNew: function() { + var stack = []; + + return { + add: function(key, value) { + stack.push({ + key: key, + value: value + }); + }, + get: function(key) { + for (var i = 0; i < stack.length; i++) { + if (key == stack[i].key) { + return stack[i]; + } + } + }, + keys: function() { + var keys = []; + for (var i = 0; i < stack.length; i++) { + keys.push(stack[i].key); + } + return keys; + }, + top: function() { + return stack[stack.length - 1]; + }, + remove: function(key) { + var idx = -1; + for (var i = 0; i < stack.length; i++) { + if (key == stack[i].key) { + idx = i; + break; + } + } + return stack.splice(idx, 1)[0]; + }, + removeTop: function() { + return stack.splice(stack.length - 1, 1)[0]; + }, + length: function() { + return stack.length; + } + }; + } + }; + }); +angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) +/** + * A helper, internal data structure that stores all references attached to key + */ + .factory('$$multiMap', function() { + return { + createNew: function() { + var map = {}; + + return { + entries: function() { + return Object.keys(map).map(function(key) { + return { + key: key, + value: map[key] + }; + }); + }, + get: function(key) { + return map[key]; + }, + hasKey: function(key) { + return !!map[key]; + }, + keys: function() { + return Object.keys(map); + }, + put: function(key, value) { + if (!map[key]) { + map[key] = []; + } + + map[key].push(value); + }, + remove: function(key, value) { + var values = map[key]; + + if (!values) { + return; + } + + var idx = values.indexOf(value); + + if (idx !== -1) { + values.splice(idx, 1); + } + + if (!values.length) { + delete map[key]; + } + } + }; + } + }; + }) + +/** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('uibModalBackdrop', [ + '$animate', '$injector', '$uibModalStack', + function($animate , $injector, $modalStack) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + replace: true, + templateUrl: 'template/modal/backdrop.html', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + // Temporary fix for prefixing + element.addClass('modal-backdrop'); + + if (attrs.modalInClass) { + if ($animateCss) { + $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + } + }]) + + .directive('uibModalWindow', [ + '$uibModalStack', '$q', '$animate', '$injector', + function($modalStack , $q , $animate, $injector) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + scope: { + index: '@' + }, + replace: true, + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'template/modal/window.html'; + }, + link: function(scope, element, attrs) { + element.addClass(attrs.windowClass || ''); + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + scope.close = function(evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is render. + var modalRenderDeferObj = $q.defer(); + // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. + // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. + attrs.$observe('modalRender', function(value) { + if (value == 'true') { + modalRenderDeferObj.resolve(); + } + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + if ($animateCss) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + animationPromise = $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + + + $q.when(animationPromise).then(function() { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } + }); + + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + }); + } + }; + }]) + + .directive('uibModalAnimationClass', function() { + return { + compile: function(tElement, tAttrs) { + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.uibModalAnimationClass); + } + } + }; + }) + + .directive('uibModalTransclude', function() { + return { + link: function($scope, $element, $attrs, controller, $transclude) { + $transclude($scope.$parent, function(clone) { + $element.empty(); + $element.append(clone); + }); + } + }; + }) + + .factory('$uibModalStack', [ + '$animate', '$timeout', '$document', '$compile', '$rootScope', + '$q', + '$injector', + '$$multiMap', + '$$stackedMap', + function($animate , $timeout , $document , $compile , $rootScope , + $q, + $injector, + $$multiMap, + $$stackedMap) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + var OPENED_MODAL_CLASS = 'modal-open'; + + var backdropDomEl, backdropScope; + var openedWindows = $$stackedMap.createNew(); + var openedClasses = $$multiMap.createNew(); + var $modalStack = { + NOW_CLOSING_EVENT: 'modal.stack.now-closing' + }; + + //Modal focus behavior + var focusableElementList; + var focusIndex = 0; + var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' + + 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' + + 'iframe, object, embed, *[tabindex], *[contenteditable=true]'; + + function backdropIndex() { + var topBackdropIndex = -1; + var opened = openedWindows.keys(); + for (var i = 0; i < opened.length; i++) { + if (openedWindows.get(opened[i]).value.backdrop) { + topBackdropIndex = i; + } + } + return topBackdropIndex; + } + + $rootScope.$watch(backdropIndex, function(newBackdropIndex) { + if (backdropScope) { + backdropScope.index = newBackdropIndex; + } + }); + + function removeModalWindow(modalInstance, elementToReceiveFocus) { + var body = $document.find('body').eq(0); + var modalWindow = openedWindows.get(modalInstance).value; + + //clean up the stack + openedWindows.remove(modalInstance); + + removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { + var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; + openedClasses.remove(modalBodyClass, modalInstance); + body.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass)); + toggleTopWindowClass(true); + }); + checkRemoveBackdrop(); + + //move focus to specified element if available, or else to body + if (elementToReceiveFocus && elementToReceiveFocus.focus) { + elementToReceiveFocus.focus(); + } else { + body.focus(); + } + } + + // Add or remove "windowTopClass" from the top window in the stack + function toggleTopWindowClass(toggleSwitch) { + var modalWindow; + + if (openedWindows.length() > 0) { + modalWindow = openedWindows.top().value; + modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch); + } + } + + function checkRemoveBackdrop() { + //remove backdrop if no longer needed + if (backdropDomEl && backdropIndex() == -1) { + var backdropScopeRef = backdropScope; + removeAfterAnimate(backdropDomEl, backdropScope, function() { + backdropScopeRef = null; + }); + backdropDomEl = undefined; + backdropScope = undefined; + } + } + + function removeAfterAnimate(domEl, scope, done) { + var asyncDeferred; + var asyncPromise = null; + var setIsAsync = function() { + if (!asyncDeferred) { + asyncDeferred = $q.defer(); + asyncPromise = asyncDeferred.promise; + } + + return function asyncDone() { + asyncDeferred.resolve(); + }; + }; + scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync); + + // Note that it's intentional that asyncPromise might be null. + // That's when setIsAsync has not been called during the + // NOW_CLOSING_EVENT broadcast. + return $q.when(asyncPromise).then(afterAnimating); + + function afterAnimating() { + if (afterAnimating.done) { + return; + } + afterAnimating.done = true; + + if ($animateCss) { + $animateCss(domEl, { + event: 'leave' + }).start().then(function() { + domEl.remove(); + }); + } else { + $animate.leave(domEl); + } + scope.$destroy(); + if (done) { + done(); + } + } + } + + $document.bind('keydown', function(evt) { + if (evt.isDefaultPrevented()) { + return evt; + } + + var modal = openedWindows.top(); + if (modal && modal.value.keyboard) { + switch (evt.which) { + case 27: { + evt.preventDefault(); + $rootScope.$apply(function() { + $modalStack.dismiss(modal.key, 'escape key press'); + }); + break; + } + case 9: { + $modalStack.loadFocusElementList(modal); + var focusChanged = false; + if (evt.shiftKey) { + if ($modalStack.isFocusInFirstItem(evt)) { + focusChanged = $modalStack.focusLastFocusableElement(); + } + } else { + if ($modalStack.isFocusInLastItem(evt)) { + focusChanged = $modalStack.focusFirstFocusableElement(); + } + } + + if (focusChanged) { + evt.preventDefault(); + evt.stopPropagation(); + } + break; + } + } + } + }); + + $modalStack.open = function(modalInstance, modal) { + var modalOpener = $document[0].activeElement, + modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS; + + toggleTopWindowClass(false); + + openedWindows.add(modalInstance, { + deferred: modal.deferred, + renderDeferred: modal.renderDeferred, + modalScope: modal.scope, + backdrop: modal.backdrop, + keyboard: modal.keyboard, + openedClass: modal.openedClass, + windowTopClass: modal.windowTopClass + }); + + openedClasses.put(modalBodyClass, modalInstance); + + var body = $document.find('body').eq(0), + currBackdropIndex = backdropIndex(); + + if (currBackdropIndex >= 0 && !backdropDomEl) { + backdropScope = $rootScope.$new(true); + backdropScope.index = currBackdropIndex; + var angularBackgroundDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>'); + angularBackgroundDomEl.attr('backdrop-class', modal.backdropClass); + if (modal.animation) { + angularBackgroundDomEl.attr('modal-animation', 'true'); + } + backdropDomEl = $compile(angularBackgroundDomEl)(backdropScope); + body.append(backdropDomEl); + } + + var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>'); + angularDomEl.attr({ + 'template-url': modal.windowTemplateUrl, + 'window-class': modal.windowClass, + 'window-top-class': modal.windowTopClass, + 'size': modal.size, + 'index': openedWindows.length() - 1, + 'animate': 'animate' + }).html(modal.content); + if (modal.animation) { + angularDomEl.attr('modal-animation', 'true'); + } + + var modalDomEl = $compile(angularDomEl)(modal.scope); + openedWindows.top().value.modalDomEl = modalDomEl; + openedWindows.top().value.modalOpener = modalOpener; + body.append(modalDomEl); + body.addClass(modalBodyClass); + + $modalStack.clearFocusListCache(); + }; + + function broadcastClosing(modalWindow, resultOrReason, closing) { + return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; + } + + $modalStack.close = function(modalInstance, result) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow && broadcastClosing(modalWindow, result, true)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.resolve(result); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + return !modalWindow; + }; + + $modalStack.dismiss = function(modalInstance, reason) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow && broadcastClosing(modalWindow, reason, false)) { + modalWindow.value.modalScope.$$uibDestructionScheduled = true; + modalWindow.value.deferred.reject(reason); + removeModalWindow(modalInstance, modalWindow.value.modalOpener); + return true; + } + return !modalWindow; + }; + + $modalStack.dismissAll = function(reason) { + var topModal = this.getTop(); + while (topModal && this.dismiss(topModal.key, reason)) { + topModal = this.getTop(); + } + }; + + $modalStack.getTop = function() { + return openedWindows.top(); + }; + + $modalStack.modalRendered = function(modalInstance) { + var modalWindow = openedWindows.get(modalInstance); + if (modalWindow) { + modalWindow.value.renderDeferred.resolve(); + } + }; + + $modalStack.focusFirstFocusableElement = function() { + if (focusableElementList.length > 0) { + focusableElementList[0].focus(); + return true; + } + return false; + }; + $modalStack.focusLastFocusableElement = function() { + if (focusableElementList.length > 0) { + focusableElementList[focusableElementList.length - 1].focus(); + return true; + } + return false; + }; + + $modalStack.isFocusInFirstItem = function(evt) { + if (focusableElementList.length > 0) { + return (evt.target || evt.srcElement) == focusableElementList[0]; + } + return false; + }; + + $modalStack.isFocusInLastItem = function(evt) { + if (focusableElementList.length > 0) { + return (evt.target || evt.srcElement) == focusableElementList[focusableElementList.length - 1]; + } + return false; + }; + + $modalStack.clearFocusListCache = function() { + focusableElementList = []; + focusIndex = 0; + }; + + $modalStack.loadFocusElementList = function(modalWindow) { + if (focusableElementList === undefined || !focusableElementList.length) { + if (modalWindow) { + var modalDomE1 = modalWindow.value.modalDomEl; + if (modalDomE1 && modalDomE1.length) { + focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector); + } + } + } + }; + + return $modalStack; + }]) + + .provider('$uibModal', function() { + var $modalProvider = { + options: { + animation: true, + backdrop: true, //can also be false or 'static' + keyboard: true + }, + $get: ['$injector', '$rootScope', '$q', '$templateRequest', '$controller', '$uibModalStack', '$modalSuppressWarning', '$log', + function ($injector, $rootScope, $q, $templateRequest, $controller, $modalStack, $modalSuppressWarning, $log) { + var $modal = {}; + + function getTemplatePromise(options) { + return options.template ? $q.when(options.template) : + $templateRequest(angular.isFunction(options.templateUrl) ? (options.templateUrl)() : options.templateUrl); + } + + function getResolvePromises(resolves) { + var promisesArr = []; + angular.forEach(resolves, function(value) { + if (angular.isFunction(value) || angular.isArray(value)) { + promisesArr.push($q.when($injector.invoke(value))); + } else if (angular.isString(value)) { + promisesArr.push($q.when($injector.get(value))); + } else { + promisesArr.push($q.when(value)); + } + }); + return promisesArr; + } + + var promiseChain = null; + $modal.getPromiseChain = function() { + return promiseChain; + }; + + $modal.open = function(modalOptions) { + var modalResultDeferred = $q.defer(); + var modalOpenedDeferred = $q.defer(); + var modalRenderDeferred = $q.defer(); + + //prepare an instance of a modal to be injected into controllers and returned to a caller + var modalInstance = { + result: modalResultDeferred.promise, + opened: modalOpenedDeferred.promise, + rendered: modalRenderDeferred.promise, + close: function (result) { + return $modalStack.close(modalInstance, result); + }, + dismiss: function (reason) { + return $modalStack.dismiss(modalInstance, reason); + } + }; + + //merge and clean up options + modalOptions = angular.extend({}, $modalProvider.options, modalOptions); + modalOptions.resolve = modalOptions.resolve || {}; + + //verify options + if (!modalOptions.template && !modalOptions.templateUrl) { + throw new Error('One of template or templateUrl options is required.'); + } + + var templateAndResolvePromise = + $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve))); + + function resolveWithTemplate() { + return templateAndResolvePromise; + } + + // Wait for the resolution of the existing promise chain. + // Then switch to our own combined promise dependency (regardless of how the previous modal fared). + // Then add to $modalStack and resolve opened. + // Finally clean up the chain variable if no subsequent modal has overwritten it. + var samePromise; + samePromise = promiseChain = $q.all([promiseChain]) + .then(resolveWithTemplate, resolveWithTemplate) + .then(function resolveSuccess(tplAndVars) { + + var modalScope = (modalOptions.scope || $rootScope).$new(); + modalScope.$close = modalInstance.close; + modalScope.$dismiss = modalInstance.dismiss; + + modalScope.$on('$destroy', function() { + if (!modalScope.$$uibDestructionScheduled) { + modalScope.$dismiss('$uibUnscheduledDestruction'); + } + }); + + var ctrlInstance, ctrlLocals = {}; + var resolveIter = 1; + + //controllers + if (modalOptions.controller) { + ctrlLocals.$scope = modalScope; + ctrlLocals.$uibModalInstance = modalInstance; + Object.defineProperty(ctrlLocals, '$modalInstance', { + get: function() { + if (!$modalSuppressWarning) { + $log.warn('$modalInstance is now deprecated. Use $uibModalInstance instead.'); + } + + return modalInstance; + } + }); + angular.forEach(modalOptions.resolve, function(value, key) { + ctrlLocals[key] = tplAndVars[resolveIter++]; + }); + + ctrlInstance = $controller(modalOptions.controller, ctrlLocals); + if (modalOptions.controllerAs) { + if (modalOptions.bindToController) { + angular.extend(ctrlInstance, modalScope); + } + + modalScope[modalOptions.controllerAs] = ctrlInstance; + } + } + + $modalStack.open(modalInstance, { + scope: modalScope, + deferred: modalResultDeferred, + renderDeferred: modalRenderDeferred, + content: tplAndVars[0], + animation: modalOptions.animation, + backdrop: modalOptions.backdrop, + keyboard: modalOptions.keyboard, + backdropClass: modalOptions.backdropClass, + windowTopClass: modalOptions.windowTopClass, + windowClass: modalOptions.windowClass, + windowTemplateUrl: modalOptions.windowTemplateUrl, + size: modalOptions.size, + openedClass: modalOptions.openedClass + }); + modalOpenedDeferred.resolve(true); + + }, function resolveError(reason) { + modalOpenedDeferred.reject(reason); + modalResultDeferred.reject(reason); + }) + .finally(function() { + if (promiseChain === samePromise) { + promiseChain = null; + } + }); + + return modalInstance; + }; + + return $modal; + } + ] + }; + + return $modalProvider; + }); + +/* deprecated modal below */ + +angular.module('ui.bootstrap.modal') + + .value('$modalSuppressWarning', false) + + /** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('modalBackdrop', [ + '$animate', '$injector', '$modalStack', '$log', '$modalSuppressWarning', + function($animate , $injector, $modalStack, $log, $modalSuppressWarning) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + replace: true, + templateUrl: 'template/modal/backdrop.html', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-backdrop is now deprecated. Use uib-modal-backdrop instead.'); + } + element.addClass('modal-backdrop'); + + if (attrs.modalInClass) { + if ($animateCss) { + $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + } + }]) + + .directive('modalWindow', [ + '$modalStack', '$q', '$animate', '$injector', '$log', '$modalSuppressWarning', + function($modalStack , $q , $animate, $injector, $log, $modalSuppressWarning) { + var $animateCss = null; + + if ($injector.has('$animateCss')) { + $animateCss = $injector.get('$animateCss'); + } + + return { + scope: { + index: '@' + }, + replace: true, + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'template/modal/window.html'; + }, + link: function(scope, element, attrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-window is now deprecated. Use uib-modal-window instead.'); + } + element.addClass(attrs.windowClass || ''); + element.addClass(attrs.windowTopClass || ''); + scope.size = attrs.size; + + scope.close = function(evt) { + var modal = $modalStack.getTop(); + if (modal && modal.value.backdrop && modal.value.backdrop !== 'static' && (evt.target === evt.currentTarget)) { + evt.preventDefault(); + evt.stopPropagation(); + $modalStack.dismiss(modal.key, 'backdrop click'); + } + }; + + // moved from template to fix issue #2280 + element.on('click', scope.close); + + // This property is only added to the scope for the purpose of detecting when this directive is rendered. + // We can detect that by using this property in the template associated with this directive and then use + // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}. + scope.$isRendered = true; + + // Deferred object that will be resolved when this modal is render. + var modalRenderDeferObj = $q.defer(); + // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready. + // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template. + attrs.$observe('modalRender', function(value) { + if (value == 'true') { + modalRenderDeferObj.resolve(); + } + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + if ($animateCss) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + } else { + animationPromise = $animate.addClass(element, attrs.modalInClass); + } + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if ($animateCss) { + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + } else { + $animate.removeClass(element, attrs.modalInClass).then(done); + } + }); + } + + + $q.when(animationPromise).then(function() { + var inputWithAutofocus = element[0].querySelector('[autofocus]'); + /** + * Auto-focusing of a freshly-opened modal element causes any child elements + * with the autofocus attribute to lose focus. This is an issue on touch + * based devices which will show and then hide the onscreen keyboard. + * Attempts to refocus the autofocus element via JavaScript will not reopen + * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus + * the modal element if the modal does not contain an autofocus element. + */ + if (inputWithAutofocus) { + inputWithAutofocus.focus(); + } else { + element[0].focus(); + } + }); + + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + }); + } + }; + }]) + + .directive('modalAnimationClass', [ + '$log', '$modalSuppressWarning', + function ($log, $modalSuppressWarning) { + return { + compile: function(tElement, tAttrs) { + if (!$modalSuppressWarning) { + $log.warn('modal-animation-class is now deprecated. Use uib-modal-animation-class instead.'); + } + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.modalAnimationClass); + } + } + }; + }]) + + .directive('modalTransclude', [ + '$log', '$modalSuppressWarning', + function ($log, $modalSuppressWarning) { + return { + link: function($scope, $element, $attrs, controller, $transclude) { + if (!$modalSuppressWarning) { + $log.warn('modal-transclude is now deprecated. Use uib-modal-transclude instead.'); + } + $transclude($scope.$parent, function(clone) { + $element.empty(); + $element.append(clone); + }); + } + }; + }]) + + .service('$modalStack', [ + '$animate', '$timeout', '$document', '$compile', '$rootScope', + '$q', + '$injector', + '$$multiMap', + '$$stackedMap', + '$uibModalStack', + '$log', + '$modalSuppressWarning', + function($animate , $timeout , $document , $compile , $rootScope , + $q, + $injector, + $$multiMap, + $$stackedMap, + $uibModalStack, + $log, + $modalSuppressWarning) { + if (!$modalSuppressWarning) { + $log.warn('$modalStack is now deprecated. Use $uibModalStack instead.'); + } + + angular.extend(this, $uibModalStack); + }]) + + .provider('$modal', ['$uibModalProvider', function($uibModalProvider) { + angular.extend(this, $uibModalProvider); + + this.$get = ['$injector', '$log', '$modalSuppressWarning', + function ($injector, $log, $modalSuppressWarning) { + if (!$modalSuppressWarning) { + $log.warn('$modal is now deprecated. Use $uibModal instead.'); + } + + return $injector.invoke($uibModalProvider.$get); + }]; + }]); + +angular.module('ui.bootstrap.pagination', []) +.controller('UibPaginationController', ['$scope', '$attrs', '$parse', function($scope, $attrs, $parse) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + + this.init = function(ngModelCtrl_, config) { + ngModelCtrl = ngModelCtrl_; + this.config = config; + + ngModelCtrl.$render = function() { + self.render(); + }; + + if ($attrs.itemsPerPage) { + $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { + self.itemsPerPage = parseInt(value, 10); + $scope.totalPages = self.calculateTotalPages(); + }); + } else { + this.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function() { + $scope.totalPages = self.calculateTotalPages(); + }); + + $scope.$watch('totalPages', function(value) { + setNumPages($scope.$parent, value); // Readonly variable + + if ( $scope.page > value ) { + $scope.selectPage(value); + } else { + ngModelCtrl.$render(); + } + }); + }; + + this.calculateTotalPages = function() { + var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + this.render = function() { + $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1; + }; + + $scope.selectPage = function(page, evt) { + if (evt) { + evt.preventDefault(); + } + + var clickAllowed = !$scope.ngDisabled || !evt; + if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { + if (evt && evt.target) { + evt.target.blur(); + } + ngModelCtrl.$setViewValue(page); + ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || self.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; +}]) + +.constant('uibPaginationConfig', { + itemsPerPage: 10, + boundaryLinks: false, + directionLinks: true, + firstText: 'First', + previousText: 'Previous', + nextText: 'Next', + lastText: 'Last', + rotate: true +}) + +.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, paginationConfig) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['uibPagination', '?ngModel'], + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pagination.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + // Setup configuration parameters + var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize, + rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate; + scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks; + scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks; + + paginationCtrl.init(ngModelCtrl, paginationConfig); + + if (attrs.maxSize) { + scope.$parent.$watch($parse(attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + paginationCtrl.render(); + }); + } + + // Create page object used in template + function makePage(number, text, isActive) { + return { + number: number, + text: text, + active: isActive + }; + } + + function getPages(currentPage, totalPages) { + var pages = []; + + // Default page limits + var startPage = 1, endPage = totalPages; + var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; + + // recompute if maxSize + if (isMaxSized) { + if (rotate) { + // Current page is displayed in the middle of the visible ones + startPage = Math.max(currentPage - Math.floor(maxSize/2), 1); + endPage = startPage + maxSize - 1; + + // Adjust if limit is exceeded + if (endPage > totalPages) { + endPage = totalPages; + startPage = endPage - maxSize + 1; + } + } else { + // Visible pages are paginated with maxSize + startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1; + + // Adjust last page if limit is exceeded + endPage = Math.min(startPage + maxSize - 1, totalPages); + } + } + + // Add page number links + for (var number = startPage; number <= endPage; number++) { + var page = makePage(number, number, number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && ! rotate) { + if (startPage > 1) { + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + + if (endPage < totalPages) { + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + } + + return pages; + } + + var originalRender = paginationCtrl.render; + paginationCtrl.render = function() { + originalRender(); + if (scope.page > 0 && scope.page <= scope.totalPages) { + scope.pages = getPages(scope.page, scope.totalPages); + } + }; + } + }; +}]) + +.constant('uibPagerConfig', { + itemsPerPage: 10, + previousText: '« Previous', + nextText: 'Next »', + align: true +}) + +.directive('uibPager', ['uibPagerConfig', function(pagerConfig) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['uibPager', '?ngModel'], + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pager.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align; + paginationCtrl.init(ngModelCtrl, pagerConfig); + } + }; +}]); + +/* Deprecated Pagination Below */ + +angular.module('ui.bootstrap.pagination') +.value('$paginationSuppressWarning', false) +.controller('PaginationController', ['$scope', '$attrs', '$parse', '$log', '$paginationSuppressWarning', function($scope, $attrs, $parse, $log, $paginationSuppressWarning) { + if (!$paginationSuppressWarning) { + $log.warn('PaginationController is now deprecated. Use UibPaginationController instead.'); + } + + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + + this.init = function(ngModelCtrl_, config) { + ngModelCtrl = ngModelCtrl_; + this.config = config; + + ngModelCtrl.$render = function() { + self.render(); + }; + + if ($attrs.itemsPerPage) { + $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { + self.itemsPerPage = parseInt(value, 10); + $scope.totalPages = self.calculateTotalPages(); + }); + } else { + this.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function() { + $scope.totalPages = self.calculateTotalPages(); + }); + + $scope.$watch('totalPages', function(value) { + setNumPages($scope.$parent, value); // Readonly variable + + if ( $scope.page > value ) { + $scope.selectPage(value); + } else { + ngModelCtrl.$render(); + } + }); + }; + + this.calculateTotalPages = function() { + var totalPages = this.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / this.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + this.render = function() { + $scope.page = parseInt(ngModelCtrl.$viewValue, 10) || 1; + }; + + $scope.selectPage = function(page, evt) { + if (evt) { + evt.preventDefault(); + } + + var clickAllowed = !$scope.ngDisabled || !evt; + if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) { + if (evt && evt.target) { + evt.target.blur(); + } + ngModelCtrl.$setViewValue(page); + ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || self.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; +}]) +.directive('pagination', ['$parse', 'uibPaginationConfig', '$log', '$paginationSuppressWarning', function($parse, paginationConfig, $log, $paginationSuppressWarning) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['pagination', '?ngModel'], + controller: 'PaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pagination.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + if (!$paginationSuppressWarning) { + $log.warn('pagination is now deprecated. Use uib-pagination instead.'); + } + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + // Setup configuration parameters + var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize, + rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate; + scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks; + scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks; + + paginationCtrl.init(ngModelCtrl, paginationConfig); + + if (attrs.maxSize) { + scope.$parent.$watch($parse(attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + paginationCtrl.render(); + }); + } + + // Create page object used in template + function makePage(number, text, isActive) { + return { + number: number, + text: text, + active: isActive + }; + } + + function getPages(currentPage, totalPages) { + var pages = []; + + // Default page limits + var startPage = 1, endPage = totalPages; + var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages; + + // recompute if maxSize + if (isMaxSized) { + if (rotate) { + // Current page is displayed in the middle of the visible ones + startPage = Math.max(currentPage - Math.floor(maxSize/2), 1); + endPage = startPage + maxSize - 1; + + // Adjust if limit is exceeded + if (endPage > totalPages) { + endPage = totalPages; + startPage = endPage - maxSize + 1; + } + } else { + // Visible pages are paginated with maxSize + startPage = ((Math.ceil(currentPage / maxSize) - 1) * maxSize) + 1; + + // Adjust last page if limit is exceeded + endPage = Math.min(startPage + maxSize - 1, totalPages); + } + } + + // Add page number links + for (var number = startPage; number <= endPage; number++) { + var page = makePage(number, number, number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && ! rotate) { + if (startPage > 1) { + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + + if (endPage < totalPages) { + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + } + + return pages; + } + + var originalRender = paginationCtrl.render; + paginationCtrl.render = function() { + originalRender(); + if (scope.page > 0 && scope.page <= scope.totalPages) { + scope.pages = getPages(scope.page, scope.totalPages); + } + }; + } + }; +}]) + +.directive('pager', ['uibPagerConfig', '$log', '$paginationSuppressWarning', function(pagerConfig, $log, $paginationSuppressWarning) { + return { + restrict: 'EA', + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['pager', '?ngModel'], + controller: 'PaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/pagination/pager.html'; + }, + replace: true, + link: function(scope, element, attrs, ctrls) { + if (!$paginationSuppressWarning) { + $log.warn('pager is now deprecated. Use uib-pager instead.'); + } + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + scope.align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : pagerConfig.align; + paginationCtrl.init(ngModelCtrl, pagerConfig); + } + }; +}]); + +/** + * The following features are still outstanding: animation as a + * function, placement as a function, inside, support for more triggers than + * just mouse enter/leave, html tooltips, and selector delegation. + */ +angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap']) + +/** + * The $tooltip service creates tooltip- and popover-like directives as well as + * houses global options for them. + */ +.provider('$uibTooltip', function() { + // The default options tooltip and popover. + var defaultOptions = { + placement: 'top', + animation: true, + popupDelay: 0, + popupCloseDelay: 0, + useContentExp: false + }; + + // Default hide triggers for each show trigger + var triggerMap = { + 'mouseenter': 'mouseleave', + 'click': 'click', + 'focus': 'blur', + 'none': '' + }; + + // The options specified to the provider globally. + var globalOptions = {}; + + /** + * `options({})` allows global configuration of all tooltips in the + * application. + * + * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) { + * // place tooltips left instead of top by default + * $tooltipProvider.options( { placement: 'left' } ); + * }); + */ + this.options = function(value) { + angular.extend(globalOptions, value); + }; + + /** + * This allows you to extend the set of trigger mappings available. E.g.: + * + * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' ); + */ + this.setTriggers = function setTriggers(triggers) { + angular.extend(triggerMap, triggers); + }; + + /** + * This is a helper function for translating camel-case to snake-case. + */ + function snake_case(name) { + var regexp = /[A-Z]/g; + var separator = '-'; + return name.replace(regexp, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); + } + + /** + * Returns the actual instance of the $tooltip service. + * TODO support multiple triggers + */ + this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) { + var openedTooltips = $$stackedMap.createNew(); + $document.on('keypress', function(e) { + if (e.which === 27) { + var last = openedTooltips.top(); + if (last) { + last.value.close(); + openedTooltips.removeTop(); + last = null; + } + } + }); + + return function $tooltip(ttType, prefix, defaultTriggerShow, options) { + options = angular.extend({}, defaultOptions, globalOptions, options); + + /** + * Returns an object of show and hide triggers. + * + * If a trigger is supplied, + * it is used to show the tooltip; otherwise, it will use the `trigger` + * option passed to the `$tooltipProvider.options` method; else it will + * default to the trigger supplied to this directive factory. + * + * The hide trigger is based on the show trigger. If the `trigger` option + * was passed to the `$tooltipProvider.options` method, it will use the + * mapped trigger from `triggerMap` or the passed trigger if the map is + * undefined; otherwise, it uses the `triggerMap` value of the show + * trigger; else it will just use the show trigger. + */ + function getTriggers(trigger) { + var show = (trigger || options.trigger || defaultTriggerShow).split(' '); + var hide = show.map(function(trigger) { + return triggerMap[trigger] || trigger; + }); + return { + show: show, + hide: hide + }; + } + + var directiveName = snake_case(ttType); + + var startSym = $interpolate.startSymbol(); + var endSym = $interpolate.endSymbol(); + var template = + '<div '+ directiveName + '-popup '+ + 'title="' + startSym + 'title' + endSym + '" '+ + (options.useContentExp ? + 'content-exp="contentExp()" ' : + 'content="' + startSym + 'content' + endSym + '" ') + + 'placement="' + startSym + 'placement' + endSym + '" '+ + 'popup-class="' + startSym + 'popupClass' + endSym + '" '+ + 'animation="animation" ' + + 'is-open="isOpen"' + + 'origin-scope="origScope" ' + + 'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' + + '>' + + '</div>'; + + return { + compile: function(tElem, tAttrs) { + var tooltipLinker = $compile(template); + + return function link(scope, element, attrs, tooltipCtrl) { + var tooltip; + var tooltipLinkedScope; + var transitionTimeout; + var showTimeout; + var hideTimeout; + var positionTimeout; + var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false; + var triggers = getTriggers(undefined); + var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']); + var ttScope = scope.$new(true); + var repositionScheduled = false; + var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false; + var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false; + var observers = []; + + var positionTooltip = function() { + // check if tooltip exists and is not empty + if (!tooltip || !tooltip.html()) { return; } + + if (!positionTimeout) { + positionTimeout = $timeout(function() { + // Reset the positioning. + tooltip.css({ top: 0, left: 0 }); + + // Now set the calculated positioning. + var ttCss = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); + ttCss.top += 'px'; + ttCss.left += 'px'; + ttCss.visibility = 'visible'; + tooltip.css(ttCss); + + positionTimeout = null; + }, 0, false); + } + }; + + // Set up the correct scope to allow transclusion later + ttScope.origScope = scope; + + // By default, the tooltip is not open. + // TODO add ability to start tooltip opened + ttScope.isOpen = false; + openedTooltips.add(ttScope, { + close: hide + }); + + function toggleTooltipBind() { + if (!ttScope.isOpen) { + showTooltipBind(); + } else { + hideTooltipBind(); + } + } + + // Show the tooltip with delay if specified, otherwise show it immediately + function showTooltipBind() { + if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) { + return; + } + + cancelHide(); + prepareTooltip(); + + if (ttScope.popupDelay) { + // Do nothing if the tooltip was already scheduled to pop-up. + // This happens if show is triggered multiple times before any hide is triggered. + if (!showTimeout) { + showTimeout = $timeout(show, ttScope.popupDelay, false); + } + } else { + show(); + } + } + + function hideTooltipBind() { + cancelShow(); + + if (ttScope.popupCloseDelay) { + if (!hideTimeout) { + hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false); + } + } else { + hide(); + } + } + + // Show the tooltip popup element. + function show() { + cancelShow(); + cancelHide(); + + // Don't show empty tooltips. + if (!ttScope.content) { + return angular.noop; + } + + createTooltip(); + + // And show the tooltip. + ttScope.$evalAsync(function() { + ttScope.isOpen = true; + assignIsOpen(true); + positionTooltip(); + }); + } + + function cancelShow() { + if (showTimeout) { + $timeout.cancel(showTimeout); + showTimeout = null; + } + + if (positionTimeout) { + $timeout.cancel(positionTimeout); + positionTimeout = null; + } + } + + // Hide the tooltip popup element. + function hide() { + cancelShow(); + cancelHide(); + + if (!ttScope) { + return; + } + + // First things first: we don't show it anymore. + ttScope.$evalAsync(function() { + ttScope.isOpen = false; + assignIsOpen(false); + // And now we remove it from the DOM. However, if we have animation, we + // need to wait for it to expire beforehand. + // FIXME: this is a placeholder for a port of the transitions library. + // The fade transition in TWBS is 150ms. + if (ttScope.animation) { + if (!transitionTimeout) { + transitionTimeout = $timeout(removeTooltip, 150, false); + } + } else { + removeTooltip(); + } + }); + } + + function cancelHide() { + if (hideTimeout) { + $timeout.cancel(hideTimeout); + hideTimeout = null; + } + if (transitionTimeout) { + $timeout.cancel(transitionTimeout); + transitionTimeout = null; + } + } + + function createTooltip() { + // There can only be one tooltip element per directive shown at once. + if (tooltip) { + return; + } + + tooltipLinkedScope = ttScope.$new(); + tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) { + if (appendToBody) { + $document.find('body').append(tooltip); + } else { + element.after(tooltip); + } + }); + + prepObservers(); + } + + function removeTooltip() { + unregisterObservers(); + + transitionTimeout = null; + if (tooltip) { + tooltip.remove(); + tooltip = null; + } + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } + } + + /** + * Set the inital scope values. Once + * the tooltip is created, the observers + * will be added to keep things in synch. + */ + function prepareTooltip() { + ttScope.title = attrs[prefix + 'Title']; + if (contentParse) { + ttScope.content = contentParse(scope); + } else { + ttScope.content = attrs[ttType]; + } + + ttScope.popupClass = attrs[prefix + 'Class']; + ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement; + + var delay = parseInt(attrs[prefix + 'PopupDelay'], 10); + var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10); + ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay; + ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay; + } + + function assignIsOpen(isOpen) { + if (isOpenParse && angular.isFunction(isOpenParse.assign)) { + isOpenParse.assign(scope, isOpen); + } + } + + ttScope.contentExp = function() { + return ttScope.content; + }; + + /** + * Observe the relevant attributes. + */ + attrs.$observe('disabled', function(val) { + if (val) { + cancelShow(); + } + + if (val && ttScope.isOpen) { + hide(); + } + }); + + if (isOpenParse) { + scope.$watch(isOpenParse, function(val) { + /*jshint -W018 */ + if (ttScope && !val === ttScope.isOpen) { + toggleTooltipBind(); + } + /*jshint +W018 */ + }); + } + + function prepObservers() { + observers.length = 0; + + if (contentParse) { + observers.push( + scope.$watch(contentParse, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } + }) + ); + + observers.push( + tooltipLinkedScope.$watch(function() { + if (!repositionScheduled) { + repositionScheduled = true; + tooltipLinkedScope.$$postDigest(function() { + repositionScheduled = false; + if (ttScope && ttScope.isOpen) { + positionTooltip(); + } + }); + } + }) + ); + } else { + observers.push( + attrs.$observe(ttType, function(val) { + ttScope.content = val; + if (!val && ttScope.isOpen) { + hide(); + } else { + positionTooltip(); + } + }) + ); + } + + observers.push( + attrs.$observe(prefix + 'Title', function(val) { + ttScope.title = val; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + + observers.push( + attrs.$observe(prefix + 'Placement', function(val) { + ttScope.placement = val ? val : options.placement; + if (ttScope.isOpen) { + positionTooltip(); + } + }) + ); + } + + function unregisterObservers() { + if (observers.length) { + angular.forEach(observers, function(observer) { + observer(); + }); + observers.length = 0; + } + } + + var unregisterTriggers = function() { + triggers.show.forEach(function(trigger) { + element.unbind(trigger, showTooltipBind); + }); + triggers.hide.forEach(function(trigger) { + trigger.split(' ').forEach(function(hideTrigger) { + element[0].removeEventListener(hideTrigger, hideTooltipBind); + }); + }); + }; + + function prepTriggers() { + var val = attrs[prefix + 'Trigger']; + unregisterTriggers(); + + triggers = getTriggers(val); + + if (triggers.show !== 'none') { + triggers.show.forEach(function(trigger, idx) { + // Using raw addEventListener due to jqLite/jQuery bug - #4060 + if (trigger === triggers.hide[idx]) { + element[0].addEventListener(trigger, toggleTooltipBind); + } else if (trigger) { + element[0].addEventListener(trigger, showTooltipBind); + triggers.hide[idx].split(' ').forEach(function(trigger) { + element[0].addEventListener(trigger, hideTooltipBind); + }); + } + + element.on('keypress', function(e) { + if (e.which === 27) { + hideTooltipBind(); + } + }); + }); + } + } + + prepTriggers(); + + var animation = scope.$eval(attrs[prefix + 'Animation']); + ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; + + var appendToBodyVal = scope.$eval(attrs[prefix + 'AppendToBody']); + appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; + + // if a tooltip is attached to <body> we need to remove it on + // location change as its parent scope will probably not be destroyed + // by the change. + if (appendToBody) { + scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() { + if (ttScope.isOpen) { + hide(); + } + }); + } + + // Make sure tooltip is destroyed and removed. + scope.$on('$destroy', function onDestroyTooltip() { + cancelShow(); + cancelHide(); + unregisterTriggers(); + removeTooltip(); + openedTooltips.remove(ttScope); + ttScope = null; + }); + }; + } + }; + }; + }]; +}) + +// This is mostly ngInclude code but with a custom scope +.directive('uibTooltipTemplateTransclude', [ + '$animate', '$sce', '$compile', '$templateRequest', +function ($animate , $sce , $compile , $templateRequest) { + return { + link: function(scope, elem, attrs) { + var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); + + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) { return; } + var newScope = origScope.$new(); + var template = response; + + var clone = $compile(template)(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, elem); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + } + }); + + scope.$on('$destroy', cleanupLastIncludeContent); + } + }; +}]) + +/** + * Note that it's intentional that these classes are *not* applied through $animate. + * They must not be animated as they're expected to be present on the tooltip on + * initialization. + */ +.directive('uibTooltipClasses', function() { + return { + restrict: 'A', + link: function(scope, element, attrs) { + if (scope.placement) { + element.addClass(scope.placement); + } + + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + + if (scope.animation()) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}) + +.directive('uibTooltipPopup', function() { + return { + replace: true, + scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-popup.html', + link: function(scope, element) { + element.addClass('tooltip'); + } + }; +}) + +.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('uibTooltipTemplatePopup', function() { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/tooltip/tooltip-template-popup.html', + link: function(scope, element) { + element.addClass('tooltip'); + } + }; +}) + +.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('uibTooltipHtmlPopup', function() { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-html-popup.html', + link: function(scope, element) { + element.addClass('tooltip'); + } + }; +}) + +.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]); + +/* Deprecated tooltip below */ + +angular.module('ui.bootstrap.tooltip') + +.value('$tooltipSuppressWarning', false) + +.provider('$tooltip', ['$uibTooltipProvider', function($uibTooltipProvider) { + angular.extend(this, $uibTooltipProvider); + + this.$get = ['$log', '$tooltipSuppressWarning', '$injector', function($log, $tooltipSuppressWarning, $injector) { + if (!$tooltipSuppressWarning) { + $log.warn('$tooltip is now deprecated. Use $uibTooltip instead.'); + } + + return $injector.invoke($uibTooltipProvider.$get); + }]; +}]) + +// This is mostly ngInclude code but with a custom scope +.directive('tooltipTemplateTransclude', [ + '$animate', '$sce', '$compile', '$templateRequest', '$log', '$tooltipSuppressWarning', +function ($animate , $sce , $compile , $templateRequest, $log, $tooltipSuppressWarning) { + return { + link: function(scope, elem, attrs) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-template-transclude is now deprecated. Use uib-tooltip-template-transclude instead.'); + } + + var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope); + + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(attrs.tooltipTemplateTransclude), function(src) { + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) { return; } + var newScope = origScope.$new(); + var template = response; + + var clone = $compile(template)(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, elem); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + } + }); + + scope.$on('$destroy', cleanupLastIncludeContent); + } + }; +}]) + +.directive('tooltipClasses', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-classes is now deprecated. Use uib-tooltip-classes instead.'); + } + + if (scope.placement) { + element.addClass(scope.placement); + } + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + if (scope.animation()) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}]) + +.directive('tooltipPopup', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + replace: true, + scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-popup.html', + link: function(scope, element) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-popup is now deprecated. Use uib-tooltip-popup instead.'); + } + + element.addClass('tooltip'); + } + }; +}]) + +.directive('tooltip', ['$tooltip', function($tooltip) { + return $tooltip('tooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('tooltipTemplatePopup', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/tooltip/tooltip-template-popup.html', + link: function(scope, element) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-template-popup is now deprecated. Use uib-tooltip-template-popup instead.'); + } + + element.addClass('tooltip'); + } + }; +}]) + +.directive('tooltipTemplate', ['$tooltip', function($tooltip) { + return $tooltip('tooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('tooltipHtmlPopup', ['$log', '$tooltipSuppressWarning', function($log, $tooltipSuppressWarning) { + return { + replace: true, + scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tooltip/tooltip-html-popup.html', + link: function(scope, element) { + if (!$tooltipSuppressWarning) { + $log.warn('tooltip-html-popup is now deprecated. Use uib-tooltip-html-popup instead.'); + } + + element.addClass('tooltip'); + } + }; +}]) + +.directive('tooltipHtml', ['$tooltip', function($tooltip) { + return $tooltip('tooltipHtml', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]); + +/** + * The following features are still outstanding: popup delay, animation as a + * function, placement as a function, inside, support for more triggers than + * just mouse enter/leave, and selector delegatation. + */ +angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip']) + +.directive('uibPopoverTemplatePopup', function() { + return { + replace: true, + scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/popover/popover-template.html', + link: function(scope, element) { + element.addClass('popover'); + } + }; +}) + +.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverHtmlPopup', function() { + return { + replace: true, + scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover-html.html', + link: function(scope, element) { + element.addClass('popover'); + } + }; +}) + +.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverPopup', function() { + return { + replace: true, + scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover.html', + link: function(scope, element) { + element.addClass('popover'); + } + }; +}) + +.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopover', 'popover', 'click'); +}]); + +/* Deprecated popover below */ + +angular.module('ui.bootstrap.popover') + +.value('$popoverSuppressWarning', false) + +.directive('popoverTemplatePopup', ['$log', '$popoverSuppressWarning', function($log, $popoverSuppressWarning) { + return { + replace: true, + scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&', + originScope: '&' }, + templateUrl: 'template/popover/popover-template.html', + link: function(scope, element) { + if (!$popoverSuppressWarning) { + $log.warn('popover-template-popup is now deprecated. Use uib-popover-template-popup instead.'); + } + + element.addClass('popover'); + } + }; +}]) + +.directive('popoverTemplate', ['$tooltip', function($tooltip) { + return $tooltip('popoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('popoverHtmlPopup', ['$log', '$popoverSuppressWarning', function($log, $popoverSuppressWarning) { + return { + replace: true, + scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover-html.html', + link: function(scope, element) { + if (!$popoverSuppressWarning) { + $log.warn('popover-html-popup is now deprecated. Use uib-popover-html-popup instead.'); + } + + element.addClass('popover'); + } + }; +}]) + +.directive('popoverHtml', ['$tooltip', function($tooltip) { + return $tooltip('popoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('popoverPopup', ['$log', '$popoverSuppressWarning', function($log, $popoverSuppressWarning) { + return { + replace: true, + scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/popover/popover.html', + link: function(scope, element) { + if (!$popoverSuppressWarning) { + $log.warn('popover-popup is now deprecated. Use uib-popover-popup instead.'); + } + + element.addClass('popover'); + } + }; +}]) + +.directive('popover', ['$tooltip', function($tooltip) { + + return $tooltip('popover', 'popover', 'click'); +}]); + +angular.module('ui.bootstrap.progressbar', []) + +.constant('uibProgressConfig', { + animate: true, + max: 100 +}) + +.controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) { + var self = this, + animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; + + this.bars = []; + $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max; + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = $scope.max; + bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; + + bar.$watch('value', function(value) { + bar.recalculatePercentage(); + }); + + bar.recalculatePercentage = function() { + var totalPercentage = self.bars.reduce(function(total, bar) { + bar.percent = +(100 * bar.value / bar.max).toFixed(2); + return total + bar.percent; + }, 0); + + if (totalPercentage > 100) { + bar.percent -= totalPercentage - 100; + } + }; + + bar.$on('$destroy', function() { + element = null; + self.removeBar(bar); + }); + }; + + this.removeBar = function(bar) { + this.bars.splice(this.bars.indexOf(bar), 1); + this.bars.forEach(function (bar) { + bar.recalculatePercentage(); + }); + }; + + $scope.$watch('max', function(max) { + self.bars.forEach(function(bar) { + bar.max = $scope.max; + bar.recalculatePercentage(); + }); + }); +}]) + +.directive('uibProgress', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + require: 'uibProgress', + scope: { + max: '=?' + }, + templateUrl: 'template/progressbar/progress.html' + }; +}) + +.directive('uibBar', function() { + return { + replace: true, + transclude: true, + require: '^uibProgress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'template/progressbar/bar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, element, attrs); + } + }; +}) + +.directive('uibProgressbar', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + scope: { + value: '=', + max: '=?', + type: '@' + }, + templateUrl: 'template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}); + +/* Deprecated progressbar below */ + +angular.module('ui.bootstrap.progressbar') + +.value('$progressSuppressWarning', false) + +.controller('ProgressController', ['$scope', '$attrs', 'uibProgressConfig', '$log', '$progressSuppressWarning', function($scope, $attrs, progressConfig, $log, $progressSuppressWarning) { + if (!$progressSuppressWarning) { + $log.warn('ProgressController is now deprecated. Use UibProgressController instead.'); + } + + var self = this, + animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate; + + this.bars = []; + $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max; + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = $scope.max; + bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar'; + + bar.$watch('value', function(value) { + bar.recalculatePercentage(); + }); + + bar.recalculatePercentage = function() { + bar.percent = +(100 * bar.value / bar.max).toFixed(2); + + var totalPercentage = self.bars.reduce(function(total, bar) { + return total + bar.percent; + }, 0); + + if (totalPercentage > 100) { + bar.percent -= totalPercentage - 100; + } + }; + + bar.$on('$destroy', function() { + element = null; + self.removeBar(bar); + }); + }; + + this.removeBar = function(bar) { + this.bars.splice(this.bars.indexOf(bar), 1); + }; + + $scope.$watch('max', function(max) { + self.bars.forEach(function(bar) { + bar.max = $scope.max; + bar.recalculatePercentage(); + }); + }); +}]) + +.directive('progress', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) { + return { + replace: true, + transclude: true, + controller: 'ProgressController', + require: 'progress', + scope: { + max: '=?', + title: '@?' + }, + templateUrl: 'template/progressbar/progress.html', + link: function() { + if (!$progressSuppressWarning) { + $log.warn('progress is now deprecated. Use uib-progress instead.'); + } + } + }; +}]) + +.directive('bar', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) { + return { + replace: true, + transclude: true, + require: '^progress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'template/progressbar/bar.html', + link: function(scope, element, attrs, progressCtrl) { + if (!$progressSuppressWarning) { + $log.warn('bar is now deprecated. Use uib-bar instead.'); + } + progressCtrl.addBar(scope, element); + } + }; +}]) + +.directive('progressbar', ['$log', '$progressSuppressWarning', function($log, $progressSuppressWarning) { + return { + replace: true, + transclude: true, + controller: 'ProgressController', + scope: { + value: '=', + max: '=?', + type: '@' + }, + templateUrl: 'template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + if (!$progressSuppressWarning) { + $log.warn('progressbar is now deprecated. Use uib-progressbar instead.'); + } + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}]); + +angular.module('ui.bootstrap.rating', []) + +.constant('uibRatingConfig', { + max: 5, + stateOn: null, + stateOff: null, + titles : ['one', 'two', 'three', 'four', 'five'] +}) + +.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { + var ngModelCtrl = { $setViewValue: angular.noop }; + + this.init = function(ngModelCtrl_) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.push(function(value) { + if (angular.isNumber(value) && value << 0 !== value) { + value = Math.round(value); + } + return value; + }); + + this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn; + this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff; + var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ; + this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ? + tmpTitles : ratingConfig.titles; + + var ratingStates = angular.isDefined($attrs.ratingStates) ? + $scope.$parent.$eval($attrs.ratingStates) : + new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max); + $scope.range = this.buildTemplateObjects(ratingStates); + }; + + this.buildTemplateObjects = function(states) { + for (var i = 0, n = states.length; i < n; i++) { + states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]); + } + return states; + }; + + this.getTitle = function(index) { + if (index >= this.titles.length) { + return index + 1; + } else { + return this.titles[index]; + } + }; + + $scope.rate = function(value) { + if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { + ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value); + ngModelCtrl.$render(); + } + }; + + $scope.enter = function(value) { + if (!$scope.readonly) { + $scope.value = value; + } + $scope.onHover({value: value}); + }; + + $scope.reset = function() { + $scope.value = ngModelCtrl.$viewValue; + $scope.onLeave(); + }; + + $scope.onKeydown = function(evt) { + if (/(37|38|39|40)/.test(evt.which)) { + evt.preventDefault(); + evt.stopPropagation(); + $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1)); + } + }; + + this.render = function() { + $scope.value = ngModelCtrl.$viewValue; + }; +}]) + +.directive('uibRating', function() { + return { + require: ['uibRating', 'ngModel'], + scope: { + readonly: '=?', + onHover: '&', + onLeave: '&' + }, + controller: 'UibRatingController', + templateUrl: 'template/rating/rating.html', + replace: true, + link: function(scope, element, attrs, ctrls) { + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}); + +/* Deprecated rating below */ + +angular.module('ui.bootstrap.rating') + +.value('$ratingSuppressWarning', false) + +.controller('RatingController', ['$scope', '$attrs', '$controller', '$log', '$ratingSuppressWarning', function($scope, $attrs, $controller, $log, $ratingSuppressWarning) { + if (!$ratingSuppressWarning) { + $log.warn('RatingController is now deprecated. Use UibRatingController instead.'); + } + + angular.extend(this, $controller('UibRatingController', { + $scope: $scope, + $attrs: $attrs + })); +}]) + +.directive('rating', ['$log', '$ratingSuppressWarning', function($log, $ratingSuppressWarning) { + return { + require: ['rating', 'ngModel'], + scope: { + readonly: '=?', + onHover: '&', + onLeave: '&' + }, + controller: 'RatingController', + templateUrl: 'template/rating/rating.html', + replace: true, + link: function(scope, element, attrs, ctrls) { + if (!$ratingSuppressWarning) { + $log.warn('rating is now deprecated. Use uib-rating instead.'); + } + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}]); + + +/** + * @ngdoc overview + * @name ui.bootstrap.tabs + * + * @description + * AngularJS version of the tabs directive. + */ + +angular.module('ui.bootstrap.tabs', []) + +.controller('UibTabsetController', ['$scope', function ($scope) { + var ctrl = this, + tabs = ctrl.tabs = $scope.tabs = []; + + ctrl.select = function(selectedTab) { + angular.forEach(tabs, function(tab) { + if (tab.active && tab !== selectedTab) { + tab.active = false; + tab.onDeselect(); + selectedTab.selectCalled = false; + } + }); + selectedTab.active = true; + // only call select if it has not already been called + if (!selectedTab.selectCalled) { + selectedTab.onSelect(); + selectedTab.selectCalled = true; + } + }; + + ctrl.addTab = function addTab(tab) { + tabs.push(tab); + // we can't run the select function on the first tab + // since that would select it twice + if (tabs.length === 1 && tab.active !== false) { + tab.active = true; + } else if (tab.active) { + ctrl.select(tab); + } else { + tab.active = false; + } + }; + + ctrl.removeTab = function removeTab(tab) { + var index = tabs.indexOf(tab); + //Select a new tab if the tab to be removed is selected and not destroyed + if (tab.active && tabs.length > 1 && !destroyed) { + //If this is the last tab, select the previous tab. else, the next tab. + var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1; + ctrl.select(tabs[newActiveIndex]); + } + tabs.splice(index, 1); + }; + + var destroyed; + $scope.$on('$destroy', function() { + destroyed = true; + }); +}]) + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tabset + * @restrict EA + * + * @description + * Tabset is the outer container for the tabs directive + * + * @param {boolean=} vertical Whether or not to use vertical styling for the tabs. + * @param {boolean=} justified Whether or not to use justified styling for the tabs. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <uib-tabset> + <uib-tab heading="Tab 1"><b>First</b> Content!</uib-tab> + <uib-tab heading="Tab 2"><i>Second</i> Content!</uib-tab> + </uib-tabset> + <hr /> + <uib-tabset vertical="true"> + <uib-tab heading="Vertical Tab 1"><b>First</b> Vertical Content!</uib-tab> + <uib-tab heading="Vertical Tab 2"><i>Second</i> Vertical Content!</uib-tab> + </uib-tabset> + <uib-tabset justified="true"> + <uib-tab heading="Justified Tab 1"><b>First</b> Justified Content!</uib-tab> + <uib-tab heading="Justified Tab 2"><i>Second</i> Justified Content!</uib-tab> + </uib-tabset> + </file> +</example> + */ +.directive('uibTabset', function() { + return { + restrict: 'EA', + transclude: true, + replace: true, + scope: { + type: '@' + }, + controller: 'UibTabsetController', + templateUrl: 'template/tabs/tabset.html', + link: function(scope, element, attrs) { + scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; + } + }; +}) + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tab + * @restrict EA + * + * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}. + * @param {string=} select An expression to evaluate when the tab is selected. + * @param {boolean=} active A binding, telling whether or not this tab is selected. + * @param {boolean=} disabled A binding, telling whether or not this tab is disabled. + * + * @description + * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <div ng-controller="TabsDemoCtrl"> + <button class="btn btn-small" ng-click="items[0].active = true"> + Select item 1, using active binding + </button> + <button class="btn btn-small" ng-click="items[1].disabled = !items[1].disabled"> + Enable/disable item 2, using disabled binding + </button> + <br /> + <uib-tabset> + <uib-tab heading="Tab 1">First Tab</uib-tab> + <uib-tab select="alertMe()"> + <uib-tab-heading><i class="icon-bell"></i> Alert me!</tab-heading> + Second Tab, with alert callback and html heading! + </uib-tab> + <uib-tab ng-repeat="item in items" + heading="{{item.title}}" + disabled="item.disabled" + active="item.active"> + {{item.content}} + </uib-tab> + </uib-tabset> + </div> + </file> + <file name="script.js"> + function TabsDemoCtrl($scope) { + $scope.items = [ + { title:"Dynamic Title 1", content:"Dynamic Item 0" }, + { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true } + ]; + + $scope.alertMe = function() { + setTimeout(function() { + alert("You've selected the alert tab!"); + }); + }; + }; + </file> +</example> + */ + +/** + * @ngdoc directive + * @name ui.bootstrap.tabs.directive:tabHeading + * @restrict EA + * + * @description + * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element. + * + * @example +<example module="ui.bootstrap"> + <file name="index.html"> + <uib-tabset> + <uib-tab> + <uib-tab-heading><b>HTML</b> in my titles?!</tab-heading> + And some content, too! + </uib-tab> + <uib-tab> + <uib-tab-heading><i class="icon-heart"></i> Icon heading?!?</tab-heading> + That's right. + </uib-tab> + </uib-tabset> + </file> +</example> + */ +.directive('uibTab', ['$parse', function($parse) { + return { + require: '^uibTabset', + restrict: 'EA', + replace: true, + templateUrl: 'template/tabs/tab.html', + transclude: true, + scope: { + active: '=?', + heading: '@', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + scope.$watch('active', function(active) { + if (active) { + tabsetCtrl.select(scope); + } + }); + + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !! value; + }); + } + + scope.select = function() { + if (!scope.disabled) { + scope.active = true; + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + } + }; +}]) + +.directive('uibTabHeadingTransclude', function() { + return { + restrict: 'A', + require: ['?^uibTab', '?^tab'], // TODO: change to '^uibTab' after deprecation removal + link: function(scope, elm) { + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; +}) + +.directive('uibTabContentTransclude', function() { + return { + restrict: 'A', + require: ['?^uibTabset', '?^tabset'], // TODO: change to '^uibTabset' after deprecation removal + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.uibTabContentTransclude); + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } else { + elm.append(node); + } + }); + }); + } + }; + + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('tab-heading') || // TODO: remove after deprecation removal + node.hasAttribute('data-tab-heading') || // TODO: remove after deprecation removal + node.hasAttribute('x-tab-heading') || // TODO: remove after deprecation removal + node.hasAttribute('uib-tab-heading') || + node.hasAttribute('data-uib-tab-heading') || + node.hasAttribute('x-uib-tab-heading') || + node.tagName.toLowerCase() === 'tab-heading' || // TODO: remove after deprecation removal + node.tagName.toLowerCase() === 'data-tab-heading' || // TODO: remove after deprecation removal + node.tagName.toLowerCase() === 'x-tab-heading' || // TODO: remove after deprecation removal + node.tagName.toLowerCase() === 'uib-tab-heading' || + node.tagName.toLowerCase() === 'data-uib-tab-heading' || + node.tagName.toLowerCase() === 'x-uib-tab-heading' + ); + } +}); + +/* deprecated tabs below */ + +angular.module('ui.bootstrap.tabs') + + .value('$tabsSuppressWarning', false) + + .controller('TabsetController', ['$scope', '$controller', '$log', '$tabsSuppressWarning', function($scope, $controller, $log, $tabsSuppressWarning) { + if (!$tabsSuppressWarning) { + $log.warn('TabsetController is now deprecated. Use UibTabsetController instead.'); + } + + angular.extend(this, $controller('UibTabsetController', { + $scope: $scope + })); + }]) + + .directive('tabset', ['$log', '$tabsSuppressWarning', function($log, $tabsSuppressWarning) { + return { + restrict: 'EA', + transclude: true, + replace: true, + scope: { + type: '@' + }, + controller: 'TabsetController', + templateUrl: 'template/tabs/tabset.html', + link: function(scope, element, attrs) { + + if (!$tabsSuppressWarning) { + $log.warn('tabset is now deprecated. Use uib-tabset instead.'); + } + scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false; + scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false; + } + }; + }]) + + .directive('tab', ['$parse', '$log', '$tabsSuppressWarning', function($parse, $log, $tabsSuppressWarning) { + return { + require: '^tabset', + restrict: 'EA', + replace: true, + templateUrl: 'template/tabs/tab.html', + transclude: true, + scope: { + active: '=?', + heading: '@', + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + if (!$tabsSuppressWarning) { + $log.warn('tab is now deprecated. Use uib-tab instead.'); + } + + scope.$watch('active', function(active) { + if (active) { + tabsetCtrl.select(scope); + } + }); + + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !!value; + }); + } + + scope.select = function() { + if (!scope.disabled) { + scope.active = true; + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + } + }; + }]) + + .directive('tabHeadingTransclude', ['$log', '$tabsSuppressWarning', function($log, $tabsSuppressWarning) { + return { + restrict: 'A', + require: '^tab', + link: function(scope, elm) { + if (!$tabsSuppressWarning) { + $log.warn('tab-heading-transclude is now deprecated. Use uib-tab-heading-transclude instead.'); + } + + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; + }]) + + .directive('tabContentTransclude', ['$log', '$tabsSuppressWarning', function($log, $tabsSuppressWarning) { + return { + restrict: 'A', + require: '^tabset', + link: function(scope, elm, attrs) { + if (!$tabsSuppressWarning) { + $log.warn('tab-content-transclude is now deprecated. Use uib-tab-content-transclude instead.'); + } + + var tab = scope.$eval(attrs.tabContentTransclude); + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } + else { + elm.append(node); + } + }); + }); + } + }; + + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('tab-heading') || + node.hasAttribute('data-tab-heading') || + node.hasAttribute('x-tab-heading') || + node.tagName.toLowerCase() === 'tab-heading' || + node.tagName.toLowerCase() === 'data-tab-heading' || + node.tagName.toLowerCase() === 'x-tab-heading' + ); + } + }]); + +angular.module('ui.bootstrap.timepicker', []) + +.constant('uibTimepickerConfig', { + hourStep: 1, + minuteStep: 1, + showMeridian: true, + meridians: null, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + showSpinners: true +}) + +.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { + var selected = new Date(), + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS; + + $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; + $element.removeAttr('tabindex'); + + this.init = function(ngModelCtrl_, inputs) { + ngModelCtrl = ngModelCtrl_; + ngModelCtrl.$render = this.render; + + ngModelCtrl.$formatters.unshift(function(modelValue) { + return modelValue ? new Date(modelValue) : null; + }); + + var hoursInputEl = inputs.eq(0), + minutesInputEl = inputs.eq(1); + + var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; + if (mousewheel) { + this.setupMousewheelEvents(hoursInputEl, minutesInputEl); + } + + var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; + if (arrowkeys) { + this.setupArrowkeyEvents(hoursInputEl, minutesInputEl); + } + + $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; + this.setupInputEvents(hoursInputEl, minutesInputEl); + }; + + var hourStep = timepickerConfig.hourStep; + if ($attrs.hourStep) { + $scope.$parent.$watch($parse($attrs.hourStep), function(value) { + hourStep = parseInt(value, 10); + }); + } + + var minuteStep = timepickerConfig.minuteStep; + if ($attrs.minuteStep) { + $scope.$parent.$watch($parse($attrs.minuteStep), function(value) { + minuteStep = parseInt(value, 10); + }); + } + + var min; + $scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + }); + + var max; + $scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + }); + + $scope.noIncrementHours = function() { + var incrementedSelected = addMinutes(selected, hourStep * 60); + return incrementedSelected > max || + (incrementedSelected < selected && incrementedSelected < min); + }; + + $scope.noDecrementHours = function() { + var decrementedSelected = addMinutes(selected, -hourStep * 60); + return decrementedSelected < min || + (decrementedSelected > selected && decrementedSelected > max); + }; + + $scope.noIncrementMinutes = function() { + var incrementedSelected = addMinutes(selected, minuteStep); + return incrementedSelected > max || + (incrementedSelected < selected && incrementedSelected < min); + }; + + $scope.noDecrementMinutes = function() { + var decrementedSelected = addMinutes(selected, -minuteStep); + return decrementedSelected < min || + (decrementedSelected > selected && decrementedSelected > max); + }; + + $scope.noToggleMeridian = function() { + if (selected.getHours() < 13) { + return addMinutes(selected, 12 * 60) > max; + } else { + return addMinutes(selected, -12 * 60) < min; + } + }; + + // 12H / 24H mode + $scope.showMeridian = timepickerConfig.showMeridian; + if ($attrs.showMeridian) { + $scope.$parent.$watch($parse($attrs.showMeridian), function(value) { + $scope.showMeridian = !!value; + + if (ngModelCtrl.$error.time) { + // Evaluate from template + var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate(); + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + refresh(); + } + } else { + updateTemplate(); + } + }); + } + + // Get $scope.hours in 24H mode if valid + function getHoursFromTemplate() { + var hours = parseInt($scope.hours, 10); + var valid = $scope.showMeridian ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24); + if (!valid) { + return undefined; + } + + if ($scope.showMeridian) { + if (hours === 12) { + hours = 0; + } + if ($scope.meridian === meridians[1]) { + hours = hours + 12; + } + } + return hours; + } + + function getMinutesFromTemplate() { + var minutes = parseInt($scope.minutes, 10); + return (minutes >= 0 && minutes < 60) ? minutes : undefined; + } + + function pad(value) { + return (angular.isDefined(value) && value.toString().length < 2) ? '0' + value : value.toString(); + } + + // Respond on mousewheel spin + this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl) { + var isScrollingUp = function(e) { + if (e.originalEvent) { + e = e.originalEvent; + } + //pick correct delta variable depending on event + var delta = (e.wheelDelta) ? e.wheelDelta : -e.deltaY; + return (e.detail || delta > 0); + }; + + hoursInputEl.bind('mousewheel wheel', function(e) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); + e.preventDefault(); + }); + + minutesInputEl.bind('mousewheel wheel', function(e) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); + e.preventDefault(); + }); + + }; + + // Respond on up/down arrowkeys + this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl) { + hoursInputEl.bind('keydown', function(e) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementHours(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementHours(); + $scope.$apply(); + } + }); + + minutesInputEl.bind('keydown', function(e) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementMinutes(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementMinutes(); + $scope.$apply(); + } + }); + }; + + this.setupInputEvents = function(hoursInputEl, minutesInputEl) { + if ($scope.readonlyInput) { + $scope.updateHours = angular.noop; + $scope.updateMinutes = angular.noop; + return; + } + + var invalidate = function(invalidHours, invalidMinutes) { + ngModelCtrl.$setViewValue(null); + ngModelCtrl.$setValidity('time', false); + if (angular.isDefined(invalidHours)) { + $scope.invalidHours = invalidHours; + } + if (angular.isDefined(invalidMinutes)) { + $scope.invalidMinutes = invalidMinutes; + } + }; + + $scope.updateHours = function() { + var hours = getHoursFromTemplate(), + minutes = getMinutesFromTemplate(); + + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + if (selected < min || selected > max) { + invalidate(true); + } else { + refresh('h'); + } + } else { + invalidate(true); + } + }; + + hoursInputEl.bind('blur', function(e) { + if (!$scope.invalidHours && $scope.hours < 10) { + $scope.$apply(function() { + $scope.hours = pad($scope.hours); + }); + } + }); + + $scope.updateMinutes = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(undefined, true); + } else { + refresh('m'); + } + } else { + invalidate(undefined, true); + } + }; + + minutesInputEl.bind('blur', function(e) { + if (!$scope.invalidMinutes && $scope.minutes < 10) { + $scope.$apply(function() { + $scope.minutes = pad($scope.minutes); + }); + } + }); + + }; + + this.render = function() { + var date = ngModelCtrl.$viewValue; + + if (isNaN(date)) { + ngModelCtrl.$setValidity('time', false); + $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'); + } else { + if (date) { + selected = date; + } + + if (selected < min || selected > max) { + ngModelCtrl.$setValidity('time', false); + $scope.invalidHours = true; + $scope.invalidMinutes = true; + } else { + makeValid(); + } + updateTemplate(); + } + }; + + // Call internally when we know that model is valid. + function refresh(keyboardChange) { + makeValid(); + ngModelCtrl.$setViewValue(new Date(selected)); + updateTemplate(keyboardChange); + } + + function makeValid() { + ngModelCtrl.$setValidity('time', true); + $scope.invalidHours = false; + $scope.invalidMinutes = false; + } + + function updateTemplate(keyboardChange) { + var hours = selected.getHours(), minutes = selected.getMinutes(); + + if ($scope.showMeridian) { + hours = (hours === 0 || hours === 12) ? 12 : hours % 12; // Convert 24 to 12 hour system + } + + $scope.hours = keyboardChange === 'h' ? hours : pad(hours); + if (keyboardChange !== 'm') { + $scope.minutes = pad(minutes); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + } + + function addMinutes(date, minutes) { + var dt = new Date(date.getTime() + minutes * 60000); + var newDate = new Date(date); + newDate.setHours(dt.getHours(), dt.getMinutes()); + return newDate; + } + + function addMinutesToSelected(minutes) { + selected = addMinutes(selected, minutes); + refresh(); + } + + $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? + $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; + + $scope.incrementHours = function() { + if (!$scope.noIncrementHours()) { + addMinutesToSelected(hourStep * 60); + } + }; + + $scope.decrementHours = function() { + if (!$scope.noDecrementHours()) { + addMinutesToSelected(-hourStep * 60); + } + }; + + $scope.incrementMinutes = function() { + if (!$scope.noIncrementMinutes()) { + addMinutesToSelected(minuteStep); + } + }; + + $scope.decrementMinutes = function() { + if (!$scope.noDecrementMinutes()) { + addMinutesToSelected(-minuteStep); + } + }; + + $scope.toggleMeridian = function() { + if (!$scope.noToggleMeridian()) { + addMinutesToSelected(12 * 60 * (selected.getHours() < 12 ? 1 : -1)); + } + }; +}]) + +.directive('uibTimepicker', function() { + return { + restrict: 'EA', + require: ['uibTimepicker', '?^ngModel'], + controller: 'UibTimepickerController', + controllerAs: 'timepicker', + replace: true, + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/timepicker/timepicker.html'; + }, + link: function(scope, element, attrs, ctrls) { + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}); + +/* Deprecated timepicker below */ + +angular.module('ui.bootstrap.timepicker') + +.value('$timepickerSuppressWarning', false) + +.controller('TimepickerController', ['$scope', '$element', '$attrs', '$controller', '$log', '$timepickerSuppressWarning', function($scope, $element, $attrs, $controller, $log, $timepickerSuppressWarning) { + if (!$timepickerSuppressWarning) { + $log.warn('TimepickerController is now deprecated. Use UibTimepickerController instead.'); + } + + angular.extend(this, $controller('UibTimepickerController', { + $scope: $scope, + $element: $element, + $attrs: $attrs + })); +}]) + +.directive('timepicker', ['$log', '$timepickerSuppressWarning', function($log, $timepickerSuppressWarning) { + return { + restrict: 'EA', + require: ['timepicker', '?^ngModel'], + controller: 'TimepickerController', + controllerAs: 'timepicker', + replace: true, + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'template/timepicker/timepicker.html'; + }, + link: function(scope, element, attrs, ctrls) { + if (!$timepickerSuppressWarning) { + $log.warn('timepicker is now deprecated. Use uib-timepicker instead.'); + } + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}]); + +angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) + +/** + * A helper service that can parse typeahead's syntax (string provided by users) + * Extracted to a separate service for ease of unit testing + */ + .factory('uibTypeaheadParser', ['$parse', function($parse) { + // 00000111000000000000022200000000000000003333333333333330000000000044000 + var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/; + return { + parse: function(input) { + var match = input.match(TYPEAHEAD_REGEXP); + if (!match) { + throw new Error( + 'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' + + ' but got "' + input + '".'); + } + + return { + itemName: match[3], + source: $parse(match[4]), + viewMapper: $parse(match[2] || match[1]), + modelMapper: $parse(match[1]) + }; + } + }; + }]) + + .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$uibPosition', 'uibTypeaheadParser', + function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) { + var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; + var modelCtrl, ngModelOptions; + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } + + //minimal wait time after last character typed before typeahead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + //should it select highlighted popup value when losing focus? + var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; + + //binding to a variable that indicates if there were no results after the query is completed + var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; + + var appendToElementId = attrs.typeaheadAppendToElementId || false; + + var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var parsedModel = $parse(attrs.ngModel); + var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); + var $setModelValue = function(scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { + return invokeModelSetter(scope, {$$$p: newValue}); + } else { + return parsedModel.assign(scope, newValue); + } + }; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.uibTypeahead); + + var hasFocus; + + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; + + //create a child scope for the typeahead directive so we are not polluting original scope + //with typeahead-specific data (matches, query etc.) + var scope = originalScope.$new(); + var offDestroy = originalScope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); + + // WAI-ARIA + var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + element.attr({ + 'aria-autocomplete': 'list', + 'aria-expanded': false, + 'aria-owns': popupId + }); + + //pop-up element used to display matches + var popUpEl = angular.element('<div uib-typeahead-popup></div>'); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { + popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); + } + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + }; + + var getMatchId = function(index) { + return popupId + '-option-' + index; + }; + + // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. + // This attribute is added or removed automatically when the `activeIdx` changes. + scope.$watch('activeIdx', function(index) { + if (index < 0) { + element.removeAttr('aria-activedescendant'); + } else { + element.attr('aria-activedescendant', getMatchId(index)); + } + }); + + var inputIsExactMatch = function(inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; + + var getMatchesAsync = function(inputValue) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(parserResult.source(originalScope, locals)).then(function(matches) { + //it might happen that several async queries were in progress if a user were typing fast + //but we are interested only in responses that correspond to the current view value + var onCurrentRequest = (inputValue === modelCtrl.$viewValue); + if (onCurrentRequest && hasFocus) { + if (matches && matches.length > 0) { + scope.activeIdx = focusFirst ? 0 : -1; + isNoResultsSetter(originalScope, false); + scope.matches.length = 0; + + //transform labels + for (var i = 0; i < matches.length; i++) { + locals[parserResult.itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: parserResult.viewMapper(scope, locals), + model: matches[i] + }); + } + + scope.query = inputValue; + //position pop-up with matches - we need to re-calculate its position each time we are opening a window + //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page + //due to other elements being rendered + recalculatePosition(); + + element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { + scope.select(0); + } + } else { + resetMatches(); + isNoResultsSetter(originalScope, true); + } + } + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function() { + resetMatches(); + isLoadingSetter(originalScope, false); + isNoResultsSetter(originalScope, true); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).bind('resize', fireRecalculating); + $document.find('body').bind('scroll', fireRecalculating); + } + + // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutEventPromise; + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + // Cancel previous timeout + if (timeoutEventPromise) { + $timeout.cancel(timeoutEventPromise); + } + + // Debounced executing recalculate after events fired + timeoutEventPromise = $timeout(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + } + + // recalculate actual position and set new values to scope + // after digest loop is popup in right position + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top += element.prop('offsetHeight'); + } + + //we need to propagate user's query so we can higlight matches + scope.query = undefined; + + //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutPromise; + + var scheduleSearchWithTimeout = function(inputValue) { + timeoutPromise = $timeout(function() { + getMatchesAsync(inputValue); + }, waitTime); + }; + + var cancelPreviousTimeout = function() { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; + + resetMatches(); + + scope.select = function(activeIdx) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + selected = true; + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals) + }); + + resetMatches(); + + //return focus to the input element if a match was selected via a mouse click event + // use timeout to avoid $rootScope:inprog error + if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { + $timeout(function() { element[0].focus(); }, 0, false); + } + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.bind('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results + if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + + if (evt.which === 40) { + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + } else if (evt.which === 38) { + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + } else if (evt.which === 13 || evt.which === 9) { + scope.$apply(function () { + scope.select(scope.activeIdx); + }); + } else if (evt.which === 27) { + evt.stopPropagation(); + + resetMatches(); + scope.$digest(); + } + }); + + element.bind('blur', function() { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + scope.select(scope.activeIdx); + }); + } + hasFocus = false; + selected = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function(evt) { + // Issue #3973 + // Firefox treats right click as a click on document + if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { + resetMatches(); + if (!$rootScope.$$phase) { + scope.$digest(); + } + } + }; + + $document.bind('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.unbind('click', dismissClickHandler); + if (appendToBody || appendToElementId) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).unbind('resize', fireRecalculating); + $document.find('body').unbind('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendToElementId !== false) { + angular.element($document[0].getElementById(appendToElementId)).append($popup); + } else { + element.after($popup); + } + + this.init = function(_modelCtrl, _ngModelOptions) { + modelCtrl = _modelCtrl; + ngModelOptions = _ngModelOptions; + + //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM + //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue + modelCtrl.$parsers.unshift(function(inputValue) { + hasFocus = true; + + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } else { + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } else { + modelCtrl.$setValidity('editable', false); + return undefined; + } + } + }); + + modelCtrl.$formatters.push(function(modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; + + // The validity may be set to false via $parsers (see above) if + // the model is restricted to selected values. If the model + // is set manually it is considered to be valid. + if (!isEditable) { + modelCtrl.$setValidity('editable', true); + } + + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } else { + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + } + }); + }; + }]) + + .directive('uibTypeahead', function() { + return { + controller: 'UibTypeaheadController', + require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'], + link: function(originalScope, element, attrs, ctrls) { + ctrls[2].init(ctrls[0], ctrls[1]); + } + }; + }) + + .directive('uibTypeaheadPopup', function() { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + return scope.matches.length > 0; + }; + + scope.isActive = function(matchIdx) { + return scope.active == matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx) { + scope.select({activeIdx:activeIdx}); + }; + } + }; + }) + + .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) { + return { + scope: { + index: '=', + match: '=', + query: '=' + }, + link:function(scope, element, attrs) { + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + $compile(tplContent.trim())(scope, function(clonedElement) { + element.replaceWith(clonedElement); + }); + }); + } + }; + }]) + + .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) { + var isSanitizePresent; + isSanitizePresent = $injector.has('$sanitize'); + + function escapeRegexp(queryToEscape) { + // Regex: capture the whole query string and replace it with the string that will be used to match + // the results, for example if the capture is "a" the result will be \a + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); + } + + function containsHtml(matchItem) { + return /<.*>/g.test(matchItem); + } + + return function(matchItem, query) { + if (!isSanitizePresent && containsHtml(matchItem)) { + $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger + } + matchItem = query? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag + if (!isSanitizePresent) { + matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive + } + return matchItem; + }; + }]); + +/* Deprecated typeahead below */ + +angular.module('ui.bootstrap.typeahead') + .value('$typeaheadSuppressWarning', false) + .service('typeaheadParser', ['$parse', 'uibTypeaheadParser', '$log', '$typeaheadSuppressWarning', function($parse, uibTypeaheadParser, $log, $typeaheadSuppressWarning) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeaheadParser is now deprecated. Use uibTypeaheadParser instead.'); + } + + return uibTypeaheadParser; + }]) + + .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$uibPosition', 'typeaheadParser', '$log', '$typeaheadSuppressWarning', + function($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser, $log, $typeaheadSuppressWarning) { + var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; + return { + require: ['ngModel', '^?ngModelOptions'], + link: function(originalScope, element, attrs, ctrls) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeahead is now deprecated. Use uib-typeahead instead.'); + } + var modelCtrl = ctrls[0]; + var ngModelOptions = ctrls[1]; + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minLength = originalScope.$eval(attrs.typeaheadMinLength); + if (!minLength && minLength !== 0) { + minLength = 1; + } + + //minimal wait time after last character typed before typeahead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + //should it select highlighted popup value when losing focus? + var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false; + + //binding to a variable that indicates if there were no results after the query is completed + var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop; + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; + + var appendToElementId = attrs.typeaheadAppendToElementId || false; + + var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var parsedModel = $parse(attrs.ngModel); + var invokeModelSetter = $parse(attrs.ngModel + '($$$p)'); + var $setModelValue = function(scope, newValue) { + if (angular.isFunction(parsedModel(originalScope)) && + ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) { + return invokeModelSetter(scope, {$$$p: newValue}); + } else { + return parsedModel.assign(scope, newValue); + } + }; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.typeahead); + + var hasFocus; + + //Used to avoid bug in iOS webview where iOS keyboard does not fire + //mousedown & mouseup events + //Issue #3699 + var selected; + + //create a child scope for the typeahead directive so we are not polluting original scope + //with typeahead-specific data (matches, query etc.) + var scope = originalScope.$new(); + var offDestroy = originalScope.$on('$destroy', function() { + scope.$destroy(); + }); + scope.$on('$destroy', offDestroy); + + // WAI-ARIA + var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + element.attr({ + 'aria-autocomplete': 'list', + 'aria-expanded': false, + 'aria-owns': popupId + }); + + //pop-up element used to display matches + var popUpEl = angular.element('<div typeahead-popup></div>'); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) { + popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl); + } + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + }; + + var getMatchId = function(index) { + return popupId + '-option-' + index; + }; + + // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead. + // This attribute is added or removed automatically when the `activeIdx` changes. + scope.$watch('activeIdx', function(index) { + if (index < 0) { + element.removeAttr('aria-activedescendant'); + } else { + element.attr('aria-activedescendant', getMatchId(index)); + } + }); + + var inputIsExactMatch = function(inputValue, index) { + if (scope.matches.length > index && inputValue) { + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; + + var getMatchesAsync = function(inputValue) { + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + isNoResultsSetter(originalScope, false); + $q.when(parserResult.source(originalScope, locals)).then(function(matches) { + //it might happen that several async queries were in progress if a user were typing fast + //but we are interested only in responses that correspond to the current view value + var onCurrentRequest = (inputValue === modelCtrl.$viewValue); + if (onCurrentRequest && hasFocus) { + if (matches && matches.length > 0) { + scope.activeIdx = focusFirst ? 0 : -1; + isNoResultsSetter(originalScope, false); + scope.matches.length = 0; + + //transform labels + for (var i = 0; i < matches.length; i++) { + locals[parserResult.itemName] = matches[i]; + scope.matches.push({ + id: getMatchId(i), + label: parserResult.viewMapper(scope, locals), + model: matches[i] + }); + } + + scope.query = inputValue; + //position pop-up with matches - we need to re-calculate its position each time we are opening a window + //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page + //due to other elements being rendered + recalculatePosition(); + + element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) { + scope.select(0); + } + } else { + resetMatches(); + isNoResultsSetter(originalScope, true); + } + } + if (onCurrentRequest) { + isLoadingSetter(originalScope, false); + } + }, function() { + resetMatches(); + isLoadingSetter(originalScope, false); + isNoResultsSetter(originalScope, true); + }); + }; + + // bind events only if appendToBody params exist - performance feature + if (appendToBody) { + angular.element($window).bind('resize', fireRecalculating); + $document.find('body').bind('scroll', fireRecalculating); + } + + // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutEventPromise; + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + // Cancel previous timeout + if (timeoutEventPromise) { + $timeout.cancel(timeoutEventPromise); + } + + // Debounced executing recalculate after events fired + timeoutEventPromise = $timeout(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + } + + // recalculate actual position and set new values to scope + // after digest loop is popup in right position + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top += element.prop('offsetHeight'); + } + + resetMatches(); + + //we need to propagate user's query so we can higlight matches + scope.query = undefined; + + //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutPromise; + + var scheduleSearchWithTimeout = function(inputValue) { + timeoutPromise = $timeout(function() { + getMatchesAsync(inputValue); + }, waitTime); + }; + + var cancelPreviousTimeout = function() { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + }; + + //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM + //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue + modelCtrl.$parsers.unshift(function(inputValue) { + hasFocus = true; + + if (minLength === 0 || inputValue && inputValue.length >= minLength) { + if (waitTime > 0) { + cancelPreviousTimeout(); + scheduleSearchWithTimeout(inputValue); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + cancelPreviousTimeout(); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } else { + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } else { + modelCtrl.$setValidity('editable', false); + return undefined; + } + } + }); + + modelCtrl.$formatters.push(function(modelValue) { + var candidateViewValue, emptyViewValue; + var locals = {}; + + // The validity may be set to false via $parsers (see above) if + // the model is restricted to selected values. If the model + // is set manually it is considered to be valid. + if (!isEditable) { + modelCtrl.$setValidity('editable', true); + } + + if (inputFormatter) { + locals.$model = modelValue; + return inputFormatter(originalScope, locals); + } else { + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue; + } + }); + + scope.select = function(activeIdx) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + selected = true; + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals) + }); + + resetMatches(); + + //return focus to the input element if a match was selected via a mouse click event + // use timeout to avoid $rootScope:inprog error + if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) { + $timeout(function() { element[0].focus(); }, 0, false); + } + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.bind('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results + if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + + if (evt.which === 40) { + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + } else if (evt.which === 38) { + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + } else if (evt.which === 13 || evt.which === 9) { + scope.$apply(function () { + scope.select(scope.activeIdx); + }); + } else if (evt.which === 27) { + evt.stopPropagation(); + + resetMatches(); + scope.$digest(); + } + }); + + element.bind('blur', function() { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + scope.select(scope.activeIdx); + }); + } + hasFocus = false; + selected = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function(evt) { + // Issue #3973 + // Firefox treats right click as a click on document + if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) { + resetMatches(); + if (!$rootScope.$$phase) { + scope.$digest(); + } + } + }; + + $document.bind('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.unbind('click', dismissClickHandler); + if (appendToBody || appendToElementId) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).unbind('resize', fireRecalculating); + $document.find('body').unbind('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendToElementId !== false) { + angular.element($document[0].getElementById(appendToElementId)).append($popup); + } else { + element.after($popup); + } + } + }; + }]) + + .directive('typeaheadPopup', ['$typeaheadSuppressWarning', '$log', function($typeaheadSuppressWarning, $log) { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + + if (!$typeaheadSuppressWarning) { + $log.warn('typeahead-popup is now deprecated. Use uib-typeahead-popup instead.'); + } + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + return scope.matches.length > 0; + }; + + scope.isActive = function(matchIdx) { + return scope.active == matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx) { + scope.select({activeIdx:activeIdx}); + }; + } + }; + }]) + + .directive('typeaheadMatch', ['$templateRequest', '$compile', '$parse', '$typeaheadSuppressWarning', '$log', function($templateRequest, $compile, $parse, $typeaheadSuppressWarning, $log) { + return { + restrict: 'EA', + scope: { + index: '=', + match: '=', + query: '=' + }, + link:function(scope, element, attrs) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeahead-match is now deprecated. Use uib-typeahead-match instead.'); + } + + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + $compile(tplContent.trim())(scope, function(clonedElement) { + element.replaceWith(clonedElement); + }); + }); + } + }; + }]) + + .filter('typeaheadHighlight', ['$sce', '$injector', '$log', '$typeaheadSuppressWarning', function($sce, $injector, $log, $typeaheadSuppressWarning) { + var isSanitizePresent; + isSanitizePresent = $injector.has('$sanitize'); + + function escapeRegexp(queryToEscape) { + // Regex: capture the whole query string and replace it with the string that will be used to match + // the results, for example if the capture is "a" the result will be \a + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); + } + + function containsHtml(matchItem) { + return /<.*>/g.test(matchItem); + } + + return function(matchItem, query) { + if (!$typeaheadSuppressWarning) { + $log.warn('typeaheadHighlight is now deprecated. Use uibTypeaheadHighlight instead.'); + } + + if (!isSanitizePresent && containsHtml(matchItem)) { + $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger + } + + matchItem = query? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag + if (!isSanitizePresent) { + matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive + } + + return matchItem; + }; + }]); +!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap.min.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap.min.js new file mode 100644 index 000000000..cbabc3283 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap.min.js @@ -0,0 +1,11 @@ +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 0.14.3 - 2015-10-23 + * License: MIT + */ +angular.module("ui.bootstrap",["ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.collapse",[]).directive("uibCollapse",["$animate","$injector",function(a,b){var c=b.has("$animateCss")?b.get("$animateCss"):null;return{link:function(b,d,e){function f(){d.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),c?c(d,{addClass:"in",easing:"ease",to:{height:d[0].scrollHeight+"px"}}).start()["finally"](g):a.addClass(d,"in",{to:{height:d[0].scrollHeight+"px"}}).then(g)}function g(){d.removeClass("collapsing").addClass("collapse").css({height:"auto"})}function h(){return d.hasClass("collapse")||d.hasClass("in")?(d.css({height:d[0].scrollHeight+"px"}).removeClass("collapse").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),void(c?c(d,{removeClass:"in",to:{height:"0"}}).start()["finally"](i):a.removeClass(d,"in",{to:{height:"0"}}).then(i))):i()}function i(){d.css({height:"0"}),d.removeClass("collapsing").addClass("collapse")}b.$watch(e.uibCollapse,function(a){a?h():f()})}}}]),angular.module("ui.bootstrap.collapse").value("$collapseSuppressWarning",!1).directive("collapse",["$animate","$injector","$log","$collapseSuppressWarning",function(a,b,c,d){var e=b.has("$animateCss")?b.get("$animateCss"):null;return{link:function(b,f,g){function h(){f.removeClass("collapse").addClass("collapsing").attr("aria-expanded",!0).attr("aria-hidden",!1),e?e(f,{easing:"ease",to:{height:f[0].scrollHeight+"px"}}).start().done(i):a.animate(f,{},{height:f[0].scrollHeight+"px"}).then(i)}function i(){f.removeClass("collapsing").addClass("collapse in").css({height:"auto"})}function j(){return f.hasClass("collapse")||f.hasClass("in")?(f.css({height:f[0].scrollHeight+"px"}).removeClass("collapse in").addClass("collapsing").attr("aria-expanded",!1).attr("aria-hidden",!0),void(e?e(f,{to:{height:"0"}}).start().done(k):a.animate(f,{},{height:"0"}).then(k))):k()}function k(){f.css({height:"0"}),f.removeClass("collapsing").addClass("collapse")}d||c.warn("collapse is now deprecated. Use uib-collapse instead."),b.$watch(g.collapse,function(a){a?j():h()})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("uibAccordionConfig",{closeOthers:!0}).controller("UibAccordionController",["$scope","$attrs","uibAccordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(c){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("uibAccordion",function(){return{controller:"UibAccordionController",controllerAs:"accordion",transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion.html"}}}).directive("uibAccordionGroup",function(){return{require:"^uibAccordion",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion-group.html"},scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){d.addGroup(a),a.openClass=c.openClass||"panel-open",a.panelClass=c.panelClass,a.$watch("isOpen",function(c){b.toggleClass(a.openClass,!!c),c&&d.closeOthers(a)}),a.toggleOpen=function(b){a.isDisabled||b&&32!==b.which||(a.isOpen=!a.isOpen)}}}}).directive("uibAccordionHeading",function(){return{transclude:!0,template:"",replace:!0,require:"^uibAccordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,angular.noop))}}}).directive("uibAccordionTransclude",function(){return{require:["?^uibAccordionGroup","?^accordionGroup"],link:function(a,b,c,d){d=d[0]?d[0]:d[1],a.$watch(function(){return d[c.uibAccordionTransclude]},function(a){a&&(b.find("span").html(""),b.find("span").append(a))})}}}),angular.module("ui.bootstrap.accordion").value("$accordionSuppressWarning",!1).controller("AccordionController",["$scope","$attrs","$controller","$log","$accordionSuppressWarning",function(a,b,c,d,e){e||d.warn("AccordionController is now deprecated. Use UibAccordionController instead."),angular.extend(this,c("UibAccordionController",{$scope:a,$attrs:b}))}]).directive("accordion",["$log","$accordionSuppressWarning",function(a,b){return{restrict:"EA",controller:"AccordionController",controllerAs:"accordion",transclude:!0,replace:!1,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion.html"},link:function(){b||a.warn("accordion is now deprecated. Use uib-accordion instead.")}}}]).directive("accordionGroup",["$log","$accordionSuppressWarning",function(a,b){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/accordion/accordion-group.html"},scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(c,d,e,f){b||a.warn("accordion-group is now deprecated. Use uib-accordion-group instead."),f.addGroup(c),c.openClass=e.openClass||"panel-open",c.panelClass=e.panelClass,c.$watch("isOpen",function(a){d.toggleClass(c.openClass,!!a),a&&f.closeOthers(c)}),c.toggleOpen=function(a){c.isDisabled||a&&32!==a.which||(c.isOpen=!c.isOpen)}}}}]).directive("accordionHeading",["$log","$accordionSuppressWarning",function(a,b){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(c,d,e,f,g){b||a.warn("accordion-heading is now deprecated. Use uib-accordion-heading instead."),f.setHeading(g(c,angular.noop))}}}]).directive("accordionTransclude",["$log","$accordionSuppressWarning",function(a,b){return{require:"^accordionGroup",link:function(c,d,e,f){b||a.warn("accordion-transclude is now deprecated. Use uib-accordion-transclude instead."),c.$watch(function(){return f[e.accordionTransclude]},function(a){a&&(d.find("span").html(""),d.find("span").append(a))})}}}]),angular.module("ui.bootstrap.alert",[]).controller("UibAlertController",["$scope","$attrs","$interpolate","$timeout",function(a,b,c,d){a.closeable=!!b.close;var e=angular.isDefined(b.dismissOnTimeout)?c(b.dismissOnTimeout)(a.$parent):null;e&&d(function(){a.close()},parseInt(e,10))}]).directive("uibAlert",function(){return{controller:"UibAlertController",controllerAs:"alert",templateUrl:function(a,b){return b.templateUrl||"template/alert/alert.html"},transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}),angular.module("ui.bootstrap.alert").value("$alertSuppressWarning",!1).controller("AlertController",["$scope","$attrs","$controller","$log","$alertSuppressWarning",function(a,b,c,d,e){e||d.warn("AlertController is now deprecated. Use UibAlertController instead."),angular.extend(this,c("UibAlertController",{$scope:a,$attrs:b}))}]).directive("alert",["$log","$alertSuppressWarning",function(a,b){return{controller:"AlertController",controllerAs:"alert",templateUrl:function(a,b){return b.templateUrl||"template/alert/alert.html"},transclude:!0,replace:!0,scope:{type:"@",close:"&"},link:function(){b||a.warn("alert is now deprecated. Use uib-alert instead.")}}}]),angular.module("ui.bootstrap.buttons",[]).constant("uibButtonConfig",{activeClass:"active",toggleEvent:"click"}).controller("UibButtonsController",["uibButtonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("uibBtnRadio",function(){return{require:["uibBtnRadio","ngModel"],controller:"UibButtonsController",controllerAs:"buttons",link:function(a,b,c,d){var e=d[0],f=d[1];b.find("input").css({display:"none"}),f.$render=function(){b.toggleClass(e.activeClass,angular.equals(f.$modelValue,a.$eval(c.uibBtnRadio)))},b.on(e.toggleEvent,function(){if(!c.disabled){var d=b.hasClass(e.activeClass);(!d||angular.isDefined(c.uncheckable))&&a.$apply(function(){f.$setViewValue(d?null:a.$eval(c.uibBtnRadio)),f.$render()})}})}}}).directive("uibBtnCheckbox",function(){return{require:["uibBtnCheckbox","ngModel"],controller:"UibButtonsController",controllerAs:"button",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){return angular.isDefined(b)?a.$eval(b):c}var h=d[0],i=d[1];b.find("input").css({display:"none"}),i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.on(h.toggleEvent,function(){c.disabled||a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.buttons").value("$buttonsSuppressWarning",!1).controller("ButtonsController",["$controller","$log","$buttonsSuppressWarning",function(a,b,c){c||b.warn("ButtonsController is now deprecated. Use UibButtonsController instead."),angular.extend(this,a("UibButtonsController"))}]).directive("btnRadio",["$log","$buttonsSuppressWarning",function(a,b){return{require:["btnRadio","ngModel"],controller:"ButtonsController",controllerAs:"buttons",link:function(c,d,e,f){b||a.warn("btn-radio is now deprecated. Use uib-btn-radio instead.");var g=f[0],h=f[1];d.find("input").css({display:"none"}),h.$render=function(){d.toggleClass(g.activeClass,angular.equals(h.$modelValue,c.$eval(e.btnRadio)))},d.bind(g.toggleEvent,function(){if(!e.disabled){var a=d.hasClass(g.activeClass);(!a||angular.isDefined(e.uncheckable))&&c.$apply(function(){h.$setViewValue(a?null:c.$eval(e.btnRadio)),h.$render()})}})}}}]).directive("btnCheckbox",["$document","$log","$buttonsSuppressWarning",function(a,b,c){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",controllerAs:"button",link:function(d,e,f,g){function h(){return j(f.btnCheckboxTrue,!0)}function i(){return j(f.btnCheckboxFalse,!1)}function j(a,b){var c=d.$eval(a);return angular.isDefined(c)?c:b}c||b.warn("btn-checkbox is now deprecated. Use uib-btn-checkbox instead.");var k=g[0],l=g[1];e.find("input").css({display:"none"}),l.$render=function(){e.toggleClass(k.activeClass,angular.equals(l.$modelValue,h()))},e.bind(k.toggleEvent,function(){f.disabled||d.$apply(function(){l.$setViewValue(e.hasClass(k.activeClass)?i():h()),l.$render()})}),e.on("keypress",function(b){f.disabled||32!==b.which||a[0].activeElement!==e[0]||d.$apply(function(){l.$setViewValue(e.hasClass(k.activeClass)?i():h()),l.$render()})})}}}]),angular.module("ui.bootstrap.carousel",[]).controller("UibCarouselController",["$scope","$element","$interval","$animate",function(a,b,c,d){function e(b,c,e){s||(angular.extend(b,{direction:e,active:!0}),angular.extend(m.currentSlide||{},{direction:e,active:!1}),d.enabled()&&!a.noTransition&&!a.$currentTransition&&b.$element&&m.slides.length>1&&(b.$element.data(q,b.direction),m.currentSlide&&m.currentSlide.$element&&m.currentSlide.$element.data(q,b.direction),a.$currentTransition=!0,o?d.on("addClass",b.$element,function(b,c){"close"===c&&(a.$currentTransition=null,d.off("addClass",b))}):b.$element.one("$animate:close",function(){a.$currentTransition=null})),m.currentSlide=b,r=c,g())}function f(a){if(angular.isUndefined(n[a].index))return n[a];var b;n.length;for(b=0;b<n.length;++b)if(n[b].index==a)return n[b]}function g(){h();var b=+a.interval;!isNaN(b)&&b>0&&(k=c(i,b))}function h(){k&&(c.cancel(k),k=null)}function i(){var b=+a.interval;l&&!isNaN(b)&&b>0&&n.length?a.next():a.pause()}function j(b){b.length||(a.$currentTransition=null)}var k,l,m=this,n=m.slides=a.slides=[],o=angular.version.minor>=4,p="uib-noTransition",q="uib-slideDirection",r=-1;m.currentSlide=null;var s=!1;m.select=a.select=function(b,c){var d=a.indexOfSlide(b);void 0===c&&(c=d>m.getCurrentIndex()?"next":"prev"),b&&b!==m.currentSlide&&!a.$currentTransition&&e(b,d,c)},a.$on("$destroy",function(){s=!0}),m.getCurrentIndex=function(){return m.currentSlide&&angular.isDefined(m.currentSlide.index)?+m.currentSlide.index:r},a.indexOfSlide=function(a){return angular.isDefined(a.index)?+a.index:n.indexOf(a)},a.next=function(){var b=(m.getCurrentIndex()+1)%n.length;return 0===b&&a.noWrap()?void a.pause():m.select(f(b),"next")},a.prev=function(){var b=m.getCurrentIndex()-1<0?n.length-1:m.getCurrentIndex()-1;return a.noWrap()&&b===n.length-1?void a.pause():m.select(f(b),"prev")},a.isActive=function(a){return m.currentSlide===a},a.$watch("interval",g),a.$watchCollection("slides",j),a.$on("$destroy",h),a.play=function(){l||(l=!0,g())},a.pause=function(){a.noPause||(l=!1,h())},m.addSlide=function(b,c){b.$element=c,n.push(b),1===n.length||b.active?(m.select(n[n.length-1]),1===n.length&&a.play()):b.active=!1},m.removeSlide=function(a){angular.isDefined(a.index)&&n.sort(function(a,b){return+a.index>+b.index});var b=n.indexOf(a);n.splice(b,1),n.length>0&&a.active?b>=n.length?m.select(n[b-1]):m.select(n[b]):r>b&&r--,0===n.length&&(m.currentSlide=null)},a.$watch("noTransition",function(a){b.data(p,a)})}]).directive("uibCarousel",[function(){return{transclude:!0,replace:!0,controller:"UibCarouselController",controllerAs:"carousel",require:"carousel",templateUrl:function(a,b){return b.templateUrl||"template/carousel/carousel.html"},scope:{interval:"=",noTransition:"=",noPause:"=",noWrap:"&"}}}]).directive("uibSlide",function(){return{require:"^uibCarousel",restrict:"EA",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/carousel/slide.html"},scope:{active:"=?",actual:"=?",index:"=?"},link:function(a,b,c,d){d.addSlide(a,b),a.$on("$destroy",function(){d.removeSlide(a)}),a.$watch("active",function(b){b&&d.select(a)})}}}).animation(".item",["$injector","$animate",function(a,b){function c(a,b,c){a.removeClass(b),c&&c()}var d="uib-noTransition",e="uib-slideDirection",f=null;return a.has("$animateCss")&&(f=a.get("$animateCss")),{beforeAddClass:function(a,g,h){if("active"==g&&a.parent()&&a.parent().parent()&&!a.parent().parent().data(d)){var i=!1,j=a.data(e),k="next"==j?"left":"right",l=c.bind(this,a,k+" "+j,h);return a.addClass(j),f?f(a,{addClass:k}).start().done(l):b.addClass(a,k).then(function(){i||l(),h()}),function(){i=!0}}h()},beforeRemoveClass:function(a,g,h){if("active"===g&&a.parent()&&a.parent().parent()&&!a.parent().parent().data(d)){var i=!1,j=a.data(e),k="next"==j?"left":"right",l=c.bind(this,a,k,h);return f?f(a,{addClass:k}).start().done(l):b.addClass(a,k).then(function(){i||l(),h()}),function(){i=!0}}h()}}}]),angular.module("ui.bootstrap.carousel").value("$carouselSuppressWarning",!1).controller("CarouselController",["$scope","$element","$controller","$log","$carouselSuppressWarning",function(a,b,c,d,e){e||d.warn("CarouselController is now deprecated. Use UibCarouselController instead."),angular.extend(this,c("UibCarouselController",{$scope:a,$element:b}))}]).directive("carousel",["$log","$carouselSuppressWarning",function(a,b){return{transclude:!0,replace:!0,controller:"CarouselController",controllerAs:"carousel",require:"carousel",templateUrl:function(a,b){return b.templateUrl||"template/carousel/carousel.html"},scope:{interval:"=",noTransition:"=",noPause:"=",noWrap:"&"},link:function(){b||a.warn("carousel is now deprecated. Use uib-carousel instead.")}}}]).directive("slide",["$log","$carouselSuppressWarning",function(a,b){return{require:"^carousel",transclude:!0,replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/carousel/slide.html"},scope:{active:"=?",actual:"=?",index:"=?"},link:function(c,d,e,f){b||a.warn("slide is now deprecated. Use uib-slide instead."),f.addSlide(c,d),c.$on("$destroy",function(){f.removeSlide(c)}),c.$watch("active",function(a){a&&f.select(c)})}}}]),angular.module("ui.bootstrap.dateparser",[]).service("uibDateParser",["$log","$locale","orderByFilter",function(a,b,c){function d(a){var b=[],d=a.split("");return angular.forEach(g,function(c,e){var f=a.indexOf(e);if(f>-1){a=a.split(""),d[f]="("+c.regex+")",a[f]="$";for(var g=f+1,h=f+e.length;h>g;g++)d[g]="",a[g]="$";a=a.join(""),b.push({index:f,apply:c.apply})}}),{regex:new RegExp("^"+d.join("")+"$"),map:c(b,"index")}}function e(a,b,c){return 1>c?!1:1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}var f,g,h=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){f=b.id,this.parsers={},g={yyyy:{regex:"\\d{4}",apply:function(a){this.year=+a}},yy:{regex:"\\d{2}",apply:function(a){this.year=+a+2e3}},y:{regex:"\\d{1,4}",apply:function(a){this.year=+a}},MMMM:{regex:b.DATETIME_FORMATS.MONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.MONTH.indexOf(a)}},MMM:{regex:b.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(a){this.month=b.DATETIME_FORMATS.SHORTMONTH.indexOf(a)}},MM:{regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1}},M:{regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1}},dd:{regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a}},d:{regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a}},EEEE:{regex:b.DATETIME_FORMATS.DAY.join("|")},EEE:{regex:b.DATETIME_FORMATS.SHORTDAY.join("|")},HH:{regex:"(?:0|1)[0-9]|2[0-3]",apply:function(a){this.hours=+a}},hh:{regex:"0[0-9]|1[0-2]",apply:function(a){this.hours=+a}},H:{regex:"1?[0-9]|2[0-3]",apply:function(a){this.hours=+a}},h:{regex:"[0-9]|1[0-2]",apply:function(a){this.hours=+a}},mm:{regex:"[0-5][0-9]",apply:function(a){this.minutes=+a}},m:{regex:"[0-9]|[1-5][0-9]",apply:function(a){this.minutes=+a}},sss:{regex:"[0-9][0-9][0-9]",apply:function(a){this.milliseconds=+a}},ss:{regex:"[0-5][0-9]",apply:function(a){this.seconds=+a}},s:{regex:"[0-9]|[1-5][0-9]",apply:function(a){this.seconds=+a}},a:{regex:b.DATETIME_FORMATS.AMPMS.join("|"),apply:function(a){12===this.hours&&(this.hours=0),"PM"===a&&(this.hours+=12)}}}},this.init(),this.parse=function(c,g,i){if(!angular.isString(c)||!g)return c;g=b.DATETIME_FORMATS[g]||g,g=g.replace(h,"\\$&"),b.id!==f&&this.init(),this.parsers[g]||(this.parsers[g]=d(g));var j=this.parsers[g],k=j.regex,l=j.map,m=c.match(k);if(m&&m.length){var n,o;angular.isDate(i)&&!isNaN(i.getTime())?n={year:i.getFullYear(),month:i.getMonth(),date:i.getDate(),hours:i.getHours(),minutes:i.getMinutes(),seconds:i.getSeconds(),milliseconds:i.getMilliseconds()}:(i&&a.warn("dateparser:","baseDate is not a valid date"),n={year:1900,month:0,date:1,hours:0,minutes:0,seconds:0,milliseconds:0});for(var p=1,q=m.length;q>p;p++){var r=l[p-1];r.apply&&r.apply.call(n,m[p])}return e(n.year,n.month,n.date)&&(angular.isDate(i)&&!isNaN(i.getTime())?(o=new Date(i),o.setFullYear(n.year,n.month,n.date,n.hours,n.minutes,n.seconds,n.milliseconds||0)):o=new Date(n.year,n.month,n.date,n.hours,n.minutes,n.seconds,n.milliseconds||0)),o}}}]),angular.module("ui.bootstrap.dateparser").value("$dateParserSuppressWarning",!1).service("dateParser",["$log","$dateParserSuppressWarning","uibDateParser",function(a,b,c){b||a.warn("dateParser is now deprecated. Use uibDateParser instead."),angular.extend(this,c)}]),angular.module("ui.bootstrap.position",[]).factory("$uibPosition",["$document","$window",function(a,b){function c(a,c){return a.currentStyle?a.currentStyle[c]:b.getComputedStyle?b.getComputedStyle(a)[c]:a.style[c]}function d(a){return"static"===(c(a,"position")||"static")}var e=function(b){for(var c=a[0],e=b.offsetParent||c;e&&e!==c&&d(e);)e=e.offsetParent;return e||c};return{position:function(b){var c=this.offset(b),d={top:0,left:0},f=e(b[0]);f!=a[0]&&(d=this.offset(angular.element(f)),d.top+=f.clientTop-f.scrollTop,d.left+=f.clientLeft-f.scrollLeft);var g=b[0].getBoundingClientRect();return{width:g.width||b.prop("offsetWidth"),height:g.height||b.prop("offsetHeight"),top:c.top-d.top,left:c.left-d.left}},offset:function(c){var d=c[0].getBoundingClientRect();return{width:d.width||c.prop("offsetWidth"),height:d.height||c.prop("offsetHeight"),top:d.top+(b.pageYOffset||a[0].documentElement.scrollTop),left:d.left+(b.pageXOffset||a[0].documentElement.scrollLeft)}},positionElements:function(a,b,c,d){var e,f,g,h,i=c.split("-"),j=i[0],k=i[1]||"center";e=d?this.offset(a):this.position(a),f=b.prop("offsetWidth"),g=b.prop("offsetHeight");var l={center:function(){return e.left+e.width/2-f/2},left:function(){return e.left},right:function(){return e.left+e.width}},m={center:function(){return e.top+e.height/2-g/2},top:function(){return e.top},bottom:function(){return e.top+e.height}};switch(j){case"right":h={top:m[k](),left:l[j]()};break;case"left":h={top:m[k](),left:e.left-f};break;case"bottom":h={top:m[j](),left:l[k]()};break;default:h={top:e.top-g,left:l[k]()}}return h}}}]),angular.module("ui.bootstrap.position").value("$positionSuppressWarning",!1).service("$position",["$log","$positionSuppressWarning","$uibPosition",function(a,b,c){b||a.warn("$position is now deprecated. Use $uibPosition instead."),angular.extend(this,c)}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.position"]).value("$datepickerSuppressError",!1).constant("uibDatepickerConfig",{formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",datepickerMode:"day",minMode:"day",maxMode:"year",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null,shortcutPropagation:!1}).controller("UibDatepickerController",["$scope","$attrs","$parse","$interpolate","$log","dateFilter","uibDatepickerConfig","$datepickerSuppressError",function(a,b,c,d,e,f,g,h){var i=this,j={$setViewValue:angular.noop};this.modes=["day","month","year"],angular.forEach(["formatDay","formatMonth","formatYear","formatDayHeader","formatDayTitle","formatMonthTitle","showWeeks","startingDay","yearRange","shortcutPropagation"],function(c,e){i[c]=angular.isDefined(b[c])?6>e?d(b[c])(a.$parent):a.$parent.$eval(b[c]):g[c]}),angular.forEach(["minDate","maxDate"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(a){i[d]=a?new Date(a):null,i.refreshView()}):i[d]=g[d]?new Date(g[d]):null}),angular.forEach(["minMode","maxMode"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(c){i[d]=angular.isDefined(c)?c:b[d],a[d]=i[d],("minMode"==d&&i.modes.indexOf(a.datepickerMode)<i.modes.indexOf(i[d])||"maxMode"==d&&i.modes.indexOf(a.datepickerMode)>i.modes.indexOf(i[d]))&&(a.datepickerMode=i[d])}):(i[d]=g[d]||null,a[d]=i[d])}),a.datepickerMode=a.datepickerMode||g.datepickerMode,a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),angular.isDefined(b.initDate)?(this.activeDate=a.$parent.$eval(b.initDate)||new Date,a.$parent.$watch(b.initDate,function(a){a&&(j.$isEmpty(j.$modelValue)||j.$invalid)&&(i.activeDate=a,i.refreshView())})):this.activeDate=new Date,a.isActive=function(b){return 0===i.compare(b.date,i.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(a){j=a,j.$render=function(){i.render()}},this.render=function(){if(j.$viewValue){var a=new Date(j.$viewValue),b=!isNaN(a);b?this.activeDate=a:h||e.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var a=j.$viewValue?new Date(j.$viewValue):null;j.$setValidity("dateDisabled",!a||this.element&&!this.isDisabled(a))}},this.createDateObject=function(a,b){var c=j.$viewValue?new Date(j.$viewValue):null;return{date:a,label:f(a,b),selected:c&&0===this.compare(a,c),disabled:this.isDisabled(a),current:0===this.compare(a,new Date),customClass:this.customClass(a)}},this.isDisabled=function(c){return this.minDate&&this.compare(c,this.minDate)<0||this.maxDate&&this.compare(c,this.maxDate)>0||b.dateDisabled&&a.dateDisabled({date:c,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===i.minMode){var c=j.$viewValue?new Date(j.$viewValue):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),j.$setViewValue(c),j.$render()}else i.activeDate=b,a.datepickerMode=i.modes[i.modes.indexOf(a.datepickerMode)-1]},a.move=function(a){var b=i.activeDate.getFullYear()+a*(i.step.years||0),c=i.activeDate.getMonth()+a*(i.step.months||0);i.activeDate.setFullYear(b,c,1),i.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===i.maxMode&&1===b||a.datepickerMode===i.minMode&&-1===b||(a.datepickerMode=i.modes[i.modes.indexOf(a.datepickerMode)+b])},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var k=function(){i.element[0].focus()};a.$on("uib:datepicker.focus",k),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey)if(b.preventDefault(),i.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(i.isDisabled(i.activeDate))return;a.select(i.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(i.handleKeyDown(c,b),i.refreshView()):a.toggleMode("up"===c?1:-1)}}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?f[b]:29}function e(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}var f=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=b,this.init=function(b){angular.extend(b,this),a.showWeeks=b.showWeeks,b.refreshView()},this.getDates=function(a,b){for(var c,d=new Array(b),e=new Date(a),f=0;b>f;)c=new Date(e),d[f++]=c,e.setDate(e.getDate()+1);return d},this._refreshView=function(){var b=this.activeDate.getFullYear(),d=this.activeDate.getMonth(),f=new Date(this.activeDate);f.setFullYear(b,d,1);var g=this.startingDay-f.getDay(),h=g>0?7-g:-g,i=new Date(f);h>0&&i.setDate(-h+1);for(var j=this.getDates(i,42),k=0;42>k;k++)j[k]=angular.extend(this.createDateObject(j[k],this.formatDay),{secondary:j[k].getMonth()!==d,uid:a.uniqueId+"-"+k});a.labels=new Array(7);for(var l=0;7>l;l++)a.labels[l]={abbr:c(j[l].date,this.formatDayHeader),full:c(j[l].date,"EEEE")};if(a.title=c(this.activeDate,this.formatDayTitle),a.rows=this.split(j,7),a.showWeeks){a.weekNumbers=[];for(var m=(11-this.startingDay)%7,n=a.rows.length,o=0;n>o;o++)a.weekNumbers.push(e(a.rows[o][m].date))}},this.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},this.handleKeyDown=function(a,b){var c=this.activeDate.getDate();if("left"===a)c-=1;else if("up"===a)c-=7;else if("right"===a)c+=1;else if("down"===a)c+=7;else if("pageup"===a||"pagedown"===a){var e=this.activeDate.getMonth()+("pageup"===a?-1:1);this.activeDate.setMonth(e,1),c=Math.min(d(this.activeDate.getFullYear(),this.activeDate.getMonth()),c)}else"home"===a?c=1:"end"===a&&(c=d(this.activeDate.getFullYear(),this.activeDate.getMonth()));this.activeDate.setDate(c)}}]).controller("UibMonthpickerController",["$scope","$element","dateFilter",function(a,b,c){this.step={years:1},this.element=b,this.init=function(a){angular.extend(a,this),a.refreshView()},this._refreshView=function(){for(var b,d=new Array(12),e=this.activeDate.getFullYear(),f=0;12>f;f++)b=new Date(this.activeDate),b.setFullYear(e,f,1),d[f]=angular.extend(this.createDateObject(b,this.formatMonth),{uid:a.uniqueId+"-"+f});a.title=c(this.activeDate,this.formatMonthTitle),a.rows=this.split(d,3)},this.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth())-new Date(b.getFullYear(),b.getMonth())},this.handleKeyDown=function(a,b){var c=this.activeDate.getMonth();if("left"===a)c-=1;else if("up"===a)c-=3;else if("right"===a)c+=1;else if("down"===a)c+=3;else if("pageup"===a||"pagedown"===a){var d=this.activeDate.getFullYear()+("pageup"===a?-1:1);this.activeDate.setFullYear(d)}else"home"===a?c=0:"end"===a&&(c=11);this.activeDate.setMonth(c)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(a,b,c){function d(a){return parseInt((a-1)/e,10)*e+1}var e;this.element=b,this.yearpickerInit=function(){e=this.yearRange,this.step={years:e}},this._refreshView=function(){for(var b,c=new Array(e),f=0,g=d(this.activeDate.getFullYear());e>f;f++)b=new Date(this.activeDate),b.setFullYear(g+f,0,1),c[f]=angular.extend(this.createDateObject(b,this.formatYear),{uid:a.uniqueId+"-"+f});a.title=[c[0].label,c[e-1].label].join(" - "),a.rows=this.split(c,5)},this.compare=function(a,b){return a.getFullYear()-b.getFullYear()},this.handleKeyDown=function(a,b){var c=this.activeDate.getFullYear();"left"===a?c-=1:"up"===a?c-=5:"right"===a?c+=1:"down"===a?c+=5:"pageup"===a||"pagedown"===a?c+=("pageup"===a?-1:1)*this.step.years:"home"===a?c=d(this.activeDate.getFullYear()):"end"===a&&(c=d(this.activeDate.getFullYear())+e-1),this.activeDate.setFullYear(c)}}]).directive("uibDatepicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/datepicker.html"},scope:{datepickerMode:"=?",dateDisabled:"&",customClass:"&",shortcutPropagation:"&?"},require:["uibDatepicker","^ngModel"],controller:"UibDatepickerController",controllerAs:"datepicker",link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}).directive("uibDaypicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/day.html"},require:["^?uibDatepicker","uibDaypicker","^?datepicker"],controller:"UibDaypickerController",link:function(a,b,c,d){var e=d[0]||d[2],f=d[1];f.init(e)}}}).directive("uibMonthpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/month.html"},require:["^?uibDatepicker","uibMonthpicker","^?datepicker"],controller:"UibMonthpickerController",link:function(a,b,c,d){var e=d[0]||d[2],f=d[1];f.init(e)}}}).directive("uibYearpicker",function(){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/year.html"},require:["^?uibDatepicker","uibYearpicker","^?datepicker"],controller:"UibYearpickerController",link:function(a,b,c,d){var e=d[0]||d[2];angular.extend(e,d[1]),e.yearpickerInit(),e.refreshView()}}}).constant("uibDatepickerPopupConfig",{datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"template/datepicker/popup.html",datepickerTemplateUrl:"template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},currentText:"Today",clearText:"Clear",closeText:"Done",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0,onOpenFocus:!0}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$parse","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(a){return a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()})}function n(b){if(angular.isNumber(b)&&(b=new Date(b)),b){if(angular.isDate(b)&&!isNaN(b))return b;if(angular.isString(b)){var c=j.parse(b,r,a.date);return isNaN(c)?void 0:c}return void 0}return null}function o(a,b){var d=a||b;if(!c.ngRequired&&!d)return!0;if(angular.isNumber(d)&&(d=new Date(d)),d){if(angular.isDate(d)&&!isNaN(d))return!0;if(angular.isString(d)){var e=j.parse(d,r);return!isNaN(e)}return!1}return!0}function p(c){var d=A[0],e=b[0].contains(c.target),f=void 0!==d.contains&&d.contains(c.target);!a.isOpen||e||f||a.$apply(function(){a.isOpen=!1})}function q(c){27===c.which&&a.isOpen?(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!1}),b[0].focus()):40!==c.which||a.isOpen||(c.preventDefault(),c.stopPropagation(),a.$apply(function(){a.isOpen=!0}))}var r,s,t,u,v,w,x,y,z,A,B={},C=!1;a.watchData={},this.init=function(h){if(z=h,s=angular.isDefined(c.closeOnDateSelection)?a.$parent.$eval(c.closeOnDateSelection):k.closeOnDateSelection, +t=angular.isDefined(c.datepickerAppendToBody)?a.$parent.$eval(c.datepickerAppendToBody):k.appendToBody,u=angular.isDefined(c.onOpenFocus)?a.$parent.$eval(c.onOpenFocus):k.onOpenFocus,v=angular.isDefined(c.datepickerPopupTemplateUrl)?c.datepickerPopupTemplateUrl:k.datepickerPopupTemplateUrl,w=angular.isDefined(c.datepickerTemplateUrl)?c.datepickerTemplateUrl:k.datepickerTemplateUrl,a.showButtonBar=angular.isDefined(c.showButtonBar)?a.$parent.$eval(c.showButtonBar):k.showButtonBar,k.html5Types[c.type]?(r=k.html5Types[c.type],C=!0):(r=c.datepickerPopup||c.uibDatepickerPopup||k.datepickerPopup,c.$observe("uibDatepickerPopup",function(a,b){var c=a||k.datepickerPopup;if(c!==r&&(r=c,z.$modelValue=null,!r))throw new Error("uibDatepickerPopup must have a date format specified.")})),!r)throw new Error("uibDatepickerPopup must have a date format specified.");if(C&&c.datepickerPopup)throw new Error("HTML5 date input types do not support custom formats.");if(x=angular.element("<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>"),x.attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":v}),y=angular.element(x.children()[0]),y.attr("template-url",w),C&&"month"===c.type&&(y.attr("datepicker-mode",'"month"'),y.attr("min-mode","month")),c.datepickerOptions){var l=a.$parent.$eval(c.datepickerOptions);l&&l.initDate&&(a.initDate=l.initDate,y.attr("init-date","initDate"),delete l.initDate),angular.forEach(l,function(a,b){y.attr(m(b),a)})}angular.forEach(["minMode","maxMode","minDate","maxDate","datepickerMode","initDate","shortcutPropagation"],function(b){if(c[b]){var d=e(c[b]);if(a.$parent.$watch(d,function(c){a.watchData[b]=c,("minDate"===b||"maxDate"===b)&&(B[b]=new Date(c))}),y.attr(m(b),"watchData."+b),"datepickerMode"===b){var f=d.assign;a.$watch("watchData."+b,function(b,c){angular.isFunction(f)&&b!==c&&f(a.$parent,b)})}}}),c.dateDisabled&&y.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),c.showWeeks&&y.attr("show-weeks",c.showWeeks),c.customClass&&y.attr("custom-class","customClass({ date: date, mode: mode })"),C?z.$formatters.push(function(b){return a.date=b,b}):(z.$$parserName="date",z.$validators.date=o,z.$parsers.unshift(n),z.$formatters.push(function(b){return a.date=b,z.$isEmpty(b)?b:i(b,r)})),z.$viewChangeListeners.push(function(){a.date=j.parse(z.$viewValue,r,a.date)}),b.bind("keydown",q),A=d(x)(a),x.remove(),t?f.find("body").append(A):b.after(A),a.$on("$destroy",function(){a.isOpen===!0&&(g.$$phase||a.$apply(function(){a.isOpen=!1})),A.remove(),b.unbind("keydown",q),f.unbind("click",p)})},a.getText=function(b){return a[b+"Text"]||k[b+"Text"]},a.isDisabled=function(b){return"today"===b&&(b=new Date),a.watchData.minDate&&a.compare(b,B.minDate)<0||a.watchData.maxDate&&a.compare(b,B.maxDate)>0},a.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},a.dateSelection=function(c){angular.isDefined(c)&&(a.date=c);var d=a.date?i(a.date,r):null;b.val(d),z.$setViewValue(d),s&&(a.isOpen=!1,b[0].focus())},a.keydown=function(c){27===c.which&&(a.isOpen=!1,b[0].focus())},a.select=function(b){if("today"===b){var c=new Date;angular.isDate(a.date)?(b=new Date(a.date),b.setFullYear(c.getFullYear(),c.getMonth(),c.getDate())):b=new Date(c.setHours(0,0,0,0))}a.dateSelection(b)},a.close=function(){a.isOpen=!1,b[0].focus()},a.$watch("isOpen",function(c){c?(a.position=t?h.offset(b):h.position(b),a.position.top=a.position.top+b.prop("offsetHeight"),l(function(){u&&a.$broadcast("uib:datepicker.focus"),f.bind("click",p)},0,!1)):f.unbind("click",p)})}]).directive("uibDatepickerPopup",function(){return{require:["ngModel","uibDatepickerPopup"],controller:"UibDatepickerPopupController",scope:{isOpen:"=?",currentText:"@",clearText:"@",closeText:"@",dateDisabled:"&",customClass:"&"},link:function(a,b,c,d){var e=d[0],f=d[1];f.init(e)}}}).directive("uibDatepickerPopupWrap",function(){return{replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/popup.html"}}}),angular.module("ui.bootstrap.datepicker").value("$datepickerSuppressWarning",!1).controller("DatepickerController",["$scope","$attrs","$parse","$interpolate","$log","dateFilter","uibDatepickerConfig","$datepickerSuppressError","$datepickerSuppressWarning",function(a,b,c,d,e,f,g,h,i){i||e.warn("DatepickerController is now deprecated. Use UibDatepickerController instead.");var j=this,k={$setViewValue:angular.noop};this.modes=["day","month","year"],angular.forEach(["formatDay","formatMonth","formatYear","formatDayHeader","formatDayTitle","formatMonthTitle","showWeeks","startingDay","yearRange","shortcutPropagation"],function(c,e){j[c]=angular.isDefined(b[c])?6>e?d(b[c])(a.$parent):a.$parent.$eval(b[c]):g[c]}),angular.forEach(["minDate","maxDate"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(a){j[d]=a?new Date(a):null,j.refreshView()}):j[d]=g[d]?new Date(g[d]):null}),angular.forEach(["minMode","maxMode"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(c){j[d]=angular.isDefined(c)?c:b[d],a[d]=j[d],("minMode"==d&&j.modes.indexOf(a.datepickerMode)<j.modes.indexOf(j[d])||"maxMode"==d&&j.modes.indexOf(a.datepickerMode)>j.modes.indexOf(j[d]))&&(a.datepickerMode=j[d])}):(j[d]=g[d]||null,a[d]=j[d])}),a.datepickerMode=a.datepickerMode||g.datepickerMode,a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),angular.isDefined(b.initDate)?(this.activeDate=a.$parent.$eval(b.initDate)||new Date,a.$parent.$watch(b.initDate,function(a){a&&(k.$isEmpty(k.$modelValue)||k.$invalid)&&(j.activeDate=a,j.refreshView())})):this.activeDate=new Date,a.isActive=function(b){return 0===j.compare(b.date,j.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(a){k=a,k.$render=function(){j.render()}},this.render=function(){if(k.$viewValue){var a=new Date(k.$viewValue),b=!isNaN(a);b?this.activeDate=a:h||e.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var a=k.$viewValue?new Date(k.$viewValue):null;k.$setValidity("dateDisabled",!a||this.element&&!this.isDisabled(a))}},this.createDateObject=function(a,b){var c=k.$viewValue?new Date(k.$viewValue):null;return{date:a,label:f(a,b),selected:c&&0===this.compare(a,c),disabled:this.isDisabled(a),current:0===this.compare(a,new Date),customClass:this.customClass(a)}},this.isDisabled=function(c){return this.minDate&&this.compare(c,this.minDate)<0||this.maxDate&&this.compare(c,this.maxDate)>0||b.dateDisabled&&a.dateDisabled({date:c,mode:a.datepickerMode})},this.customClass=function(b){return a.customClass({date:b,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},this.fixTimeZone=function(a){var b=a.getHours();a.setHours(23===b?b+2:0)},a.select=function(b){if(a.datepickerMode===j.minMode){var c=k.$viewValue?new Date(k.$viewValue):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),k.$setViewValue(c),k.$render()}else j.activeDate=b,a.datepickerMode=j.modes[j.modes.indexOf(a.datepickerMode)-1]},a.move=function(a){var b=j.activeDate.getFullYear()+a*(j.step.years||0),c=j.activeDate.getMonth()+a*(j.step.months||0);j.activeDate.setFullYear(b,c,1),j.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===j.maxMode&&1===b||a.datepickerMode===j.minMode&&-1===b||(a.datepickerMode=j.modes[j.modes.indexOf(a.datepickerMode)+b])},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var l=function(){j.element[0].focus()};a.$on("uib:datepicker.focus",l),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey)if(b.preventDefault(),j.shortcutPropagation||b.stopPropagation(),"enter"===c||"space"===c){if(j.isDisabled(j.activeDate))return;a.select(j.activeDate)}else!b.ctrlKey||"up"!==c&&"down"!==c?(j.handleKeyDown(c,b),j.refreshView()):a.toggleMode("up"===c?1:-1)}}]).directive("datepicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/datepicker.html"},scope:{datepickerMode:"=?",dateDisabled:"&",customClass:"&",shortcutPropagation:"&?"},require:["datepicker","^ngModel"],controller:"DatepickerController",controllerAs:"datepicker",link:function(c,d,e,f){b||a.warn("datepicker is now deprecated. Use uib-datepicker instead.");var g=f[0],h=f[1];g.init(h)}}}]).directive("daypicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:"template/datepicker/day.html",require:["^datepicker","daypicker"],controller:"UibDaypickerController",link:function(c,d,e,f){b||a.warn("daypicker is now deprecated. Use uib-daypicker instead.");var g=f[0],h=f[1];h.init(g)}}}]).directive("monthpicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:"template/datepicker/month.html",require:["^datepicker","monthpicker"],controller:"UibMonthpickerController",link:function(c,d,e,f){b||a.warn("monthpicker is now deprecated. Use uib-monthpicker instead.");var g=f[0],h=f[1];h.init(g)}}}]).directive("yearpicker",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,templateUrl:"template/datepicker/year.html",require:["^datepicker","yearpicker"],controller:"UibYearpickerController",link:function(c,d,e,f){b||a.warn("yearpicker is now deprecated. Use uib-yearpicker instead.");var g=f[0];angular.extend(g,f[1]),g.yearpickerInit(),g.refreshView()}}}]).directive("datepickerPopup",["$log","$datepickerSuppressWarning",function(a,b){return{require:["ngModel","datepickerPopup"],controller:"UibDatepickerPopupController",scope:{isOpen:"=?",currentText:"@",clearText:"@",closeText:"@",dateDisabled:"&",customClass:"&"},link:function(c,d,e,f){b||a.warn("datepicker-popup is now deprecated. Use uib-datepicker-popup instead.");var g=f[0],h=f[1];h.init(g)}}}]).directive("datepickerPopupWrap",["$log","$datepickerSuppressWarning",function(a,b){return{replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/datepicker/popup.html"},link:function(){b||a.warn("datepicker-popup-wrap is now deprecated. Use uib-datepicker-popup-wrap instead.")}}}]),angular.module("ui.bootstrap.dropdown",["ui.bootstrap.position"]).constant("uibDropdownConfig",{openClass:"open"}).service("uibDropdownService",["$document","$rootScope",function(a,b){var c=null;this.open=function(b){c||(a.bind("click",d),a.bind("keydown",e)),c&&c!==b&&(c.isOpen=!1),c=b},this.close=function(b){c===b&&(c=null,a.unbind("click",d),a.unbind("keydown",e))};var d=function(a){if(c&&(!a||"disabled"!==c.getAutoClose())){var d=c.getToggleElement();if(!(a&&d&&d[0].contains(a.target))){var e=c.getDropdownElement();a&&"outsideClick"===c.getAutoClose()&&e&&e[0].contains(a.target)||(c.isOpen=!1,b.$$phase||c.$apply())}}},e=function(a){27===a.which?(c.focusToggleElement(),d()):c.isKeynavEnabled()&&/(38|40)/.test(a.which)&&c.isOpen&&(a.preventDefault(),a.stopPropagation(),c.focusDropdownEntry(a.which))}}]).controller("UibDropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest",function(a,b,c,d,e,f,g,h,i,j,k){var l,m,n=this,o=a.$new(),p=e.openClass,q=angular.noop,r=c.onToggle?d(c.onToggle):angular.noop,s=!1,t=!1;b.addClass("dropdown"),this.init=function(){c.isOpen&&(m=d(c.isOpen),q=m.assign,a.$watch(m,function(a){o.isOpen=!!a})),s=angular.isDefined(c.dropdownAppendToBody),t=angular.isDefined(c.uibKeyboardNav),s&&n.dropdownMenu&&(i.find("body").append(n.dropdownMenu),b.on("$destroy",function(){n.dropdownMenu.remove()}))},this.toggle=function(a){return o.isOpen=arguments.length?!!a:!o.isOpen},this.isOpen=function(){return o.isOpen},o.getToggleElement=function(){return n.toggleElement},o.getAutoClose=function(){return c.autoClose||"always"},o.getElement=function(){return b},o.isKeynavEnabled=function(){return t},o.focusDropdownEntry=function(a){var c=n.dropdownMenu?angular.element(n.dropdownMenu).find("a"):angular.element(b).find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(n.selectedOption)?n.selectedOption=n.selectedOption===c.length-1?n.selectedOption:n.selectedOption+1:n.selectedOption=0;break;case 38:angular.isNumber(n.selectedOption)?n.selectedOption=0===n.selectedOption?0:n.selectedOption-1:n.selectedOption=c.length-1}c[n.selectedOption].focus()},o.getDropdownElement=function(){return n.dropdownMenu},o.focusToggleElement=function(){n.toggleElement&&n.toggleElement[0].focus()},o.$watch("isOpen",function(c,d){if(s&&n.dropdownMenu){var e=h.positionElements(b,n.dropdownMenu,"bottom-left",!0),i={top:e.top+"px",display:c?"block":"none"},m=n.dropdownMenu.hasClass("dropdown-menu-right");m?(i.left="auto",i.right=window.innerWidth-(e.left+b.prop("offsetWidth"))+"px"):(i.left=e.left+"px",i.right="auto"),n.dropdownMenu.css(i)}if(g[c?"addClass":"removeClass"](b,p).then(function(){angular.isDefined(c)&&c!==d&&r(a,{open:!!c})}),c)n.dropdownMenuTemplateUrl&&k(n.dropdownMenuTemplateUrl).then(function(a){l=o.$new(),j(a.trim())(l,function(a){var b=a;n.dropdownMenu.replaceWith(b),n.dropdownMenu=b})}),o.focusToggleElement(),f.open(o);else{if(n.dropdownMenuTemplateUrl){l&&l.$destroy();var t=angular.element('<ul class="dropdown-menu"></ul>');n.dropdownMenu.replaceWith(t),n.dropdownMenu=t}f.close(o),n.selectedOption=null}angular.isFunction(q)&&q(a,c)}),a.$on("$locationChangeSuccess",function(){"disabled"!==o.getAutoClose()&&(o.isOpen=!1)});var u=a.$on("$destroy",function(){o.$destroy()});o.$on("$destroy",u)}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(a,b,c,d){d.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"AC",require:"?^uibDropdown",link:function(a,b,c,d){if(d&&!angular.isDefined(c.dropdownNested)){b.addClass("dropdown-menu");var e=c.templateUrl;e&&(d.dropdownMenuTemplateUrl=e),d.dropdownMenu||(d.dropdownMenu=b)}}}}).directive("uibKeyboardNav",function(){return{restrict:"A",require:"?^uibDropdown",link:function(a,b,c,d){b.bind("keydown",function(a){if(-1!==[38,40].indexOf(a.which)){a.preventDefault(),a.stopPropagation();var b=d.dropdownMenu.find("a");switch(a.which){case 40:angular.isNumber(d.selectedOption)?d.selectedOption=d.selectedOption===b.length-1?d.selectedOption:d.selectedOption+1:d.selectedOption=0;break;case 38:angular.isNumber(d.selectedOption)?d.selectedOption=0===d.selectedOption?0:d.selectedOption-1:d.selectedOption=b.length-1}b[d.selectedOption].focus()}})}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(a,b,c,d){if(d){b.addClass("dropdown-toggle"),d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.bind("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.unbind("click",e)})}}}}),angular.module("ui.bootstrap.dropdown").value("$dropdownSuppressWarning",!1).service("dropdownService",["$log","$dropdownSuppressWarning","uibDropdownService",function(a,b,c){b||a.warn("dropdownService is now deprecated. Use uibDropdownService instead."),angular.extend(this,c)}]).controller("DropdownController",["$scope","$element","$attrs","$parse","uibDropdownConfig","uibDropdownService","$animate","$uibPosition","$document","$compile","$templateRequest","$log","$dropdownSuppressWarning",function(a,b,c,d,e,f,g,h,i,j,k,l,m){m||l.warn("DropdownController is now deprecated. Use UibDropdownController instead.");var n,o,p=this,q=a.$new(),r=e.openClass,s=angular.noop,t=c.onToggle?d(c.onToggle):angular.noop,u=!1,v=!1;b.addClass("dropdown"),this.init=function(){c.isOpen&&(o=d(c.isOpen),s=o.assign,a.$watch(o,function(a){q.isOpen=!!a})),u=angular.isDefined(c.dropdownAppendToBody),v=angular.isDefined(c.uibKeyboardNav),u&&p.dropdownMenu&&(i.find("body").append(p.dropdownMenu),b.on("$destroy",function(){p.dropdownMenu.remove()}))},this.toggle=function(a){return q.isOpen=arguments.length?!!a:!q.isOpen},this.isOpen=function(){return q.isOpen},q.getToggleElement=function(){return p.toggleElement},q.getAutoClose=function(){return c.autoClose||"always"},q.getElement=function(){return b},q.isKeynavEnabled=function(){return v},q.focusDropdownEntry=function(a){var c=p.dropdownMenu?angular.element(p.dropdownMenu).find("a"):angular.element(b).find("ul").eq(0).find("a");switch(a){case 40:angular.isNumber(p.selectedOption)?p.selectedOption=p.selectedOption===c.length-1?p.selectedOption:p.selectedOption+1:p.selectedOption=0;break;case 38:angular.isNumber(p.selectedOption)?p.selectedOption=0===p.selectedOption?0:p.selectedOption-1:p.selectedOption=c.length-1}c[p.selectedOption].focus()},q.getDropdownElement=function(){return p.dropdownMenu},q.focusToggleElement=function(){p.toggleElement&&p.toggleElement[0].focus()},q.$watch("isOpen",function(c,d){if(u&&p.dropdownMenu){var e=h.positionElements(b,p.dropdownMenu,"bottom-left",!0),i={top:e.top+"px",display:c?"block":"none"},l=p.dropdownMenu.hasClass("dropdown-menu-right");l?(i.left="auto",i.right=window.innerWidth-(e.left+b.prop("offsetWidth"))+"px"):(i.left=e.left+"px",i.right="auto"),p.dropdownMenu.css(i)}if(g[c?"addClass":"removeClass"](b,r).then(function(){angular.isDefined(c)&&c!==d&&t(a,{open:!!c})}),c)p.dropdownMenuTemplateUrl&&k(p.dropdownMenuTemplateUrl).then(function(a){n=q.$new(),j(a.trim())(n,function(a){var b=a;p.dropdownMenu.replaceWith(b),p.dropdownMenu=b})}),q.focusToggleElement(),f.open(q);else{if(p.dropdownMenuTemplateUrl){n&&n.$destroy();var m=angular.element('<ul class="dropdown-menu"></ul>');p.dropdownMenu.replaceWith(m),p.dropdownMenu=m}f.close(q),p.selectedOption=null}angular.isFunction(s)&&s(a,c)}),a.$on("$locationChangeSuccess",function(){"disabled"!==q.getAutoClose()&&(q.isOpen=!1)});var w=a.$on("$destroy",function(){q.$destroy()});q.$on("$destroy",w)}]).directive("dropdown",["$log","$dropdownSuppressWarning",function(a,b){return{controller:"DropdownController",link:function(c,d,e,f){b||a.warn("dropdown is now deprecated. Use uib-dropdown instead."),f.init()}}}]).directive("dropdownMenu",["$log","$dropdownSuppressWarning",function(a,b){return{restrict:"AC",require:"?^dropdown",link:function(c,d,e,f){if(f&&!angular.isDefined(e.dropdownNested)){b||a.warn("dropdown-menu is now deprecated. Use uib-dropdown-menu instead."),d.addClass("dropdown-menu");var g=e.templateUrl;g&&(f.dropdownMenuTemplateUrl=g),f.dropdownMenu||(f.dropdownMenu=d)}}}}]).directive("keyboardNav",["$log","$dropdownSuppressWarning",function(a,b){return{restrict:"A",require:"?^dropdown",link:function(c,d,e,f){b||a.warn("keyboard-nav is now deprecated. Use uib-keyboard-nav instead."),d.bind("keydown",function(a){if(-1!==[38,40].indexOf(a.which)){a.preventDefault(),a.stopPropagation();var b=f.dropdownMenu.find("a");switch(a.which){case 40:angular.isNumber(f.selectedOption)?f.selectedOption=f.selectedOption===b.length-1?f.selectedOption:f.selectedOption+1:f.selectedOption=0;break;case 38:angular.isNumber(f.selectedOption)?f.selectedOption=0===f.selectedOption?0:f.selectedOption-1:f.selectedOption=b.length-1}b[f.selectedOption].focus()}})}}}]).directive("dropdownToggle",["$log","$dropdownSuppressWarning",function(a,b){return{require:"?^dropdown",link:function(c,d,e,f){if(b||a.warn("dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead."),f){d.addClass("dropdown-toggle"),f.toggleElement=d;var g=function(a){a.preventDefault(),d.hasClass("disabled")||e.disabled||c.$apply(function(){f.toggle()})};d.bind("click",g),d.attr({"aria-haspopup":!0,"aria-expanded":!1}),c.$watch(f.isOpen,function(a){d.attr("aria-expanded",!!a)}),c.$on("$destroy",function(){d.unbind("click",g)})}}}}]),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b==a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b==a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.splice(a.length-1,1)[0]},length:function(){return a.length}}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.stackedMap"]).factory("$$multiMap",function(){return{createNew:function(){var a={};return{entries:function(){return Object.keys(a).map(function(b){return{key:b,value:a[b]}})},get:function(b){return a[b]},hasKey:function(b){return!!a[b]},keys:function(){return Object.keys(a)},put:function(b,c){a[b]||(a[b]=[]),a[b].push(c)},remove:function(b,c){var d=a[b];if(d){var e=d.indexOf(c);-1!==e&&d.splice(e,1),d.length||delete a[b]}}}}}}).directive("uibModalBackdrop",["$animate","$injector","$uibModalStack",function(a,b,c){function d(b,d,f){d.addClass("modal-backdrop"),f.modalInClass&&(e?e(d,{addClass:f.modalInClass}).start():a.addClass(d,f.modalInClass),b.$on(c.NOW_CLOSING_EVENT,function(b,c){var g=c();e?e(d,{removeClass:f.modalInClass}).start().then(g):a.removeClass(d,f.modalInClass).then(g)}))}var e=null;return b.has("$animateCss")&&(e=b.get("$animateCss")),{replace:!0,templateUrl:"template/modal/backdrop.html",compile:function(a,b){return a.addClass(b.backdropClass),d}}}]).directive("uibModalWindow",["$uibModalStack","$q","$animate","$injector",function(a,b,c,d){var e=null;return d.has("$animateCss")&&(e=d.get("$animateCss")),{scope:{index:"@"},replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/modal/window.html"},link:function(d,f,g){f.addClass(g.windowClass||""),f.addClass(g.windowTopClass||""),d.size=g.size,d.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!==c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))},f.on("click",d.close),d.$isRendered=!0;var h=b.defer();g.$observe("modalRender",function(a){"true"==a&&h.resolve()}),h.promise.then(function(){var h=null;g.modalInClass&&(h=e?e(f,{addClass:g.modalInClass}).start():c.addClass(f,g.modalInClass),d.$on(a.NOW_CLOSING_EVENT,function(a,b){var d=b();e?e(f,{removeClass:g.modalInClass}).start().then(d):c.removeClass(f,g.modalInClass).then(d)})),b.when(h).then(function(){var a=f[0].querySelector("[autofocus]");a?a.focus():f[0].focus()});var i=a.getTop();i&&a.modalRendered(i.key)})}}}]).directive("uibModalAnimationClass",function(){return{compile:function(a,b){b.modalAnimation&&a.addClass(b.uibModalAnimationClass)}}}).directive("uibModalTransclude",function(){return{link:function(a,b,c,d,e){e(a.$parent,function(a){b.empty(),b.append(a)})}}}).factory("$uibModalStack",["$animate","$timeout","$document","$compile","$rootScope","$q","$injector","$$multiMap","$$stackedMap",function(a,b,c,d,e,f,g,h,i){function j(){for(var a=-1,b=u.keys(),c=0;c<b.length;c++)u.get(b[c]).value.backdrop&&(a=c);return a}function k(a,b){var d=c.find("body").eq(0),e=u.get(a).value;u.remove(a),n(e.modalDomEl,e.modalScope,function(){var b=e.openedClass||t;v.remove(b,a),d.toggleClass(b,v.hasKey(b)),l(!0)}),m(),b&&b.focus?b.focus():d.focus()}function l(a){var b;u.length()>0&&(b=u.top().value,b.modalDomEl.toggleClass(b.windowTopClass||"",a))}function m(){if(q&&-1==j()){var a=r;n(q,r,function(){a=null}),q=void 0,r=void 0}}function n(b,c,d){function e(){e.done||(e.done=!0,p?p(b,{event:"leave"}).start().then(function(){b.remove()}):a.leave(b),c.$destroy(),d&&d())}var g,h=null,i=function(){return g||(g=f.defer(),h=g.promise),function(){g.resolve()}};return c.$broadcast(w.NOW_CLOSING_EVENT,i),f.when(h).then(e)}function o(a,b,c){return!a.value.modalScope.$broadcast("modal.closing",b,c).defaultPrevented}var p=null;g.has("$animateCss")&&(p=g.get("$animateCss"));var q,r,s,t="modal-open",u=i.createNew(),v=h.createNew(),w={NOW_CLOSING_EVENT:"modal.stack.now-closing"},x=0,y="a[href], area[href], input:not([disabled]), button:not([disabled]),select:not([disabled]), textarea:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable=true]";return e.$watch(j,function(a){r&&(r.index=a)}),c.bind("keydown",function(a){if(a.isDefaultPrevented())return a;var b=u.top();if(b&&b.value.keyboard)switch(a.which){case 27:a.preventDefault(),e.$apply(function(){w.dismiss(b.key,"escape key press")});break;case 9:w.loadFocusElementList(b);var c=!1;a.shiftKey?w.isFocusInFirstItem(a)&&(c=w.focusLastFocusableElement()):w.isFocusInLastItem(a)&&(c=w.focusFirstFocusableElement()),c&&(a.preventDefault(),a.stopPropagation())}}),w.open=function(a,b){var f=c[0].activeElement,g=b.openedClass||t;l(!1),u.add(a,{deferred:b.deferred,renderDeferred:b.renderDeferred,modalScope:b.scope,backdrop:b.backdrop,keyboard:b.keyboard,openedClass:b.openedClass,windowTopClass:b.windowTopClass}),v.put(g,a);var h=c.find("body").eq(0),i=j();if(i>=0&&!q){r=e.$new(!0),r.index=i;var k=angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');k.attr("backdrop-class",b.backdropClass),b.animation&&k.attr("modal-animation","true"),q=d(k)(r),h.append(q)}var m=angular.element('<div uib-modal-window="modal-window"></div>');m.attr({"template-url":b.windowTemplateUrl,"window-class":b.windowClass,"window-top-class":b.windowTopClass,size:b.size,index:u.length()-1,animate:"animate"}).html(b.content),b.animation&&m.attr("modal-animation","true");var n=d(m)(b.scope);u.top().value.modalDomEl=n,u.top().value.modalOpener=f,h.append(n),h.addClass(g),w.clearFocusListCache()},w.close=function(a,b){var c=u.get(a);return c&&o(c,b,!0)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.resolve(b),k(a,c.value.modalOpener),!0):!c},w.dismiss=function(a,b){var c=u.get(a);return c&&o(c,b,!1)?(c.value.modalScope.$$uibDestructionScheduled=!0,c.value.deferred.reject(b),k(a,c.value.modalOpener),!0):!c},w.dismissAll=function(a){for(var b=this.getTop();b&&this.dismiss(b.key,a);)b=this.getTop()},w.getTop=function(){return u.top()},w.modalRendered=function(a){var b=u.get(a);b&&b.value.renderDeferred.resolve()},w.focusFirstFocusableElement=function(){return s.length>0?(s[0].focus(),!0):!1},w.focusLastFocusableElement=function(){return s.length>0?(s[s.length-1].focus(),!0):!1},w.isFocusInFirstItem=function(a){return s.length>0?(a.target||a.srcElement)==s[0]:!1},w.isFocusInLastItem=function(a){return s.length>0?(a.target||a.srcElement)==s[s.length-1]:!1},w.clearFocusListCache=function(){s=[],x=0},w.loadFocusElementList=function(a){if((void 0===s||!s.length)&&a){var b=a.value.modalDomEl;b&&b.length&&(s=b[0].querySelectorAll(y))}},w}]).provider("$uibModal",function(){var a={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$templateRequest","$controller","$uibModalStack","$modalSuppressWarning","$log",function(b,c,d,e,f,g,h,i){function j(a){return a.template?d.when(a.template):e(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl)}function k(a){var c=[];return angular.forEach(a,function(a){angular.isFunction(a)||angular.isArray(a)?c.push(d.when(b.invoke(a))):angular.isString(a)?c.push(d.when(b.get(a))):c.push(d.when(a))}),c}var l={},m=null;return l.getPromiseChain=function(){return m},l.open=function(b){function e(){return r}var l=d.defer(),n=d.defer(),o=d.defer(),p={result:l.promise,opened:n.promise,rendered:o.promise,close:function(a){return g.close(p,a)},dismiss:function(a){return g.dismiss(p,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var q,r=d.all([j(b)].concat(k(b.resolve)));return q=m=d.all([m]).then(e,e).then(function(a){var d=(b.scope||c).$new();d.$close=p.close,d.$dismiss=p.dismiss,d.$on("$destroy",function(){d.$$uibDestructionScheduled||d.$dismiss("$uibUnscheduledDestruction")});var e,j={},k=1;b.controller&&(j.$scope=d,j.$uibModalInstance=p,Object.defineProperty(j,"$modalInstance",{get:function(){return h||i.warn("$modalInstance is now deprecated. Use $uibModalInstance instead."),p}}),angular.forEach(b.resolve,function(b,c){j[c]=a[k++]}),e=f(b.controller,j),b.controllerAs&&(b.bindToController&&angular.extend(e,d),d[b.controllerAs]=e)),g.open(p,{scope:d,deferred:l,renderDeferred:o,content:a[0],animation:b.animation,backdrop:b.backdrop,keyboard:b.keyboard,backdropClass:b.backdropClass,windowTopClass:b.windowTopClass,windowClass:b.windowClass,windowTemplateUrl:b.windowTemplateUrl,size:b.size,openedClass:b.openedClass}),n.resolve(!0)},function(a){n.reject(a),l.reject(a)})["finally"](function(){m===q&&(m=null)}),p},l}]};return a}),angular.module("ui.bootstrap.modal").value("$modalSuppressWarning",!1).directive("modalBackdrop",["$animate","$injector","$modalStack","$log","$modalSuppressWarning",function(a,b,c,d,e){function f(b,f,h){e||d.warn("modal-backdrop is now deprecated. Use uib-modal-backdrop instead."),f.addClass("modal-backdrop"),h.modalInClass&&(g?g(f,{addClass:h.modalInClass}).start():a.addClass(f,h.modalInClass),b.$on(c.NOW_CLOSING_EVENT,function(b,c){var d=c();g?g(f,{removeClass:h.modalInClass}).start().then(d):a.removeClass(f,h.modalInClass).then(d)}))}var g=null;return b.has("$animateCss")&&(g=b.get("$animateCss")),{replace:!0,templateUrl:"template/modal/backdrop.html",compile:function(a,b){return a.addClass(b.backdropClass),f}}}]).directive("modalWindow",["$modalStack","$q","$animate","$injector","$log","$modalSuppressWarning",function(a,b,c,d,e,f){var g=null;return d.has("$animateCss")&&(g=d.get("$animateCss")),{scope:{index:"@"},replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/modal/window.html"},link:function(d,h,i){f||e.warn("modal-window is now deprecated. Use uib-modal-window instead."),h.addClass(i.windowClass||""),h.addClass(i.windowTopClass||""),d.size=i.size,d.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!==c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))},h.on("click",d.close),d.$isRendered=!0;var j=b.defer();i.$observe("modalRender",function(a){"true"==a&&j.resolve()}),j.promise.then(function(){var e=null;i.modalInClass&&(e=g?g(h,{addClass:i.modalInClass}).start():c.addClass(h,i.modalInClass),d.$on(a.NOW_CLOSING_EVENT,function(a,b){var d=b();g?g(h,{removeClass:i.modalInClass}).start().then(d):c.removeClass(h,i.modalInClass).then(d)})),b.when(e).then(function(){var a=h[0].querySelector("[autofocus]");a?a.focus():h[0].focus()});var f=a.getTop();f&&a.modalRendered(f.key)})}}}]).directive("modalAnimationClass",["$log","$modalSuppressWarning",function(a,b){return{compile:function(c,d){b||a.warn("modal-animation-class is now deprecated. Use uib-modal-animation-class instead."),d.modalAnimation&&c.addClass(d.modalAnimationClass)}}}]).directive("modalTransclude",["$log","$modalSuppressWarning",function(a,b){return{link:function(c,d,e,f,g){b||a.warn("modal-transclude is now deprecated. Use uib-modal-transclude instead."),g(c.$parent,function(a){d.empty(),d.append(a)})}}}]).service("$modalStack",["$animate","$timeout","$document","$compile","$rootScope","$q","$injector","$$multiMap","$$stackedMap","$uibModalStack","$log","$modalSuppressWarning",function(a,b,c,d,e,f,g,h,i,j,k,l){l||k.warn("$modalStack is now deprecated. Use $uibModalStack instead."),angular.extend(this,j)}]).provider("$modal",["$uibModalProvider",function(a){angular.extend(this,a),this.$get=["$injector","$log","$modalSuppressWarning",function(b,c,d){return d||c.warn("$modal is now deprecated. Use $uibModal instead."),b.invoke(a.$get)}]}]),angular.module("ui.bootstrap.pagination",[]).controller("UibPaginationController",["$scope","$attrs","$parse",function(a,b,c){var d=this,e={$setViewValue:angular.noop},f=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(g,h){e=g,this.config=h,e.$render=function(){d.render()},b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){d.itemsPerPage=parseInt(b,10), +a.totalPages=d.calculateTotalPages()}):this.itemsPerPage=h.itemsPerPage,a.$watch("totalItems",function(){a.totalPages=d.calculateTotalPages()}),a.$watch("totalPages",function(b){f(a.$parent,b),a.page>b?a.selectPage(b):e.$render()})},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.render=function(){a.page=parseInt(e.$viewValue,10)||1},a.selectPage=function(b,c){c&&c.preventDefault();var d=!a.ngDisabled||!c;d&&a.page!==b&&b>0&&b<=a.totalPages&&(c&&c.target&&c.target.blur(),e.$setViewValue(b),e.$render())},a.getText=function(b){return a[b+"Text"]||d.config[b+"Text"]},a.noPrevious=function(){return 1===a.page},a.noNext=function(){return a.page===a.totalPages}}]).constant("uibPaginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("uibPagination",["$parse","uibPaginationConfig",function(a,b){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["uibPagination","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pagination.html"},replace:!0,link:function(c,d,e,f){function g(a,b,c){return{number:a,text:b,active:c}}function h(a,b){var c=[],d=1,e=b,f=angular.isDefined(k)&&b>k;f&&(l?(d=Math.max(a-Math.floor(k/2),1),e=d+k-1,e>b&&(e=b,d=e-k+1)):(d=(Math.ceil(a/k)-1)*k+1,e=Math.min(d+k-1,b)));for(var h=d;e>=h;h++){var i=g(h,h,h===a);c.push(i)}if(f&&!l){if(d>1){var j=g(d-1,"...",!1);c.unshift(j)}if(b>e){var m=g(e+1,"...",!1);c.push(m)}}return c}var i=f[0],j=f[1];if(j){var k=angular.isDefined(e.maxSize)?c.$parent.$eval(e.maxSize):b.maxSize,l=angular.isDefined(e.rotate)?c.$parent.$eval(e.rotate):b.rotate;c.boundaryLinks=angular.isDefined(e.boundaryLinks)?c.$parent.$eval(e.boundaryLinks):b.boundaryLinks,c.directionLinks=angular.isDefined(e.directionLinks)?c.$parent.$eval(e.directionLinks):b.directionLinks,i.init(j,b),e.maxSize&&c.$parent.$watch(a(e.maxSize),function(a){k=parseInt(a,10),i.render()});var m=i.render;i.render=function(){m(),c.page>0&&c.page<=c.totalPages&&(c.pages=h(c.page,c.totalPages))}}}}}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(a){return{restrict:"EA",scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],controller:"UibPaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pager.html"},replace:!0,link:function(b,c,d,e){var f=e[0],g=e[1];g&&(b.align=angular.isDefined(d.align)?b.$parent.$eval(d.align):a.align,f.init(g,a))}}}]),angular.module("ui.bootstrap.pagination").value("$paginationSuppressWarning",!1).controller("PaginationController",["$scope","$attrs","$parse","$log","$paginationSuppressWarning",function(a,b,c,d,e){e||d.warn("PaginationController is now deprecated. Use UibPaginationController instead.");var f=this,g={$setViewValue:angular.noop},h=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(d,e){g=d,this.config=e,g.$render=function(){f.render()},b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){f.itemsPerPage=parseInt(b,10),a.totalPages=f.calculateTotalPages()}):this.itemsPerPage=e.itemsPerPage,a.$watch("totalItems",function(){a.totalPages=f.calculateTotalPages()}),a.$watch("totalPages",function(b){h(a.$parent,b),a.page>b?a.selectPage(b):g.$render()})},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.render=function(){a.page=parseInt(g.$viewValue,10)||1},a.selectPage=function(b,c){c&&c.preventDefault();var d=!a.ngDisabled||!c;d&&a.page!==b&&b>0&&b<=a.totalPages&&(c&&c.target&&c.target.blur(),g.$setViewValue(b),g.$render())},a.getText=function(b){return a[b+"Text"]||f.config[b+"Text"]},a.noPrevious=function(){return 1===a.page},a.noNext=function(){return a.page===a.totalPages}}]).directive("pagination",["$parse","uibPaginationConfig","$log","$paginationSuppressWarning",function(a,b,c,d){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@",ngDisabled:"="},require:["pagination","?ngModel"],controller:"PaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pagination.html"},replace:!0,link:function(e,f,g,h){function i(a,b,c){return{number:a,text:b,active:c}}function j(a,b){var c=[],d=1,e=b,f=angular.isDefined(m)&&b>m;f&&(n?(d=Math.max(a-Math.floor(m/2),1),e=d+m-1,e>b&&(e=b,d=e-m+1)):(d=(Math.ceil(a/m)-1)*m+1,e=Math.min(d+m-1,b)));for(var g=d;e>=g;g++){var h=i(g,g,g===a);c.push(h)}if(f&&!n){if(d>1){var j=i(d-1,"...",!1);c.unshift(j)}if(b>e){var k=i(e+1,"...",!1);c.push(k)}}return c}d||c.warn("pagination is now deprecated. Use uib-pagination instead.");var k=h[0],l=h[1];if(l){var m=angular.isDefined(g.maxSize)?e.$parent.$eval(g.maxSize):b.maxSize,n=angular.isDefined(g.rotate)?e.$parent.$eval(g.rotate):b.rotate;e.boundaryLinks=angular.isDefined(g.boundaryLinks)?e.$parent.$eval(g.boundaryLinks):b.boundaryLinks,e.directionLinks=angular.isDefined(g.directionLinks)?e.$parent.$eval(g.directionLinks):b.directionLinks,k.init(l,b),g.maxSize&&e.$parent.$watch(a(g.maxSize),function(a){m=parseInt(a,10),k.render()});var o=k.render;k.render=function(){o(),e.page>0&&e.page<=e.totalPages&&(e.pages=j(e.page,e.totalPages))}}}}}]).directive("pager",["uibPagerConfig","$log","$paginationSuppressWarning",function(a,b,c){return{restrict:"EA",scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["pager","?ngModel"],controller:"PaginationController",controllerAs:"pagination",templateUrl:function(a,b){return b.templateUrl||"template/pagination/pager.html"},replace:!0,link:function(d,e,f,g){c||b.warn("pager is now deprecated. Use uib-pager instead.");var h=g[0],i=g[1];i&&(d.align=angular.isDefined(f.align)?d.$parent.$eval(f.align):a.align,h.init(i,a))}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.stackedMap"]).provider("$uibTooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",animation:!0,popupDelay:0,popupCloseDelay:0,useContentExp:!1},c={mouseenter:"mouseleave",click:"click",focus:"blur",none:""},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$uibPosition","$interpolate","$rootScope","$parse","$$stackedMap",function(e,f,g,h,i,j,k,l,m){var n=m.createNew();return h.on("keypress",function(a){if(27===a.which){var b=n.top();b&&(b.value.close(),n.removeTop(),b=null)}}),function(e,k,m,o){function p(a){var b=(a||o.trigger||m).split(" "),d=b.map(function(a){return c[a]||a});return{show:b,hide:d}}o=angular.extend({},b,d,o);var q=a(e),r=j.startSymbol(),s=j.endSymbol(),t="<div "+q+'-popup title="'+r+"title"+s+'" '+(o.useContentExp?'content-exp="contentExp()" ':'content="'+r+"content"+s+'" ')+'placement="'+r+"placement"+s+'" popup-class="'+r+"popupClass"+s+'" animation="animation" is-open="isOpen"origin-scope="origScope" style="visibility: hidden; display: block; top: -9999px; left: -9999px;"></div>';return{compile:function(a,b){var c=f(t);return function(a,b,d,f){function j(){L.isOpen?q():m()}function m(){(!K||a.$eval(d[k+"Enable"]))&&(u(),x(),L.popupDelay?F||(F=g(r,L.popupDelay,!1)):r())}function q(){s(),L.popupCloseDelay?G||(G=g(t,L.popupCloseDelay,!1)):t()}function r(){return s(),u(),L.content?(v(),void L.$evalAsync(function(){L.isOpen=!0,y(!0),Q()})):angular.noop}function s(){F&&(g.cancel(F),F=null),H&&(g.cancel(H),H=null)}function t(){s(),u(),L&&L.$evalAsync(function(){L.isOpen=!1,y(!1),L.animation?E||(E=g(w,150,!1)):w()})}function u(){G&&(g.cancel(G),G=null),E&&(g.cancel(E),E=null)}function v(){C||(D=L.$new(),C=c(D,function(a){I?h.find("body").append(a):b.after(a)}),z())}function w(){A(),E=null,C&&(C.remove(),C=null),D&&(D.$destroy(),D=null)}function x(){L.title=d[k+"Title"],O?L.content=O(a):L.content=d[e],L.popupClass=d[k+"Class"],L.placement=angular.isDefined(d[k+"Placement"])?d[k+"Placement"]:o.placement;var b=parseInt(d[k+"PopupDelay"],10),c=parseInt(d[k+"PopupCloseDelay"],10);L.popupDelay=isNaN(b)?o.popupDelay:b,L.popupCloseDelay=isNaN(c)?o.popupCloseDelay:c}function y(b){N&&angular.isFunction(N.assign)&&N.assign(a,b)}function z(){P.length=0,O?(P.push(a.$watch(O,function(a){L.content=a,!a&&L.isOpen&&t()})),P.push(D.$watch(function(){M||(M=!0,D.$$postDigest(function(){M=!1,L&&L.isOpen&&Q()}))}))):P.push(d.$observe(e,function(a){L.content=a,!a&&L.isOpen?t():Q()})),P.push(d.$observe(k+"Title",function(a){L.title=a,L.isOpen&&Q()})),P.push(d.$observe(k+"Placement",function(a){L.placement=a?a:o.placement,L.isOpen&&Q()}))}function A(){P.length&&(angular.forEach(P,function(a){a()}),P.length=0)}function B(){var a=d[k+"Trigger"];R(),J=p(a),"none"!==J.show&&J.show.forEach(function(a,c){a===J.hide[c]?b[0].addEventListener(a,j):a&&(b[0].addEventListener(a,m),J.hide[c].split(" ").forEach(function(a){b[0].addEventListener(a,q)})),b.on("keypress",function(a){27===a.which&&q()})})}var C,D,E,F,G,H,I=angular.isDefined(o.appendToBody)?o.appendToBody:!1,J=p(void 0),K=angular.isDefined(d[k+"Enable"]),L=a.$new(!0),M=!1,N=angular.isDefined(d[k+"IsOpen"])?l(d[k+"IsOpen"]):!1,O=o.useContentExp?l(d[e]):!1,P=[],Q=function(){C&&C.html()&&(H||(H=g(function(){C.css({top:0,left:0});var a=i.positionElements(b,C,L.placement,I);a.top+="px",a.left+="px",a.visibility="visible",C.css(a),H=null},0,!1)))};L.origScope=a,L.isOpen=!1,n.add(L,{close:t}),L.contentExp=function(){return L.content},d.$observe("disabled",function(a){a&&s(),a&&L.isOpen&&t()}),N&&a.$watch(N,function(a){L&&!a===L.isOpen&&j()});var R=function(){J.show.forEach(function(a){b.unbind(a,m)}),J.hide.forEach(function(a){a.split(" ").forEach(function(a){b[0].removeEventListener(a,q)})})};B();var S=a.$eval(d[k+"Animation"]);L.animation=angular.isDefined(S)?!!S:o.animation;var T=a.$eval(d[k+"AppendToBody"]);I=angular.isDefined(T)?T:I,I&&a.$on("$locationChangeSuccess",function(){L.isOpen&&t()}),a.$on("$destroy",function(){s(),u(),R(),w(),n.remove(L),L=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(a,b,c,d){return{link:function(e,f,g){var h,i,j,k=e.$eval(g.tooltipTemplateTranscludeScope),l=0,m=function(){i&&(i.remove(),i=null),h&&(h.$destroy(),h=null),j&&(a.leave(j).then(function(){i=null}),i=j,j=null)};e.$watch(b.parseAsResourceUrl(g.uibTooltipTemplateTransclude),function(b){var g=++l;b?(d(b,!0).then(function(d){if(g===l){var e=k.$new(),i=d,n=c(i)(e,function(b){m(),a.enter(b,f)});h=e,j=n,h.$emit("$includeContentLoaded",b)}},function(){g===l&&(m(),e.$emit("$includeContentError",b))}),e.$emit("$includeContentRequested",b)):m()}),e.$on("$destroy",m)}}}]).directive("uibTooltipClasses",function(){return{restrict:"A",link:function(a,b,c){a.placement&&b.addClass(a.placement),a.popupClass&&b.addClass(a.popupClass),a.animation()&&b.addClass(c.tooltipAnimationClass)}}}).directive("uibTooltipPopup",function(){return{replace:!0,scope:{content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html",link:function(a,b){b.addClass("tooltip")}}}).directive("uibTooltip",["$uibTooltip",function(a){return a("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/tooltip/tooltip-template-popup.html",link:function(a,b){b.addClass("tooltip")}}}).directive("uibTooltipTemplate",["$uibTooltip",function(a){return a("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-popup.html",link:function(a,b){b.addClass("tooltip")}}}).directive("uibTooltipHtml",["$uibTooltip",function(a){return a("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.tooltip").value("$tooltipSuppressWarning",!1).provider("$tooltip",["$uibTooltipProvider",function(a){angular.extend(this,a),this.$get=["$log","$tooltipSuppressWarning","$injector",function(b,c,d){return c||b.warn("$tooltip is now deprecated. Use $uibTooltip instead."),d.invoke(a.$get)}]}]).directive("tooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest","$log","$tooltipSuppressWarning",function(a,b,c,d,e,f){return{link:function(g,h,i){f||e.warn("tooltip-template-transclude is now deprecated. Use uib-tooltip-template-transclude instead.");var j,k,l,m=g.$eval(i.tooltipTemplateTranscludeScope),n=0,o=function(){k&&(k.remove(),k=null),j&&(j.$destroy(),j=null),l&&(a.leave(l).then(function(){k=null}),k=l,l=null)};g.$watch(b.parseAsResourceUrl(i.tooltipTemplateTransclude),function(b){var e=++n;b?(d(b,!0).then(function(d){if(e===n){var f=m.$new(),g=d,i=c(g)(f,function(b){o(),a.enter(b,h)});j=f,l=i,j.$emit("$includeContentLoaded",b)}},function(){e===n&&(o(),g.$emit("$includeContentError",b))}),g.$emit("$includeContentRequested",b)):o()}),g.$on("$destroy",o)}}}]).directive("tooltipClasses",["$log","$tooltipSuppressWarning",function(a,b){return{restrict:"A",link:function(c,d,e){b||a.warn("tooltip-classes is now deprecated. Use uib-tooltip-classes instead."),c.placement&&d.addClass(c.placement),c.popupClass&&d.addClass(c.popupClass),c.animation()&&d.addClass(e.tooltipAnimationClass)}}}]).directive("tooltipPopup",["$log","$tooltipSuppressWarning",function(a,b){return{replace:!0,scope:{content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html",link:function(c,d){b||a.warn("tooltip-popup is now deprecated. Use uib-tooltip-popup instead."),d.addClass("tooltip")}}}]).directive("tooltip",["$tooltip",function(a){return a("tooltip","tooltip","mouseenter")}]).directive("tooltipTemplatePopup",["$log","$tooltipSuppressWarning",function(a,b){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/tooltip/tooltip-template-popup.html",link:function(c,d){b||a.warn("tooltip-template-popup is now deprecated. Use uib-tooltip-template-popup instead."),d.addClass("tooltip")}}}]).directive("tooltipTemplate",["$tooltip",function(a){return a("tooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("tooltipHtmlPopup",["$log","$tooltipSuppressWarning",function(a,b){return{replace:!0,scope:{contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-popup.html",link:function(c,d){b||a.warn("tooltip-html-popup is now deprecated. Use uib-tooltip-html-popup instead."),d.addClass("tooltip")}}}]).directive("tooltipHtml",["$tooltip",function(a){return a("tooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{replace:!0,scope:{title:"@",contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/popover/popover-template.html",link:function(a,b){b.addClass("popover")}}}).directive("uibPopoverTemplate",["$uibTooltip",function(a){return a("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{replace:!0,scope:{contentExp:"&",title:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover-html.html",link:function(a,b){b.addClass("popover")}}}).directive("uibPopoverHtml",["$uibTooltip",function(a){return a("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{replace:!0,scope:{title:"@",content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html",link:function(a,b){b.addClass("popover")}}}).directive("uibPopover",["$uibTooltip",function(a){return a("uibPopover","popover","click")}]),angular.module("ui.bootstrap.popover").value("$popoverSuppressWarning",!1).directive("popoverTemplatePopup",["$log","$popoverSuppressWarning",function(a,b){return{replace:!0,scope:{title:"@",contentExp:"&",placement:"@",popupClass:"@",animation:"&",isOpen:"&",originScope:"&"},templateUrl:"template/popover/popover-template.html",link:function(c,d){b||a.warn("popover-template-popup is now deprecated. Use uib-popover-template-popup instead."),d.addClass("popover")}}}]).directive("popoverTemplate",["$tooltip",function(a){return a("popoverTemplate","popover","click",{useContentExp:!0})}]).directive("popoverHtmlPopup",["$log","$popoverSuppressWarning",function(a,b){return{replace:!0,scope:{contentExp:"&",title:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover-html.html",link:function(c,d){b||a.warn("popover-html-popup is now deprecated. Use uib-popover-html-popup instead."),d.addClass("popover")}}}]).directive("popoverHtml",["$tooltip",function(a){return a("popoverHtml","popover","click",{useContentExp:!0})}]).directive("popoverPopup",["$log","$popoverSuppressWarning",function(a,b){return{replace:!0,scope:{title:"@",content:"@",placement:"@",popupClass:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html",link:function(c,d){b||a.warn("popover-popup is now deprecated. Use uib-popover-popup instead."),d.addClass("popover")}}}]).directive("popover",["$tooltip",function(a){return a("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(a,b,c){var d=this,e=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=angular.isDefined(a.max)?a.max:c.max,this.addBar=function(b,c,f){e||c.css({transition:"none"}),this.bars.push(b),b.max=a.max,b.title=f&&angular.isDefined(f.title)?f.title:"progressbar",b.$watch("value",function(a){b.recalculatePercentage()}),b.recalculatePercentage=function(){var a=d.bars.reduce(function(a,b){return b.percent=+(100*b.value/b.max).toFixed(2),a+b.percent},0);a>100&&(b.percent-=a-100)},b.$on("$destroy",function(){c=null,d.removeBar(b)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1),this.bars.forEach(function(a){a.recalculatePercentage()})},a.$watch("max",function(b){d.bars.forEach(function(b){b.max=a.max,b.recalculatePercentage()})})}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{max:"=?"},templateUrl:"template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b,c)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",max:"=?",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]),{title:c.title})}}}),angular.module("ui.bootstrap.progressbar").value("$progressSuppressWarning",!1).controller("ProgressController",["$scope","$attrs","uibProgressConfig","$log","$progressSuppressWarning",function(a,b,c,d,e){e||d.warn("ProgressController is now deprecated. Use UibProgressController instead.");var f=this,g=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=angular.isDefined(a.max)?a.max:c.max,this.addBar=function(b,c,d){g||c.css({transition:"none"}),this.bars.push(b),b.max=a.max,b.title=d&&angular.isDefined(d.title)?d.title:"progressbar",b.$watch("value",function(a){b.recalculatePercentage()}),b.recalculatePercentage=function(){b.percent=+(100*b.value/b.max).toFixed(2);var a=f.bars.reduce(function(a,b){return a+b.percent},0);a>100&&(b.percent-=a-100)},b.$on("$destroy",function(){c=null,f.removeBar(b)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1)},a.$watch("max",function(b){f.bars.forEach(function(b){b.max=a.max,b.recalculatePercentage()})})}]).directive("progress",["$log","$progressSuppressWarning",function(a,b){return{replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{max:"=?",title:"@?"},templateUrl:"template/progressbar/progress.html",link:function(){b||a.warn("progress is now deprecated. Use uib-progress instead.")}}}]).directive("bar",["$log","$progressSuppressWarning",function(a,b){return{replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(c,d,e,f){b||a.warn("bar is now deprecated. Use uib-bar instead."),f.addBar(c,d)}}}]).directive("progressbar",["$log","$progressSuppressWarning",function(a,b){return{replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",max:"=?",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(c,d,e,f){b||a.warn("progressbar is now deprecated. Use uib-progressbar instead."),f.addBar(c,angular.element(d.children()[0]),{title:e.title})}}}]),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(a,b,c){var d={$setViewValue:angular.noop};this.init=function(e){d=e,d.$render=this.render,d.$formatters.push(function(a){return angular.isNumber(a)&&a<<0!==a&&(a=Math.round(a)),a}),this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff;var f=angular.isDefined(b.titles)?a.$parent.$eval(b.titles):c.titles;this.titles=angular.isArray(f)&&f.length>0?f:c.titles;var g=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(g)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff,title:this.getTitle(b)},a[b]);return a},this.getTitle=function(a){return a>=this.titles.length?a+1:this.titles[a]},a.rate=function(b){!a.readonly&&b>=0&&b<=a.range.length&&(d.$setViewValue(d.$viewValue===b?0:b),d.$render())},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],scope:{readonly:"=?",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"template/rating/rating.html",replace:!0,link:function(a,b,c,d){var e=d[0],f=d[1];e.init(f)}}}),angular.module("ui.bootstrap.rating").value("$ratingSuppressWarning",!1).controller("RatingController",["$scope","$attrs","$controller","$log","$ratingSuppressWarning",function(a,b,c,d,e){e||d.warn("RatingController is now deprecated. Use UibRatingController instead."),angular.extend(this,c("UibRatingController",{$scope:a,$attrs:b}))}]).directive("rating",["$log","$ratingSuppressWarning",function(a,b){return{require:["rating","ngModel"],scope:{readonly:"=?",onHover:"&",onLeave:"&"},controller:"RatingController",templateUrl:"template/rating/rating.html",replace:!0,link:function(c,d,e,f){b||a.warn("rating is now deprecated. Use uib-rating instead.");var g=f[0],h=f[1];g.init(h)}}}]),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(a){var b=this,c=b.tabs=a.tabs=[];b.select=function(a){angular.forEach(c,function(b){b.active&&b!==a&&(b.active=!1,b.onDeselect(),a.selectCalled=!1)}),a.active=!0,a.selectCalled||(a.onSelect(),a.selectCalled=!0)},b.addTab=function(a){c.push(a),1===c.length&&a.active!==!1?a.active=!0:a.active?b.select(a):a.active=!1},b.removeTab=function(a){var e=c.indexOf(a);if(a.active&&c.length>1&&!d){var f=e==c.length-1?e-1:e+1;b.select(c[f])}c.splice(e,1)};var d;a.$on("$destroy",function(){d=!0})}]).directive("uibTabset",function(){return{restrict:"EA",transclude:!0,replace:!0,scope:{type:"@"},controller:"UibTabsetController",templateUrl:"template/tabs/tabset.html",link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1}}}).directive("uibTab",["$parse",function(a){return{require:"^uibTabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{active:"=?",heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},link:function(b,c,d,e,f){b.$watch("active",function(a){a&&e.select(b)}),b.disabled=!1,d.disable&&b.$parent.$watch(a(d.disable),function(a){b.disabled=!!a}),b.select=function(){b.disabled||(b.active=!0)},e.addTab(b),b.$on("$destroy",function(){e.removeTab(b)}),b.$transcludeFn=f}}}]).directive("uibTabHeadingTransclude",function(){return{restrict:"A",require:["?^uibTab","?^tab"],link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}).directive("uibTabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||a.hasAttribute("x-tab-heading")||a.hasAttribute("uib-tab-heading")||a.hasAttribute("data-uib-tab-heading")||a.hasAttribute("x-uib-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase()||"x-tab-heading"===a.tagName.toLowerCase()||"uib-tab-heading"===a.tagName.toLowerCase()||"data-uib-tab-heading"===a.tagName.toLowerCase()||"x-uib-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:["?^uibTabset","?^tabset"],link:function(b,c,d){var e=b.$eval(d.uibTabContentTransclude);e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("ui.bootstrap.tabs").value("$tabsSuppressWarning",!1).controller("TabsetController",["$scope","$controller","$log","$tabsSuppressWarning",function(a,b,c,d){d||c.warn("TabsetController is now deprecated. Use UibTabsetController instead."),angular.extend(this,b("UibTabsetController",{$scope:a}))}]).directive("tabset",["$log","$tabsSuppressWarning",function(a,b){return{restrict:"EA",transclude:!0,replace:!0,scope:{type:"@"},controller:"TabsetController",templateUrl:"template/tabs/tabset.html",link:function(c,d,e){b||a.warn("tabset is now deprecated. Use uib-tabset instead."),c.vertical=angular.isDefined(e.vertical)?c.$parent.$eval(e.vertical):!1,c.justified=angular.isDefined(e.justified)?c.$parent.$eval(e.justified):!1}}}]).directive("tab",["$parse","$log","$tabsSuppressWarning",function(a,b,c){return{require:"^tabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{active:"=?",heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},link:function(d,e,f,g,h){c||b.warn("tab is now deprecated. Use uib-tab instead."),d.$watch("active",function(a){a&&g.select(d)}),d.disabled=!1,f.disable&&d.$parent.$watch(a(f.disable),function(a){d.disabled=!!a}),d.select=function(){d.disabled||(d.active=!0)},g.addTab(d),d.$on("$destroy",function(){g.removeTab(d)}),d.$transcludeFn=h}}}]).directive("tabHeadingTransclude",["$log","$tabsSuppressWarning",function(a,b){return{restrict:"A",require:"^tab",link:function(c,d){b||a.warn("tab-heading-transclude is now deprecated. Use uib-tab-heading-transclude instead."),c.$watch("headingElement",function(a){a&&(d.html(""),d.append(a))})}}}]).directive("tabContentTransclude",["$log","$tabsSuppressWarning",function(a,b){function c(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||a.hasAttribute("x-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase()||"x-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^tabset",link:function(d,e,f){b||a.warn("tab-content-transclude is now deprecated. Use uib-tab-content-transclude instead.");var g=d.$eval(f.tabContentTransclude);g.$transcludeFn(g.$parent,function(a){angular.forEach(a,function(a){c(a)?g.headingElement=a:e.append(a)})})}}}]),angular.module("ui.bootstrap.timepicker",[]).constant("uibTimepickerConfig",{hourStep:1,minuteStep:1,showMeridian:!0,meridians:null,readonlyInput:!1,mousewheel:!0,arrowkeys:!0,showSpinners:!0}).controller("UibTimepickerController",["$scope","$element","$attrs","$parse","$log","$locale","uibTimepickerConfig",function(a,b,c,d,e,f,g){function h(){var b=parseInt(a.hours,10),c=a.showMeridian?b>0&&13>b:b>=0&&24>b;return c?(a.showMeridian&&(12===b&&(b=0),a.meridian===r[1]&&(b+=12)),b):void 0}function i(){var b=parseInt(a.minutes,10);return b>=0&&60>b?b:void 0}function j(a){return angular.isDefined(a)&&a.toString().length<2?"0"+a:a.toString()}function k(a){l(),q.$setViewValue(new Date(p)),m(a)}function l(){q.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1}function m(b){var c=p.getHours(),d=p.getMinutes();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:j(c),"m"!==b&&(a.minutes=j(d)),a.meridian=p.getHours()<12?r[0]:r[1]}function n(a,b){var c=new Date(a.getTime()+6e4*b),d=new Date(a);return d.setHours(c.getHours(),c.getMinutes()),d}function o(a){p=n(p,a),k()}var p=new Date,q={$setViewValue:angular.noop},r=angular.isDefined(c.meridians)?a.$parent.$eval(c.meridians):g.meridians||f.DATETIME_FORMATS.AMPMS;a.tabindex=angular.isDefined(c.tabindex)?c.tabindex:0,b.removeAttr("tabindex"),this.init=function(b,d){q=b,q.$render=this.render,q.$formatters.unshift(function(a){return a?new Date(a):null});var e=d.eq(0),f=d.eq(1),h=angular.isDefined(c.mousewheel)?a.$parent.$eval(c.mousewheel):g.mousewheel;h&&this.setupMousewheelEvents(e,f);var i=angular.isDefined(c.arrowkeys)?a.$parent.$eval(c.arrowkeys):g.arrowkeys;i&&this.setupArrowkeyEvents(e,f),a.readonlyInput=angular.isDefined(c.readonlyInput)?a.$parent.$eval(c.readonlyInput):g.readonlyInput,this.setupInputEvents(e,f)};var s=g.hourStep;c.hourStep&&a.$parent.$watch(d(c.hourStep),function(a){s=parseInt(a,10)});var t=g.minuteStep;c.minuteStep&&a.$parent.$watch(d(c.minuteStep),function(a){t=parseInt(a,10)});var u;a.$parent.$watch(d(c.min),function(a){var b=new Date(a);u=isNaN(b)?void 0:b});var v;a.$parent.$watch(d(c.max),function(a){var b=new Date(a);v=isNaN(b)?void 0:b}),a.noIncrementHours=function(){var a=n(p,60*s);return a>v||p>a&&u>a},a.noDecrementHours=function(){var a=n(p,60*-s);return u>a||a>p&&a>v},a.noIncrementMinutes=function(){var a=n(p,t);return a>v||p>a&&u>a},a.noDecrementMinutes=function(){var a=n(p,-t);return u>a||a>p&&a>v},a.noToggleMeridian=function(){return p.getHours()<13?n(p,720)>v:n(p,-720)<u},a.showMeridian=g.showMeridian,c.showMeridian&&a.$parent.$watch(d(c.showMeridian),function(b){if(a.showMeridian=!!b,q.$error.time){var c=h(),d=i();angular.isDefined(c)&&angular.isDefined(d)&&(p.setHours(c),k())}else m()}),this.setupMousewheelEvents=function(b,c){var d=function(a){a.originalEvent&&(a=a.originalEvent);var b=a.wheelDelta?a.wheelDelta:-a.deltaY;return a.detail||b>0};b.bind("mousewheel wheel",function(b){a.$apply(d(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.bind("mousewheel wheel",function(b){a.$apply(d(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()})},this.setupArrowkeyEvents=function(b,c){b.bind("keydown",function(b){38===b.which?(b.preventDefault(),a.incrementHours(),a.$apply()):40===b.which&&(b.preventDefault(), +a.decrementHours(),a.$apply())}),c.bind("keydown",function(b){38===b.which?(b.preventDefault(),a.incrementMinutes(),a.$apply()):40===b.which&&(b.preventDefault(),a.decrementMinutes(),a.$apply())})},this.setupInputEvents=function(b,c){if(a.readonlyInput)return a.updateHours=angular.noop,void(a.updateMinutes=angular.noop);var d=function(b,c){q.$setViewValue(null),q.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b),angular.isDefined(c)&&(a.invalidMinutes=c)};a.updateHours=function(){var a=h(),b=i();angular.isDefined(a)&&angular.isDefined(b)?(p.setHours(a),u>p||p>v?d(!0):k("h")):d(!0)},b.bind("blur",function(b){!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=j(a.hours)})}),a.updateMinutes=function(){var a=i(),b=h();angular.isDefined(a)&&angular.isDefined(b)?(p.setMinutes(a),u>p||p>v?d(void 0,!0):k("m")):d(void 0,!0)},c.bind("blur",function(b){!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=j(a.minutes)})})},this.render=function(){var b=q.$viewValue;isNaN(b)?(q.$setValidity("time",!1),e.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(b&&(p=b),u>p||p>v?(q.$setValidity("time",!1),a.invalidHours=!0,a.invalidMinutes=!0):l(),m())},a.showSpinners=angular.isDefined(c.showSpinners)?a.$parent.$eval(c.showSpinners):g.showSpinners,a.incrementHours=function(){a.noIncrementHours()||o(60*s)},a.decrementHours=function(){a.noDecrementHours()||o(60*-s)},a.incrementMinutes=function(){a.noIncrementMinutes()||o(t)},a.decrementMinutes=function(){a.noDecrementMinutes()||o(-t)},a.toggleMeridian=function(){a.noToggleMeridian()||o(720*(p.getHours()<12?1:-1))}}]).directive("uibTimepicker",function(){return{restrict:"EA",require:["uibTimepicker","?^ngModel"],controller:"UibTimepickerController",controllerAs:"timepicker",replace:!0,scope:{},templateUrl:function(a,b){return b.templateUrl||"template/timepicker/timepicker.html"},link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}),angular.module("ui.bootstrap.timepicker").value("$timepickerSuppressWarning",!1).controller("TimepickerController",["$scope","$element","$attrs","$controller","$log","$timepickerSuppressWarning",function(a,b,c,d,e,f){f||e.warn("TimepickerController is now deprecated. Use UibTimepickerController instead."),angular.extend(this,d("UibTimepickerController",{$scope:a,$element:b,$attrs:c}))}]).directive("timepicker",["$log","$timepickerSuppressWarning",function(a,b){return{restrict:"EA",require:["timepicker","?^ngModel"],controller:"TimepickerController",controllerAs:"timepicker",replace:!0,scope:{},templateUrl:function(a,b){return b.templateUrl||"template/timepicker/timepicker.html"},link:function(c,d,e,f){b||a.warn("timepicker is now deprecated. Use uib-timepicker instead.");var g=f[0],h=f[1];h&&g.init(h,d.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$uibPosition","uibTypeaheadParser",function(a,b,c,d,e,f,g,h,i,j,k,l){function m(){K.moveInProgress||(K.moveInProgress=!0,K.$digest()),S&&g.cancel(S),S=g(function(){K.matches.length&&n(),K.moveInProgress=!1},r)}function n(){K.position=C?k.offset(b):k.position(b),K.position.top+=b.prop("offsetHeight")}var o,p,q=[9,13,27,38,40],r=200,s=a.$eval(c.typeaheadMinLength);s||0===s||(s=1);var t,u,v=a.$eval(c.typeaheadWaitMs)||0,w=a.$eval(c.typeaheadEditable)!==!1,x=e(c.typeaheadLoading).assign||angular.noop,y=e(c.typeaheadOnSelect),z=angular.isDefined(c.typeaheadSelectOnBlur)?a.$eval(c.typeaheadSelectOnBlur):!1,A=e(c.typeaheadNoResults).assign||angular.noop,B=c.typeaheadInputFormatter?e(c.typeaheadInputFormatter):void 0,C=c.typeaheadAppendToBody?a.$eval(c.typeaheadAppendToBody):!1,D=c.typeaheadAppendToElementId||!1,E=a.$eval(c.typeaheadFocusFirst)!==!1,F=c.typeaheadSelectOnExact?a.$eval(c.typeaheadSelectOnExact):!1,G=e(c.ngModel),H=e(c.ngModel+"($$$p)"),I=function(b,c){return angular.isFunction(G(a))&&p&&p.$options&&p.$options.getterSetter?H(b,{$$$p:c}):G.assign(b,c)},J=l.parse(c.uibTypeahead),K=a.$new(),L=a.$on("$destroy",function(){K.$destroy()});K.$on("$destroy",L);var M="typeahead-"+K.$id+"-"+Math.floor(1e4*Math.random());b.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":M});var N=angular.element("<div uib-typeahead-popup></div>");N.attr({id:M,matches:"matches",active:"activeIdx",select:"select(activeIdx)","move-in-progress":"moveInProgress",query:"query",position:"position"}),angular.isDefined(c.typeaheadTemplateUrl)&&N.attr("template-url",c.typeaheadTemplateUrl),angular.isDefined(c.typeaheadPopupTemplateUrl)&&N.attr("popup-template-url",c.typeaheadPopupTemplateUrl);var O=function(){K.matches=[],K.activeIdx=-1,b.attr("aria-expanded",!1)},P=function(a){return M+"-option-"+a};K.$watch("activeIdx",function(a){0>a?b.removeAttr("aria-activedescendant"):b.attr("aria-activedescendant",P(a))});var Q=function(a,b){return K.matches.length>b&&a?a.toUpperCase()===K.matches[b].label.toUpperCase():!1},R=function(c){var d={$viewValue:c};x(a,!0),A(a,!1),f.when(J.source(a,d)).then(function(e){var f=c===o.$viewValue;if(f&&t)if(e&&e.length>0){K.activeIdx=E?0:-1,A(a,!1),K.matches.length=0;for(var g=0;g<e.length;g++)d[J.itemName]=e[g],K.matches.push({id:P(g),label:J.viewMapper(K,d),model:e[g]});K.query=c,n(),b.attr("aria-expanded",!0),F&&1===K.matches.length&&Q(c,0)&&K.select(0)}else O(),A(a,!0);f&&x(a,!1)},function(){O(),x(a,!1),A(a,!0)})};C&&(angular.element(i).bind("resize",m),h.find("body").bind("scroll",m));var S;K.moveInProgress=!1,K.query=void 0;var T,U=function(a){T=g(function(){R(a)},v)},V=function(){T&&g.cancel(T)};O(),K.select=function(d){var e,f,h={};u=!0,h[J.itemName]=f=K.matches[d].model,e=J.modelMapper(a,h),I(a,e),o.$setValidity("editable",!0),o.$setValidity("parse",!0),y(a,{$item:f,$model:e,$label:J.viewMapper(a,h)}),O(),K.$eval(c.typeaheadFocusOnSelect)!==!1&&g(function(){b[0].focus()},0,!1)},b.bind("keydown",function(a){if(0!==K.matches.length&&-1!==q.indexOf(a.which)){if(-1===K.activeIdx&&(9===a.which||13===a.which))return O(),void K.$digest();a.preventDefault(),40===a.which?(K.activeIdx=(K.activeIdx+1)%K.matches.length,K.$digest()):38===a.which?(K.activeIdx=(K.activeIdx>0?K.activeIdx:K.matches.length)-1,K.$digest()):13===a.which||9===a.which?K.$apply(function(){K.select(K.activeIdx)}):27===a.which&&(a.stopPropagation(),O(),K.$digest())}}),b.bind("blur",function(){z&&K.matches.length&&-1!==K.activeIdx&&!u&&(u=!0,K.$apply(function(){K.select(K.activeIdx)})),t=!1,u=!1});var W=function(a){b[0]!==a.target&&3!==a.which&&0!==K.matches.length&&(O(),j.$$phase||K.$digest())};h.bind("click",W),a.$on("$destroy",function(){h.unbind("click",W),(C||D)&&X.remove(),C&&(angular.element(i).unbind("resize",m),h.find("body").unbind("scroll",m)),N.remove()});var X=d(N)(K);C?h.find("body").append(X):D!==!1?angular.element(h[0].getElementById(D)).append(X):b.after(X),this.init=function(b,c){o=b,p=c,o.$parsers.unshift(function(b){return t=!0,0===s||b&&b.length>=s?v>0?(V(),U(b)):R(b):(x(a,!1),V(),O()),w?b:b?void o.$setValidity("editable",!1):(o.$setValidity("editable",!0),null)}),o.$formatters.push(function(b){var c,d,e={};return w||o.$setValidity("editable",!0),B?(e.$model=b,B(a,e)):(e[J.itemName]=b,c=J.viewMapper(a,e),e[J.itemName]=void 0,d=J.viewMapper(a,e),c!==d?c:b)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","^?ngModelOptions","uibTypeahead"],link:function(a,b,c,d){d[2].init(d[0],d[1])}}}).directive("uibTypeaheadPopup",function(){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"template/typeahead/typeahead-popup.html"},link:function(a,b,c){a.templateUrl=c.templateUrl,a.isOpen=function(){return a.matches.length>0},a.isActive=function(b){return a.active==b},a.selectActive=function(b){a.active=b},a.selectMatch=function(b){a.select({activeIdx:b})}}}}).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(a,b,c){return{scope:{index:"=",match:"=",query:"="},link:function(d,e,f){var g=c(f.templateUrl)(d.$parent)||"template/typeahead/typeahead-match.html";a(g).then(function(a){b(a.trim())(d,function(a){e.replaceWith(a)})})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(a,b,c){function d(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function e(a){return/<.*>/g.test(a)}var f;return f=b.has("$sanitize"),function(b,g){return!f&&e(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=g?(""+b).replace(new RegExp(d(g),"gi"),"<strong>$&</strong>"):b,f||(b=a.trustAsHtml(b)),b}}]),angular.module("ui.bootstrap.typeahead").value("$typeaheadSuppressWarning",!1).service("typeaheadParser",["$parse","uibTypeaheadParser","$log","$typeaheadSuppressWarning",function(a,b,c,d){return d||c.warn("typeaheadParser is now deprecated. Use uibTypeaheadParser instead."),b}]).directive("typeahead",["$compile","$parse","$q","$timeout","$document","$window","$rootScope","$uibPosition","typeaheadParser","$log","$typeaheadSuppressWarning",function(a,b,c,d,e,f,g,h,i,j,k){var l=[9,13,27,38,40],m=200;return{require:["ngModel","^?ngModelOptions"],link:function(n,o,p,q){function r(){N.moveInProgress||(N.moveInProgress=!0,N.$digest()),V&&d.cancel(V),V=d(function(){N.matches.length&&s(),N.moveInProgress=!1},m)}function s(){N.position=F?h.offset(o):h.position(o),N.position.top+=o.prop("offsetHeight")}k||j.warn("typeahead is now deprecated. Use uib-typeahead instead.");var t=q[0],u=q[1],v=n.$eval(p.typeaheadMinLength);v||0===v||(v=1);var w,x,y=n.$eval(p.typeaheadWaitMs)||0,z=n.$eval(p.typeaheadEditable)!==!1,A=b(p.typeaheadLoading).assign||angular.noop,B=b(p.typeaheadOnSelect),C=angular.isDefined(p.typeaheadSelectOnBlur)?n.$eval(p.typeaheadSelectOnBlur):!1,D=b(p.typeaheadNoResults).assign||angular.noop,E=p.typeaheadInputFormatter?b(p.typeaheadInputFormatter):void 0,F=p.typeaheadAppendToBody?n.$eval(p.typeaheadAppendToBody):!1,G=p.typeaheadAppendToElementId||!1,H=n.$eval(p.typeaheadFocusFirst)!==!1,I=p.typeaheadSelectOnExact?n.$eval(p.typeaheadSelectOnExact):!1,J=b(p.ngModel),K=b(p.ngModel+"($$$p)"),L=function(a,b){return angular.isFunction(J(n))&&u&&u.$options&&u.$options.getterSetter?K(a,{$$$p:b}):J.assign(a,b)},M=i.parse(p.typeahead),N=n.$new(),O=n.$on("$destroy",function(){N.$destroy()});N.$on("$destroy",O);var P="typeahead-"+N.$id+"-"+Math.floor(1e4*Math.random());o.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":P});var Q=angular.element("<div typeahead-popup></div>");Q.attr({id:P,matches:"matches",active:"activeIdx",select:"select(activeIdx)","move-in-progress":"moveInProgress",query:"query",position:"position"}),angular.isDefined(p.typeaheadTemplateUrl)&&Q.attr("template-url",p.typeaheadTemplateUrl),angular.isDefined(p.typeaheadPopupTemplateUrl)&&Q.attr("popup-template-url",p.typeaheadPopupTemplateUrl);var R=function(){N.matches=[],N.activeIdx=-1,o.attr("aria-expanded",!1)},S=function(a){return P+"-option-"+a};N.$watch("activeIdx",function(a){0>a?o.removeAttr("aria-activedescendant"):o.attr("aria-activedescendant",S(a))});var T=function(a,b){return N.matches.length>b&&a?a.toUpperCase()===N.matches[b].label.toUpperCase():!1},U=function(a){var b={$viewValue:a};A(n,!0),D(n,!1),c.when(M.source(n,b)).then(function(c){var d=a===t.$viewValue;if(d&&w)if(c&&c.length>0){N.activeIdx=H?0:-1,D(n,!1),N.matches.length=0;for(var e=0;e<c.length;e++)b[M.itemName]=c[e],N.matches.push({id:S(e),label:M.viewMapper(N,b),model:c[e]});N.query=a,s(),o.attr("aria-expanded",!0),I&&1===N.matches.length&&T(a,0)&&N.select(0)}else R(),D(n,!0);d&&A(n,!1)},function(){R(),A(n,!1),D(n,!0)})};F&&(angular.element(f).bind("resize",r),e.find("body").bind("scroll",r));var V;N.moveInProgress=!1,R(),N.query=void 0;var W,X=function(a){W=d(function(){U(a)},y)},Y=function(){W&&d.cancel(W)};t.$parsers.unshift(function(a){return w=!0,0===v||a&&a.length>=v?y>0?(Y(),X(a)):U(a):(A(n,!1),Y(),R()),z?a:a?void t.$setValidity("editable",!1):(t.$setValidity("editable",!0),null)}),t.$formatters.push(function(a){var b,c,d={};return z||t.$setValidity("editable",!0),E?(d.$model=a,E(n,d)):(d[M.itemName]=a,b=M.viewMapper(n,d),d[M.itemName]=void 0,c=M.viewMapper(n,d),b!==c?b:a)}),N.select=function(a){var b,c,e={};x=!0,e[M.itemName]=c=N.matches[a].model,b=M.modelMapper(n,e),L(n,b),t.$setValidity("editable",!0),t.$setValidity("parse",!0),B(n,{$item:c,$model:b,$label:M.viewMapper(n,e)}),R(),N.$eval(p.typeaheadFocusOnSelect)!==!1&&d(function(){o[0].focus()},0,!1)},o.bind("keydown",function(a){if(0!==N.matches.length&&-1!==l.indexOf(a.which)){if(-1===N.activeIdx&&(9===a.which||13===a.which))return R(),void N.$digest();a.preventDefault(),40===a.which?(N.activeIdx=(N.activeIdx+1)%N.matches.length,N.$digest()):38===a.which?(N.activeIdx=(N.activeIdx>0?N.activeIdx:N.matches.length)-1,N.$digest()):13===a.which||9===a.which?N.$apply(function(){N.select(N.activeIdx)}):27===a.which&&(a.stopPropagation(),R(),N.$digest())}}),o.bind("blur",function(){C&&N.matches.length&&-1!==N.activeIdx&&!x&&(x=!0,N.$apply(function(){N.select(N.activeIdx)})),w=!1,x=!1});var Z=function(a){o[0]!==a.target&&3!==a.which&&0!==N.matches.length&&(R(),g.$$phase||N.$digest())};e.bind("click",Z),n.$on("$destroy",function(){e.unbind("click",Z),(F||G)&&$.remove(),F&&(angular.element(f).unbind("resize",r),e.find("body").unbind("scroll",r)),Q.remove()});var $=a(Q)(N);F?e.find("body").append($):G!==!1?angular.element(e[0].getElementById(G)).append($):o.after($)}}}]).directive("typeaheadPopup",["$typeaheadSuppressWarning","$log",function(a,b){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&"},replace:!0,templateUrl:function(a,b){return b.popupTemplateUrl||"template/typeahead/typeahead-popup.html"},link:function(c,d,e){a||b.warn("typeahead-popup is now deprecated. Use uib-typeahead-popup instead."),c.templateUrl=e.templateUrl,c.isOpen=function(){return c.matches.length>0},c.isActive=function(a){return c.active==a},c.selectActive=function(a){c.active=a},c.selectMatch=function(a){c.select({activeIdx:a})}}}}]).directive("typeaheadMatch",["$templateRequest","$compile","$parse","$typeaheadSuppressWarning","$log",function(a,b,c,d,e){return{restrict:"EA",scope:{index:"=",match:"=",query:"="},link:function(f,g,h){d||e.warn("typeahead-match is now deprecated. Use uib-typeahead-match instead.");var i=c(h.templateUrl)(f.$parent)||"template/typeahead/typeahead-match.html";a(i).then(function(a){b(a.trim())(f,function(a){g.replaceWith(a)})})}}}]).filter("typeaheadHighlight",["$sce","$injector","$log","$typeaheadSuppressWarning",function(a,b,c,d){function e(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}function f(a){return/<.*>/g.test(a)}var g;return g=b.has("$sanitize"),function(b,h){return d||c.warn("typeaheadHighlight is now deprecated. Use uibTypeaheadHighlight instead."),!g&&f(b)&&c.warn("Unsafe use of typeahead please use ngSanitize"),b=h?(""+b).replace(new RegExp(e(h),"gi"),"<strong>$&</strong>"):b,g||(b=a.trustAsHtml(b)),b}}]),!angular.$$csp()&&angular.element(document).find("head").prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>');
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/.bower.json new file mode 100644 index 000000000..7c9c3ad15 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/.bower.json @@ -0,0 +1,42 @@ +{ + "name": "angular-busy", + "version": "4.1.3", + "main": [ + "dist/angular-busy.js", + "dist/angular-busy.css" + ], + "dependencies": { + "angular": "~1.3", + "angular-animate": "~1.3" + }, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "components", + "test", + "temp", + "demo", + "lib", + "./angular-busy.css", + "./angular-busy.html", + "./angular-busy.js", + "Gruntfile.js", + "package.json", + "README.md" + ], + "devDependencies": { + "angular-mocks": "~1.3", + "jquery": "~2.1.0" + }, + "homepage": "https://github.com/cgross/angular-busy", + "_release": "4.1.3", + "_resolution": { + "type": "version", + "tag": "v4.1.3", + "commit": "f2e3261a0982da1f023881a6e7e3239cb626c9a4" + }, + "_source": "https://github.com/cgross/angular-busy.git", + "_target": "4.1.3", + "_originalSource": "angular-busy" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/LICENSE b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/LICENSE new file mode 100755 index 000000000..2fcddc39d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/LICENSE @@ -0,0 +1,20 @@ +Copyright 2013 Chris Gross + +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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.css b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.css new file mode 100755 index 000000000..7999513a0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.css @@ -0,0 +1,285 @@ +.cg-busy{ + position:absolute; + top:0px; + left:0px; + right:0px; + bottom:0px; + z-index:1001; +} + +.cg-busy-animation.ng-hide-add, +.cg-busy-animation.ng-hide-remove { + -webkit-transition:all .3s ease; + -moz-transition:all .3s ease; + -o-transition:all .3s ease; + transition:all .3s ease; + display:block !important; +} +.cg-busy-animation.ng-hide-remove { + opacity:0; + -webkit-transform:translate(0px,-40px); + -moz-transform:translate(0px,-40px); + -ms-transform:translate(0px,-40px); + -o-transform:translate(0px,-40px); + transform:translate(0px,-40px); +} +.cg-busy-animation.ng-hide-remove.ng-hide-remove-active { + opacity:1; + -webkit-transform:translate(0px,0px); + -moz-transform:translate(0px,0px); + -ms-transform:translate(0px,0px); + -o-transform:translate(0px,0px); + transform:translate(0px,0px); +} +.cg-busy-animation.ng-hide-add { + opacity:1; + -webkit-transform:translate(0px,0px); + -moz-transform:translate(0px,0px); + -ms-transform:translate(0px,0px); + -o-transform:translate(0px,0px); + transform:translate(0px,0px); +} +.cg-busy-animation.ng-hide-add.ng-hide-add-active { + opacity:0; + -webkit-transform:translate(0px,-40px); + -moz-transform:translate(0px,-40px); + -ms-transform:translate(0px,-40px); + -o-transform:translate(0px,-40px); + transform:translate(0px,-40px); +} + +.cg-busy-backdrop { + background-color:white; + opacity:.7; +} + +.cg-busy-backdrop-animation.ng-hide-add, +.cg-busy-backdrop-animation.ng-hide-remove { + -webkit-transition:opacity .3s ease; + -moz-transition:opacity .3s ease; + -o-transition:opacity .3s ease; + transition:opacity .3s ease; + display:block !important; +} + +.cg-busy-backdrop-animation.ng-hide { + opacity:0; +} + +/* All styles below are for the default template. */ + +.cg-busy-default-wrapper { + text-align:center; +} + +.cg-busy-default-sign{ + display: inline-block; + position:relative; + z-index:1002; + padding-bottom: 6px; + color:#333333; + text-shadow:0 1px 1px rgba(255, 255, 255, 0.75); + background-color:#e9eeee; + border:1px solid #dddddd; + border-top-width:0; + -webkit-border-radius:7px; + -moz-border-radius:7px; + border-radius:7px; + border-top-left-radius:0; + border-top-right-radius:0; + -webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + -moz-box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); + box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); +} +.cg-busy-default-text{ + margin:13px 12px 6px 49px; + font-size:16px; + color:#555; + text-align: left; + max-width: 400px; +} +.cg-busy-default-spinner{ + position:absolute; + width:25px; + height:25px; + display:inline-block; + top:12px; + left:14px; +} +.cg-busy-default-spinner div{ + width:12%; + height:26%; + background:#000; + position:absolute; + left:44.5%; + top:37%; + opacity:0; + -webkit-animation:cg-busy-spinner-anim 1s linear infinite; + -moz-animation:cg-busy-spinner-anim 1s linear infinite; + -ms-animation:cg-busy-spinner-anim 1s linear infinite; + -o-animation:cg-busy-spinner-anim 1s linear infinite; + animation:cg-busy-spinner-anim 1s linear infinite; + -webkit-border-radius:50px; + -moz-border-radius:50px; + border-radius:50px; + -webkit-box-shadow:0 0 3px rgba(0,0,0,0.2); + -moz-box-shadow:0 0 3px rgba(0,0,0,0.2); + box-shadow:0 0 3px rgba(0,0,0,0.2); +} +.cg-busy-default-spinner div.bar1{ + -webkit-transform:rotate(0deg) translate(0, -142%); + -moz-transform:rotate(0deg) translate(0, -142%); + -ms-transform:rotate(0deg) translate(0, -142%); + -o-transform:rotate(0deg) translate(0, -142%); + transform:rotate(0deg) translate(0, -142%); + -webkit-animation-delay:0s; + -moz-animation-delay:0s; + -ms-animation-delay:0s; + -o-animation-delay:0s; + animation-delay:0s; +} +.cg-busy-default-spinner div.bar2{ + -webkit-transform:rotate(30deg) translate(0, -142%); + -moz-transform:rotate(30deg) translate(0, -142%); + -ms-transform:rotate(30deg) translate(0, -142%); + -o-transform:rotate(30deg) translate(0, -142%); + transform:rotate(30deg) translate(0, -142%); + -webkit-animation-delay:-0.9167s; + -moz-animation-delay:-0.9167s; + -ms-animation-delay:-0.9167s; + -o-animation-delay:-0.9167s; + animation-delay:-0.9167s; +} +.cg-busy-default-spinner div.bar3{ + -webkit-transform:rotate(60deg) translate(0, -142%); + -moz-transform:rotate(60deg) translate(0, -142%); + -ms-transform:rotate(60deg) translate(0, -142%); + -o-transform:rotate(60deg) translate(0, -142%); + transform:rotate(60deg) translate(0, -142%); + -webkit-animation-delay:-0.833s; + -moz-animation-delay:-0.833s; + -ms-animation-delay:-0.833s; + -o-animation-delay:-0.833s; + animation-delay:-0.833s; +} +.cg-busy-default-spinner div.bar4{ + -webkit-transform:rotate(90deg) translate(0, -142%); + -moz-transform:rotate(90deg) translate(0, -142%); + -ms-transform:rotate(90deg) translate(0, -142%); + -o-transform:rotate(90deg) translate(0, -142%); + transform:rotate(90deg) translate(0, -142%); + -webkit-animation-delay:-0.75s; + -moz-animation-delay:-0.75s; + -ms-animation-delay:-0.75s; + -o-animation-delay:-0.75s; + animation-delay:-0.75s; +} +.cg-busy-default-spinner div.bar5{ + -webkit-transform:rotate(120deg) translate(0, -142%); + -moz-transform:rotate(120deg) translate(0, -142%); + -ms-transform:rotate(120deg) translate(0, -142%); + -o-transform:rotate(120deg) translate(0, -142%); + transform:rotate(120deg) translate(0, -142%); + -webkit-animation-delay:-0.667s; + -moz-animation-delay:-0.667s; + -ms-animation-delay:-0.667s; + -o-animation-delay:-0.667s; + animation-delay:-0.667s; +} +.cg-busy-default-spinner div.bar6{ + -webkit-transform:rotate(150deg) translate(0, -142%); + -moz-transform:rotate(150deg) translate(0, -142%); + -ms-transform:rotate(150deg) translate(0, -142%); + -o-transform:rotate(150deg) translate(0, -142%); + transform:rotate(150deg) translate(0, -142%); + -webkit-animation-delay:-0.5833s; + -moz-animation-delay:-0.5833s; + -ms-animation-delay:-0.5833s; + -o-animation-delay:-0.5833s; + animation-delay:-0.5833s; +} +.cg-busy-default-spinner div.bar7{ + -webkit-transform:rotate(180deg) translate(0, -142%); + -moz-transform:rotate(180deg) translate(0, -142%); + -ms-transform:rotate(180deg) translate(0, -142%); + -o-transform:rotate(180deg) translate(0, -142%); + transform:rotate(180deg) translate(0, -142%); + -webkit-animation-delay:-0.5s; + -moz-animation-delay:-0.5s; + -ms-animation-delay:-0.5s; + -o-animation-delay:-0.5s; + animation-delay:-0.5s; +} +.cg-busy-default-spinner div.bar8{ + -webkit-transform:rotate(210deg) translate(0, -142%); + -moz-transform:rotate(210deg) translate(0, -142%); + -ms-transform:rotate(210deg) translate(0, -142%); + -o-transform:rotate(210deg) translate(0, -142%); + transform:rotate(210deg) translate(0, -142%); + -webkit-animation-delay:-0.41667s; + -moz-animation-delay:-0.41667s; + -ms-animation-delay:-0.41667s; + -o-animation-delay:-0.41667s; + animation-delay:-0.41667s; +} +.cg-busy-default-spinner div.bar9{ + -webkit-transform:rotate(240deg) translate(0, -142%); + -moz-transform:rotate(240deg) translate(0, -142%); + -ms-transform:rotate(240deg) translate(0, -142%); + -o-transform:rotate(240deg) translate(0, -142%); + transform:rotate(240deg) translate(0, -142%); + -webkit-animation-delay:-0.333s; + -moz-animation-delay:-0.333s; + -ms-animation-delay:-0.333s; + -o-animation-delay:-0.333s; + animation-delay:-0.333s; +} +.cg-busy-default-spinner div.bar10{ + -webkit-transform:rotate(270deg) translate(0, -142%); + -moz-transform:rotate(270deg) translate(0, -142%); + -ms-transform:rotate(270deg) translate(0, -142%); + -o-transform:rotate(270deg) translate(0, -142%); + transform:rotate(270deg) translate(0, -142%); + -webkit-animation-delay:-0.25s; + -moz-animation-delay:-0.25s; + -ms-animation-delay:-0.25s; + -o-animation-delay:-0.25s; + animation-delay:-0.25s; +} +.cg-busy-default-spinner div.bar11{ + -webkit-transform:rotate(300deg) translate(0, -142%); + -moz-transform:rotate(300deg) translate(0, -142%); + -ms-transform:rotate(300deg) translate(0, -142%); + -o-transform:rotate(300deg) translate(0, -142%); + transform:rotate(300deg) translate(0, -142%); + -webkit-animation-delay:-0.1667s; + -moz-animation-delay:-0.1667s; + -ms-animation-delay:-0.1667s; + -o-animation-delay:-0.1667s; + animation-delay:-0.1667s; +} +.cg-busy-default-spinner div.bar12{ + -webkit-transform:rotate(330deg) translate(0, -142%); + -moz-transform:rotate(330deg) translate(0, -142%); + -ms-transform:rotate(330deg) translate(0, -142%); + -o-transform:rotate(330deg) translate(0, -142%); + transform:rotate(330deg) translate(0, -142%); + -webkit-animation-delay:-0.0833s; + -moz-animation-delay:-0.0833s; + -ms-animation-delay:-0.0833s; + -o-animation-delay:-0.0833s; + animation-delay:-0.0833s; +} + +@-webkit-keyframes cg-busy-spinner-anim{ + from {opacity: 1;} + to {opacity: 0.25;} +} +@-moz-keyframes cg-busy-spinner-anim{ + from {opacity: 1;} + to {opacity: 0.25;} +} +@keyframes cg-busy-spinner-anim{ + from {opacity: 1;} + to {opacity: 0.25;} +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.html b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.html new file mode 100755 index 000000000..f3f0756ef --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.html @@ -0,0 +1,24 @@ +<div class="cg-busy-default-wrapper"> + + <div class="cg-busy-default-sign"> + + <div class="cg-busy-default-spinner"> + <div class="bar1"></div> + <div class="bar2"></div> + <div class="bar3"></div> + <div class="bar4"></div> + <div class="bar5"></div> + <div class="bar6"></div> + <div class="bar7"></div> + <div class="bar8"></div> + <div class="bar9"></div> + <div class="bar10"></div> + <div class="bar11"></div> + <div class="bar12"></div> + </div> + + <div class="cg-busy-default-text">{{$message}}</div> + + </div> + +</div>
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.js new file mode 100755 index 000000000..557435c6e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/angular-busy.js @@ -0,0 +1,244 @@ +angular.module('cgBusy',[]); + +//loosely modeled after angular-promise-tracker +angular.module('cgBusy').factory('_cgBusyTrackerFactory',['$timeout','$q',function($timeout,$q){ + + return function(){ + + var tracker = {}; + tracker.promises = []; + tracker.delayPromise = null; + tracker.durationPromise = null; + tracker.delayJustFinished = false; + + tracker.reset = function(options){ + tracker.minDuration = options.minDuration; + + tracker.promises = []; + angular.forEach(options.promises,function(p){ + if (!p || p.$cgBusyFulfilled) { + return; + } + addPromiseLikeThing(p); + }); + + if (tracker.promises.length === 0) { + //if we have no promises then dont do the delay or duration stuff + return; + } + + tracker.delayJustFinished = false; + if (options.delay) { + tracker.delayPromise = $timeout(function(){ + tracker.delayPromise = null; + tracker.delayJustFinished = true; + },parseInt(options.delay,10)); + } + if (options.minDuration) { + tracker.durationPromise = $timeout(function(){ + tracker.durationPromise = null; + },parseInt(options.minDuration,10) + (options.delay ? parseInt(options.delay,10) : 0)); + } + }; + + tracker.isPromise = function(promiseThing){ + var then = promiseThing && (promiseThing.then || promiseThing.$then || + (promiseThing.$promise && promiseThing.$promise.then)); + + return typeof then !== 'undefined'; + }; + + tracker.callThen = function(promiseThing,success,error){ + var promise; + if (promiseThing.then || promiseThing.$then){ + promise = promiseThing; + } else if (promiseThing.$promise){ + promise = promiseThing.$promise; + } else if (promiseThing.denodeify){ + promise = $q.when(promiseThing); + } + + var then = (promise.then || promise.$then); + + then.call(promise,success,error); + }; + + var addPromiseLikeThing = function(promise){ + + if (!tracker.isPromise(promise)) { + throw new Error('cgBusy expects a promise (or something that has a .promise or .$promise'); + } + + if (tracker.promises.indexOf(promise) !== -1){ + return; + } + tracker.promises.push(promise); + + tracker.callThen(promise, function(){ + promise.$cgBusyFulfilled = true; + if (tracker.promises.indexOf(promise) === -1) { + return; + } + tracker.promises.splice(tracker.promises.indexOf(promise),1); + },function(){ + promise.$cgBusyFulfilled = true; + if (tracker.promises.indexOf(promise) === -1) { + return; + } + tracker.promises.splice(tracker.promises.indexOf(promise),1); + }); + }; + + tracker.active = function(){ + if (tracker.delayPromise){ + return false; + } + + if (!tracker.delayJustFinished){ + if (tracker.durationPromise){ + return true; + } + return tracker.promises.length > 0; + } else { + //if both delay and min duration are set, + //we don't want to initiate the min duration if the + //promise finished before the delay was complete + tracker.delayJustFinished = false; + if (tracker.promises.length === 0) { + tracker.durationPromise = null; + } + return tracker.promises.length > 0; + } + }; + + return tracker; + + }; +}]); + +angular.module('cgBusy').value('cgBusyDefaults',{}); + +angular.module('cgBusy').directive('cgBusy',['$compile','$templateCache','cgBusyDefaults','$http','_cgBusyTrackerFactory', + function($compile,$templateCache,cgBusyDefaults,$http,_cgBusyTrackerFactory){ + return { + restrict: 'A', + link: function(scope, element, attrs, fn) { + + //Apply position:relative to parent element if necessary + var position = element.css('position'); + if (position === 'static' || position === '' || typeof position === 'undefined'){ + element.css('position','relative'); + } + + var templateElement; + var backdropElement; + var currentTemplate; + var templateScope; + var backdrop; + var tracker = _cgBusyTrackerFactory(); + + var defaults = { + templateUrl: 'angular-busy.html', + delay:0, + minDuration:0, + backdrop: true, + message:'Please Wait...', + wrapperClass: 'cg-busy cg-busy-animation' + }; + + angular.extend(defaults,cgBusyDefaults); + + scope.$watchCollection(attrs.cgBusy,function(options){ + + if (!options) { + options = {promise:null}; + } + + if (angular.isString(options)) { + throw new Error('Invalid value for cg-busy. cgBusy no longer accepts string ids to represent promises/trackers.'); + } + + //is it an array (of promises) or one promise + if (angular.isArray(options) || tracker.isPromise(options)) { + options = {promise:options}; + } + + options = angular.extend(angular.copy(defaults),options); + + if (!options.templateUrl){ + options.templateUrl = defaults.templateUrl; + } + + if (!angular.isArray(options.promise)){ + options.promise = [options.promise]; + } + + // options.promise = angular.isArray(options.promise) ? options.promise : [options.promise]; + // options.message = options.message ? options.message : 'Please Wait...'; + // options.template = options.template ? options.template : cgBusyTemplateName; + // options.minDuration = options.minDuration ? options.minDuration : 0; + // options.delay = options.delay ? options.delay : 0; + + if (!templateScope) { + templateScope = scope.$new(); + } + + templateScope.$message = options.message; + + if (!angular.equals(tracker.promises,options.promise)) { + tracker.reset({ + promises:options.promise, + delay:options.delay, + minDuration: options.minDuration + }); + } + + templateScope.$cgBusyIsActive = function() { + return tracker.active(); + }; + + + if (!templateElement || currentTemplate !== options.templateUrl || backdrop !== options.backdrop) { + + if (templateElement) { + templateElement.remove(); + } + if (backdropElement){ + backdropElement.remove(); + } + + currentTemplate = options.templateUrl; + backdrop = options.backdrop; + + $http.get(currentTemplate,{cache: $templateCache}).success(function(indicatorTemplate){ + + options.backdrop = typeof options.backdrop === 'undefined' ? true : options.backdrop; + + if (options.backdrop){ + var backdrop = '<div class="cg-busy cg-busy-backdrop cg-busy-backdrop-animation ng-hide" ng-show="$cgBusyIsActive()"></div>'; + backdropElement = $compile(backdrop)(templateScope); + element.append(backdropElement); + } + + var template = '<div class="'+options.wrapperClass+' ng-hide" ng-show="$cgBusyIsActive()">' + indicatorTemplate + '</div>'; + templateElement = $compile(template)(templateScope); + + angular.element(templateElement.children()[0]) + .css('position','absolute') + .css('top',0) + .css('left',0) + .css('right',0) + .css('bottom',0); + element.append(templateElement); + + }).error(function(data){ + throw new Error('Template specified for cgBusy ('+options.templateUrl+') could not be loaded. ' + data); + }); + } + + },true); + } + }; + } +]); + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/bower.json new file mode 100755 index 000000000..e670a4871 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-busy/bower.json @@ -0,0 +1,32 @@ +{ + "name": "angular-busy", + "version": "4.1.3", + "main": [ + "dist/angular-busy.js", + "dist/angular-busy.css" + ], + "dependencies": { + "angular": "~1.3", + "angular-animate": "~1.3" + }, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "components", + "test", + "temp", + "demo", + "lib", + "./angular-busy.css", + "./angular-busy.html", + "./angular-busy.js", + "Gruntfile.js", + "package.json", + "README.md" + ], + "devDependencies": { + "angular-mocks": "~1.3", + "jquery": "~2.1.0" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.bower.json new file mode 100644 index 000000000..37422d3cf --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.bower.json @@ -0,0 +1,20 @@ +{ + "name": "angular-confirm-modal", + "main": "angular-confirm.min.js", + "version": "1.2.3", + "homepage": "https://github.com/Schlogen/angular-confirm", + "authors": [ + "James Kleeh" + ], + "description": "Confirmation modal dialog for AngularJS", + "license": "Apache-2.0", + "_release": "1.2.3", + "_resolution": { + "type": "version", + "tag": "1.2.3", + "commit": "d458be429fb7695059057f29745701cc86bc1cee" + }, + "_source": "https://github.com/Schlogen/angular-confirm.git", + "_target": "1.2.3", + "_originalSource": "angular-confirm-modal" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.gitignore b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.gitignore new file mode 100644 index 000000000..a74e01142 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.gitignore @@ -0,0 +1,4 @@ +/.idea +/node_modules +/npm-debug.log +/angular-confirm.min.js diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.travis.yml b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.travis.yml new file mode 100644 index 000000000..3674c3c20 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/.travis.yml @@ -0,0 +1,11 @@ +language: node_js +node_js: + - "0.12" + - "0.11" + - "0.10" +before_script: + - npm install -g gulp@3.9.0 +before_install: + - export CHROME_BIN=chromium-browser + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/LICENSE b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/LICENSE new file mode 100644 index 000000000..50e805053 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2015 James Kleeh + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/README.md new file mode 100644 index 000000000..7b8d37aba --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/README.md @@ -0,0 +1,15 @@ +angular-confirm +=============== +[![Build Status](https://travis-ci.org/Schlogen/angular-confirm.svg?branch=master)](https://travis-ci.org/Schlogen/angular-confirm) + +Confirm modal dialog for AngularJS + +To use ui-bootstrap < 0.14.x you must keep on the 1.1.x release track for this module + +This module requires the Angular-UI $modal service http://angular-ui.github.io/bootstrap/#/modal. + +[NPM](https://www.npmjs.com/package/angular-confirm) + +To install with bower: `bower install angular-confirm-modal` + +[DEMO](http://schlogen.github.io/angular-confirm) diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/angular-confirm.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/angular-confirm.js new file mode 100644 index 000000000..ed08ca63f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/angular-confirm.js @@ -0,0 +1,108 @@ +/* + * angular-confirm + * https://github.com/Schlogen/angular-confirm + * @version v1.2.3 - 2016-01-26 + * @license Apache + */ +(function (root, factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define(['angular'], factory); + } else if (typeof module !== 'undefined' && typeof module.exports === 'object') { + module.exports = factory(require('angular')); + } else { + return factory(root.angular); + } +}(this, function (angular) { +angular.module('angular-confirm', ['ui.bootstrap.modal']) + .controller('ConfirmModalController', function ($scope, $uibModalInstance, data) { + $scope.data = angular.copy(data); + + $scope.ok = function (closeMessage) { + $uibModalInstance.close(closeMessage); + }; + + $scope.cancel = function (dismissMessage) { + if (angular.isUndefined(dismissMessage)) { + dismissMessage = 'cancel'; + } + $uibModalInstance.dismiss(dismissMessage); + }; + + }) + .value('$confirmModalDefaults', { + template: '<div class="modal-header"><h3 class="modal-title">{{data.title}}</h3></div>' + + '<div class="modal-body">{{data.text}}</div>' + + '<div class="modal-footer">' + + '<button class="btn btn-primary" ng-click="ok()">{{data.ok}}</button>' + + '<button class="btn btn-default" ng-click="cancel()">{{data.cancel}}</button>' + + '</div>', + controller: 'ConfirmModalController', + defaultLabels: { + title: 'Confirm', + ok: 'OK', + cancel: 'Cancel' + } + }) + .factory('$confirm', function ($uibModal, $confirmModalDefaults) { + return function (data, settings) { + var defaults = angular.copy($confirmModalDefaults); + settings = angular.extend(defaults, (settings || {})); + + data = angular.extend({}, settings.defaultLabels, data || {}); + + if ('templateUrl' in settings && 'template' in settings) { + delete settings.template; + } + + settings.resolve = { + data: function () { + return data; + } + }; + + return $uibModal.open(settings).result; + }; + }) + .directive('confirm', function ($confirm) { + return { + priority: 1, + restrict: 'A', + scope: { + confirmIf: "=", + ngClick: '&', + confirm: '@', + confirmSettings: "=", + confirmTitle: '@', + confirmOk: '@', + confirmCancel: '@' + }, + link: function (scope, element, attrs) { + + element.unbind("click").bind("click", function ($event) { + + $event.preventDefault(); + + if (angular.isUndefined(scope.confirmIf) || scope.confirmIf) { + + var data = {text: scope.confirm}; + if (scope.confirmTitle) { + data.title = scope.confirmTitle; + } + if (scope.confirmOk) { + data.ok = scope.confirmOk; + } + if (scope.confirmCancel) { + data.cancel = scope.confirmCancel; + } + $confirm(data, scope.confirmSettings || {}).then(scope.ngClick); + } else { + + scope.$apply(scope.ngClick); + } + }); + + } + } + }); +})); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/bower.json new file mode 100644 index 000000000..b2af2b9e4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/bower.json @@ -0,0 +1,11 @@ +{ + "name": "angular-confirm-modal", + "main": "angular-confirm.min.js", + "version": "1.2.3", + "homepage": "https://github.com/Schlogen/angular-confirm", + "authors": [ + "James Kleeh" + ], + "description": "Confirmation modal dialog for AngularJS", + "license": "Apache-2.0" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/gulpfile.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/gulpfile.js new file mode 100644 index 000000000..456712d4c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/gulpfile.js @@ -0,0 +1,29 @@ +var gulp = require('gulp'), + header = require('gulp-header'), + bump = require('gulp-bump'), + git = require('gulp-git'), + uglify = require('gulp-uglify'), + ngAnnotate = require('gulp-ng-annotate'), + rename = require("gulp-rename"), + karma = require('karma').server; + +gulp.task('test-unit', function(done) { + karma.start({ + configFile: __dirname + '/test/karma.conf.js', + singleRun: true + }, done); +}); + +gulp.task('dest', function() { + gulp.src('./angular-confirm.js') + .pipe(ngAnnotate()) + .pipe(uglify({preserveComments: 'all'})) + .pipe(rename("angular-confirm.min.js")) + .pipe(gulp.dest('./')) +}); + +gulp.task('bump-json', function (done) { + return gulp.src(['./package.json', './bower.json']) + .pipe(bump()) + .pipe(gulp.dest('./')); +});
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/package.json new file mode 100644 index 000000000..0e68aeaea --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/package.json @@ -0,0 +1,41 @@ +{ + "name": "angular-confirm", + "version": "1.2.3", + "description": "Angular Confirm Modal", + "main": "angular-confirm.min.js", + "scripts": { + "test": "gulp test-unit" + }, + "repository": { + "type": "git", + "url": "https://github.com/Schlogen/angular-confirm.git" + }, + "keywords": [ + "angular", + "confirm", + "angular-ui", + "$modal" + ], + "author": "James Kleeh", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/Schlogen/angular-confirm/issues" + }, + "homepage": "https://github.com/Schlogen/angular-confirm", + "devDependencies": { + "angular": "1.4.7", + "angular-ui-bootstrap": "0.14.3", + "angular-mocks": "^1.4.7", + "gulp": "3.9.0", + "gulp-bump": "^1.0.0", + "gulp-git": "^1.6.0", + "gulp-header": "^1.7.1", + "gulp-ng-annotate": "^1.1.0", + "gulp-rename": "^1.2.2", + "gulp-uglify": "^1.4.2", + "jasmine-core": "^2.3.4", + "karma": "0.13.19", + "karma-chrome-launcher": "^0.2.2", + "karma-jasmine": "^0.3.6" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/test/karma.conf.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/test/karma.conf.js new file mode 100644 index 000000000..886ed61d1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/test/karma.conf.js @@ -0,0 +1,81 @@ +// Karma configuration +// Generated on Tue Jul 14 2015 10:10:58 GMT-0400 (Eastern Daylight Time) + +module.exports = function(config) { + var configuration = { + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + '../node_modules/angular/angular.js', + '../node_modules/angular-ui-bootstrap/ui-bootstrap-tpls.min.js', + '../node_modules/angular-mocks/angular-mocks.js', + '../angular-confirm.js', + 'unit/**/*Spec.js' + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + 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 + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['Chrome'], + + customLaunchers: { + Chrome_travis_ci: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }; + + if(process.env.TRAVIS){ + configuration.browsers = ['Chrome_travis_ci']; + } + + config.set(configuration); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/test/unit/confirmSpec.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/test/unit/confirmSpec.js new file mode 100644 index 000000000..8434de407 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-confirm-modal/test/unit/confirmSpec.js @@ -0,0 +1,244 @@ +describe('angular-confirm', function() { + + var $rootScope, $uibModal; + + beforeEach(angular.mock.module('angular-confirm', function ($provide) { + + $provide.decorator('$uibModal', function($delegate) { + $uibModal = { + open: jasmine.createSpy('$uibModal.open', function(settings) { + return {result: settings}; + }) + }; + return $uibModal; + }); + + $provide.decorator('$confirm', function($delegate) { + return jasmine.createSpy('$confirm', $delegate); + }); + + })); + + beforeEach(angular.mock.inject(function (_$rootScope_) { + $rootScope = _$rootScope_; + })); + + describe('ConfirmModalController', function() { + var $scope, controller, data = {testVal: 1}, $uibModalInstance; + + beforeEach(angular.mock.inject(function($controller) { + $scope = $rootScope.$new(); + $uibModalInstance = { + close: jasmine.createSpy('$uibModalInstance.close'), + dismiss: jasmine.createSpy('$uibModalInstance.dismiss'), + result: { + then: jasmine.createSpy('$uibModalInstance.result.then') + } + }; + controller = $controller('ConfirmModalController', {"$scope": $scope, "$uibModalInstance": $uibModalInstance, "data": data}); + })); + + it("should copy the data, not use it as a reference", function() { + data.testVal = 2; + expect($scope.data.testVal).toEqual(1); + }); + + it("should call close when $scope.ok is invoked", function() { + $scope.ok(); + expect($uibModalInstance.close).toHaveBeenCalled(); + }); + + it("should call dismiss when $scope.cancel is invoked", function() { + $scope.cancel(); + expect($uibModalInstance.dismiss).toHaveBeenCalledWith('cancel'); + }); + + }); + + describe('$confirm factory', function() { + + var $confirm, $confirmModalDefaults; + + beforeEach(angular.mock.inject(function(_$confirm_, _$confirmModalDefaults_) { + $confirm = _$confirm_; + $confirm.and.callThrough(); + $confirmModalDefaults = _$confirmModalDefaults_; + $uibModal.open.and.callThrough(); + })); + + it("should call $uibModal.open", function() { + $confirm(); + expect($uibModal.open).toHaveBeenCalled(); + }); + + it("should override the defaults with settings passed in", function() { + var settings = $confirm({}, {"template": "hello"}); + expect(settings.template).toEqual("hello"); + }); + + it("should not change the defaults", function() { + var settings = $confirm({}, {"templateUrl": "hello"}); + expect(settings.templateUrl).toEqual("hello"); + expect(settings.template).not.toBeDefined(); + expect($confirmModalDefaults.template).toBeDefined(); + expect($confirmModalDefaults.templateUrl).not.toBeDefined(); + }); + + it("should override the default labels with the data passed in", function() { + var settings = $confirm({title: "Title"}); + var data = settings.resolve.data(); + expect(data.title).toEqual("Title"); + expect(data.ok).toEqual('OK'); + }); + + it("should remove template if templateUrl is passed in", function() { + var settings = $confirm({}, {templateUrl: "abc.txt"}); + expect(settings.template).not.toBeDefined(); + }); + + }); + + describe('confirm directive', function() { + var $scope, element, $confirm, data; + + beforeEach(angular.mock.inject(function (_$confirm_, $compile) { + $confirm = _$confirm_; + + $confirm.and.callFake(function(d) { + data = d; + return {then: function() {}} + }); + + $scope = $rootScope.$new(); + $scope.click = jasmine.createSpy('$scope.click'); + })); + + describe('resolve properties in title', function() { + beforeEach(angular.mock.inject(function($compile) { + $scope.name = 'Joe'; + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure, {{name}}?">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should resolve the name to the text property", function() { + element.triggerHandler('click'); + expect(data.text).toEqual('Are you sure, Joe?'); + }); + }); + + describe('without confirmIf', function() { + + beforeEach(angular.mock.inject(function($compile) { + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should call confirm on click and not call the function", function() { + element.triggerHandler('click'); + expect($scope.click).not.toHaveBeenCalled(); + expect($confirm).toHaveBeenCalled(); + }); + + }); + + describe('with confirmIf option', function() { + + beforeEach(angular.mock.inject(function($compile) { + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?" confirm-if="truthy">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should call confirm on click and not call the function", function() { + $scope.truthy = true; + $scope.$apply(); + element.triggerHandler('click'); + expect($scope.click).not.toHaveBeenCalled(); + expect($confirm).toHaveBeenCalled(); + }); + + it("should call the function", function() { + $scope.truthy = false; + $scope.$apply(); + element.triggerHandler('click'); + expect($scope.click).toHaveBeenCalled(); + expect($confirm).not.toHaveBeenCalled(); + }); + + }); + + describe('with confirmTitle option', function() { + beforeEach(angular.mock.inject(function($compile) { + $scope.name = 'Joe'; + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?" confirm-title="Hello, {{name}}!">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should resolve the confirmTitle to the title property", function() { + element.triggerHandler('click'); + expect(data.title).toEqual('Hello, Joe!'); + }); + + }); + + describe('with confirmOk option', function() { + beforeEach(angular.mock.inject(function($compile) { + $scope.name = 'Joe'; + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?" confirm-ok="Okie Dokie, {{name}}!">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should resolve the confirmTitle to the title property", function() { + element.triggerHandler('click'); + expect(data.ok).toEqual('Okie Dokie, Joe!'); + }); + }); + + describe('with confirmCancel option', function() { + beforeEach(angular.mock.inject(function($compile) { + $scope.name = 'Joe'; + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?" confirm-cancel="No Way, {{name}}!">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should resolve the confirmTitle to the title property", function() { + element.triggerHandler('click'); + expect(data.cancel).toEqual('No Way, Joe!'); + }); + }); + + describe('with confirmSettings option', function() { + beforeEach(angular.mock.inject(function($compile) { + $scope.settings = {name: 'Joe'}; + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?" confirm-settings="settings">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should pass the settings to $confirm", function() { + element.triggerHandler('click'); + expect($confirm).toHaveBeenCalledWith({text: "Are you sure?"}, $scope.settings) + }); + }); + + describe('with confirmSettings option direct entry', function() { + beforeEach(angular.mock.inject(function($compile) { + element = angular.element('<button type="button" ng-click="click()" confirm="Are you sure?" confirm-settings="{name: \'Joe\'}">Delete</button>'); + $compile(element)($scope); + $scope.$digest(); + })); + + it("should pass the settings to $confirm", function() { + element.triggerHandler('click'); + expect($confirm).toHaveBeenCalledWith({text: "Are you sure?"}, {name: "Joe"}) + }); + }); + + }); + +});
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/.bower.json new file mode 100644 index 000000000..3f2b2012d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/.bower.json @@ -0,0 +1,19 @@ +{ + "name": "angular-mocks", + "version": "1.3.15", + "main": "./angular-mocks.js", + "ignore": [], + "dependencies": { + "angular": "1.3.15" + }, + "homepage": "https://github.com/angular/bower-angular-mocks", + "_release": "1.3.15", + "_resolution": { + "type": "version", + "tag": "v1.3.15", + "commit": "1ffdfef850b10d40f2838c1bba41a95755c2d8da" + }, + "_source": "https://github.com/angular/bower-angular-mocks.git", + "_target": "1.3.15", + "_originalSource": "angular-mocks" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/README.md new file mode 100644 index 000000000..440cce9b7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/README.md @@ -0,0 +1,63 @@ +# 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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/angular-mocks.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/angular-mocks.js new file mode 100644 index 000000000..cbf36ffe2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/angular-mocks.js @@ -0,0 +1,2468 @@ +/** + * @license AngularJS v1.3.15 + * (c) 2010-2014 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) { + +'use strict'; + +/** + * @ngdoc object + * @name angular.mock + * @description + * + * Namespace from 'angular-mocks.js' which contains testing related code. + */ +angular.mock = {}; + +/** + * ! This is a private undocumented service ! + * + * @name $browser + * + * @description + * This service is a mock implementation of {@link ng.$browser}. It provides fake + * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr, + * cookies, etc... + * + * The api of this service is the same as that of the real {@link ng.$browser $browser}, except + * that there are several helper methods available which can be used in tests. + */ +angular.mock.$BrowserProvider = function() { + this.$get = function() { + return new angular.mock.$Browser(); + }; +}; + +angular.mock.$Browser = function() { + var self = this; + + this.isMock = true; + self.$$url = "http://server/"; + self.$$lastUrl = self.$$url; // used by url polling fn + self.pollFns = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = angular.noop; + self.$$incOutstandingRequestCount = angular.noop; + + + // register url polling fn + + self.onUrlChange = function(listener) { + self.pollFns.push( + function() { + if (self.$$lastUrl !== self.$$url || self.$$state !== self.$$lastState) { + self.$$lastUrl = self.$$url; + self.$$lastState = self.$$state; + listener(self.$$url, self.$$state); + } + } + ); + + return listener; + }; + + self.$$checkUrlChange = angular.noop; + + self.cookieHash = {}; + self.lastCookieHash = {}; + self.deferredFns = []; + self.deferredNextId = 0; + + self.defer = function(fn, delay) { + delay = delay || 0; + self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); + self.deferredFns.sort(function(a, b) { return a.time - b.time;}); + return self.deferredNextId++; + }; + + + /** + * @name $browser#defer.now + * + * @description + * Current milliseconds mock time. + */ + self.defer.now = 0; + + + self.defer.cancel = function(deferId) { + var fnIndex; + + angular.forEach(self.deferredFns, function(fn, index) { + if (fn.id === deferId) fnIndex = index; + }); + + if (fnIndex !== undefined) { + self.deferredFns.splice(fnIndex, 1); + return true; + } + + return false; + }; + + + /** + * @name $browser#defer.flush + * + * @description + * Flushes all pending requests and executes the defer callbacks. + * + * @param {number=} number of milliseconds to flush. See {@link #defer.now} + */ + self.defer.flush = function(delay) { + if (angular.isDefined(delay)) { + self.defer.now += delay; + } else { + if (self.deferredFns.length) { + self.defer.now = self.deferredFns[self.deferredFns.length - 1].time; + } else { + throw new Error('No deferred tasks to be flushed'); + } + } + + while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { + self.deferredFns.shift().fn(); + } + }; + + self.$$baseHref = '/'; + self.baseHref = function() { + return this.$$baseHref; + }; +}; +angular.mock.$Browser.prototype = { + +/** + * @name $browser#poll + * + * @description + * run all fns in pollFns + */ + poll: function poll() { + angular.forEach(this.pollFns, function(pollFn) { + pollFn(); + }); + }, + + addPollFn: function(pollFn) { + this.pollFns.push(pollFn); + return pollFn; + }, + + url: function(url, replace, state) { + if (angular.isUndefined(state)) { + state = null; + } + if (url) { + this.$$url = url; + // Native pushState serializes & copies the object; simulate it. + this.$$state = angular.copy(state); + return this; + } + + return this.$$url; + }, + + state: function() { + return this.$$state; + }, + + cookies: function(name, value) { + if (name) { + if (angular.isUndefined(value)) { + delete this.cookieHash[name]; + } else { + if (angular.isString(value) && //strings only + value.length <= 4096) { //strict cookie storage limits + this.cookieHash[name] = value; + } + } + } else { + if (!angular.equals(this.cookieHash, this.lastCookieHash)) { + this.lastCookieHash = angular.copy(this.cookieHash); + this.cookieHash = angular.copy(this.cookieHash); + } + return this.cookieHash; + } + }, + + notifyWhenNoOutstandingRequests: function(fn) { + fn(); + } +}; + + +/** + * @ngdoc provider + * @name $exceptionHandlerProvider + * + * @description + * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors + * passed to the `$exceptionHandler`. + */ + +/** + * @ngdoc service + * @name $exceptionHandler + * + * @description + * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed + * to it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration + * information. + * + * + * ```js + * describe('$exceptionHandlerProvider', function() { + * + * it('should capture log messages and exceptions', function() { + * + * module(function($exceptionHandlerProvider) { + * $exceptionHandlerProvider.mode('log'); + * }); + * + * inject(function($log, $exceptionHandler, $timeout) { + * $timeout(function() { $log.log(1); }); + * $timeout(function() { $log.log(2); throw 'banana peel'; }); + * $timeout(function() { $log.log(3); }); + * expect($exceptionHandler.errors).toEqual([]); + * expect($log.assertEmpty()); + * $timeout.flush(); + * expect($exceptionHandler.errors).toEqual(['banana peel']); + * expect($log.log.logs).toEqual([[1], [2], [3]]); + * }); + * }); + * }); + * ``` + */ + +angular.mock.$ExceptionHandlerProvider = function() { + var handler; + + /** + * @ngdoc method + * @name $exceptionHandlerProvider#mode + * + * @description + * Sets the logging mode. + * + * @param {string} mode Mode of operation, defaults to `rethrow`. + * + * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` + * mode stores an array of errors in `$exceptionHandler.errors`, to allow later + * assertion of them. See {@link ngMock.$log#assertEmpty assertEmpty()} and + * {@link ngMock.$log#reset reset()} + * - `rethrow`: If any errors are passed to the handler in tests, it typically means that there + * is a bug in the application or test, so this mock will make these tests fail. + * For any implementations that expect exceptions to be thrown, the `rethrow` mode + * will also maintain a log of thrown errors. + */ + this.mode = function(mode) { + + switch (mode) { + case 'log': + case 'rethrow': + var errors = []; + handler = function(e) { + if (arguments.length == 1) { + errors.push(e); + } else { + errors.push([].slice.call(arguments, 0)); + } + if (mode === "rethrow") { + throw e; + } + }; + handler.errors = errors; + break; + default: + throw new Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); + } + }; + + this.$get = function() { + return handler; + }; + + this.mode('rethrow'); +}; + + +/** + * @ngdoc service + * @name $log + * + * @description + * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays + * (one array per logging level). These arrays are exposed as `logs` property of each of the + * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. + * + */ +angular.mock.$LogProvider = function() { + var debug = true; + + function concat(array1, array2, index) { + return array1.concat(Array.prototype.slice.call(array2, index)); + } + + this.debugEnabled = function(flag) { + if (angular.isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; + + this.$get = function() { + var $log = { + log: function() { $log.log.logs.push(concat([], arguments, 0)); }, + warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, + info: function() { $log.info.logs.push(concat([], arguments, 0)); }, + error: function() { $log.error.logs.push(concat([], arguments, 0)); }, + debug: function() { + if (debug) { + $log.debug.logs.push(concat([], arguments, 0)); + } + } + }; + + /** + * @ngdoc method + * @name $log#reset + * + * @description + * Reset all of the logging arrays to empty. + */ + $log.reset = function() { + /** + * @ngdoc property + * @name $log#log.logs + * + * @description + * Array of messages logged using {@link ng.$log#log `log()`}. + * + * @example + * ```js + * $log.log('Some Log'); + * var first = $log.log.logs.unshift(); + * ``` + */ + $log.log.logs = []; + /** + * @ngdoc property + * @name $log#info.logs + * + * @description + * Array of messages logged using {@link ng.$log#info `info()`}. + * + * @example + * ```js + * $log.info('Some Info'); + * var first = $log.info.logs.unshift(); + * ``` + */ + $log.info.logs = []; + /** + * @ngdoc property + * @name $log#warn.logs + * + * @description + * Array of messages logged using {@link ng.$log#warn `warn()`}. + * + * @example + * ```js + * $log.warn('Some Warning'); + * var first = $log.warn.logs.unshift(); + * ``` + */ + $log.warn.logs = []; + /** + * @ngdoc property + * @name $log#error.logs + * + * @description + * Array of messages logged using {@link ng.$log#error `error()`}. + * + * @example + * ```js + * $log.error('Some Error'); + * var first = $log.error.logs.unshift(); + * ``` + */ + $log.error.logs = []; + /** + * @ngdoc property + * @name $log#debug.logs + * + * @description + * Array of messages logged using {@link ng.$log#debug `debug()`}. + * + * @example + * ```js + * $log.debug('Some Error'); + * var first = $log.debug.logs.unshift(); + * ``` + */ + $log.debug.logs = []; + }; + + /** + * @ngdoc method + * @name $log#assertEmpty + * + * @description + * Assert that all of the logging methods have no logged messages. If any messages are present, + * an exception is thrown. + */ + $log.assertEmpty = function() { + var errors = []; + angular.forEach(['error', 'warn', 'info', 'log', 'debug'], function(logLevel) { + angular.forEach($log[logLevel].logs, function(log) { + angular.forEach(log, function(logItem) { + errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + + (logItem.stack || '')); + }); + }); + }); + if (errors.length) { + errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or " + + "an expected log message was not checked and removed:"); + errors.push(''); + throw new Error(errors.join('\n---------\n')); + } + }; + + $log.reset(); + return $log; + }; +}; + + +/** + * @ngdoc service + * @name $interval + * + * @description + * Mock implementation of the $interval service. + * + * Use {@link ngMock.$interval#flush `$interval.flush(millis)`} to + * move forward by `millis` milliseconds and trigger any functions scheduled to run in that + * time. + * + * @param {function()} fn A function that should be called repeatedly. + * @param {number} delay Number of milliseconds between each function call. + * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat + * indefinitely. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {promise} A promise which will be notified on each iteration. + */ +angular.mock.$IntervalProvider = function() { + this.$get = ['$browser', '$rootScope', '$q', '$$q', + function($browser, $rootScope, $q, $$q) { + var repeatFns = [], + nextRepeatId = 0, + now = 0; + + var $interval = function(fn, delay, count, invokeApply) { + var iteration = 0, + skipApply = (angular.isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise; + + count = (angular.isDefined(count)) ? count : 0; + promise.then(null, null, fn); + + promise.$$intervalId = nextRepeatId; + + function tick() { + deferred.notify(iteration++); + + if (count > 0 && iteration >= count) { + var fnIndex; + deferred.resolve(iteration); + + angular.forEach(repeatFns, function(fn, index) { + if (fn.id === promise.$$intervalId) fnIndex = index; + }); + + if (fnIndex !== undefined) { + repeatFns.splice(fnIndex, 1); + } + } + + if (skipApply) { + $browser.defer.flush(); + } else { + $rootScope.$apply(); + } + } + + repeatFns.push({ + nextTime:(now + delay), + delay: delay, + fn: tick, + id: nextRepeatId, + deferred: deferred + }); + repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;}); + + nextRepeatId++; + return promise; + }; + /** + * @ngdoc method + * @name $interval#cancel + * + * @description + * Cancels a task associated with the `promise`. + * + * @param {promise} promise A promise from calling the `$interval` function. + * @returns {boolean} Returns `true` if the task was successfully cancelled. + */ + $interval.cancel = function(promise) { + if (!promise) return false; + var fnIndex; + + angular.forEach(repeatFns, function(fn, index) { + if (fn.id === promise.$$intervalId) fnIndex = index; + }); + + if (fnIndex !== undefined) { + repeatFns[fnIndex].deferred.reject('canceled'); + repeatFns.splice(fnIndex, 1); + return true; + } + + return false; + }; + + /** + * @ngdoc method + * @name $interval#flush + * @description + * + * Runs interval tasks scheduled to be run in the next `millis` milliseconds. + * + * @param {number=} millis maximum timeout amount to flush up until. + * + * @return {number} The amount of time moved forward. + */ + $interval.flush = function(millis) { + now += millis; + while (repeatFns.length && repeatFns[0].nextTime <= now) { + var task = repeatFns[0]; + task.fn(); + task.nextTime += task.delay; + repeatFns.sort(function(a, b) { return a.nextTime - b.nextTime;}); + } + return millis; + }; + + return $interval; + }]; +}; + + +/* jshint -W101 */ +/* The R_ISO8061_STR regex is never going to fit into the 100 char limit! + * This directive should go inside the anonymous function but a bug in JSHint means that it would + * not be enacted early enough to prevent the warning. + */ +var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; + +function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8061_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0; + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); + date.setUTCHours(int(match[4] || 0) - tzHour, + int(match[5] || 0) - tzMin, + int(match[6] || 0), + int(match[7] || 0)); + return date; + } + return string; +} + +function int(str) { + return parseInt(str, 10); +} + +function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; + } + num = '' + num; + while (num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; +} + + +/** + * @ngdoc type + * @name angular.mock.TzDate + * @description + * + * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. + * + * Mock of the Date type which has its timezone specified via constructor arg. + * + * The main purpose is to create Date-like instances with timezone fixed to the specified timezone + * offset, so that we can test code that depends on local timezone settings without dependency on + * the time zone settings of the machine where the code is running. + * + * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) + * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* + * + * @example + * !!!! WARNING !!!!! + * This is not a complete Date object so only methods that were implemented can be called safely. + * To make matters worse, TzDate instances inherit stuff from Date via a prototype. + * + * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is + * incomplete we might be missing some non-standard methods. This can result in errors like: + * "Date.prototype.foo called on incompatible Object". + * + * ```js + * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z'); + * newYearInBratislava.getTimezoneOffset() => -60; + * newYearInBratislava.getFullYear() => 2010; + * newYearInBratislava.getMonth() => 0; + * newYearInBratislava.getDate() => 1; + * newYearInBratislava.getHours() => 0; + * newYearInBratislava.getMinutes() => 0; + * newYearInBratislava.getSeconds() => 0; + * ``` + * + */ +angular.mock.TzDate = function(offset, timestamp) { + var self = new Date(0); + if (angular.isString(timestamp)) { + var tsStr = timestamp; + + self.origDate = jsonStringToDate(timestamp); + + timestamp = self.origDate.getTime(); + if (isNaN(timestamp)) + throw { + name: "Illegal Argument", + message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" + }; + } else { + self.origDate = new Date(timestamp); + } + + var localOffset = new Date(timestamp).getTimezoneOffset(); + self.offsetDiff = localOffset * 60 * 1000 - offset * 1000 * 60 * 60; + self.date = new Date(timestamp + self.offsetDiff); + + self.getTime = function() { + return self.date.getTime() - self.offsetDiff; + }; + + self.toLocaleDateString = function() { + return self.date.toLocaleDateString(); + }; + + self.getFullYear = function() { + return self.date.getFullYear(); + }; + + self.getMonth = function() { + return self.date.getMonth(); + }; + + self.getDate = function() { + return self.date.getDate(); + }; + + self.getHours = function() { + return self.date.getHours(); + }; + + self.getMinutes = function() { + return self.date.getMinutes(); + }; + + self.getSeconds = function() { + return self.date.getSeconds(); + }; + + self.getMilliseconds = function() { + return self.date.getMilliseconds(); + }; + + self.getTimezoneOffset = function() { + return offset * 60; + }; + + self.getUTCFullYear = function() { + return self.origDate.getUTCFullYear(); + }; + + self.getUTCMonth = function() { + return self.origDate.getUTCMonth(); + }; + + self.getUTCDate = function() { + return self.origDate.getUTCDate(); + }; + + self.getUTCHours = function() { + return self.origDate.getUTCHours(); + }; + + self.getUTCMinutes = function() { + return self.origDate.getUTCMinutes(); + }; + + self.getUTCSeconds = function() { + return self.origDate.getUTCSeconds(); + }; + + self.getUTCMilliseconds = function() { + return self.origDate.getUTCMilliseconds(); + }; + + self.getDay = function() { + return self.date.getDay(); + }; + + // provide this method only on browsers that already have it + if (self.toISOString) { + self.toISOString = function() { + return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + + padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + + padNumber(self.origDate.getUTCDate(), 2) + 'T' + + padNumber(self.origDate.getUTCHours(), 2) + ':' + + padNumber(self.origDate.getUTCMinutes(), 2) + ':' + + padNumber(self.origDate.getUTCSeconds(), 2) + '.' + + padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z'; + }; + } + + //hide all methods not implemented in this mock that the Date prototype exposes + var unimplementedMethods = ['getUTCDay', + 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', + 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', + 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', + 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', + 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; + + angular.forEach(unimplementedMethods, function(methodName) { + self[methodName] = function() { + throw new Error("Method '" + methodName + "' is not implemented in the TzDate mock"); + }; + }); + + return self; +}; + +//make "tzDateInstance instanceof Date" return true +angular.mock.TzDate.prototype = Date.prototype; +/* jshint +W101 */ + +angular.mock.animate = angular.module('ngAnimateMock', ['ng']) + + .config(['$provide', function($provide) { + + var reflowQueue = []; + $provide.value('$$animateReflow', function(fn) { + var index = reflowQueue.length; + reflowQueue.push(fn); + return function cancel() { + reflowQueue.splice(index, 1); + }; + }); + + $provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser', + function($delegate, $$asyncCallback, $timeout, $browser) { + var animate = { + queue: [], + cancel: $delegate.cancel, + enabled: $delegate.enabled, + triggerCallbackEvents: function() { + $$asyncCallback.flush(); + }, + triggerCallbackPromise: function() { + $timeout.flush(0); + }, + triggerCallbacks: function() { + this.triggerCallbackEvents(); + this.triggerCallbackPromise(); + }, + triggerReflow: function() { + angular.forEach(reflowQueue, function(fn) { + fn(); + }); + reflowQueue = []; + } + }; + + angular.forEach( + ['animate','enter','leave','move','addClass','removeClass','setClass'], function(method) { + animate[method] = function() { + animate.queue.push({ + event: method, + element: arguments[0], + options: arguments[arguments.length - 1], + args: arguments + }); + return $delegate[method].apply($delegate, arguments); + }; + }); + + return animate; + }]); + + }]); + + +/** + * @ngdoc function + * @name angular.mock.dump + * @description + * + * *NOTE*: this is not an injectable instance, just a globally available function. + * + * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for + * debugging. + * + * This method is also available on window, where it can be used to display objects on debug + * console. + * + * @param {*} object - any object to turn into string. + * @return {string} a serialized string of the argument + */ +angular.mock.dump = function(object) { + return serialize(object); + + function serialize(object) { + var out; + + if (angular.isElement(object)) { + object = angular.element(object); + out = angular.element('<div></div>'); + angular.forEach(object, function(element) { + out.append(angular.element(element).clone()); + }); + out = out.html(); + } else if (angular.isArray(object)) { + out = []; + angular.forEach(object, function(o) { + out.push(serialize(o)); + }); + out = '[ ' + out.join(', ') + ' ]'; + } else if (angular.isObject(object)) { + if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { + out = serializeScope(object); + } else if (object instanceof Error) { + out = object.stack || ('' + object.name + ': ' + object.message); + } else { + // TODO(i): this prevents methods being logged, + // we should have a better way to serialize objects + out = angular.toJson(object, true); + } + } else { + out = String(object); + } + + return out; + } + + function serializeScope(scope, offset) { + offset = offset || ' '; + var log = [offset + 'Scope(' + scope.$id + '): {']; + for (var key in scope) { + if (Object.prototype.hasOwnProperty.call(scope, key) && !key.match(/^(\$|this)/)) { + log.push(' ' + key + ': ' + angular.toJson(scope[key])); + } + } + var child = scope.$$childHead; + while (child) { + log.push(serializeScope(child, offset + ' ')); + child = child.$$nextSibling; + } + log.push('}'); + return log.join('\n' + offset); + } +}; + +/** + * @ngdoc service + * @name $httpBackend + * @description + * Fake HTTP backend implementation suitable for unit testing applications that use the + * {@link ng.$http $http service}. + * + * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less + * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. + * + * During unit testing, we want our unit tests to run quickly and have no external dependencies so + * we don’t want to send [XHR](https://developer.mozilla.org/en/xmlhttprequest) or + * [JSONP](http://en.wikipedia.org/wiki/JSONP) requests to a real server. All we really need is + * to verify whether a certain request has been sent or not, or alternatively just let the + * application make requests, respond with pre-trained responses and assert that the end result is + * what we expect it to be. + * + * This mock implementation can be used to respond with static or dynamic responses via the + * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc). + * + * When an Angular application needs some data from a server, it calls the $http service, which + * sends the request to a real server using $httpBackend service. With dependency injection, it is + * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify + * the requests and respond with some testing data without sending a request to a real server. + * + * There are two ways to specify what test data should be returned as http responses by the mock + * backend when the code under test makes http requests: + * + * - `$httpBackend.expect` - specifies a request expectation + * - `$httpBackend.when` - specifies a backend definition + * + * + * # Request Expectations vs Backend Definitions + * + * Request expectations provide a way to make assertions about requests made by the application and + * to define responses for those requests. The test will fail if the expected requests are not made + * or they are made in the wrong order. + * + * Backend definitions allow you to define a fake backend for your application which doesn't assert + * if a particular request was made or not, it just returns a trained response if a request is made. + * The test will pass whether or not the request gets made during testing. + * + * + * <table class="table"> + * <tr><th width="220px"></th><th>Request expectations</th><th>Backend definitions</th></tr> + * <tr> + * <th>Syntax</th> + * <td>.expect(...).respond(...)</td> + * <td>.when(...).respond(...)</td> + * </tr> + * <tr> + * <th>Typical usage</th> + * <td>strict unit tests</td> + * <td>loose (black-box) unit testing</td> + * </tr> + * <tr> + * <th>Fulfills multiple requests</th> + * <td>NO</td> + * <td>YES</td> + * </tr> + * <tr> + * <th>Order of requests matters</th> + * <td>YES</td> + * <td>NO</td> + * </tr> + * <tr> + * <th>Request required</th> + * <td>YES</td> + * <td>NO</td> + * </tr> + * <tr> + * <th>Response required</th> + * <td>optional (see below)</td> + * <td>YES</td> + * </tr> + * </table> + * + * In cases where both backend definitions and request expectations are specified during unit + * testing, the request expectations are evaluated first. + * + * If a request expectation has no response specified, the algorithm will search your backend + * definitions for an appropriate response. + * + * If a request didn't match any expectation or if the expectation doesn't have the response + * defined, the backend definitions are evaluated in sequential order to see if any of them match + * the request. The response from the first matched definition is returned. + * + * + * # Flushing HTTP requests + * + * The $httpBackend used in production always responds to requests asynchronously. If we preserved + * this behavior in unit testing, we'd have to create async unit tests, which are hard to write, + * to follow and to maintain. But neither can the testing mock respond synchronously; that would + * change the execution of the code under test. For this reason, the mock $httpBackend has a + * `flush()` method, which allows the test to explicitly flush pending requests. This preserves + * the async api of the backend, while allowing the test to execute synchronously. + * + * + * # Unit testing with mock $httpBackend + * The following code shows how to setup and use the mock backend when unit testing a controller. + * First we create the controller under test: + * + ```js + // The module code + angular + .module('MyApp', []) + .controller('MyController', MyController); + + // The controller code + function MyController($scope, $http) { + var authToken; + + $http.get('/auth.py').success(function(data, status, headers) { + authToken = headers('A-Token'); + $scope.user = data; + }); + + $scope.saveMessage = function(message) { + var headers = { 'Authorization': authToken }; + $scope.status = 'Saving...'; + + $http.post('/add-msg.py', message, { headers: headers } ).success(function(response) { + $scope.status = ''; + }).error(function() { + $scope.status = 'ERROR!'; + }); + }; + } + ``` + * + * Now we setup the mock backend and create the test specs: + * + ```js + // testing controller + describe('MyController', function() { + var $httpBackend, $rootScope, createController, authRequestHandler; + + // Set up the module + beforeEach(module('MyApp')); + + beforeEach(inject(function($injector) { + // Set up the mock http service responses + $httpBackend = $injector.get('$httpBackend'); + // backend definition common for all tests + authRequestHandler = $httpBackend.when('GET', '/auth.py') + .respond({userId: 'userX'}, {'A-Token': 'xxx'}); + + // Get hold of a scope (i.e. the root scope) + $rootScope = $injector.get('$rootScope'); + // The $controller service is used to create instances of controllers + var $controller = $injector.get('$controller'); + + createController = function() { + return $controller('MyController', {'$scope' : $rootScope }); + }; + })); + + + afterEach(function() { + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); + }); + + + it('should fetch authentication token', function() { + $httpBackend.expectGET('/auth.py'); + var controller = createController(); + $httpBackend.flush(); + }); + + + it('should fail authentication', function() { + + // Notice how you can change the response even after it was set + authRequestHandler.respond(401, ''); + + $httpBackend.expectGET('/auth.py'); + var controller = createController(); + $httpBackend.flush(); + expect($rootScope.status).toBe('Failed...'); + }); + + + it('should send msg to server', function() { + var controller = createController(); + $httpBackend.flush(); + + // now you don’t care about the authentication, but + // the controller will still send the request and + // $httpBackend will respond without you having to + // specify the expectation and response for this request + + $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, ''); + $rootScope.saveMessage('message content'); + expect($rootScope.status).toBe('Saving...'); + $httpBackend.flush(); + expect($rootScope.status).toBe(''); + }); + + + it('should send auth header', function() { + var controller = createController(); + $httpBackend.flush(); + + $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) { + // check if the header was send, if it wasn't the expectation won't + // match the request and the test will fail + return headers['Authorization'] == 'xxx'; + }).respond(201, ''); + + $rootScope.saveMessage('whatever'); + $httpBackend.flush(); + }); + }); + ``` + */ +angular.mock.$HttpBackendProvider = function() { + this.$get = ['$rootScope', '$timeout', createHttpBackendMock]; +}; + +/** + * General factory function for $httpBackend mock. + * Returns instance for unit testing (when no arguments specified): + * - passing through is disabled + * - auto flushing is disabled + * + * Returns instance for e2e testing (when `$delegate` and `$browser` specified): + * - passing through (delegating request to real backend) is enabled + * - auto flushing is enabled + * + * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified) + * @param {Object=} $browser Auto-flushing enabled if specified + * @return {Object} Instance of $httpBackend mock + */ +function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) { + var definitions = [], + expectations = [], + responses = [], + responsesPush = angular.bind(responses, responses.push), + copy = angular.copy; + + function createResponse(status, data, headers, statusText) { + if (angular.isFunction(status)) return status; + + return function() { + return angular.isNumber(status) + ? [status, data, headers, statusText] + : [200, status, data, headers]; + }; + } + + // TODO(vojta): change params to: method, url, data, headers, callback + function $httpBackend(method, url, data, callback, headers, timeout, withCredentials) { + var xhr = new MockXhr(), + expectation = expectations[0], + wasExpected = false; + + function prettyPrint(data) { + return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) + ? data + : angular.toJson(data); + } + + function wrapResponse(wrapped) { + if (!$browser && timeout) { + timeout.then ? timeout.then(handleTimeout) : $timeout(handleTimeout, timeout); + } + + return handleResponse; + + function handleResponse() { + var response = wrapped.response(method, url, data, headers); + xhr.$$respHeaders = response[2]; + callback(copy(response[0]), copy(response[1]), xhr.getAllResponseHeaders(), + copy(response[3] || '')); + } + + function handleTimeout() { + for (var i = 0, ii = responses.length; i < ii; i++) { + if (responses[i] === handleResponse) { + responses.splice(i, 1); + callback(-1, undefined, ''); + break; + } + } + } + } + + if (expectation && expectation.match(method, url)) { + if (!expectation.matchData(data)) + throw new Error('Expected ' + expectation + ' with different data\n' + + 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); + + if (!expectation.matchHeaders(headers)) + throw new Error('Expected ' + expectation + ' with different headers\n' + + 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + + prettyPrint(headers)); + + expectations.shift(); + + if (expectation.response) { + responses.push(wrapResponse(expectation)); + return; + } + wasExpected = true; + } + + var i = -1, definition; + while ((definition = definitions[++i])) { + if (definition.match(method, url, data, headers || {})) { + if (definition.response) { + // if $browser specified, we do auto flush all requests + ($browser ? $browser.defer : responsesPush)(wrapResponse(definition)); + } else if (definition.passThrough) { + $delegate(method, url, data, callback, headers, timeout, withCredentials); + } else throw new Error('No response defined !'); + return; + } + } + throw wasExpected ? + new Error('No response defined !') : + new Error('Unexpected request: ' + method + ' ' + url + '\n' + + (expectation ? 'Expected ' + expectation : 'No more request expected')); + } + + /** + * @ngdoc method + * @name $httpBackend#when + * @description + * Creates a new backend definition. + * + * @param {string} method HTTP method. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives + * data string and returns true if the data is as expected. + * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header + * object and returns true if the headers match the current definition. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + * + * - respond – + * `{function([status,] data[, headers, statusText]) + * | function(function(method, url, data, headers)}` + * – The respond method takes a set of static data to be returned or a function that can + * return an array containing response status (number), response data (string), response + * headers (Object), and the text for the status (string). The respond method returns the + * `requestHandler` object for possible overrides. + */ + $httpBackend.when = function(method, url, data, headers) { + var definition = new MockHttpExpectation(method, url, data, headers), + chain = { + respond: function(status, data, headers, statusText) { + definition.passThrough = undefined; + definition.response = createResponse(status, data, headers, statusText); + return chain; + } + }; + + if ($browser) { + chain.passThrough = function() { + definition.response = undefined; + definition.passThrough = true; + return chain; + }; + } + + definitions.push(definition); + return chain; + }; + + /** + * @ngdoc method + * @name $httpBackend#whenGET + * @description + * Creates a new backend definition for GET requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#whenHEAD + * @description + * Creates a new backend definition for HEAD requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#whenDELETE + * @description + * Creates a new backend definition for DELETE requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#whenPOST + * @description + * Creates a new backend definition for POST requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives + * data string and returns true if the data is as expected. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#whenPUT + * @description + * Creates a new backend definition for PUT requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string))=} data HTTP request body or function that receives + * data string and returns true if the data is as expected. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#whenJSONP + * @description + * Creates a new backend definition for JSONP requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + createShortMethods('when'); + + + /** + * @ngdoc method + * @name $httpBackend#expect + * @description + * Creates a new request expectation. + * + * @param {string} method HTTP method. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that + * receives data string and returns true if the data is as expected, or Object if request body + * is in JSON format. + * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header + * object and returns true if the headers match the current expectation. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + * + * - respond – + * `{function([status,] data[, headers, statusText]) + * | function(function(method, url, data, headers)}` + * – The respond method takes a set of static data to be returned or a function that can + * return an array containing response status (number), response data (string), response + * headers (Object), and the text for the status (string). The respond method returns the + * `requestHandler` object for possible overrides. + */ + $httpBackend.expect = function(method, url, data, headers) { + var expectation = new MockHttpExpectation(method, url, data, headers), + chain = { + respond: function(status, data, headers, statusText) { + expectation.response = createResponse(status, data, headers, statusText); + return chain; + } + }; + + expectations.push(expectation); + return chain; + }; + + + /** + * @ngdoc method + * @name $httpBackend#expectGET + * @description + * Creates a new request expectation for GET requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {Object=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. See #expect for more info. + */ + + /** + * @ngdoc method + * @name $httpBackend#expectHEAD + * @description + * Creates a new request expectation for HEAD requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {Object=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#expectDELETE + * @description + * Creates a new request expectation for DELETE requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {Object=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#expectPOST + * @description + * Creates a new request expectation for POST requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that + * receives data string and returns true if the data is as expected, or Object if request body + * is in JSON format. + * @param {Object=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#expectPUT + * @description + * Creates a new request expectation for PUT requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that + * receives data string and returns true if the data is as expected, or Object if request body + * is in JSON format. + * @param {Object=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#expectPATCH + * @description + * Creates a new request expectation for PATCH requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp|function(string)|Object)=} data HTTP request body or function that + * receives data string and returns true if the data is as expected, or Object if request body + * is in JSON format. + * @param {Object=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + + /** + * @ngdoc method + * @name $httpBackend#expectJSONP + * @description + * Creates a new request expectation for JSONP requests. For more info see `expect()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @returns {requestHandler} Returns an object with `respond` method that controls how a matched + * request is handled. You can save this object for later use and invoke `respond` again in + * order to change how a matched request is handled. + */ + createShortMethods('expect'); + + + /** + * @ngdoc method + * @name $httpBackend#flush + * @description + * Flushes all pending requests using the trained responses. + * + * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, + * all pending requests will be flushed. If there are no pending requests when the flush method + * is called an exception is thrown (as this typically a sign of programming error). + */ + $httpBackend.flush = function(count, digest) { + if (digest !== false) $rootScope.$digest(); + if (!responses.length) throw new Error('No pending request to flush !'); + + if (angular.isDefined(count) && count !== null) { + while (count--) { + if (!responses.length) throw new Error('No more pending request to flush !'); + responses.shift()(); + } + } else { + while (responses.length) { + responses.shift()(); + } + } + $httpBackend.verifyNoOutstandingExpectation(digest); + }; + + + /** + * @ngdoc method + * @name $httpBackend#verifyNoOutstandingExpectation + * @description + * Verifies that all of the requests defined via the `expect` api were made. If any of the + * requests were not made, verifyNoOutstandingExpectation throws an exception. + * + * Typically, you would call this method following each test case that asserts requests using an + * "afterEach" clause. + * + * ```js + * afterEach($httpBackend.verifyNoOutstandingExpectation); + * ``` + */ + $httpBackend.verifyNoOutstandingExpectation = function(digest) { + if (digest !== false) $rootScope.$digest(); + if (expectations.length) { + throw new Error('Unsatisfied requests: ' + expectations.join(', ')); + } + }; + + + /** + * @ngdoc method + * @name $httpBackend#verifyNoOutstandingRequest + * @description + * Verifies that there are no outstanding requests that need to be flushed. + * + * Typically, you would call this method following each test case that asserts requests using an + * "afterEach" clause. + * + * ```js + * afterEach($httpBackend.verifyNoOutstandingRequest); + * ``` + */ + $httpBackend.verifyNoOutstandingRequest = function() { + if (responses.length) { + throw new Error('Unflushed requests: ' + responses.length); + } + }; + + + /** + * @ngdoc method + * @name $httpBackend#resetExpectations + * @description + * Resets all request expectations, but preserves all backend definitions. Typically, you would + * call resetExpectations during a multiple-phase test when you want to reuse the same instance of + * $httpBackend mock. + */ + $httpBackend.resetExpectations = function() { + expectations.length = 0; + responses.length = 0; + }; + + return $httpBackend; + + + function createShortMethods(prefix) { + angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD'], function(method) { + $httpBackend[prefix + method] = function(url, headers) { + return $httpBackend[prefix](method, url, undefined, headers); + }; + }); + + angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { + $httpBackend[prefix + method] = function(url, data, headers) { + return $httpBackend[prefix](method, url, data, headers); + }; + }); + } +} + +function MockHttpExpectation(method, url, data, headers) { + + this.data = data; + this.headers = headers; + + this.match = function(m, u, d, h) { + if (method != m) return false; + if (!this.matchUrl(u)) return false; + if (angular.isDefined(d) && !this.matchData(d)) return false; + if (angular.isDefined(h) && !this.matchHeaders(h)) return false; + return true; + }; + + this.matchUrl = function(u) { + if (!url) return true; + if (angular.isFunction(url.test)) return url.test(u); + if (angular.isFunction(url)) return url(u); + return url == u; + }; + + this.matchHeaders = function(h) { + if (angular.isUndefined(headers)) return true; + if (angular.isFunction(headers)) return headers(h); + return angular.equals(headers, h); + }; + + this.matchData = function(d) { + if (angular.isUndefined(data)) return true; + if (data && angular.isFunction(data.test)) return data.test(d); + if (data && angular.isFunction(data)) return data(d); + if (data && !angular.isString(data)) { + return angular.equals(angular.fromJson(angular.toJson(data)), angular.fromJson(d)); + } + return data == d; + }; + + this.toString = function() { + return method + ' ' + url; + }; +} + +function createMockXhr() { + return new MockXhr(); +} + +function MockXhr() { + + // hack for testing $http, $httpBackend + MockXhr.$$lastInstance = this; + + this.open = function(method, url, async) { + this.$$method = method; + this.$$url = url; + this.$$async = async; + this.$$reqHeaders = {}; + this.$$respHeaders = {}; + }; + + this.send = function(data) { + this.$$data = data; + }; + + this.setRequestHeader = function(key, value) { + this.$$reqHeaders[key] = value; + }; + + this.getResponseHeader = function(name) { + // the lookup must be case insensitive, + // that's why we try two quick lookups first and full scan last + var header = this.$$respHeaders[name]; + if (header) return header; + + name = angular.lowercase(name); + header = this.$$respHeaders[name]; + if (header) return header; + + header = undefined; + angular.forEach(this.$$respHeaders, function(headerVal, headerName) { + if (!header && angular.lowercase(headerName) == name) header = headerVal; + }); + return header; + }; + + this.getAllResponseHeaders = function() { + var lines = []; + + angular.forEach(this.$$respHeaders, function(value, key) { + lines.push(key + ': ' + value); + }); + return lines.join('\n'); + }; + + this.abort = angular.noop; +} + + +/** + * @ngdoc service + * @name $timeout + * @description + * + * This service is just a simple decorator for {@link ng.$timeout $timeout} service + * that adds a "flush" and "verifyNoPendingTasks" methods. + */ + +angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $browser) { + + /** + * @ngdoc method + * @name $timeout#flush + * @description + * + * Flushes the queue of pending tasks. + * + * @param {number=} delay maximum timeout amount to flush up until + */ + $delegate.flush = function(delay) { + $browser.defer.flush(delay); + }; + + /** + * @ngdoc method + * @name $timeout#verifyNoPendingTasks + * @description + * + * Verifies that there are no pending tasks that need to be flushed. + */ + $delegate.verifyNoPendingTasks = function() { + if ($browser.deferredFns.length) { + throw new Error('Deferred tasks to flush (' + $browser.deferredFns.length + '): ' + + formatPendingTasksAsString($browser.deferredFns)); + } + }; + + function formatPendingTasksAsString(tasks) { + var result = []; + angular.forEach(tasks, function(task) { + result.push('{id: ' + task.id + ', ' + 'time: ' + task.time + '}'); + }); + + return result.join(', '); + } + + return $delegate; +}]; + +angular.mock.$RAFDecorator = ['$delegate', function($delegate) { + var queue = []; + var rafFn = function(fn) { + var index = queue.length; + queue.push(fn); + return function() { + queue.splice(index, 1); + }; + }; + + rafFn.supported = $delegate.supported; + + rafFn.flush = function() { + if (queue.length === 0) { + throw new Error('No rAF callbacks present'); + } + + var length = queue.length; + for (var i = 0; i < length; i++) { + queue[i](); + } + + queue = []; + }; + + return rafFn; +}]; + +angular.mock.$AsyncCallbackDecorator = ['$delegate', function($delegate) { + var callbacks = []; + var addFn = function(fn) { + callbacks.push(fn); + }; + addFn.flush = function() { + angular.forEach(callbacks, function(fn) { + fn(); + }); + callbacks = []; + }; + return addFn; +}]; + +/** + * + */ +angular.mock.$RootElementProvider = function() { + this.$get = function() { + return angular.element('<div ng-app></div>'); + }; +}; + +/** + * @ngdoc service + * @name $controller + * @description + * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing + * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}. + * + * + * ## Example + * + * ```js + * + * // Directive definition ... + * + * myMod.directive('myDirective', { + * controller: 'MyDirectiveController', + * bindToController: { + * name: '@' + * } + * }); + * + * + * // Controller definition ... + * + * myMod.controller('MyDirectiveController', ['log', function($log) { + * $log.info(this.name); + * })]; + * + * + * // In a test ... + * + * describe('myDirectiveController', function() { + * it('should write the bound name to the log', inject(function($controller, $log) { + * var ctrl = $controller('MyDirective', { /* no locals */ }, { name: 'Clark Kent' }); + * expect(ctrl.name).toEqual('Clark Kent'); + * expect($log.info.logs).toEqual(['Clark Kent']); + * }); + * }); + * + * ``` + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global + * `window` object (not recommended) + * + * The string can use the `controller as property` syntax, where the controller instance is published + * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this + * to work correctly. + * + * @param {Object} locals Injection locals for Controller. + * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used + * to simulate the `bindToController` feature and simplify certain kinds of tests. + * @return {Object} Instance of given controller. + */ +angular.mock.$ControllerDecorator = ['$delegate', function($delegate) { + return function(expression, locals, later, ident) { + if (later && typeof later === 'object') { + var create = $delegate(expression, locals, true, ident); + angular.extend(create.instance, later); + return create(); + } + return $delegate(expression, locals, later, ident); + }; +}]; + + +/** + * @ngdoc module + * @name ngMock + * @packageName angular-mocks + * @description + * + * # ngMock + * + * The `ngMock` module provides support to inject and mock Angular services into unit tests. + * In addition, ngMock also extends various core ng services such that they can be + * inspected and controlled in a synchronous manner within test code. + * + * + * <div doc-module-components="ngMock"></div> + * + */ +angular.module('ngMock', ['ng']).provider({ + $browser: angular.mock.$BrowserProvider, + $exceptionHandler: angular.mock.$ExceptionHandlerProvider, + $log: angular.mock.$LogProvider, + $interval: angular.mock.$IntervalProvider, + $httpBackend: angular.mock.$HttpBackendProvider, + $rootElement: angular.mock.$RootElementProvider +}).config(['$provide', function($provide) { + $provide.decorator('$timeout', angular.mock.$TimeoutDecorator); + $provide.decorator('$$rAF', angular.mock.$RAFDecorator); + $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator); + $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator); + $provide.decorator('$controller', angular.mock.$ControllerDecorator); +}]); + +/** + * @ngdoc module + * @name ngMockE2E + * @module ngMockE2E + * @packageName angular-mocks + * @description + * + * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. + * Currently there is only one mock present in this module - + * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. + */ +angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) { + $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); +}]); + +/** + * @ngdoc service + * @name $httpBackend + * @module ngMockE2E + * @description + * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of + * applications that use the {@link ng.$http $http service}. + * + * *Note*: For fake http backend implementation suitable for unit testing please see + * {@link ngMock.$httpBackend unit-testing $httpBackend mock}. + * + * This implementation can be used to respond with static or dynamic responses via the `when` api + * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the + * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch + * templates from a webserver). + * + * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application + * is being developed with the real backend api replaced with a mock, it is often desirable for + * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch + * templates or static files from the webserver). To configure the backend with this behavior + * use the `passThrough` request handler of `when` instead of `respond`. + * + * Additionally, we don't want to manually have to flush mocked out requests like we do during unit + * testing. For this reason the e2e $httpBackend flushes mocked out requests + * automatically, closely simulating the behavior of the XMLHttpRequest object. + * + * To setup the application to run with this http backend, you have to create a module that depends + * on the `ngMockE2E` and your application modules and defines the fake backend: + * + * ```js + * myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']); + * myAppDev.run(function($httpBackend) { + * phones = [{name: 'phone1'}, {name: 'phone2'}]; + * + * // returns the current list of phones + * $httpBackend.whenGET('/phones').respond(phones); + * + * // adds a new phone to the phones array + * $httpBackend.whenPOST('/phones').respond(function(method, url, data) { + * var phone = angular.fromJson(data); + * phones.push(phone); + * return [200, phone, {}]; + * }); + * $httpBackend.whenGET(/^\/templates\//).passThrough(); + * //... + * }); + * ``` + * + * Afterwards, bootstrap your app with this new module. + */ + +/** + * @ngdoc method + * @name $httpBackend#when + * @module ngMockE2E + * @description + * Creates a new backend definition. + * + * @param {string} method HTTP method. + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp)=} data HTTP request body. + * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header + * object and returns true if the headers match the current definition. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + * + * - respond – + * `{function([status,] data[, headers, statusText]) + * | function(function(method, url, data, headers)}` + * – The respond method takes a set of static data to be returned or a function that can return + * an array containing response status (number), response data (string), response headers + * (Object), and the text for the status (string). + * - passThrough – `{function()}` – Any request matching a backend definition with + * `passThrough` handler will be passed through to the real backend (an XHR request will be made + * to the server.) + * - Both methods return the `requestHandler` object for possible overrides. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenGET + * @module ngMockE2E + * @description + * Creates a new backend definition for GET requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenHEAD + * @module ngMockE2E + * @description + * Creates a new backend definition for HEAD requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenDELETE + * @module ngMockE2E + * @description + * Creates a new backend definition for DELETE requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenPOST + * @module ngMockE2E + * @description + * Creates a new backend definition for POST requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp)=} data HTTP request body. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenPUT + * @module ngMockE2E + * @description + * Creates a new backend definition for PUT requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp)=} data HTTP request body. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenPATCH + * @module ngMockE2E + * @description + * Creates a new backend definition for PATCH requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @param {(string|RegExp)=} data HTTP request body. + * @param {(Object|function(Object))=} headers HTTP headers. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ + +/** + * @ngdoc method + * @name $httpBackend#whenJSONP + * @module ngMockE2E + * @description + * Creates a new backend definition for JSONP requests. For more info see `when()`. + * + * @param {string|RegExp|function(string)} url HTTP url or function that receives the url + * and returns true if the url match the current definition. + * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that + * control how a matched request is handled. You can save this object for later use and invoke + * `respond` or `passThrough` again in order to change how a matched request is handled. + */ +angular.mock.e2e = {}; +angular.mock.e2e.$httpBackendDecorator = + ['$rootScope', '$timeout', '$delegate', '$browser', createHttpBackendMock]; + + +/** + * @ngdoc type + * @name $rootScope.Scope + * @module ngMock + * @description + * {@link ng.$rootScope.Scope Scope} type decorated with helper methods useful for testing. These + * methods are automatically available on any {@link ng.$rootScope.Scope Scope} instance when + * `ngMock` module is loaded. + * + * In addition to all the regular `Scope` methods, the following helper methods are available: + */ +angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) { + + var $rootScopePrototype = Object.getPrototypeOf($delegate); + + $rootScopePrototype.$countChildScopes = countChildScopes; + $rootScopePrototype.$countWatchers = countWatchers; + + return $delegate; + + // ------------------------------------------------------------------------------------------ // + + /** + * @ngdoc method + * @name $rootScope.Scope#$countChildScopes + * @module ngMock + * @description + * Counts all the direct and indirect child scopes of the current scope. + * + * The current scope is excluded from the count. The count includes all isolate child scopes. + * + * @returns {number} Total number of child scopes. + */ + function countChildScopes() { + // jshint validthis: true + var count = 0; // exclude the current scope + var pendingChildHeads = [this.$$childHead]; + var currentScope; + + while (pendingChildHeads.length) { + currentScope = pendingChildHeads.shift(); + + while (currentScope) { + count += 1; + pendingChildHeads.push(currentScope.$$childHead); + currentScope = currentScope.$$nextSibling; + } + } + + return count; + } + + + /** + * @ngdoc method + * @name $rootScope.Scope#$countWatchers + * @module ngMock + * @description + * Counts all the watchers of direct and indirect child scopes of the current scope. + * + * The watchers of the current scope are included in the count and so are all the watchers of + * isolate child scopes. + * + * @returns {number} Total number of watchers. + */ + function countWatchers() { + // jshint validthis: true + var count = this.$$watchers ? this.$$watchers.length : 0; // include the current scope + var pendingChildHeads = [this.$$childHead]; + var currentScope; + + while (pendingChildHeads.length) { + currentScope = pendingChildHeads.shift(); + + while (currentScope) { + count += currentScope.$$watchers ? currentScope.$$watchers.length : 0; + pendingChildHeads.push(currentScope.$$childHead); + currentScope = currentScope.$$nextSibling; + } + } + + return count; + } +}]; + + +if (window.jasmine || window.mocha) { + + var currentSpec = null, + annotatedFunctions = [], + isSpecRunning = function() { + return !!currentSpec; + }; + + angular.mock.$$annotate = angular.injector.$$annotate; + angular.injector.$$annotate = function(fn) { + if (typeof fn === 'function' && !fn.$inject) { + annotatedFunctions.push(fn); + } + return angular.mock.$$annotate.apply(this, arguments); + }; + + + (window.beforeEach || window.setup)(function() { + annotatedFunctions = []; + currentSpec = this; + }); + + (window.afterEach || window.teardown)(function() { + var injector = currentSpec.$injector; + + annotatedFunctions.forEach(function(fn) { + delete fn.$inject; + }); + + angular.forEach(currentSpec.$modules, function(module) { + if (module && module.$$hashKey) { + module.$$hashKey = undefined; + } + }); + + currentSpec.$injector = null; + currentSpec.$modules = null; + currentSpec = null; + + if (injector) { + injector.get('$rootElement').off(); + injector.get('$browser').pollFns.length = 0; + } + + // clean up jquery's fragment cache + angular.forEach(angular.element.fragments, function(val, key) { + delete angular.element.fragments[key]; + }); + + MockXhr.$$lastInstance = null; + + angular.forEach(angular.callbacks, function(val, key) { + delete angular.callbacks[key]; + }); + angular.callbacks.counter = 0; + }); + + /** + * @ngdoc function + * @name angular.mock.module + * @description + * + * *NOTE*: This function is also published on window for easy access.<br> + * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha + * + * This function registers a module configuration code. It collects the configuration information + * which will be used when the injector is created by {@link angular.mock.inject inject}. + * + * See {@link angular.mock.inject inject} for usage example + * + * @param {...(string|Function|Object)} fns any number of modules which are represented as string + * aliases or as anonymous module initialization functions. The modules are used to + * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an + * object literal is passed they will be registered as values in the module, the key being + * the module name and the value being what is returned. + */ + window.module = angular.mock.module = function() { + var moduleFns = Array.prototype.slice.call(arguments, 0); + return isSpecRunning() ? workFn() : workFn; + ///////////////////// + function workFn() { + if (currentSpec.$injector) { + throw new Error('Injector already created, can not register a module!'); + } else { + var modules = currentSpec.$modules || (currentSpec.$modules = []); + angular.forEach(moduleFns, function(module) { + if (angular.isObject(module) && !angular.isArray(module)) { + modules.push(function($provide) { + angular.forEach(module, function(value, key) { + $provide.value(key, value); + }); + }); + } else { + modules.push(module); + } + }); + } + } + }; + + /** + * @ngdoc function + * @name angular.mock.inject + * @description + * + * *NOTE*: This function is also published on window for easy access.<br> + * *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha + * + * The inject function wraps a function into an injectable function. The inject() creates new + * instance of {@link auto.$injector $injector} per test, which is then used for + * resolving references. + * + * + * ## Resolving References (Underscore Wrapping) + * Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this + * in multiple `it()` clauses. To be able to do this we must assign the reference to a variable + * that is declared in the scope of the `describe()` block. Since we would, most likely, want + * the variable to have the same name of the reference we have a problem, since the parameter + * to the `inject()` function would hide the outer variable. + * + * To help with this, the injected parameters can, optionally, be enclosed with underscores. + * These are ignored by the injector when the reference name is resolved. + * + * For example, the parameter `_myService_` would be resolved as the reference `myService`. + * Since it is available in the function body as _myService_, we can then assign it to a variable + * defined in an outer scope. + * + * ``` + * // Defined out reference variable outside + * var myService; + * + * // Wrap the parameter in underscores + * beforeEach( inject( function(_myService_){ + * myService = _myService_; + * })); + * + * // Use myService in a series of tests. + * it('makes use of myService', function() { + * myService.doStuff(); + * }); + * + * ``` + * + * See also {@link angular.mock.module angular.mock.module} + * + * ## Example + * Example of what a typical jasmine tests looks like with the inject method. + * ```js + * + * angular.module('myApplicationModule', []) + * .value('mode', 'app') + * .value('version', 'v1.0.1'); + * + * + * describe('MyApp', function() { + * + * // You need to load modules that you want to test, + * // it loads only the "ng" module by default. + * beforeEach(module('myApplicationModule')); + * + * + * // inject() is used to inject arguments of all given functions + * it('should provide a version', inject(function(mode, version) { + * expect(version).toEqual('v1.0.1'); + * expect(mode).toEqual('app'); + * })); + * + * + * // The inject and module method can also be used inside of the it or beforeEach + * it('should override a version and test the new version is injected', function() { + * // module() takes functions or strings (module aliases) + * module(function($provide) { + * $provide.value('version', 'overridden'); // override version here + * }); + * + * inject(function(version) { + * expect(version).toEqual('overridden'); + * }); + * }); + * }); + * + * ``` + * + * @param {...Function} fns any number of functions which will be injected using the injector. + */ + + + + var ErrorAddingDeclarationLocationStack = function(e, errorForStack) { + this.message = e.message; + this.name = e.name; + if (e.line) this.line = e.line; + if (e.sourceId) this.sourceId = e.sourceId; + if (e.stack && errorForStack) + this.stack = e.stack + '\n' + errorForStack.stack; + if (e.stackArray) this.stackArray = e.stackArray; + }; + ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString; + + window.inject = angular.mock.inject = function() { + var blockFns = Array.prototype.slice.call(arguments, 0); + var errorForStack = new Error('Declaration Location'); + return isSpecRunning() ? workFn.call(currentSpec) : workFn; + ///////////////////// + function workFn() { + var modules = currentSpec.$modules || []; + var strictDi = !!currentSpec.$injectorStrict; + modules.unshift('ngMock'); + modules.unshift('ng'); + var injector = currentSpec.$injector; + if (!injector) { + if (strictDi) { + // If strictDi is enabled, annotate the providerInjector blocks + angular.forEach(modules, function(moduleFn) { + if (typeof moduleFn === "function") { + angular.injector.$$annotate(moduleFn); + } + }); + } + injector = currentSpec.$injector = angular.injector(modules, strictDi); + currentSpec.$injectorStrict = strictDi; + } + for (var i = 0, ii = blockFns.length; i < ii; i++) { + if (currentSpec.$injectorStrict) { + // If the injector is strict / strictDi, and the spec wants to inject using automatic + // annotation, then annotate the function here. + injector.annotate(blockFns[i]); + } + try { + /* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */ + injector.invoke(blockFns[i] || angular.noop, this); + /* jshint +W040 */ + } catch (e) { + if (e.stack && errorForStack) { + throw new ErrorAddingDeclarationLocationStack(e, errorForStack); + } + throw e; + } finally { + errorForStack = null; + } + } + } + }; + + + angular.mock.inject.strictDi = function(value) { + value = arguments.length ? !!value : true; + return isSpecRunning() ? workFn() : workFn; + + function workFn() { + if (value !== currentSpec.$injectorStrict) { + if (currentSpec.$injector) { + throw new Error('Injector already created, can not modify strict annotations'); + } else { + currentSpec.$injectorStrict = value; + } + } + } + }; +} + + +})(window, window.angular); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/bower.json new file mode 100644 index 000000000..a9bc5e7a4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/bower.json @@ -0,0 +1,9 @@ +{ + "name": "angular-mocks", + "version": "1.3.15", + "main": "./angular-mocks.js", + "ignore": [], + "dependencies": { + "angular": "1.3.15" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngAnimateMock.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngAnimateMock.js new file mode 100644 index 000000000..6f99e62ef --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngAnimateMock.js @@ -0,0 +1,2 @@ +require('./angular-mocks'); +module.exports = 'ngAnimateMock'; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngMock.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngMock.js new file mode 100644 index 000000000..7944de7d5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngMock.js @@ -0,0 +1,2 @@ +require('./angular-mocks'); +module.exports = 'ngMock'; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngMockE2E.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngMockE2E.js new file mode 100644 index 000000000..fc2e539db --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/ngMockE2E.js @@ -0,0 +1,2 @@ +require('./angular-mocks'); +module.exports = 'ngMockE2E'; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/package.json new file mode 100644 index 000000000..c54bda5fd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-mocks/package.json @@ -0,0 +1,27 @@ +{ + "name": "angular-mocks", + "version": "1.3.15", + "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" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/.bower.json new file mode 100644 index 000000000..d17186d54 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/.bower.json @@ -0,0 +1,19 @@ +{ + "name": "angular-resource", + "version": "1.3.15", + "main": "./angular-resource.js", + "ignore": [], + "dependencies": { + "angular": "1.3.15" + }, + "homepage": "https://github.com/angular/bower-angular-resource", + "_release": "1.3.15", + "_resolution": { + "type": "version", + "tag": "v1.3.15", + "commit": "4cc3a759e2077f27aafb273a4a4929241aae1fd6" + }, + "_source": "https://github.com/angular/bower-angular-resource.git", + "_target": "1.3.15", + "_originalSource": "angular-resource" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/README.md new file mode 100644 index 000000000..f3bd119ce --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/README.md @@ -0,0 +1,68 @@ +# 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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.js new file mode 100644 index 000000000..19900d379 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.js @@ -0,0 +1,668 @@ +/** + * @license AngularJS v1.3.15 + * (c) 2010-2014 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular, undefined) {'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 && obj !== undefined; 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.$resource `$resource`} for usage. + */ + +/** + * @ngdoc service + * @name $resource + * @requires $http + * + * @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 parametrized 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 any of the parameter value is a function, it will be executed every time + * when a param value needs to be obtained for a request (unless the param was overridden). + * + * 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 an action method). For + * example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam` + * will be `data.someProp`. + * + * @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 executed every time when a param value needs to + * be obtained for a request (unless the param was overridden). + * - **`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|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that + * should abort the request when resolved. + * - **`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 only supported option is + * + * Where: + * + * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing + * slashes from any calculated URL will be stripped. (Defaults to true.) + * + * @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. 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 collection 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 resolved 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. + * + * @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`. + * 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(u, getResponseHeaders){ + u.abc = true; + u.$save(function(u, putResponseHeaders) { + //u => 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; + }); + ``` + + * # 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 + * }]); + * ``` + */ +angular.module('ngResource', ['ng']). + provider('$resource', function() { + var provider = this; + + this.defaults = { + // Strip slashes by default + stripTrailingSlashes: true, + + // Default actions configuration + actions: { + 'get': {method: 'GET'}, + 'save': {method: 'POST'}, + 'query': {method: 'GET', isArray: true}, + 'remove': {method: 'DELETE'}, + 'delete': {method: 'DELETE'} + } + }; + + this.$get = ['$http', '$q', function($http, $q) { + + 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; + + 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] = true; + } + }); + url = url.replace(/\\:/g, ':'); + + params = params || {}; + forEach(self.urlParams, function(_, urlParam) { + val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; + if (angular.isDefined(val) && val !== null) { + 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 = 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(); } + 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); + + 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; + + forEach(action, function(value, key) { + if (key != 'params' && key != 'isArray' && key != 'interceptor') { + httpConfig[key] = copy(value); + } + }); + + 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, + promise = value.$promise; + + 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}', name, action.isArray ? 'array' : 'object', + angular.isArray(data) ? 'array' : 'object'); + } + // 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 { + shallowClearAndCopy(data, value); + value.$promise = promise; + } + } + + value.$resolved = true; + + response.resource = value; + + return response; + }, function(response) { + value.$resolved = true; + + (error || noop)(response); + + return $q.reject(response); + }); + + 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; + + 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); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.min.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.min.js new file mode 100644 index 000000000..02ec4eea8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.min.js @@ -0,0 +1,13 @@ +/* + AngularJS v1.3.15 + (c) 2010-2014 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(I,d,B){'use strict';function D(f,q){q=q||{};d.forEach(q,function(d,h){delete q[h]});for(var h in f)!f.hasOwnProperty(h)||"$"===h.charAt(0)&&"$"===h.charAt(1)||(q[h]=f[h]);return q}var w=d.$$minErr("$resource"),C=/^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;d.module("ngResource",["ng"]).provider("$resource",function(){var f=this;this.defaults={stripTrailingSlashes:!0,actions:{get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}}}; +this.$get=["$http","$q",function(q,h){function t(d,g){this.template=d;this.defaults=s({},f.defaults,g);this.urlParams={}}function v(x,g,l,m){function c(b,k){var c={};k=s({},g,k);r(k,function(a,k){u(a)&&(a=a());var d;if(a&&a.charAt&&"@"==a.charAt(0)){d=b;var e=a.substr(1);if(null==e||""===e||"hasOwnProperty"===e||!C.test("."+e))throw w("badmember",e);for(var e=e.split("."),n=0,g=e.length;n<g&&d!==B;n++){var h=e[n];d=null!==d?d[h]:B}}else d=a;c[k]=d});return c}function F(b){return b.resource}function e(b){D(b|| +{},this)}var G=new t(x,m);l=s({},f.defaults.actions,l);e.prototype.toJSON=function(){var b=s({},this);delete b.$promise;delete b.$resolved;return b};r(l,function(b,k){var g=/^(POST|PUT|PATCH)$/i.test(b.method);e[k]=function(a,y,m,x){var n={},f,l,z;switch(arguments.length){case 4:z=x,l=m;case 3:case 2:if(u(y)){if(u(a)){l=a;z=y;break}l=y;z=m}else{n=a;f=y;l=m;break}case 1:u(a)?l=a:g?f=a:n=a;break;case 0:break;default:throw w("badargs",arguments.length);}var t=this instanceof e,p=t?f:b.isArray?[]:new e(f), +A={},v=b.interceptor&&b.interceptor.response||F,C=b.interceptor&&b.interceptor.responseError||B;r(b,function(b,a){"params"!=a&&"isArray"!=a&&"interceptor"!=a&&(A[a]=H(b))});g&&(A.data=f);G.setUrlParams(A,s({},c(f,b.params||{}),n),b.url);n=q(A).then(function(a){var c=a.data,g=p.$promise;if(c){if(d.isArray(c)!==!!b.isArray)throw w("badcfg",k,b.isArray?"array":"object",d.isArray(c)?"array":"object");b.isArray?(p.length=0,r(c,function(a){"object"===typeof a?p.push(new e(a)):p.push(a)})):(D(c,p),p.$promise= +g)}p.$resolved=!0;a.resource=p;return a},function(a){p.$resolved=!0;(z||E)(a);return h.reject(a)});n=n.then(function(a){var b=v(a);(l||E)(b,a.headers);return b},C);return t?n:(p.$promise=n,p.$resolved=!1,p)};e.prototype["$"+k]=function(a,b,c){u(a)&&(c=b,b=a,a={});a=e[k].call(this,a,this,b,c);return a.$promise||a}});e.bind=function(b){return v(x,s({},g,b),l)};return e}var E=d.noop,r=d.forEach,s=d.extend,H=d.copy,u=d.isFunction;t.prototype={setUrlParams:function(f,g,l){var m=this,c=l||m.template,h, +e,q=m.urlParams={};r(c.split(/\W/),function(b){if("hasOwnProperty"===b)throw w("badname");!/^\d+$/.test(b)&&b&&(new RegExp("(^|[^\\\\]):"+b+"(\\W|$)")).test(c)&&(q[b]=!0)});c=c.replace(/\\:/g,":");g=g||{};r(m.urlParams,function(b,k){h=g.hasOwnProperty(k)?g[k]:m.defaults[k];d.isDefined(h)&&null!==h?(e=encodeURIComponent(h).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"%20").replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"),c=c.replace(new RegExp(":"+ +k+"(\\W|$)","g"),function(b,a){return e+a})):c=c.replace(new RegExp("(/?):"+k+"(\\W|$)","g"),function(b,a,c){return"/"==c.charAt(0)?c:a+c})});m.defaults.stripTrailingSlashes&&(c=c.replace(/\/+$/,"")||"/");c=c.replace(/\/\.(?=\w+($|\?))/,".");f.url=c.replace(/\/\\\./,"/.");r(g,function(b,c){m.urlParams[c]||(f.params=f.params||{},f.params[c]=b)})}};return v}]})})(window,window.angular); +//# sourceMappingURL=angular-resource.min.js.map diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.min.js.map b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.min.js.map new file mode 100644 index 000000000..aede37e1e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/angular-resource.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular-resource.min.js", +"lineCount":12, +"mappings":"A;;;;;aAKC,SAAQ,CAACA,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAA6B,CA6BtCC,QAASA,EAAmB,CAACC,CAAD,CAAMC,CAAN,CAAW,CACrCA,CAAA,CAAMA,CAAN,EAAa,EAEbJ,EAAAK,QAAA,CAAgBD,CAAhB,CAAqB,QAAQ,CAACE,CAAD,CAAQC,CAAR,CAAa,CACxC,OAAOH,CAAA,CAAIG,CAAJ,CADiC,CAA1C,CAIA,KAASA,IAAAA,CAAT,GAAgBJ,EAAhB,CACM,CAAAA,CAAAK,eAAA,CAAmBD,CAAnB,CAAJ,EAAmD,GAAnD,GAAiCA,CAAAE,OAAA,CAAW,CAAX,CAAjC,EAA4E,GAA5E,GAA0DF,CAAAE,OAAA,CAAW,CAAX,CAA1D,GACEL,CAAA,CAAIG,CAAJ,CADF,CACaJ,CAAA,CAAII,CAAJ,CADb,CAKF,OAAOH,EAb8B,CA3BvC,IAAIM,EAAkBV,CAAAW,SAAA,CAAiB,WAAjB,CAAtB,CAKIC,EAAoB,iCAoVxBZ,EAAAa,OAAA,CAAe,YAAf,CAA6B,CAAC,IAAD,CAA7B,CAAAC,SAAA,CACW,WADX,CACwB,QAAQ,EAAG,CAC/B,IAAIA,EAAW,IAEf,KAAAC,SAAA,CAAgB,CAEdC,qBAAsB,CAAA,CAFR,CAKdC,QAAS,CACP,IAAO,CAACC,OAAQ,KAAT,CADA,CAEP,KAAQ,CAACA,OAAQ,MAAT,CAFD,CAGP,MAAS,CAACA,OAAQ,KAAT,CAAgBC,QAAS,CAAA,CAAzB,CAHF,CAIP,OAAU,CAACD,OAAQ,QAAT,CAJH,CAKP,SAAU,CAACA,OAAQ,QAAT,CALH,CALK,CAchB;IAAAE,KAAA,CAAY,CAAC,OAAD,CAAU,IAAV,CAAgB,QAAQ,CAACC,CAAD,CAAQC,CAAR,CAAY,CA+C9CC,QAASA,EAAK,CAACC,CAAD,CAAWT,CAAX,CAAqB,CACjC,IAAAS,SAAA,CAAgBA,CAChB,KAAAT,SAAA,CAAgBU,CAAA,CAAO,EAAP,CAAWX,CAAAC,SAAX,CAA8BA,CAA9B,CAChB,KAAAW,UAAA,CAAiB,EAHgB,CAoEnCC,QAASA,EAAe,CAACC,CAAD,CAAMC,CAAN,CAAqBZ,CAArB,CAA8Ba,CAA9B,CAAuC,CAK7DC,QAASA,EAAa,CAACC,CAAD,CAAOC,CAAP,CAAqB,CACzC,IAAIC,EAAM,EACVD,EAAA,CAAeR,CAAA,CAAO,EAAP,CAAWI,CAAX,CAA0BI,CAA1B,CACf5B,EAAA,CAAQ4B,CAAR,CAAsB,QAAQ,CAAC3B,CAAD,CAAQC,CAAR,CAAa,CACrC4B,CAAA,CAAW7B,CAAX,CAAJ,GAAyBA,CAAzB,CAAiCA,CAAA,EAAjC,CACW,KAAA,CAAA,IAAAA,CAAA,EAASA,CAAAG,OAAT,EAA4C,GAA5C,EAAyBH,CAAAG,OAAA,CAAa,CAAb,CAAzB,CAAA,CACT,CAAA,CAAA,CAAA,KAAA,EAAA,CAAA,OAAA,CAAA,CAAA,CA5dZ,IALgB,IAKhB,EAAuB2B,CAAvB,EALiC,EAKjC,GAAuBA,CAAvB,EALgD,gBAKhD,GAAuBA,CAAvB,EAJI,CAAAxB,CAAAyB,KAAA,CAAuB,GAAvB,CAImBD,CAJnB,CAIJ,CACE,KAAM1B,EAAA,CAAgB,WAAhB,CAAsE0B,CAAtE,CAAN,CAGF,IADIE,IAAAA,EAAOF,CAAAG,MAAA,CAAW,GAAX,CAAPD,CACKE,EAAI,CADTF,CACYG,EAAKH,CAAAI,OAArB,CAAkCF,CAAlC,CAAsCC,CAAtC,EAA4CE,CAA5C,GAAoD1C,CAApD,CAA+DuC,CAAA,EAA/D,CAAoE,CAClE,IAAIjC,EAAM+B,CAAA,CAAKE,CAAL,CACVG,EAAA,CAAe,IAAT,GAACA,CAAD,CAAiBA,CAAA,CAAIpC,CAAJ,CAAjB,CAA4BN,CAFgC,CAud/C,CAAA,IACiCK,EAAAA,CAAAA,CAD5C4B,EAAA,CAAI3B,CAAJ,CAAA,CAAW,CAF8B,CAA3C,CAKA,OAAO2B,EARkC,CAW3CU,QAASA,EAA0B,CAACC,CAAD,CAAW,CAC5C,MAAOA,EAAAC,SADqC,CAI9CC,QAASA,EAAQ,CAACzC,CAAD,CAAQ,CACvBJ,CAAA,CAAoBI,CAApB;AAA6B,EAA7B,CAAiC,IAAjC,CADuB,CAnBzB,IAAI0C,EAAQ,IAAIzB,CAAJ,CAAUK,CAAV,CAAeE,CAAf,CAEZb,EAAA,CAAUQ,CAAA,CAAO,EAAP,CAAWX,CAAAC,SAAAE,QAAX,CAAsCA,CAAtC,CAqBV8B,EAAAE,UAAAC,OAAA,CAA4BC,QAAQ,EAAG,CACrC,IAAInB,EAAOP,CAAA,CAAO,EAAP,CAAW,IAAX,CACX,QAAOO,CAAAoB,SACP,QAAOpB,CAAAqB,UACP,OAAOrB,EAJ8B,CAOvC3B,EAAA,CAAQY,CAAR,CAAiB,QAAQ,CAACqC,CAAD,CAASC,CAAT,CAAe,CACtC,IAAIC,EAAU,qBAAAnB,KAAA,CAA2BiB,CAAApC,OAA3B,CAEd6B,EAAA,CAASQ,CAAT,CAAA,CAAiB,QAAQ,CAACE,CAAD,CAAKC,CAAL,CAASC,CAAT,CAAaC,CAAb,CAAiB,CAAA,IACpCC,EAAS,EAD2B,CACvB7B,CADuB,CACjB8B,CADiB,CACRC,CAGhC,QAAQC,SAAAtB,OAAR,EACE,KAAK,CAAL,CACEqB,CACA,CADQH,CACR,CAAAE,CAAA,CAAUH,CAEZ,MAAK,CAAL,CACA,KAAK,CAAL,CACE,GAAIxB,CAAA,CAAWuB,CAAX,CAAJ,CAAoB,CAClB,GAAIvB,CAAA,CAAWsB,CAAX,CAAJ,CAAoB,CAClBK,CAAA,CAAUL,CACVM,EAAA,CAAQL,CACR,MAHkB,CAMpBI,CAAA,CAAUJ,CACVK,EAAA,CAAQJ,CARU,CAApB,IAUO,CACLE,CAAA,CAASJ,CACTzB,EAAA,CAAO0B,CACPI,EAAA,CAAUH,CACV,MAJK,CAMT,KAAK,CAAL,CACMxB,CAAA,CAAWsB,CAAX,CAAJ,CAAoBK,CAApB,CAA8BL,CAA9B,CACSD,CAAJ,CAAaxB,CAAb,CAAoByB,CAApB,CACAI,CADA,CACSJ,CACd,MACF,MAAK,CAAL,CAAQ,KACR,SACE,KAAM/C,EAAA,CAAgB,SAAhB,CAEJsD,SAAAtB,OAFI,CAAN,CA9BJ,CAoCA,IAAIuB,EAAiB,IAAjBA,WAAiClB,EAArC,CACIzC,EAAQ2D,CAAA,CAAiBjC,CAAjB,CAAyBsB,CAAAnC,QAAA,CAAiB,EAAjB,CAAsB,IAAI4B,CAAJ,CAAaf,CAAb,CAD3D;AAEIkC,EAAa,EAFjB,CAGIC,EAAsBb,CAAAc,YAAtBD,EAA4Cb,CAAAc,YAAAvB,SAA5CsB,EACFvB,CAJF,CAKIyB,EAA2Bf,CAAAc,YAA3BC,EAAiDf,CAAAc,YAAAE,cAAjDD,EACFpE,CAEFI,EAAA,CAAQiD,CAAR,CAAgB,QAAQ,CAAChD,CAAD,CAAQC,CAAR,CAAa,CACxB,QAAX,EAAIA,CAAJ,EAA8B,SAA9B,EAAuBA,CAAvB,EAAkD,aAAlD,EAA2CA,CAA3C,GACE2D,CAAA,CAAW3D,CAAX,CADF,CACoBgE,CAAA,CAAKjE,CAAL,CADpB,CADmC,CAArC,CAMIkD,EAAJ,GAAaU,CAAAlC,KAAb,CAA+BA,CAA/B,CACAgB,EAAAwB,aAAA,CAAmBN,CAAnB,CACEzC,CAAA,CAAO,EAAP,CAAWM,CAAA,CAAcC,CAAd,CAAoBsB,CAAAO,OAApB,EAAqC,EAArC,CAAX,CAAqDA,CAArD,CADF,CAEEP,CAAA1B,IAFF,CAII6C,EAAAA,CAAUpD,CAAA,CAAM6C,CAAN,CAAAQ,KAAA,CAAuB,QAAQ,CAAC7B,CAAD,CAAW,CAAA,IAClDb,EAAOa,CAAAb,KAD2C,CAEpDyC,EAAUnE,CAAA8C,SAEZ,IAAIpB,CAAJ,CAAU,CAGR,GAAIhC,CAAAmB,QAAA,CAAgBa,CAAhB,CAAJ,GAA+B,CAAEb,CAAAmC,CAAAnC,QAAjC,CACE,KAAMT,EAAA,CAAgB,QAAhB,CAE+B6C,CAF/B,CAEqCD,CAAAnC,QAAA,CAAiB,OAAjB,CAA2B,QAFhE,CAGJnB,CAAAmB,QAAA,CAAgBa,CAAhB,CAAA,CAAwB,OAAxB,CAAkC,QAH9B,CAAN,CAMEsB,CAAAnC,QAAJ,EACEb,CAAAoC,OACA,CADe,CACf,CAAArC,CAAA,CAAQ2B,CAAR,CAAc,QAAQ,CAAC2C,CAAD,CAAO,CACP,QAApB,GAAI,MAAOA,EAAX,CACErE,CAAAsE,KAAA,CAAW,IAAI7B,CAAJ,CAAa4B,CAAb,CAAX,CADF,CAMErE,CAAAsE,KAAA,CAAWD,CAAX,CAPyB,CAA7B,CAFF,GAaEzE,CAAA,CAAoB8B,CAApB,CAA0B1B,CAA1B,CACA,CAAAA,CAAA8C,SAAA;AAAiBqB,CAdnB,CAVQ,CA4BVnE,CAAA+C,UAAA,CAAkB,CAAA,CAElBR,EAAAC,SAAA,CAAoBxC,CAEpB,OAAOuC,EApC+C,CAA1C,CAqCX,QAAQ,CAACA,CAAD,CAAW,CACpBvC,CAAA+C,UAAA,CAAkB,CAAA,CAElB,EAACU,CAAD,EAAUc,CAAV,EAAgBhC,CAAhB,CAEA,OAAOvB,EAAAwD,OAAA,CAAUjC,CAAV,CALa,CArCR,CA6Cd4B,EAAA,CAAUA,CAAAC,KAAA,CACR,QAAQ,CAAC7B,CAAD,CAAW,CACjB,IAAIvC,EAAQ6D,CAAA,CAAoBtB,CAApB,CACZ,EAACiB,CAAD,EAAYe,CAAZ,EAAkBvE,CAAlB,CAAyBuC,CAAAkC,QAAzB,CACA,OAAOzE,EAHU,CADX,CAMR+D,CANQ,CAQV,OAAKJ,EAAL,CAWOQ,CAXP,EAIEnE,CAAA8C,SAGO9C,CAHUmE,CAGVnE,CAFPA,CAAA+C,UAEO/C,CAFW,CAAA,CAEXA,CAAAA,CAPT,CAhHwC,CA+H1CyC,EAAAE,UAAA,CAAmB,GAAnB,CAAyBM,CAAzB,CAAA,CAAiC,QAAQ,CAACM,CAAD,CAASC,CAAT,CAAkBC,CAAlB,CAAyB,CAC5D5B,CAAA,CAAW0B,CAAX,CAAJ,GACEE,CAAmC,CAA3BD,CAA2B,CAAlBA,CAAkB,CAARD,CAAQ,CAAAA,CAAA,CAAS,EAD9C,CAGImB,EAAAA,CAASjC,CAAA,CAASQ,CAAT,CAAA0B,KAAA,CAAoB,IAApB,CAA0BpB,CAA1B,CAAkC,IAAlC,CAAwCC,CAAxC,CAAiDC,CAAjD,CACb,OAAOiB,EAAA5B,SAAP,EAA0B4B,CALsC,CAlI5B,CAAxC,CA2IAjC,EAAAmC,KAAA,CAAgBC,QAAQ,CAACC,CAAD,CAA0B,CAChD,MAAOzD,EAAA,CAAgBC,CAAhB,CAAqBH,CAAA,CAAO,EAAP,CAAWI,CAAX,CAA0BuD,CAA1B,CAArB,CAAyEnE,CAAzE,CADyC,CAIlD,OAAO8B,EA9KsD,CAnHjB,IAE1C8B,EAAO7E,CAAA6E,KAFmC,CAG5CxE,EAAUL,CAAAK,QAHkC,CAI5CoB,EAASzB,CAAAyB,OAJmC,CAK5C8C,EAAOvE,CAAAuE,KALqC,CAM5CpC,EAAanC,CAAAmC,WA+CfZ,EAAA0B,UAAA,CAAkB,CAChBuB,aAAcA,QAAQ,CAACa,CAAD,CAASxB,CAAT,CAAiByB,CAAjB,CAA4B,CAAA,IAC5CC,EAAO,IADqC,CAE9C3D,EAAM0D,CAAN1D,EAAmB2D,CAAA/D,SAF2B,CAG9CgE,CAH8C;AAI9CC,CAJ8C,CAM5C/D,EAAY6D,CAAA7D,UAAZA,CAA6B,EACjCrB,EAAA,CAAQuB,CAAAW,MAAA,CAAU,IAAV,CAAR,CAAyB,QAAQ,CAACmD,CAAD,CAAQ,CACvC,GAAc,gBAAd,GAAIA,CAAJ,CACE,KAAMhF,EAAA,CAAgB,SAAhB,CAAN,CAEI,CAAA,OAAA2B,KAAA,CAA0BqD,CAA1B,CAAN,EAA2CA,CAA3C,EACGrD,CAAA,IAAIsD,MAAJ,CAAW,cAAX,CAA4BD,CAA5B,CAAoC,SAApC,CAAArD,MAAA,CAAoDT,CAApD,CADH,GAEEF,CAAA,CAAUgE,CAAV,CAFF,CAEqB,CAAA,CAFrB,CAJuC,CAAzC,CASA9D,EAAA,CAAMA,CAAAgE,QAAA,CAAY,MAAZ,CAAoB,GAApB,CAEN/B,EAAA,CAASA,CAAT,EAAmB,EACnBxD,EAAA,CAAQkF,CAAA7D,UAAR,CAAwB,QAAQ,CAACmE,CAAD,CAAIC,CAAJ,CAAc,CAC5CN,CAAA,CAAM3B,CAAArD,eAAA,CAAsBsF,CAAtB,CAAA,CAAkCjC,CAAA,CAAOiC,CAAP,CAAlC,CAAqDP,CAAAxE,SAAA,CAAc+E,CAAd,CACvD9F,EAAA+F,UAAA,CAAkBP,CAAlB,CAAJ,EAAsC,IAAtC,GAA8BA,CAA9B,EACEC,CACA,CAtCCO,kBAAA,CAqC6BR,CArC7B,CAAAI,QAAA,CACG,OADH,CACY,GADZ,CAAAA,QAAA,CAEG,OAFH,CAEY,GAFZ,CAAAA,QAAA,CAGG,MAHH,CAGW,GAHX,CAAAA,QAAA,CAIG,OAJH,CAIY,GAJZ,CAAAA,QAAA,CAKG,MALH,CAK8B,KAL9B,CAnBAA,QAAA,CACG,OADH,CACY,GADZ,CAAAA,QAAA,CAEG,OAFH,CAEY,GAFZ,CAAAA,QAAA,CAGG,OAHH,CAGY,GAHZ,CAyDD,CAAAhE,CAAA,CAAMA,CAAAgE,QAAA,CAAY,IAAID,MAAJ,CAAW,GAAX;AAAiBG,CAAjB,CAA4B,SAA5B,CAAuC,GAAvC,CAAZ,CAAyD,QAAQ,CAACG,CAAD,CAAQC,CAAR,CAAY,CACjF,MAAOT,EAAP,CAAoBS,CAD6D,CAA7E,CAFR,EAMEtE,CANF,CAMQA,CAAAgE,QAAA,CAAY,IAAID,MAAJ,CAAW,OAAX,CAAsBG,CAAtB,CAAiC,SAAjC,CAA4C,GAA5C,CAAZ,CAA8D,QAAQ,CAACG,CAAD,CACxEE,CADwE,CACxDC,CADwD,CAClD,CACxB,MAAsB,GAAtB,EAAIA,CAAA3F,OAAA,CAAY,CAAZ,CAAJ,CACS2F,CADT,CAGSD,CAHT,CAG0BC,CAJF,CADpB,CARoC,CAA9C,CAoBIb,EAAAxE,SAAAC,qBAAJ,GACEY,CADF,CACQA,CAAAgE,QAAA,CAAY,MAAZ,CAAoB,EAApB,CADR,EACmC,GADnC,CAMAhE,EAAA,CAAMA,CAAAgE,QAAA,CAAY,mBAAZ,CAAiC,GAAjC,CAENP,EAAAzD,IAAA,CAAaA,CAAAgE,QAAA,CAAY,QAAZ,CAAsB,IAAtB,CAIbvF,EAAA,CAAQwD,CAAR,CAAgB,QAAQ,CAACvD,CAAD,CAAQC,CAAR,CAAa,CAC9BgF,CAAA7D,UAAA,CAAenB,CAAf,CAAL,GACE8E,CAAAxB,OACA,CADgBwB,CAAAxB,OAChB,EADiC,EACjC,CAAAwB,CAAAxB,OAAA,CAActD,CAAd,CAAA,CAAqBD,CAFvB,CADmC,CAArC,CAnDgD,CADlC,CA+OlB,OAAOqB,EApSuC,CAApC,CAjBmB,CADnC,CA3VsC,CAArC,CAAD,CAspBG5B,MAtpBH,CAspBWA,MAAAC,QAtpBX;", +"sources":["angular-resource.js"], +"names":["window","angular","undefined","shallowClearAndCopy","src","dst","forEach","value","key","hasOwnProperty","charAt","$resourceMinErr","$$minErr","MEMBER_NAME_REGEX","module","provider","defaults","stripTrailingSlashes","actions","method","isArray","$get","$http","$q","Route","template","extend","urlParams","resourceFactory","url","paramDefaults","options","extractParams","data","actionParams","ids","isFunction","path","test","keys","split","i","ii","length","obj","defaultResponseInterceptor","response","resource","Resource","route","prototype","toJSON","Resource.prototype.toJSON","$promise","$resolved","action","name","hasBody","a1","a2","a3","a4","params","success","error","arguments","isInstanceCall","httpConfig","responseInterceptor","interceptor","responseErrorInterceptor","responseError","copy","setUrlParams","promise","then","item","push","noop","reject","headers","result","call","bind","Resource.bind","additionalParamDefaults","config","actionUrl","self","val","encodedVal","param","RegExp","replace","_","urlParam","isDefined","encodeURIComponent","match","p1","leadingSlashes","tail"] +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/bower.json new file mode 100644 index 000000000..a617e4668 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/bower.json @@ -0,0 +1,9 @@ +{ + "name": "angular-resource", + "version": "1.3.15", + "main": "./angular-resource.js", + "ignore": [], + "dependencies": { + "angular": "1.3.15" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/index.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/index.js new file mode 100644 index 000000000..fc4015298 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/index.js @@ -0,0 +1,2 @@ +require('./angular-resource'); +module.exports = 'ngResource'; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/package.json new file mode 100644 index 000000000..0e203827b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-resource/package.json @@ -0,0 +1,26 @@ +{ + "name": "angular-resource", + "version": "1.3.15", + "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" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/.bower.json new file mode 100644 index 000000000..879b42f52 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/.bower.json @@ -0,0 +1,33 @@ +{ + "name": "angular-ui-router", + "version": "0.2.13", + "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.2.13", + "_resolution": { + "type": "version", + "tag": "0.2.13", + "commit": "2e580f271defdec34f464aab0cca519e41d1ee33" + }, + "_source": "https://github.com/angular-ui/angular-ui-router-bower.git", + "_target": "0.2.13", + "_originalSource": "angular-ui-router" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/CHANGELOG.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/CHANGELOG.md new file mode 100644 index 000000000..e0848c38c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/CHANGELOG.md @@ -0,0 +1,197 @@ +<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)) diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/CONTRIBUTING.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/CONTRIBUTING.md new file mode 100644 index 000000000..63829a549 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/CONTRIBUTING.md @@ -0,0 +1,65 @@ + +# 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](http://angular-ui.github.io/ui-router/images/notactionable.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](http://angular-ui.github.io/ui-router/images/plunkrplease.png): Please [create a plunkr](http://bit.ly/UIR-Plunk) + - ![StackOverflow](http://angular-ui.github.io/ui-router/images/stackoverflow.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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/LICENSE b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/LICENSE new file mode 100644 index 000000000..939f8f8a7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2014 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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/README.md new file mode 100644 index 000000000..f02d83bcb --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/README.md @@ -0,0 +1,243 @@ +# 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.11](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)**) **|** +**[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)** + +--- + +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)) + - via **[Bower](http://bower.io/)**: by running `$ bower 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 **[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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/api/angular-ui-router.d.ts b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/api/angular-ui-router.d.ts new file mode 100644 index 000000000..55c5d5e07 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/api/angular-ui-router.d.ts @@ -0,0 +1,126 @@ +// Type definitions for Angular JS 1.1.5+ (ui.router module) +// Project: https://github.com/angular-ui/ui-router +// Definitions by: Michel Salib <https://github.com/michelsalib> +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module ng.ui { + + interface IState { + name?: string; + template?: string; + templateUrl?: any; // string || () => string + templateProvider?: any; // () => string || IPromise<string> + controller?: any; + controllerAs?: string; + controllerProvider?: any; + resolve?: {}; + url?: string; + params?: any; + views?: {}; + abstract?: boolean; + onEnter?: (...args: any[]) => void; + onExit?: (...args: any[]) => void; + data?: any; + reloadOnSearch?: boolean; + } + + interface ITypedState<T> extends IState { + data?: T; + } + + interface IStateProvider extends IServiceProvider { + state(name: string, config: IState): IStateProvider; + state(config: IState): IStateProvider; + decorator(name?: string, decorator?: (state: IState, parent: Function) => any): any; + } + + interface IUrlMatcher { + concat(pattern: string): IUrlMatcher; + exec(path: string, searchParams: {}): {}; + parameters(): string[]; + format(values: {}): string; + } + + interface IUrlMatcherFactory { + compile(pattern: string): IUrlMatcher; + isMatcher(o: any): boolean; + } + + interface IUrlRouterProvider extends IServiceProvider { + when(whenPath: RegExp, handler: Function): IUrlRouterProvider; + when(whenPath: RegExp, handler: any[]): IUrlRouterProvider; + when(whenPath: RegExp, toPath: string): IUrlRouterProvider; + when(whenPath: IUrlMatcher, hanlder: Function): IUrlRouterProvider; + when(whenPath: IUrlMatcher, handler: any[]): IUrlRouterProvider; + when(whenPath: IUrlMatcher, toPath: string): IUrlRouterProvider; + when(whenPath: string, handler: Function): IUrlRouterProvider; + when(whenPath: string, handler: any[]): IUrlRouterProvider; + when(whenPath: string, toPath: string): IUrlRouterProvider; + otherwise(handler: Function): IUrlRouterProvider; + otherwise(handler: any[]): IUrlRouterProvider; + otherwise(path: string): IUrlRouterProvider; + rule(handler: Function): IUrlRouterProvider; + rule(handler: any[]): IUrlRouterProvider; + } + + interface IStateOptions { + location?: any; + inherit?: boolean; + relative?: IState; + notify?: boolean; + reload?: boolean; + } + + interface IHrefOptions { + lossy?: boolean; + inherit?: boolean; + relative?: IState; + absolute?: boolean; + } + + interface IStateService { + go(to: string, params?: {}, options?: IStateOptions): IPromise<any>; + transitionTo(state: string, params?: {}, updateLocation?: boolean): void; + transitionTo(state: string, params?: {}, options?: IStateOptions): void; + includes(state: string, params?: {}): boolean; + is(state:string, params?: {}): boolean; + is(state: IState, params?: {}): boolean; + href(state: IState, params?: {}, options?: IHrefOptions): string; + href(state: string, params?: {}, options?: IHrefOptions): string; + get(state: string): IState; + get(): IState[]; + current: IState; + params: any; + reload(): void; + } + + interface IStateParamsService { + [key: string]: any; + } + + interface IStateParams { + [key: string]: any; + } + + interface IUrlRouterService { + /* + * 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(). + * + */ + sync(): void; + } + + interface IUiViewScrollProvider { + /* + * Reverts back to using the core $anchorScroll service for scrolling + * based on the url anchor. + */ + useAnchorScroll(): void; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/bower.json new file mode 100644 index 000000000..45e802a88 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/bower.json @@ -0,0 +1,23 @@ +{ + "name": "angular-ui-router", + "version": "0.2.13", + "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" + ] +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.js new file mode 100644 index 000000000..d2636f8ec --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.js @@ -0,0 +1,4232 @@ +/** + * State-based routing for AngularJS + * @version v0.2.13 + * @link http://angular-ui.github.com/ + * @license MIT License, http://www.opensource.org/licenses/MIT + */ + +/* commonjs package manager support (eg componentjs) */ +if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){ + module.exports = 'ui.router'; +} + +(function (window, angular, undefined) { +/*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; + +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 = []; + + angular.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].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']); + +/** + * @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 caues `$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); + + +/** + * @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); + +var $$UMFP; // reference to $UrlMatcherFactoryProvider + +/** + * @ngdoc object + * @name ui.router.util.type:UrlMatcher + * + * @description + * Matches URLs against patterns and extracts named parameters from the path or the search + * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list + * of search parameters. Multiple search parameter names are separated by '&'. Search parameters + * do not influence whether or not a URL is matched, but their values are passed through into + * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}. + * + * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace + * syntax, which optionally allows a regular expression for the parameter to be specified: + * + * * `':'` name - colon placeholder + * * `'*'` name - catch-all placeholder + * * `'{' name '}'` - curly placeholder + * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the + * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash. + * + * Parameter names may contain only word characters (latin letters, digits, and underscore) and + * must be unique within the pattern (across both path and search parameters). For colon + * placeholders or curly placeholders without an explicit regexp, a path parameter matches any + * number of characters other than '/'. For catch-all placeholders the path parameter matches + * any number of characters. + * + * Examples: + * + * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for + * trailing slashes, and patterns have to match the entire path, not just a prefix. + * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or + * '/user/bob/details'. The second path segment will be captured as the parameter 'id'. + * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax. + * * `'/user/{id:[^/]*}'` - Same as the previous example. + * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id + * parameter consists of 1 to 8 hex digits. + * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the + * path into the parameter 'path'. + * * `'/files/*path'` - ditto. + * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined + * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start + * + * @param {string} pattern The pattern to compile into a matcher. + * @param {Object} config A configuration object hash: + * @param {Object=} parentMatcher Used to concatenate the pattern/config onto + * an existing UrlMatcher + * + * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`. + * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`. + * + * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any + * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns + * non-null) will start with this prefix. + * + * @property {string} source The pattern that was passed into the constructor + * + * @property {string} sourcePath The path portion of the source property + * + * @property {string} sourceSearch The search portion of the source property + * + * @property {string} regex The constructed regex that will be used to match against the url when + * it is time to determine which url will match. + * + * @returns {Object} New `UrlMatcher` object + */ +function UrlMatcher(pattern, config, parentMatcher) { + config = extend({ params: {} }, isObject(config) ? config : {}); + + // Find all placeholders and create a compiled pattern, using either classic or curly syntax: + // '*' name + // ':' name + // '{' name '}' + // '{' name ':' regexp '}' + // The regular expression is somewhat complicated due to the need to allow curly braces + // inside the regular expression. The placeholder regexp breaks down as follows: + // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case) + // \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case + // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either + // [^{}\\]+ - anything other than curly braces or backslash + // \\. - a backslash escape + // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms + var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + compiled = '^', last = 0, m, + segments = this.segments = [], + parentParams = parentMatcher ? parentMatcher.params : {}, + params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(), + paramNames = []; + + function addParameter(id, type, config, location) { + paramNames.push(id); + if (parentParams[id]) return parentParams[id]; + if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); + if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); + params[id] = new $$UMFP.Param(id, type, config, location); + return params[id]; + } + + function quoteRegExp(string, pattern, squash) { + var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); + if (!pattern) return result; + switch(squash) { + case false: surroundPattern = ['(', ')']; break; + case true: surroundPattern = ['?(', ')?']; break; + default: surroundPattern = ['(' + squash + "|", ')?']; break; + } + return result + surroundPattern[0] + pattern + surroundPattern[1]; + } + + this.source = pattern; + + // Split into static segments separated by path parameter placeholders. + // The number of segments is always 1 more than the number of parameters. + function matchDetails(m, isSearch) { + var id, regexp, segment, type, cfg, arrayMode; + id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null + cfg = config.params[id]; + segment = pattern.substring(last, m.index); + regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null); + type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp) }); + return { + id: id, regexp: regexp, segment: segment, type: type, cfg: cfg + }; + } + + var p, param, segment; + while ((m = placeholder.exec(pattern))) { + p = matchDetails(m, false); + if (p.segment.indexOf('?') >= 0) break; // we're into the search part + + param = addParameter(p.id, p.type, p.cfg, "path"); + compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash); + segments.push(p.segment); + last = placeholder.lastIndex; + } + segment = pattern.substring(last); + + // Find any search parameter names and remove them from the last segment + var i = segment.indexOf('?'); + + if (i >= 0) { + var search = this.sourceSearch = segment.substring(i); + segment = segment.substring(0, i); + this.sourcePath = pattern.substring(0, last + i); + + if (search.length > 0) { + last = 0; + while ((m = searchPlaceholder.exec(search))) { + p = matchDetails(m, true); + param = addParameter(p.id, p.type, p.cfg, "search"); + last = placeholder.lastIndex; + // check if ?& + } + } + } else { + this.sourcePath = pattern; + this.sourceSearch = ''; + } + + compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$'; + segments.push(segment); + + this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined); + this.prefix = segments[0]; + this.$$paramNames = paramNames; +} + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#concat + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Returns a new matcher for a pattern constructed by appending the path part and adding the + * search parameters of the specified pattern to this pattern. The current pattern is not + * modified. This can be understood as creating a pattern for URLs that are relative to (or + * suffixes of) the current pattern. + * + * @example + * The following two matchers are equivalent: + * <pre> + * new UrlMatcher('/user/{id}?q').concat('/details?date'); + * new UrlMatcher('/user/{id}/details?q&date'); + * </pre> + * + * @param {string} pattern The pattern to append. + * @param {Object} config An object hash of the configuration for the matcher. + * @returns {UrlMatcher} A matcher for the concatenated pattern. + */ +UrlMatcher.prototype.concat = function (pattern, config) { + // Because order of search parameters is irrelevant, we can add our own search + // parameters to the end of the new pattern. Parse the new pattern by itself + // and then join the bits together, but it's much easier to do this on a string level. + var defaultConfig = { + caseInsensitive: $$UMFP.caseInsensitive(), + strict: $$UMFP.strictMode(), + squash: $$UMFP.defaultSquashPolicy() + }; + return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this); +}; + +UrlMatcher.prototype.toString = function () { + return this.source; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#exec + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Tests the specified path against this matcher, and returns an object containing the captured + * parameter values, or null if the path does not match. The returned object contains the values + * of any search parameters that are mentioned in the pattern, but their value may be null if + * they are not present in `searchParams`. This means that search parameters are always treated + * as optional. + * + * @example + * <pre> + * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { + * x: '1', q: 'hello' + * }); + * // returns { id: 'bob', q: 'hello', r: null } + * </pre> + * + * @param {string} path The URL path to match, e.g. `$location.path()`. + * @param {Object} searchParams URL search parameters, e.g. `$location.search()`. + * @returns {Object} The captured parameter values. + */ +UrlMatcher.prototype.exec = function (path, searchParams) { + var m = this.regexp.exec(path); + if (!m) return null; + searchParams = searchParams || {}; + + var paramNames = this.parameters(), nTotal = paramNames.length, + nPath = this.segments.length - 1, + values = {}, i, j, cfg, paramName; + + if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'"); + + function decodePathArray(string) { + function reverseString(str) { return str.split("").reverse().join(""); } + function unquoteDashes(str) { return str.replace(/\\-/, "-"); } + + var split = reverseString(string).split(/-(?!\\)/); + var allReversed = map(split, reverseString); + return map(allReversed, unquoteDashes).reverse(); + } + + for (i = 0; i < nPath; i++) { + paramName = paramNames[i]; + var param = this.params[paramName]; + var paramVal = m[i+1]; + // if the param value matches a pre-replace pair, replace the value before decoding. + for (j = 0; j < param.replace; j++) { + if (param.replace[j].from === paramVal) paramVal = param.replace[j].to; + } + if (paramVal && param.array === true) paramVal = decodePathArray(paramVal); + values[paramName] = param.value(paramVal); + } + for (/**/; i < nTotal; i++) { + paramName = paramNames[i]; + values[paramName] = this.params[paramName].value(searchParams[paramName]); + } + + return values; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#parameters + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Returns the names of all path and search parameters of this pattern in an unspecified order. + * + * @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the + * pattern has no parameters, an empty array is returned. + */ +UrlMatcher.prototype.parameters = function (param) { + if (!isDefined(param)) return this.$$paramNames; + return this.params[param] || null; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#validate + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Checks an object hash of parameters to validate their correctness according to the parameter + * types of this `UrlMatcher`. + * + * @param {Object} params The object hash of parameters to validate. + * @returns {boolean} Returns `true` if `params` validates, otherwise `false`. + */ +UrlMatcher.prototype.validates = function (params) { + return this.params.$$validates(params); +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#format + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Creates a URL that matches this pattern by substituting the specified values + * for the path and search parameters. Null values for path parameters are + * treated as empty strings. + * + * @example + * <pre> + * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' }); + * // returns '/user/bob?q=yes' + * </pre> + * + * @param {Object} values the values to substitute for the parameters in this pattern. + * @returns {string} the formatted URL (path and optionally search part). + */ +UrlMatcher.prototype.format = function (values) { + values = values || {}; + var segments = this.segments, params = this.parameters(), paramset = this.params; + if (!this.validates(values)) return null; + + var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0]; + + function encodeDashes(str) { // Replace dashes with encoded "\-" + return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); }); + } + + for (i = 0; i < nTotal; i++) { + var isPathParam = i < nPath; + var name = params[i], param = paramset[name], value = param.value(values[name]); + var isDefaultValue = param.isOptional && param.type.equals(param.value(), value); + var squash = isDefaultValue ? param.squash : false; + var encoded = param.type.encode(value); + + if (isPathParam) { + var nextSegment = segments[i + 1]; + if (squash === false) { + if (encoded != null) { + if (isArray(encoded)) { + result += map(encoded, encodeDashes).join("-"); + } else { + result += encodeURIComponent(encoded); + } + } + result += nextSegment; + } else if (squash === true) { + var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/; + result += nextSegment.match(capture)[1]; + } else if (isString(squash)) { + result += squash + nextSegment; + } + } else { + if (encoded == null || (isDefaultValue && squash !== false)) continue; + if (!isArray(encoded)) encoded = [ encoded ]; + encoded = map(encoded, encodeURIComponent).join('&' + name + '='); + result += (search ? '&' : '?') + (name + '=' + encoded); + search = true; + } + } + + return result; +}; + +/** + * @ngdoc object + * @name ui.router.util.type:Type + * + * @description + * Implements an interface to define custom parameter types that can be decoded from and encoded to + * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`} + * objects when matching or formatting URLs, or comparing or validating parameter values. + * + * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more + * information on registering custom types. + * + * @param {Object} config A configuration object which contains the custom type definition. The object's + * properties will override the default methods and/or pattern in `Type`'s public interface. + * @example + * <pre> + * { + * decode: function(val) { return parseInt(val, 10); }, + * encode: function(val) { return val && val.toString(); }, + * equals: function(a, b) { return this.is(a) && a === b; }, + * is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; }, + * pattern: /\d+/ + * } + * </pre> + * + * @property {RegExp} pattern The regular expression pattern used to match values of this type when + * coming from a substring of a URL. + * + * @returns {Object} Returns a new `Type` object. + */ +function Type(config) { + extend(this, config); +} + +/** + * @ngdoc function + * @name ui.router.util.type:Type#is + * @methodOf ui.router.util.type:Type + * + * @description + * Detects whether a value is of a particular type. Accepts a native (decoded) value + * and determines whether it matches the current `Type` object. + * + * @param {*} val The value to check. + * @param {string} key Optional. If the type check is happening in the context of a specific + * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the + * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects. + * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`. + */ +Type.prototype.is = function(val, key) { + return true; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#encode + * @methodOf ui.router.util.type:Type + * + * @description + * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the + * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it + * only needs to be a representation of `val` that has been coerced to a string. + * + * @param {*} val The value to encode. + * @param {string} key The name of the parameter in which `val` is stored. Can be used for + * meta-programming of `Type` objects. + * @returns {string} Returns a string representation of `val` that can be encoded in a URL. + */ +Type.prototype.encode = function(val, key) { + return val; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#decode + * @methodOf ui.router.util.type:Type + * + * @description + * Converts a parameter value (from URL string or transition param) to a custom/native value. + * + * @param {string} val The URL parameter value to decode. + * @param {string} key The name of the parameter in which `val` is stored. Can be used for + * meta-programming of `Type` objects. + * @returns {*} Returns a custom representation of the URL parameter value. + */ +Type.prototype.decode = function(val, key) { + return val; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#equals + * @methodOf ui.router.util.type:Type + * + * @description + * Determines whether two decoded values are equivalent. + * + * @param {*} a A value to compare against. + * @param {*} b A value to compare against. + * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`. + */ +Type.prototype.equals = function(a, b) { + return a == b; +}; + +Type.prototype.$subPattern = function() { + var sub = this.pattern.toString(); + return sub.substr(1, sub.length - 2); +}; + +Type.prototype.pattern = /.*/; + +Type.prototype.toString = function() { return "{Type:" + this.name + "}"; }; + +/* + * Wraps an existing custom Type as an array of Type, depending on 'mode'. + * e.g.: + * - urlmatcher pattern "/path?{queryParam[]:int}" + * - url: "/path?queryParam=1&queryParam=2 + * - $stateParams.queryParam will be [1, 2] + * if `mode` is "auto", then + * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1 + * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2] + */ +Type.prototype.$asArray = function(mode, isSearch) { + if (!mode) return this; + if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only"); + return new ArrayType(this, mode); + + function ArrayType(type, mode) { + function bindTo(type, callbackName) { + return function() { + return type[callbackName].apply(type, arguments); + }; + } + + // Wrap non-array value as array + function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); } + // Unwrap array value for "auto" mode. Return undefined for empty array. + function arrayUnwrap(val) { + switch(val.length) { + case 0: return undefined; + case 1: return mode === "auto" ? val[0] : val; + default: return val; + } + } + function falsey(val) { return !val; } + + // Wraps type (.is/.encode/.decode) functions to operate on each value of an array + function arrayHandler(callback, allTruthyMode) { + return function handleArray(val) { + val = arrayWrap(val); + var result = map(val, callback); + if (allTruthyMode === true) + return filter(result, falsey).length === 0; + return arrayUnwrap(result); + }; + } + + // Wraps type (.equals) functions to operate on each value of an array + function arrayEqualsHandler(callback) { + return function handleArray(val1, val2) { + var left = arrayWrap(val1), right = arrayWrap(val2); + if (left.length !== right.length) return false; + for (var i = 0; i < left.length; i++) { + if (!callback(left[i], right[i])) return false; + } + return true; + }; + } + + this.encode = arrayHandler(bindTo(type, 'encode')); + this.decode = arrayHandler(bindTo(type, 'decode')); + this.is = arrayHandler(bindTo(type, 'is'), true); + this.equals = arrayEqualsHandler(bindTo(type, 'equals')); + this.pattern = type.pattern; + this.$arrayMode = mode; + } +}; + + + +/** + * @ngdoc object + * @name ui.router.util.$urlMatcherFactory + * + * @description + * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory + * is also available to providers under the name `$urlMatcherFactoryProvider`. + */ +function $UrlMatcherFactory() { + $$UMFP = this; + + var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false; + + function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; } + function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; } +// TODO: in 1.0, make string .is() return false if value is undefined by default. +// function regexpMatches(val) { /*jshint validthis:true */ return isDefined(val) && this.pattern.test(val); } + function regexpMatches(val) { /*jshint validthis:true */ return this.pattern.test(val); } + + var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = { + string: { + encode: valToString, + decode: valFromString, + is: regexpMatches, + pattern: /[^/]*/ + }, + int: { + encode: valToString, + decode: function(val) { return parseInt(val, 10); }, + is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; }, + pattern: /\d+/ + }, + bool: { + encode: function(val) { return val ? 1 : 0; }, + decode: function(val) { return parseInt(val, 10) !== 0; }, + is: function(val) { return val === true || val === false; }, + pattern: /0|1/ + }, + date: { + encode: function (val) { + if (!this.is(val)) + return undefined; + return [ val.getFullYear(), + ('0' + (val.getMonth() + 1)).slice(-2), + ('0' + val.getDate()).slice(-2) + ].join("-"); + }, + decode: function (val) { + if (this.is(val)) return val; + var match = this.capture.exec(val); + return match ? new Date(match[1], match[2] - 1, match[3]) : undefined; + }, + is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); }, + equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); }, + pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/, + capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/ + }, + json: { + encode: angular.toJson, + decode: angular.fromJson, + is: angular.isObject, + equals: angular.equals, + pattern: /[^/]*/ + }, + any: { // does not encode/decode + encode: angular.identity, + decode: angular.identity, + is: angular.identity, + equals: angular.equals, + pattern: /.*/ + } + }; + + function getDefaultConfig() { + return { + strict: isStrictMode, + caseInsensitive: isCaseInsensitive + }; + } + + function isInjectable(value) { + return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1]))); + } + + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + $UrlMatcherFactory.$$getDefaultValue = function(config) { + if (!isInjectable(config.value)) return config.value; + if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); + return injector.invoke(config.value); + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#caseInsensitive + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Defines whether URL matching should be case sensitive (the default behavior), or not. + * + * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`; + * @returns {boolean} the current value of caseInsensitive + */ + this.caseInsensitive = function(value) { + if (isDefined(value)) + isCaseInsensitive = value; + return isCaseInsensitive; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#strictMode + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Defines whether URLs should match trailing slashes, or not (the default behavior). + * + * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`. + * @returns {boolean} the current value of strictMode + */ + this.strictMode = function(value) { + if (isDefined(value)) + isStrictMode = value; + return isStrictMode; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Sets the default behavior when generating or matching URLs with default parameter values. + * + * @param {string} value A string that defines the default parameter URL squashing behavior. + * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL + * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the + * parameter is surrounded by slashes, squash (remove) one slash from the URL + * any other string, e.g. "~": When generating an href with a default parameter value, squash (remove) + * the parameter value from the URL and replace it with this string. + */ + this.defaultSquashPolicy = function(value) { + if (!isDefined(value)) return defaultSquashPolicy; + if (value !== true && value !== false && !isString(value)) + throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string"); + defaultSquashPolicy = value; + return value; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#compile + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern. + * + * @param {string} pattern The URL pattern. + * @param {Object} config The config object hash. + * @returns {UrlMatcher} The UrlMatcher. + */ + this.compile = function (pattern, config) { + return new UrlMatcher(pattern, extend(getDefaultConfig(), config)); + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#isMatcher + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Returns true if the specified object is a `UrlMatcher`, or false otherwise. + * + * @param {Object} object The object to perform the type check against. + * @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by + * implementing all the same methods. + */ + this.isMatcher = function (o) { + if (!isObject(o)) return false; + var result = true; + + forEach(UrlMatcher.prototype, function(val, name) { + if (isFunction(val)) { + result = result && (isDefined(o[name]) && isFunction(o[name])); + } + }); + return result; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#type + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to + * generate URLs with typed parameters. + * + * @param {string} name The type name. + * @param {Object|Function} definition The type definition. See + * {@link ui.router.util.type:Type `Type`} for information on the values accepted. + * @param {Object|Function} definitionFn (optional) A function that is injected before the app + * runtime starts. The result of this function is merged into the existing `definition`. + * See {@link ui.router.util.type:Type `Type`} for information on the values accepted. + * + * @returns {Object} Returns `$urlMatcherFactoryProvider`. + * + * @example + * This is a simple example of a custom type that encodes and decodes items from an + * array, using the array index as the URL-encoded value: + * + * <pre> + * var list = ['John', 'Paul', 'George', 'Ringo']; + * + * $urlMatcherFactoryProvider.type('listItem', { + * encode: function(item) { + * // Represent the list item in the URL using its corresponding index + * return list.indexOf(item); + * }, + * decode: function(item) { + * // Look up the list item by index + * return list[parseInt(item, 10)]; + * }, + * is: function(item) { + * // Ensure the item is valid by checking to see that it appears + * // in the list + * return list.indexOf(item) > -1; + * } + * }); + * + * $stateProvider.state('list', { + * url: "/list/{item:listItem}", + * controller: function($scope, $stateParams) { + * console.log($stateParams.item); + * } + * }); + * + * // ... + * + * // Changes URL to '/list/3', logs "Ringo" to the console + * $state.go('list', { item: "Ringo" }); + * </pre> + * + * This is a more complex example of a type that relies on dependency injection to + * interact with services, and uses the parameter name from the URL to infer how to + * handle encoding and decoding parameter values: + * + * <pre> + * // Defines a custom type that gets a value from a service, + * // where each service gets different types of values from + * // a backend API: + * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) { + * + * // Matches up services to URL parameter names + * var services = { + * user: Users, + * post: Posts + * }; + * + * return { + * encode: function(object) { + * // Represent the object in the URL using its unique ID + * return object.id; + * }, + * decode: function(value, key) { + * // Look up the object by ID, using the parameter + * // name (key) to call the correct service + * return services[key].findById(value); + * }, + * is: function(object, key) { + * // Check that object is a valid dbObject + * return angular.isObject(object) && object.id && services[key]; + * } + * equals: function(a, b) { + * // Check the equality of decoded objects by comparing + * // their unique IDs + * return a.id === b.id; + * } + * }; + * }); + * + * // In a config() block, you can then attach URLs with + * // type-annotated parameters: + * $stateProvider.state('users', { + * url: "/users", + * // ... + * }).state('users.item', { + * url: "/{user:dbObject}", + * controller: function($scope, $stateParams) { + * // $stateParams.user will now be an object returned from + * // the Users service + * }, + * // ... + * }); + * </pre> + */ + this.type = function (name, definition, definitionFn) { + if (!isDefined(definition)) return $types[name]; + if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined."); + + $types[name] = new Type(extend({ name: name }, definition)); + if (definitionFn) { + typeQueue.push({ name: name, def: definitionFn }); + if (!enqueue) flushTypeQueue(); + } + return this; + }; + + // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s + function flushTypeQueue() { + while(typeQueue.length) { + var type = typeQueue.shift(); + if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime."); + angular.extend($types[type.name], injector.invoke(type.def)); + } + } + + // Register default types. Store them in the prototype of $types. + forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); }); + $types = inherit($types, {}); + + /* No need to document $get, since it returns this */ + this.$get = ['$injector', function ($injector) { + injector = $injector; + enqueue = false; + flushTypeQueue(); + + forEach(defaultTypes, function(type, name) { + if (!$types[name]) $types[name] = new Type(type); + }); + return this; + }]; + + this.Param = function Param(id, type, config, location) { + var self = this; + config = unwrapShorthand(config); + type = getType(config, type, location); + var arrayMode = getArrayMode(); + type = arrayMode ? type.$asArray(arrayMode, location === "search") : type; + if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined) + config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to "" + var isOptional = config.value !== undefined; + var squash = getSquashPolicy(config, isOptional); + var replace = getReplace(config, arrayMode, isOptional, squash); + + function unwrapShorthand(config) { + var keys = isObject(config) ? objectKeys(config) : []; + var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 && + indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1; + if (isShorthand) config = { value: config }; + config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; }; + return config; + } + + function getType(config, urlType, location) { + if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations."); + if (urlType) return urlType; + if (!config.type) return (location === "config" ? $types.any : $types.string); + return config.type instanceof Type ? config.type : new Type(config.type); + } + + // array config: param name (param[]) overrides default settings. explicit config overrides param name. + function getArrayMode() { + var arrayDefaults = { array: (location === "search" ? "auto" : false) }; + var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {}; + return extend(arrayDefaults, arrayParamNomenclature, config).array; + } + + /** + * returns false, true, or the squash value to indicate the "default parameter url squash policy". + */ + function getSquashPolicy(config, isOptional) { + var squash = config.squash; + if (!isOptional || squash === false) return false; + if (!isDefined(squash) || squash == null) return defaultSquashPolicy; + if (squash === true || isString(squash)) return squash; + throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string"); + } + + function getReplace(config, arrayMode, isOptional, squash) { + var replace, configuredKeys, defaultPolicy = [ + { from: "", to: (isOptional || arrayMode ? undefined : "") }, + { from: null, to: (isOptional || arrayMode ? undefined : "") } + ]; + replace = isArray(config.replace) ? config.replace : []; + if (isString(squash)) + replace.push({ from: squash, to: undefined }); + configuredKeys = map(replace, function(item) { return item.from; } ); + return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace); + } + + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + function $$getDefaultValue() { + if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); + return injector.invoke(config.$$fn); + } + + /** + * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the + * default value, which may be the result of an injectable function. + */ + function $value(value) { + function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; } + function $replace(value) { + var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; }); + return replacement.length ? replacement[0] : value; + } + value = $replace(value); + return isDefined(value) ? self.type.decode(value) : $$getDefaultValue(); + } + + function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; } + + extend(this, { + id: id, + type: type, + location: location, + array: arrayMode, + squash: squash, + replace: replace, + isOptional: isOptional, + value: $value, + dynamic: undefined, + config: config, + toString: toString + }); + }; + + function ParamSet(params) { + extend(this, params || {}); + } + + ParamSet.prototype = { + $$new: function() { + return inherit(this, extend(new ParamSet(), { $$parent: this})); + }, + $$keys: function () { + var keys = [], chain = [], parent = this, + ignore = objectKeys(ParamSet.prototype); + while (parent) { chain.push(parent); parent = parent.$$parent; } + chain.reverse(); + forEach(chain, function(paramset) { + forEach(objectKeys(paramset), function(key) { + if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key); + }); + }); + return keys; + }, + $$values: function(paramValues) { + var values = {}, self = this; + forEach(self.$$keys(), function(key) { + values[key] = self[key].value(paramValues && paramValues[key]); + }); + return values; + }, + $$equals: function(paramValues1, paramValues2) { + var equal = true, self = this; + forEach(self.$$keys(), function(key) { + var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key]; + if (!self[key].type.equals(left, right)) equal = false; + }); + return equal; + }, + $$validates: function $$validate(paramValues) { + var result = true, isOptional, val, param, self = this; + + forEach(this.$$keys(), function(key) { + param = self[key]; + val = paramValues[key]; + isOptional = !val && param.isOptional; + result = result && (isOptional || !!param.type.is(val)); + }); + return result; + }, + $$parent: undefined + }; + + this.ParamSet = ParamSet; +} + +// Register as a provider so it's available to other providers +angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory); +angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]); + +/** + * @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 {object} 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|object} 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 handle 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|object} 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']; + function $get( $location, $rootScope, $injector, $browser) { + + 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; + 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) { + $location.url(urlMatcher.format(params || {})); + 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; + } + + var url = urlMatcher.format(params); + options = options || {}; + + if (!isHtml5 && url !== null) { + url = "#" + $locationProvider.hashPrefix() + url; + } + 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); + +/** + * @ngdoc object + * @name ui.router.state.$stateProvider + * + * @requires ui.router.router.$urlRouterProvider + * @requires ui.router.util.$urlMatcherFactoryProvider + * + * @description + * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely + * on state. + * + * A state corresponds to a "place" in the application in terms of the overall UI and + * navigation. A state describes (via the controller / template / view properties) what + * the UI looks like and does at that place. + * + * States often have things in common, and the primary way of factoring out these + * commonalities in this model is via the state hierarchy, i.e. parent/child states aka + * nested states. + * + * The `$stateProvider` provides interfaces to declare these states for your app. + */ +$StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider']; +function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { + + var root, states = {}, $state, queue = {}, abstractKey = 'abstract'; + + // Builds state properties from definition passed to registerState() + var stateBuilder = { + + // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined. + // state.children = []; + // if (parent) parent.children.push(state); + parent: function(state) { + if (isDefined(state.parent) && state.parent) return findState(state.parent); + // regex matches any valid composite state name + // would match "contact.list" but not "contacts" + var compositeName = /^(.+)\.[^.]+$/.exec(state.name); + return compositeName ? findState(compositeName[1]) : root; + }, + + // inherit 'data' from parent and override by own values (if any) + data: function(state) { + if (state.parent && state.parent.data) { + state.data = state.self.data = extend({}, state.parent.data, state.data); + } + return state.data; + }, + + // Build a URLMatcher if necessary, either via a relative or absolute URL + url: function(state) { + var url = state.url, config = { params: state.params || {} }; + + if (isString(url)) { + if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config); + return (state.parent.navigable || root).url.concat(url, config); + } + + if (!url || $urlMatcherFactory.isMatcher(url)) return url; + throw new Error("Invalid url '" + url + "' in state '" + state + "'"); + }, + + // Keep track of the closest ancestor state that has a URL (i.e. is navigable) + navigable: function(state) { + return state.url ? state : (state.parent ? state.parent.navigable : null); + }, + + // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params + ownParams: function(state) { + var params = state.url && state.url.params || new $$UMFP.ParamSet(); + forEach(state.params || {}, function(config, id) { + if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config"); + }); + return params; + }, + + // Derive parameters for this state and ensure they're a super-set of parent's parameters + params: function(state) { + return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet(); + }, + + // If there is no explicit multi-view configuration, make one up so we don't have + // to handle both cases in the view directive later. Note that having an explicit + // 'views' property will mean the default unnamed view properties are ignored. This + // is also a good time to resolve view names to absolute names, so everything is a + // straight lookup at link time. + views: function(state) { + var views = {}; + + forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) { + if (name.indexOf('@') < 0) name += '@' + state.parent.name; + views[name] = view; + }); + return views; + }, + + // Keep a full path from the root down to this state as this is needed for state activation. + path: function(state) { + return state.parent ? state.parent.path.concat(state) : []; // exclude root from path + }, + + // Speed up $state.contains() as it's used a lot + includes: function(state) { + var includes = state.parent ? extend({}, state.parent.includes) : {}; + includes[state.name] = true; + return includes; + }, + + $delegates: {} + }; + + function isRelative(stateName) { + return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0; + } + + function findState(stateOrName, base) { + if (!stateOrName) return undefined; + + var isStr = isString(stateOrName), + name = isStr ? stateOrName : stateOrName.name, + path = isRelative(name); + + if (path) { + if (!base) throw new Error("No reference point given for path '" + name + "'"); + base = findState(base); + + var rel = name.split("."), i = 0, pathLength = rel.length, current = base; + + for (; i < pathLength; i++) { + if (rel[i] === "" && i === 0) { + current = base; + continue; + } + if (rel[i] === "^") { + if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'"); + current = current.parent; + continue; + } + break; + } + rel = rel.slice(i).join("."); + name = current.name + (current.name && rel ? "." : "") + rel; + } + var state = states[name]; + + if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) { + return state; + } + return undefined; + } + + function queueState(parentName, state) { + if (!queue[parentName]) { + queue[parentName] = []; + } + queue[parentName].push(state); + } + + function flushQueuedChildren(parentName) { + var queued = queue[parentName] || []; + while(queued.length) { + registerState(queued.shift()); + } + } + + function registerState(state) { + // Wrap a new object around the state so we can store our private details easily. + state = inherit(state, { + self: state, + resolve: state.resolve || {}, + toString: function() { return this.name; } + }); + + var name = state.name; + if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name"); + if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined"); + + // Get parent name + var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.')) + : (isString(state.parent)) ? state.parent + : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name + : ''; + + // If parent is not registered yet, add state to queue and register later + if (parentName && !states[parentName]) { + return queueState(parentName, state.self); + } + + for (var key in stateBuilder) { + if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]); + } + states[name] = state; + + // Register the state in the global state list and with $urlRouter if necessary. + if (!state[abstractKey] && state.url) { + $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) { + if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) { + $state.transitionTo(state, $match, { inherit: true, location: false }); + } + }]); + } + + // Register any queued children + flushQueuedChildren(name); + + return state; + } + + // Checks text to see if it looks like a glob. + function isGlob (text) { + return text.indexOf('*') > -1; + } + + // Returns true if glob matches current $state name. + function doesStateMatchGlob (glob) { + var globSegments = glob.split('.'), + segments = $state.$current.name.split('.'); + + //match greedy starts + if (globSegments[0] === '**') { + segments = segments.slice(indexOf(segments, globSegments[1])); + segments.unshift('**'); + } + //match greedy ends + if (globSegments[globSegments.length - 1] === '**') { + segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE); + segments.push('**'); + } + + if (globSegments.length != segments.length) { + return false; + } + + //match single stars + for (var i = 0, l = globSegments.length; i < l; i++) { + if (globSegments[i] === '*') { + segments[i] = '*'; + } + } + + return segments.join('') === globSegments.join(''); + } + + + // Implicit root state that is always active + root = registerState({ + name: '', + url: '^', + views: null, + 'abstract': true + }); + root.navigable = null; + + + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#decorator + * @methodOf ui.router.state.$stateProvider + * + * @description + * Allows you to extend (carefully) or override (at your own peril) the + * `stateBuilder` object used internally by `$stateProvider`. This can be used + * to add custom functionality to ui-router, for example inferring templateUrl + * based on the state name. + * + * When passing only a name, it returns the current (original or decorated) builder + * function that matches `name`. + * + * The builder functions that can be decorated are listed below. Though not all + * necessarily have a good use case for decoration, that is up to you to decide. + * + * In addition, users can attach custom decorators, which will generate new + * properties within the state's internal definition. There is currently no clear + * use-case for this beyond accessing internal states (i.e. $state.$current), + * however, expect this to become increasingly relevant as we introduce additional + * meta-programming features. + * + * **Warning**: Decorators should not be interdependent because the order of + * execution of the builder functions in non-deterministic. Builder functions + * should only be dependent on the state definition object and super function. + * + * + * Existing builder functions and current return values: + * + * - **parent** `{object}` - returns the parent state object. + * - **data** `{object}` - returns state data, including any inherited data that is not + * overridden by own values (if any). + * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher} + * or `null`. + * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is + * navigable). + * - **params** `{object}` - returns an array of state params that are ensured to + * be a super-set of parent's params. + * - **views** `{object}` - returns a views object where each key is an absolute view + * name (i.e. "viewName@stateName") and each value is the config object + * (template, controller) for the view. Even when you don't use the views object + * explicitly on a state config, one is still created for you internally. + * So by decorating this builder function you have access to decorating template + * and controller properties. + * - **ownParams** `{object}` - returns an array of params that belong to the state, + * not including any params defined by ancestor states. + * - **path** `{string}` - returns the full path from the root down to this state. + * Needed for state activation. + * - **includes** `{object}` - returns an object that includes every state that + * would pass a `$state.includes()` test. + * + * @example + * <pre> + * // Override the internal 'views' builder with a function that takes the state + * // definition, and a reference to the internal function being overridden: + * $stateProvider.decorator('views', function (state, parent) { + * var result = {}, + * views = parent(state); + * + * angular.forEach(views, function (config, name) { + * var autoName = (state.name + '.' + name).replace('.', '/'); + * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html'; + * result[name] = config; + * }); + * return result; + * }); + * + * $stateProvider.state('home', { + * views: { + * 'contact.list': { controller: 'ListController' }, + * 'contact.item': { controller: 'ItemController' } + * } + * }); + * + * // ... + * + * $state.go('home'); + * // Auto-populates list and item views with /partials/home/contact/list.html, + * // and /partials/home/contact/item.html, respectively. + * </pre> + * + * @param {string} name The name of the builder function to decorate. + * @param {object} func A function that is responsible for decorating the original + * builder function. The function receives two parameters: + * + * - `{object}` - state - The state config object. + * - `{object}` - super - The original builder function. + * + * @return {object} $stateProvider - $stateProvider instance + */ + this.decorator = decorator; + function decorator(name, func) { + /*jshint validthis: true */ + if (isString(name) && !isDefined(func)) { + return stateBuilder[name]; + } + if (!isFunction(func) || !isString(name)) { + return this; + } + if (stateBuilder[name] && !stateBuilder.$delegates[name]) { + stateBuilder.$delegates[name] = stateBuilder[name]; + } + stateBuilder[name] = func; + return this; + } + + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#state + * @methodOf ui.router.state.$stateProvider + * + * @description + * Registers a state configuration under a given state name. The stateConfig object + * has the following acceptable properties. + * + * @param {string} name A unique state name, e.g. "home", "about", "contacts". + * To create a parent/child state use a dot, e.g. "about.sales", "home.newest". + * @param {object} stateConfig State configuration object. + * @param {string|function=} stateConfig.template + * <a id='template'></a> + * html template as a string or a function that returns + * an html template as a string which should be used by the uiView directives. This property + * takes precedence over templateUrl. + * + * If `template` is a function, it will be called with the following parameters: + * + * - {array.<object>} - state parameters extracted from the current $location.path() by + * applying the current state + * + * <pre>template: + * "<h1>inline template definition</h1>" + + * "<div ui-view></div>"</pre> + * <pre>template: function(params) { + * return "<h1>generated template</h1>"; }</pre> + * </div> + * + * @param {string|function=} stateConfig.templateUrl + * <a id='templateUrl'></a> + * + * path or function that returns a path to an html + * template that should be used by uiView. + * + * If `templateUrl` is a function, it will be called with the following parameters: + * + * - {array.<object>} - state parameters extracted from the current $location.path() by + * applying the current state + * + * <pre>templateUrl: "home.html"</pre> + * <pre>templateUrl: function(params) { + * return myTemplates[params.pageId]; }</pre> + * + * @param {function=} stateConfig.templateProvider + * <a id='templateProvider'></a> + * Provider function that returns HTML content string. + * <pre> templateProvider: + * function(MyTemplateService, params) { + * return MyTemplateService.getTemplate(params.pageId); + * }</pre> + * + * @param {string|function=} stateConfig.controller + * <a id='controller'></a> + * + * Controller fn that should be associated with newly + * related scope or the name of a registered controller if passed as a string. + * Optionally, the ControllerAs may be declared here. + * <pre>controller: "MyRegisteredController"</pre> + * <pre>controller: + * "MyRegisteredController as fooCtrl"}</pre> + * <pre>controller: function($scope, MyService) { + * $scope.data = MyService.getData(); }</pre> + * + * @param {function=} stateConfig.controllerProvider + * <a id='controllerProvider'></a> + * + * Injectable provider function that returns the actual controller or string. + * <pre>controllerProvider: + * function(MyResolveData) { + * if (MyResolveData.foo) + * return "FooCtrl" + * else if (MyResolveData.bar) + * return "BarCtrl"; + * else return function($scope) { + * $scope.baz = "Qux"; + * } + * }</pre> + * + * @param {string=} stateConfig.controllerAs + * <a id='controllerAs'></a> + * + * A controller alias name. If present the controller will be + * published to scope under the controllerAs name. + * <pre>controllerAs: "myCtrl"</pre> + * + * @param {object=} stateConfig.resolve + * <a id='resolve'></a> + * + * An optional map<string, function> of dependencies which + * should be injected into the controller. If any of these dependencies are promises, + * the router will wait for them all to be resolved before the controller is instantiated. + * If all the promises are resolved successfully, the $stateChangeSuccess event is fired + * and the values of the resolved promises are injected into any controllers that reference them. + * If any of the promises are rejected the $stateChangeError event is fired. + * + * The map object is: + * + * - key - {string}: name of dependency to be injected into controller + * - factory - {string|function}: If string then it is alias for service. Otherwise if function, + * it is injected and return value it treated as dependency. If result is a promise, it is + * resolved before its value is injected into controller. + * + * <pre>resolve: { + * myResolve1: + * function($http, $stateParams) { + * return $http.get("/api/foos/"+stateParams.fooID); + * } + * }</pre> + * + * @param {string=} stateConfig.url + * <a id='url'></a> + * + * A url fragment with optional parameters. When a state is navigated or + * transitioned to, the `$stateParams` service will be populated with any + * parameters that were passed. + * + * examples: + * <pre>url: "/home" + * url: "/users/:userid" + * url: "/books/{bookid:[a-zA-Z_-]}" + * url: "/books/{categoryid:int}" + * url: "/books/{publishername:string}/{categoryid:int}" + * url: "/messages?before&after" + * url: "/messages?{before:date}&{after:date}"</pre> + * url: "/messages/:mailboxid?{before:date}&{after:date}" + * + * @param {object=} stateConfig.views + * <a id='views'></a> + * an optional map<string, object> which defined multiple views, or targets views + * manually/explicitly. + * + * Examples: + * + * Targets three named `ui-view`s in the parent state's template + * <pre>views: { + * header: { + * controller: "headerCtrl", + * templateUrl: "header.html" + * }, body: { + * controller: "bodyCtrl", + * templateUrl: "body.html" + * }, footer: { + * controller: "footCtrl", + * templateUrl: "footer.html" + * } + * }</pre> + * + * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template. + * <pre>views: { + * 'header@top': { + * controller: "msgHeaderCtrl", + * templateUrl: "msgHeader.html" + * }, 'body': { + * controller: "messagesCtrl", + * templateUrl: "messages.html" + * } + * }</pre> + * + * @param {boolean=} [stateConfig.abstract=false] + * <a id='abstract'></a> + * An abstract state will never be directly activated, + * but can provide inherited properties to its common children states. + * <pre>abstract: true</pre> + * + * @param {function=} stateConfig.onEnter + * <a id='onEnter'></a> + * + * Callback function for when a state is entered. Good way + * to trigger an action or dispatch an event, such as opening a dialog. + * If minifying your scripts, make sure to explictly annotate this function, + * because it won't be automatically annotated by your build tools. + * + * <pre>onEnter: function(MyService, $stateParams) { + * MyService.foo($stateParams.myParam); + * }</pre> + * + * @param {function=} stateConfig.onExit + * <a id='onExit'></a> + * + * Callback function for when a state is exited. Good way to + * trigger an action or dispatch an event, such as opening a dialog. + * If minifying your scripts, make sure to explictly annotate this function, + * because it won't be automatically annotated by your build tools. + * + * <pre>onExit: function(MyService, $stateParams) { + * MyService.cleanup($stateParams.myParam); + * }</pre> + * + * @param {boolean=} [stateConfig.reloadOnSearch=true] + * <a id='reloadOnSearch'></a> + * + * If `false`, will not retrigger the same state + * just because a search/query parameter has changed (via $location.search() or $location.hash()). + * Useful for when you'd like to modify $location.search() without triggering a reload. + * <pre>reloadOnSearch: false</pre> + * + * @param {object=} stateConfig.data + * <a id='data'></a> + * + * Arbitrary data object, useful for custom configuration. The parent state's `data` is + * prototypally inherited. In other words, adding a data property to a state adds it to + * the entire subtree via prototypal inheritance. + * + * <pre>data: { + * requiredRole: 'foo' + * } </pre> + * + * @param {object=} stateConfig.params + * <a id='params'></a> + * + * A map which optionally configures parameters declared in the `url`, or + * defines additional non-url parameters. For each parameter being + * configured, add a configuration object keyed to the name of the parameter. + * + * Each parameter configuration object may contain the following properties: + * + * - ** value ** - {object|function=}: specifies the default value for this + * parameter. This implicitly sets this parameter as optional. + * + * When UI-Router routes to a state and no value is + * specified for this parameter in the URL or transition, the + * default value will be used instead. If `value` is a function, + * it will be injected and invoked, and the return value used. + * + * *Note*: `undefined` is treated as "no default value" while `null` + * is treated as "the default value is `null`". + * + * *Shorthand*: If you only need to configure the default value of the + * parameter, you may use a shorthand syntax. In the **`params`** + * map, instead mapping the param name to a full parameter configuration + * object, simply set map it to the default parameter value, e.g.: + * + * <pre>// define a parameter's default value + * params: { + * param1: { value: "defaultValue" } + * } + * // shorthand default values + * params: { + * param1: "defaultValue", + * param2: "param2Default" + * }</pre> + * + * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be + * treated as an array of values. If you specified a Type, the value will be + * treated as an array of the specified Type. Note: query parameter values + * default to a special `"auto"` mode. + * + * For query parameters in `"auto"` mode, if multiple values for a single parameter + * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values + * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if + * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single + * value (e.g.: `{ foo: '1' }`). + * + * <pre>params: { + * param1: { array: true } + * }</pre> + * + * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when + * the current parameter value is the same as the default value. If `squash` is not set, it uses the + * configured default squash policy. + * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`}) + * + * There are three squash settings: + * + * - false: The parameter's default value is not squashed. It is encoded and included in the URL + * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed + * by slashes in the state's `url` declaration, then one of those slashes are omitted. + * This can allow for cleaner looking URLs. + * - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of your choice. + * + * <pre>params: { + * param1: { + * value: "defaultId", + * squash: true + * } } + * // squash "defaultValue" to "~" + * params: { + * param1: { + * value: "defaultValue", + * squash: "~" + * } } + * </pre> + * + * + * @example + * <pre> + * // Some state name examples + * + * // stateName can be a single top-level name (must be unique). + * $stateProvider.state("home", {}); + * + * // Or it can be a nested state name. This state is a child of the + * // above "home" state. + * $stateProvider.state("home.newest", {}); + * + * // Nest states as deeply as needed. + * $stateProvider.state("home.newest.abc.xyz.inception", {}); + * + * // state() returns $stateProvider, so you can chain state declarations. + * $stateProvider + * .state("home", {}) + * .state("about", {}) + * .state("contacts", {}); + * </pre> + * + */ + this.state = state; + function state(name, definition) { + /*jshint validthis: true */ + if (isObject(name)) definition = name; + else definition.name = name; + registerState(definition); + return this; + } + + /** + * @ngdoc object + * @name ui.router.state.$state + * + * @requires $rootScope + * @requires $q + * @requires ui.router.state.$view + * @requires $injector + * @requires ui.router.util.$resolve + * @requires ui.router.state.$stateParams + * @requires ui.router.router.$urlRouter + * + * @property {object} params A param object, e.g. {sectionId: section.id)}, that + * you'd like to test against the current active state. + * @property {object} current A reference to the state's config object. However + * you passed it in. Useful for accessing custom data. + * @property {object} transition Currently pending transition. A promise that'll + * resolve or reject. + * + * @description + * `$state` service is responsible for representing states as well as transitioning + * between them. It also provides interfaces to ask for current state or even states + * you're coming from. + */ + this.$get = $get; + $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory']; + function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $urlRouter, $location, $urlMatcherFactory) { + + var TransitionSuperseded = $q.reject(new Error('transition superseded')); + var TransitionPrevented = $q.reject(new Error('transition prevented')); + var TransitionAborted = $q.reject(new Error('transition aborted')); + var TransitionFailed = $q.reject(new Error('transition failed')); + + // Handles the case where a state which is the target of a transition is not found, and the user + // can optionally retry or defer the transition + function handleRedirect(redirect, state, params, options) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateNotFound + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when a requested state **cannot be found** using the provided state name during transition. + * The event is broadcast allowing any handlers a single chance to deal with the error (usually by + * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler, + * you can see its three properties in the example. You can use `event.preventDefault()` to abort the + * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value. + * + * @param {Object} event Event object. + * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties. + * @param {State} fromState Current state object. + * @param {Object} fromParams Current state params. + * + * @example + * + * <pre> + * // somewhere, assume lazy.state has not been defined + * $state.go("lazy.state", {a:1, b:2}, {inherit:false}); + * + * // somewhere else + * $scope.$on('$stateNotFound', + * function(event, unfoundState, fromState, fromParams){ + * console.log(unfoundState.to); // "lazy.state" + * console.log(unfoundState.toParams); // {a:1, b:2} + * console.log(unfoundState.options); // {inherit:false} + default options + * }) + * </pre> + */ + var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params); + + if (evt.defaultPrevented) { + $urlRouter.update(); + return TransitionAborted; + } + + if (!evt.retry) { + return null; + } + + // Allow the handler to return a promise to defer state lookup retry + if (options.$retry) { + $urlRouter.update(); + return TransitionFailed; + } + var retryTransition = $state.transition = $q.when(evt.retry); + + retryTransition.then(function() { + if (retryTransition !== $state.transition) return TransitionSuperseded; + redirect.options.$retry = true; + return $state.transitionTo(redirect.to, redirect.toParams, redirect.options); + }, function() { + return TransitionAborted; + }); + $urlRouter.update(); + + return retryTransition; + } + + root.locals = { resolve: null, globals: { $stateParams: {} } }; + + $state = { + params: {}, + current: root.self, + $current: root, + transition: null + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#reload + * @methodOf ui.router.state.$state + * + * @description + * A method that force reloads the current state. All resolves are re-resolved, events are not re-fired, + * and controllers reinstantiated (bug with controllers reinstantiating right now, fixing soon). + * + * @example + * <pre> + * var app angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.reload = function(){ + * $state.reload(); + * } + * }); + * </pre> + * + * `reload()` is just an alias for: + * <pre> + * $state.transitionTo($state.current, $stateParams, { + * reload: true, inherit: false, notify: true + * }); + * </pre> + * + * @returns {promise} A promise representing the state of the new transition. See + * {@link ui.router.state.$state#methods_go $state.go}. + */ + $state.reload = function reload() { + return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true }); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#go + * @methodOf ui.router.state.$state + * + * @description + * Convenience method for transitioning to a new state. `$state.go` calls + * `$state.transitionTo` internally but automatically sets options to + * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. + * This allows you to easily use an absolute or relative to path and specify + * only the parameters you'd like to update (while letting unspecified parameters + * inherit from the currently active ancestor states). + * + * @example + * <pre> + * var app = angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.changeState = function () { + * $state.go('contact.detail'); + * }; + * }); + * </pre> + * <img src='../ngdoc_assets/StateGoExamples.png'/> + * + * @param {string} to Absolute state name or relative state path. Some examples: + * + * - `$state.go('contact.detail')` - will go to the `contact.detail` state + * - `$state.go('^')` - will go to a parent state + * - `$state.go('^.sibling')` - will go to a sibling state + * - `$state.go('.child.grandchild')` - will go to grandchild state + * + * @param {object=} params A map of the parameters that will be sent to the state, + * will populate $stateParams. Any parameters that are not specified will be inherited from currently + * defined parameters. This allows, for example, going to a sibling state that shares parameters + * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e. + * transitioning to a sibling will get you the parameters for all parents, transitioning to a child + * will get you all current parameters, etc. + * @param {object=} options Options object. The options are: + * + * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` + * will not. If string, must be `"replace"`, which will update url and also replace last history record. + * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. + * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params + * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd + * use this when you want to force a reload when *everything* is the same, including search params. + * + * @returns {promise} A promise representing the state of the new transition. + * + * Possible success values: + * + * - $state.current + * + * <br/>Possible rejection values: + * + * - 'transition superseded' - when a newer transition has been started after this one + * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener + * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or + * when a `$stateNotFound` `event.retry` promise errors. + * - 'transition failed' - when a state has been unsuccessfully found after 2 tries. + * - *resolve error* - when an error has occurred with a `resolve` + * + */ + $state.go = function go(to, params, options) { + return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options)); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#transitionTo + * @methodOf ui.router.state.$state + * + * @description + * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go} + * uses `transitionTo` internally. `$state.go` is recommended in most situations. + * + * @example + * <pre> + * var app = angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.changeState = function () { + * $state.transitionTo('contact.detail'); + * }; + * }); + * </pre> + * + * @param {string} to State name. + * @param {object=} toParams A map of the parameters that will be sent to the state, + * will populate $stateParams. + * @param {object=} options Options object. The options are: + * + * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` + * will not. If string, must be `"replace"`, which will update url and also replace last history record. + * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. + * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params + * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd + * use this when you want to force a reload when *everything* is the same, including search params. + * + * @returns {promise} A promise representing the state of the new transition. See + * {@link ui.router.state.$state#methods_go $state.go}. + */ + $state.transitionTo = function transitionTo(to, toParams, options) { + toParams = toParams || {}; + options = extend({ + location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false + }, options || {}); + + var from = $state.$current, fromParams = $state.params, fromPath = from.path; + var evt, toState = findState(to, options.relative); + + if (!isDefined(toState)) { + var redirect = { to: to, toParams: toParams, options: options }; + var redirectResult = handleRedirect(redirect, from.self, fromParams, options); + + if (redirectResult) { + return redirectResult; + } + + // Always retry once if the $stateNotFound was not prevented + // (handles either redirect changed or state lazy-definition) + to = redirect.to; + toParams = redirect.toParams; + options = redirect.options; + toState = findState(to, options.relative); + + if (!isDefined(toState)) { + if (!options.relative) throw new Error("No such state '" + to + "'"); + throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'"); + } + } + if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'"); + if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState); + if (!toState.params.$$validates(toParams)) return TransitionFailed; + + toParams = toState.params.$$values(toParams); + to = toState; + + var toPath = to.path; + + // Starting from the root of the path, keep all levels that haven't changed + var keep = 0, state = toPath[keep], locals = root.locals, toLocals = []; + + if (!options.reload) { + while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) { + locals = toLocals[keep] = state.locals; + keep++; + state = toPath[keep]; + } + } + + // If we're going to the same state and all locals are kept, we've got nothing to do. + // But clear 'transition', as we still want to cancel any other pending transitions. + // TODO: We may not want to bump 'transition' if we're called from a location change + // that we've initiated ourselves, because we might accidentally abort a legitimate + // transition initiated from code? + if (shouldTriggerReload(to, from, locals, options)) { + if (to.self.reloadOnSearch !== false) $urlRouter.update(); + $state.transition = null; + return $q.when($state.current); + } + + // Filter parameters before we pass them to event handlers etc. + toParams = filterByKeys(to.params.$$keys(), toParams || {}); + + // Broadcast start event and cancel the transition if requested + if (options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeStart + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when the state transition **begins**. You can use `event.preventDefault()` + * to prevent the transition from happening and then the transition promise will be + * rejected with a `'transition prevented'` value. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + * + * @example + * + * <pre> + * $rootScope.$on('$stateChangeStart', + * function(event, toState, toParams, fromState, fromParams){ + * event.preventDefault(); + * // transitionTo() promise will be rejected with + * // a 'transition prevented' error + * }) + * </pre> + */ + if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) { + $urlRouter.update(); + return TransitionPrevented; + } + } + + // Resolve locals for the remaining states, but don't update any global state just + // yet -- if anything fails to resolve the current state needs to remain untouched. + // We also set up an inheritance chain for the locals here. This allows the view directive + // to quickly look up the correct definition for each view in the current state. Even + // though we create the locals object itself outside resolveState(), it is initially + // empty and gets filled asynchronously. We need to keep track of the promise for the + // (fully resolved) current locals, and pass this down the chain. + var resolved = $q.when(locals); + + for (var l = keep; l < toPath.length; l++, state = toPath[l]) { + locals = toLocals[l] = inherit(locals); + resolved = resolveState(state, toParams, state === to, resolved, locals, options); + } + + // Once everything is resolved, we are ready to perform the actual transition + // and return a promise for the new state. We also keep track of what the + // current promise is, so that we can detect overlapping transitions and + // keep only the outcome of the last transition. + var transition = $state.transition = resolved.then(function () { + var l, entering, exiting; + + if ($state.transition !== transition) return TransitionSuperseded; + + // Exit 'from' states not kept + for (l = fromPath.length - 1; l >= keep; l--) { + exiting = fromPath[l]; + if (exiting.self.onExit) { + $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals); + } + exiting.locals = null; + } + + // Enter 'to' states not kept + for (l = keep; l < toPath.length; l++) { + entering = toPath[l]; + entering.locals = toLocals[l]; + if (entering.self.onEnter) { + $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals); + } + } + + // Run it again, to catch any transitions in callbacks + if ($state.transition !== transition) return TransitionSuperseded; + + // Update globals in $state + $state.$current = to; + $state.current = to.self; + $state.params = toParams; + copy($state.params, $stateParams); + $state.transition = null; + + if (options.location && to.navigable) { + $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, { + $$avoidResync: true, replace: options.location === 'replace' + }); + } + + if (options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeSuccess + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired once the state transition is **complete**. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + */ + $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams); + } + $urlRouter.update(true); + + return $state.current; + }, function (error) { + if ($state.transition !== transition) return TransitionSuperseded; + + $state.transition = null; + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeError + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when an **error occurs** during transition. It's important to note that if you + * have any errors in your resolve functions (javascript errors, non-existent services, etc) + * they will not throw traditionally. You must listen for this $stateChangeError event to + * catch **ALL** errors. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + * @param {Error} error The resolve error object. + */ + evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error); + + if (!evt.defaultPrevented) { + $urlRouter.update(); + } + + return $q.reject(error); + }); + + return transition; + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#is + * @methodOf ui.router.state.$state + * + * @description + * Similar to {@link ui.router.state.$state#methods_includes $state.includes}, + * but only checks for the full state name. If params is supplied then it will be + * tested for strict equality against the current active params object, so all params + * must match with none missing and no extras. + * + * @example + * <pre> + * $state.$current.name = 'contacts.details.item'; + * + * // absolute name + * $state.is('contact.details.item'); // returns true + * $state.is(contactDetailItemStateObject); // returns true + * + * // relative name (. and ^), typically from a template + * // E.g. from the 'contacts.details' template + * <div ng-class="{highlighted: $state.is('.item')}">Item</div> + * </pre> + * + * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check. + * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like + * to test against the current active state. + * @param {object=} options An options object. The options are: + * + * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will + * test relative to `options.relative` state (or name). + * + * @returns {boolean} Returns true if it is the state. + */ + $state.is = function is(stateOrName, params, options) { + options = extend({ relative: $state.$current }, options || {}); + var state = findState(stateOrName, options.relative); + + if (!isDefined(state)) { return undefined; } + if ($state.$current !== state) { return false; } + return params ? equalForKeys(state.params.$$values(params), $stateParams) : true; + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#includes + * @methodOf ui.router.state.$state + * + * @description + * A method to determine if the current active state is equal to or is the child of the + * state stateName. If any params are passed then they will be tested for a match as well. + * Not all the parameters need to be passed, just the ones you'd like to test for equality. + * + * @example + * Partial and relative names + * <pre> + * $state.$current.name = 'contacts.details.item'; + * + * // Using partial names + * $state.includes("contacts"); // returns true + * $state.includes("contacts.details"); // returns true + * $state.includes("contacts.details.item"); // returns true + * $state.includes("contacts.list"); // returns false + * $state.includes("about"); // returns false + * + * // Using relative names (. and ^), typically from a template + * // E.g. from the 'contacts.details' template + * <div ng-class="{highlighted: $state.includes('.item')}">Item</div> + * </pre> + * + * Basic globbing patterns + * <pre> + * $state.$current.name = 'contacts.details.item.url'; + * + * $state.includes("*.details.*.*"); // returns true + * $state.includes("*.details.**"); // returns true + * $state.includes("**.item.**"); // returns true + * $state.includes("*.details.item.url"); // returns true + * $state.includes("*.details.*.url"); // returns true + * $state.includes("*.details.*"); // returns false + * $state.includes("item.**"); // returns false + * </pre> + * + * @param {string} stateOrName A partial name, relative name, or glob pattern + * to be searched for within the current state name. + * @param {object=} params A param object, e.g. `{sectionId: section.id}`, + * that you'd like to test against the current active state. + * @param {object=} options An options object. The options are: + * + * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set, + * .includes will test relative to `options.relative` state (or name). + * + * @returns {boolean} Returns true if it does include the state + */ + $state.includes = function includes(stateOrName, params, options) { + options = extend({ relative: $state.$current }, options || {}); + if (isString(stateOrName) && isGlob(stateOrName)) { + if (!doesStateMatchGlob(stateOrName)) { + return false; + } + stateOrName = $state.$current.name; + } + + var state = findState(stateOrName, options.relative); + if (!isDefined(state)) { return undefined; } + if (!isDefined($state.$current.includes[state.name])) { return false; } + return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true; + }; + + + /** + * @ngdoc function + * @name ui.router.state.$state#href + * @methodOf ui.router.state.$state + * + * @description + * A url generation method that returns the compiled url for the given state populated with the given params. + * + * @example + * <pre> + * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob"); + * </pre> + * + * @param {string|object} stateOrName The state name or state object you'd like to generate a url from. + * @param {object=} params An object of parameter values to fill the state's required parameters. + * @param {object=} options Options object. The options are: + * + * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the + * first parameter, then the constructed href url will be built from the first navigable ancestor (aka + * ancestor with a valid url). + * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". + * + * @returns {string} compiled state url + */ + $state.href = function href(stateOrName, params, options) { + options = extend({ + lossy: true, + inherit: true, + absolute: false, + relative: $state.$current + }, options || {}); + + var state = findState(stateOrName, options.relative); + + if (!isDefined(state)) return null; + if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state); + + var nav = (state && options.lossy) ? state.navigable : state; + + if (!nav || nav.url === undefined || nav.url === null) { + return null; + } + return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys(), params || {}), { + absolute: options.absolute + }); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#get + * @methodOf ui.router.state.$state + * + * @description + * Returns the state configuration object for any specific state or all states. + * + * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for + * the requested state. If not provided, returns an array of ALL state configs. + * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context. + * @returns {Object|Array} State configuration object or array of all objects. + */ + $state.get = function (stateOrName, context) { + if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; }); + var state = findState(stateOrName, context || $state.$current); + return (state && state.self) ? state.self : null; + }; + + function resolveState(state, params, paramsAreFiltered, inherited, dst, options) { + // Make a restricted $stateParams with only the parameters that apply to this state if + // necessary. In addition to being available to the controller and onEnter/onExit callbacks, + // we also need $stateParams to be available for any $injector calls we make during the + // dependency resolution process. + var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params); + var locals = { $stateParams: $stateParams }; + + // Resolve 'global' dependencies for the state, i.e. those not specific to a view. + // We're also including $stateParams in this; that way the parameters are restricted + // to the set that should be visible to the state, and are independent of when we update + // the global $state and $stateParams values. + dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state); + var promises = [dst.resolve.then(function (globals) { + dst.globals = globals; + })]; + if (inherited) promises.push(inherited); + + // Resolve template and dependencies for all views. + forEach(state.views, function (view, name) { + var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {}); + injectables.$template = [ function () { + return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: options.notify }) || ''; + }]; + + promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) { + // References to the controller (only instantiated at link time) + if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) { + var injectLocals = angular.extend({}, injectables, locals); + result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals); + } else { + result.$$controller = view.controller; + } + // Provide access to the state itself for internal use + result.$$state = state; + result.$$controllerAs = view.controllerAs; + dst[name] = result; + })); + }); + + // Wait for all the promises and then return the activation object + return $q.all(promises).then(function (values) { + return dst; + }); + } + + return $state; + } + + function shouldTriggerReload(to, from, locals, options) { + if (to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false))) { + return true; + } + } +} + +angular.module('ui.router.state') + .value('$stateParams', {}) + .provider('$state', $StateProvider); + + +$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); + } + if (result && options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$viewContentLoading + * @eventOf ui.router.state.$view + * @eventType broadcast on root scope + * @description + * + * Fired once the view **begins loading**, *before* the DOM is rendered. + * + * @param {Object} event Event object. + * @param {Object} viewConfig The view config properties (template, controller, etc). + * + * @example + * + * <pre> + * $scope.$on('$viewContentLoading', + * function(event, viewConfig){ + * // Access to all the view config properties. + * // and one special property 'targetView' + * // viewConfig.targetView + * }); + * </pre> + */ + $rootScope.$broadcast('$viewContentLoading', options); + } + return result; + } + }; + } +} + +angular.module('ui.router.state').provider('$view', $ViewProvider); + +/** + * @ngdoc object + * @name ui.router.state.$uiViewScrollProvider + * + * @description + * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. + */ +function $ViewScrollProvider() { + + var useAnchorScroll = false; + + /** + * @ngdoc function + * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll + * @methodOf ui.router.state.$uiViewScrollProvider + * + * @description + * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for + * scrolling based on the url anchor. + */ + this.useAnchorScroll = function () { + useAnchorScroll = true; + }; + + /** + * @ngdoc object + * @name ui.router.state.$uiViewScroll + * + * @requires $anchorScroll + * @requires $timeout + * + * @description + * When called with a jqLite element, it scrolls the element into view (after a + * `$timeout` so the DOM has time to refresh). + * + * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, + * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. + */ + this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { + if (useAnchorScroll) { + return $anchorScroll; + } + + return function ($element) { + $timeout(function () { + $element[0].scrollIntoView(); + }, 0, false); + }; + }]; +} + +angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); + +/** + * @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#views `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> + */ +$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate']; +function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) { + + 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) { + var promise = $animate.enter(element, null, target, cb); + if (promise && promise.then) promise.then(cb); + }, + leave: function(element, cb) { + var promise = $animate.leave(element, cb); + if (promise && promise.then) promise.then(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); + + scope.$on('$stateChangeSuccess', function() { + updateView(false); + }); + scope.$on('$viewContentLoading', function() { + updateView(false); + }); + + updateView(true); + + function cleanupLastView() { + if (previousEl) { + previousEl.remove(); + previousEl = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentEl) { + renderer.leave(currentEl, function() { + 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]; + + var clone = $transclude(newScope, function(clone) { + renderer.enter(clone, $element, function onUiViewEnter() { + 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. + */ + currentScope.$emit('$viewContentLoaded'); + 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 link = $compile($element.contents()); + + if (locals.$$controller) { + locals.$scope = scope; + var controller = $controller(locals.$$controller, locals); + if (locals.$$controllerAs) { + scope[locals.$$controllerAs] = controller; + } + $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 inherited = element.inheritedData('$uiView'); + return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : '')); +} + +angular.module('ui.router.state').directive('uiView', $ViewDirective); +angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); + +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; + } +} + +/** + * @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#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#go $state.go()} + */ +$StateRefDirective.$inject = ['$state', '$timeout']; +function $StateRefDirective($state, $timeout) { + var allowedOptions = ['location', 'inherit', 'reload']; + + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function(scope, element, attrs, uiSrefActive) { + var ref = parseStateRef(attrs.uiSref, $state.current.name); + var params = null, url = null, base = stateContext(element) || $state.$current; + var newHref = null, isAnchor = element.prop("tagName") === "A"; + var isForm = element[0].nodeName === "FORM"; + var attr = isForm ? "action" : "href", nav = true; + + var options = { relative: base, inherit: true }; + var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {}; + + angular.forEach(allowedOptions, function(option) { + if (option in optionsOverride) { + options[option] = optionsOverride[option]; + } + }); + + var update = function(newVal) { + if (newVal) params = angular.copy(newVal); + if (!nav) return; + + newHref = $state.href(ref.state, params, options); + + var activeDirective = uiSrefActive[1] || uiSrefActive[0]; + if (activeDirective) { + activeDirective.$$setStateInfo(ref.state, params); + } + if (newHref === null) { + nav = false; + return false; + } + attrs.$set(attr, newHref); + }; + + if (ref.paramExpr) { + scope.$watch(ref.paramExpr, function(newVal, oldVal) { + if (newVal !== params) update(newVal); + }, true); + params = angular.copy(scope.$eval(ref.paramExpr)); + } + update(); + + if (isForm) return; + + element.bind("click", function(e) { + var button = e.which || e.button; + if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { + // HACK: This is to allow ng-clicks to be processed before the transition is initiated: + var transition = $timeout(function() { + $state.go(ref.state, params, options); + }); + e.preventDefault(); + + // if the state has no URL, ignore one preventDefault from the <a> directive. + var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0; + e.preventDefault = function() { + if (ignorePreventDefaultCount-- <= 0) + $timeout.cancel(transition); + }; + } + }); + } + }; +} + +/** + * @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> + */ + +/** + * @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', function ($scope, $element, $attrs) { + var state, params, activeClass; + + // There probably isn't much point in $observing this + // uiSrefActive and uiSrefActiveEq share the same directive object with some + // slight difference in logic routing + activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope); + + // Allow uiSref to communicate with uiSrefActive[Equals] + this.$$setStateInfo = function (newState, newParams) { + state = $state.get(newState, stateContext($element)); + params = newParams; + update(); + }; + + $scope.$on('$stateChangeSuccess', update); + + // Update route state + function update() { + if (isMatch()) { + $element.addClass(activeClass); + } else { + $element.removeClass(activeClass); + } + } + + function isMatch() { + if (typeof $attrs.uiSrefActiveEq !== 'undefined') { + return state && $state.is(state.name, params); + } else { + return state && $state.includes(state.name, params); + } + } + }] + }; +} + +angular.module('ui.router.state') + .directive('uiSref', $StateRefDirective) + .directive('uiSrefActive', $StateRefActiveDirective) + .directive('uiSrefActiveEq', $StateRefActiveDirective); + +/** + * @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) { + return $state.is(state); + }; + 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) { + return $state.includes(state); + }; + includesFilter.$stateful = true; + return includesFilter; +} + +angular.module('ui.router.state') + .filter('isState', $IsStateFilter) + .filter('includedByState', $IncludedByStateFilter); +})(window, window.angular);
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.min.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.min.js new file mode 100644 index 000000000..be06fb5b0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.min.js @@ -0,0 +1,7 @@ +/** + * State-based routing for AngularJS + * @version v0.2.13 + * @link http://angular-ui.github.com/ + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(a,b,c){"use strict";function d(a,b){return M(new(M(function(){},{prototype:a})),b)}function e(a){return L(arguments,function(b){b!==a&&L(b,function(b,c){a.hasOwnProperty(c)||(a[c]=b)})}),a}function f(a,b){var c=[];for(var d in a.path){if(a.path[d]!==b.path[d])break;c.push(a.path[d])}return c}function g(a){if(Object.keys)return Object.keys(a);var c=[];return b.forEach(a,function(a,b){c.push(b)}),c}function h(a,b){if(Array.prototype.indexOf)return a.indexOf(b,Number(arguments[2])||0);var c=a.length>>>0,d=Number(arguments[2])||0;for(d=0>d?Math.ceil(d):Math.floor(d),0>d&&(d+=c);c>d;d++)if(d in a&&a[d]===b)return d;return-1}function i(a,b,c,d){var e,i=f(c,d),j={},k=[];for(var l in i)if(i[l].params&&(e=g(i[l].params),e.length))for(var m in e)h(k,e[m])>=0||(k.push(e[m]),j[e[m]]=a[e[m]]);return M({},j,b)}function j(a,b,c){if(!c){c=[];for(var d in a)c.push(d)}for(var e=0;e<c.length;e++){var f=c[e];if(a[f]!=b[f])return!1}return!0}function k(a,b){var c={};return L(a,function(a){c[a]=b[a]}),c}function l(a){var b={},c=Array.prototype.concat.apply(Array.prototype,Array.prototype.slice.call(arguments,1));for(var d in a)-1==h(c,d)&&(b[d]=a[d]);return b}function m(a,b){var c=K(a),d=c?[]:{};return L(a,function(a,e){b(a,e)&&(d[c?d.length:e]=a)}),d}function n(a,b){var c=K(a)?[]:{};return L(a,function(a,d){c[d]=b(a,d)}),c}function o(a,b){var d=1,f=2,i={},j=[],k=i,m=M(a.when(i),{$$promises:i,$$values:i});this.study=function(i){function n(a,c){if(s[c]!==f){if(r.push(c),s[c]===d)throw r.splice(0,h(r,c)),new Error("Cyclic dependency: "+r.join(" -> "));if(s[c]=d,I(a))q.push(c,[function(){return b.get(a)}],j);else{var e=b.annotate(a);L(e,function(a){a!==c&&i.hasOwnProperty(a)&&n(i[a],a)}),q.push(c,a,e)}r.pop(),s[c]=f}}function o(a){return J(a)&&a.then&&a.$$promises}if(!J(i))throw new Error("'invocables' must be an object");var p=g(i||{}),q=[],r=[],s={};return L(i,n),i=r=s=null,function(d,f,g){function h(){--u||(v||e(t,f.$$values),r.$$values=t,r.$$promises=r.$$promises||!0,delete r.$$inheritedValues,n.resolve(t))}function i(a){r.$$failure=a,n.reject(a)}function j(c,e,f){function j(a){l.reject(a),i(a)}function k(){if(!G(r.$$failure))try{l.resolve(b.invoke(e,g,t)),l.promise.then(function(a){t[c]=a,h()},j)}catch(a){j(a)}}var l=a.defer(),m=0;L(f,function(a){s.hasOwnProperty(a)&&!d.hasOwnProperty(a)&&(m++,s[a].then(function(b){t[a]=b,--m||k()},j))}),m||k(),s[c]=l.promise}if(o(d)&&g===c&&(g=f,f=d,d=null),d){if(!J(d))throw new Error("'locals' must be an object")}else d=k;if(f){if(!o(f))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else f=m;var n=a.defer(),r=n.promise,s=r.$$promises={},t=M({},d),u=1+q.length/3,v=!1;if(G(f.$$failure))return i(f.$$failure),r;f.$$inheritedValues&&e(t,l(f.$$inheritedValues,p)),M(s,f.$$promises),f.$$values?(v=e(t,l(f.$$values,p)),r.$$inheritedValues=l(f.$$values,p),h()):(f.$$inheritedValues&&(r.$$inheritedValues=l(f.$$inheritedValues,p)),f.then(h,i));for(var w=0,x=q.length;x>w;w+=3)d.hasOwnProperty(q[w])?h():j(q[w],q[w+1],q[w+2]);return r}},this.resolve=function(a,b,c,d){return this.study(a)(b,c,d)}}function p(a,b,c){this.fromConfig=function(a,b,c){return G(a.template)?this.fromString(a.template,b):G(a.templateUrl)?this.fromUrl(a.templateUrl,b):G(a.templateProvider)?this.fromProvider(a.templateProvider,b,c):null},this.fromString=function(a,b){return H(a)?a(b):a},this.fromUrl=function(c,d){return H(c)&&(c=c(d)),null==c?null:a.get(c,{cache:b,headers:{Accept:"text/html"}}).then(function(a){return a.data})},this.fromProvider=function(a,b,d){return c.invoke(a,null,d||{params:b})}}function q(a,b,e){function f(b,c,d,e){if(q.push(b),o[b])return o[b];if(!/^\w+(-+\w+)*(?:\[\])?$/.test(b))throw new Error("Invalid parameter name '"+b+"' in pattern '"+a+"'");if(p[b])throw new Error("Duplicate parameter name '"+b+"' in pattern '"+a+"'");return p[b]=new O.Param(b,c,d,e),p[b]}function g(a,b,c){var d=["",""],e=a.replace(/[\\\[\]\^$*+?.()|{}]/g,"\\$&");if(!b)return e;switch(c){case!1:d=["(",")"];break;case!0:d=["?(",")?"];break;default:d=["("+c+"|",")?"]}return e+d[0]+b+d[1]}function h(c,e){var f,g,h,i,j;return f=c[2]||c[3],j=b.params[f],h=a.substring(m,c.index),g=e?c[4]:c[4]||("*"==c[1]?".*":null),i=O.type(g||"string")||d(O.type("string"),{pattern:new RegExp(g)}),{id:f,regexp:g,segment:h,type:i,cfg:j}}b=M({params:{}},J(b)?b:{});var i,j=/([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,k=/([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,l="^",m=0,n=this.segments=[],o=e?e.params:{},p=this.params=e?e.params.$$new():new O.ParamSet,q=[];this.source=a;for(var r,s,t;(i=j.exec(a))&&(r=h(i,!1),!(r.segment.indexOf("?")>=0));)s=f(r.id,r.type,r.cfg,"path"),l+=g(r.segment,s.type.pattern.source,s.squash),n.push(r.segment),m=j.lastIndex;t=a.substring(m);var u=t.indexOf("?");if(u>=0){var v=this.sourceSearch=t.substring(u);if(t=t.substring(0,u),this.sourcePath=a.substring(0,m+u),v.length>0)for(m=0;i=k.exec(v);)r=h(i,!0),s=f(r.id,r.type,r.cfg,"search"),m=j.lastIndex}else this.sourcePath=a,this.sourceSearch="";l+=g(t)+(b.strict===!1?"/?":"")+"$",n.push(t),this.regexp=new RegExp(l,b.caseInsensitive?"i":c),this.prefix=n[0],this.$$paramNames=q}function r(a){M(this,a)}function s(){function a(a){return null!=a?a.toString().replace(/\//g,"%2F"):a}function e(a){return null!=a?a.toString().replace(/%2F/g,"/"):a}function f(a){return this.pattern.test(a)}function i(){return{strict:t,caseInsensitive:p}}function j(a){return H(a)||K(a)&&H(a[a.length-1])}function k(){for(;x.length;){var a=x.shift();if(a.pattern)throw new Error("You cannot override a type's .pattern at runtime.");b.extend(v[a.name],o.invoke(a.def))}}function l(a){M(this,a||{})}O=this;var o,p=!1,t=!0,u=!1,v={},w=!0,x=[],y={string:{encode:a,decode:e,is:f,pattern:/[^/]*/},"int":{encode:a,decode:function(a){return parseInt(a,10)},is:function(a){return G(a)&&this.decode(a.toString())===a},pattern:/\d+/},bool:{encode:function(a){return a?1:0},decode:function(a){return 0!==parseInt(a,10)},is:function(a){return a===!0||a===!1},pattern:/0|1/},date:{encode:function(a){return this.is(a)?[a.getFullYear(),("0"+(a.getMonth()+1)).slice(-2),("0"+a.getDate()).slice(-2)].join("-"):c},decode:function(a){if(this.is(a))return a;var b=this.capture.exec(a);return b?new Date(b[1],b[2]-1,b[3]):c},is:function(a){return a instanceof Date&&!isNaN(a.valueOf())},equals:function(a,b){return this.is(a)&&this.is(b)&&a.toISOString()===b.toISOString()},pattern:/[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,capture:/([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/},json:{encode:b.toJson,decode:b.fromJson,is:b.isObject,equals:b.equals,pattern:/[^/]*/},any:{encode:b.identity,decode:b.identity,is:b.identity,equals:b.equals,pattern:/.*/}};s.$$getDefaultValue=function(a){if(!j(a.value))return a.value;if(!o)throw new Error("Injectable functions cannot be called at configuration time");return o.invoke(a.value)},this.caseInsensitive=function(a){return G(a)&&(p=a),p},this.strictMode=function(a){return G(a)&&(t=a),t},this.defaultSquashPolicy=function(a){if(!G(a))return u;if(a!==!0&&a!==!1&&!I(a))throw new Error("Invalid squash policy: "+a+". Valid policies: false, true, arbitrary-string");return u=a,a},this.compile=function(a,b){return new q(a,M(i(),b))},this.isMatcher=function(a){if(!J(a))return!1;var b=!0;return L(q.prototype,function(c,d){H(c)&&(b=b&&G(a[d])&&H(a[d]))}),b},this.type=function(a,b,c){if(!G(b))return v[a];if(v.hasOwnProperty(a))throw new Error("A type named '"+a+"' has already been defined.");return v[a]=new r(M({name:a},b)),c&&(x.push({name:a,def:c}),w||k()),this},L(y,function(a,b){v[b]=new r(M({name:b},a))}),v=d(v,{}),this.$get=["$injector",function(a){return o=a,w=!1,k(),L(y,function(a,b){v[b]||(v[b]=new r(a))}),this}],this.Param=function(a,b,d,e){function f(a){var b=J(a)?g(a):[],c=-1===h(b,"value")&&-1===h(b,"type")&&-1===h(b,"squash")&&-1===h(b,"array");return c&&(a={value:a}),a.$$fn=j(a.value)?a.value:function(){return a.value},a}function i(b,c,d){if(b.type&&c)throw new Error("Param '"+a+"' has two type configurations.");return c?c:b.type?b.type instanceof r?b.type:new r(b.type):"config"===d?v.any:v.string}function k(){var b={array:"search"===e?"auto":!1},c=a.match(/\[\]$/)?{array:!0}:{};return M(b,c,d).array}function l(a,b){var c=a.squash;if(!b||c===!1)return!1;if(!G(c)||null==c)return u;if(c===!0||I(c))return c;throw new Error("Invalid squash policy: '"+c+"'. Valid policies: false, true, or arbitrary string")}function p(a,b,d,e){var f,g,i=[{from:"",to:d||b?c:""},{from:null,to:d||b?c:""}];return f=K(a.replace)?a.replace:[],I(e)&&f.push({from:e,to:c}),g=n(f,function(a){return a.from}),m(i,function(a){return-1===h(g,a.from)}).concat(f)}function q(){if(!o)throw new Error("Injectable functions cannot be called at configuration time");return o.invoke(d.$$fn)}function s(a){function b(a){return function(b){return b.from===a}}function c(a){var c=n(m(w.replace,b(a)),function(a){return a.to});return c.length?c[0]:a}return a=c(a),G(a)?w.type.decode(a):q()}function t(){return"{Param:"+a+" "+b+" squash: '"+z+"' optional: "+y+"}"}var w=this;d=f(d),b=i(d,b,e);var x=k();b=x?b.$asArray(x,"search"===e):b,"string"!==b.name||x||"path"!==e||d.value!==c||(d.value="");var y=d.value!==c,z=l(d,y),A=p(d,x,y,z);M(this,{id:a,type:b,location:e,array:x,squash:z,replace:A,isOptional:y,value:s,dynamic:c,config:d,toString:t})},l.prototype={$$new:function(){return d(this,M(new l,{$$parent:this}))},$$keys:function(){for(var a=[],b=[],c=this,d=g(l.prototype);c;)b.push(c),c=c.$$parent;return b.reverse(),L(b,function(b){L(g(b),function(b){-1===h(a,b)&&-1===h(d,b)&&a.push(b)})}),a},$$values:function(a){var b={},c=this;return L(c.$$keys(),function(d){b[d]=c[d].value(a&&a[d])}),b},$$equals:function(a,b){var c=!0,d=this;return L(d.$$keys(),function(e){var f=a&&a[e],g=b&&b[e];d[e].type.equals(f,g)||(c=!1)}),c},$$validates:function(a){var b,c,d,e=!0,f=this;return L(this.$$keys(),function(g){d=f[g],c=a[g],b=!c&&d.isOptional,e=e&&(b||!!d.type.is(c))}),e},$$parent:c},this.ParamSet=l}function t(a,d){function e(a){var b=/^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(a.source);return null!=b?b[1].replace(/\\(.)/g,"$1"):""}function f(a,b){return a.replace(/\$(\$|\d{1,2})/,function(a,c){return b["$"===c?0:Number(c)]})}function g(a,b,c){if(!c)return!1;var d=a.invoke(b,b,{$match:c});return G(d)?d:!0}function h(d,e,f,g){function h(a,b,c){return"/"===p?a:b?p.slice(0,-1)+a:c?p.slice(1)+a:a}function m(a){function b(a){var b=a(f,d);return b?(I(b)&&d.replace().url(b),!0):!1}if(!a||!a.defaultPrevented){var e=o&&d.url()===o;if(o=c,e)return!0;var g,h=j.length;for(g=0;h>g;g++)if(b(j[g]))return;k&&b(k)}}function n(){return i=i||e.$on("$locationChangeSuccess",m)}var o,p=g.baseHref(),q=d.url();return l||n(),{sync:function(){m()},listen:function(){return n()},update:function(a){return a?void(q=d.url()):void(d.url()!==q&&(d.url(q),d.replace()))},push:function(a,b,e){d.url(a.format(b||{})),o=e&&e.$$avoidResync?d.url():c,e&&e.replace&&d.replace()},href:function(c,e,f){if(!c.validates(e))return null;var g=a.html5Mode();b.isObject(g)&&(g=g.enabled);var i=c.format(e);if(f=f||{},g||null===i||(i="#"+a.hashPrefix()+i),i=h(i,g,f.absolute),!f.absolute||!i)return i;var j=!g&&i?"/":"",k=d.port();return k=80===k||443===k?"":":"+k,[d.protocol(),"://",d.host(),k,j,i].join("")}}}var i,j=[],k=null,l=!1;this.rule=function(a){if(!H(a))throw new Error("'rule' must be a function");return j.push(a),this},this.otherwise=function(a){if(I(a)){var b=a;a=function(){return b}}else if(!H(a))throw new Error("'rule' must be a function");return k=a,this},this.when=function(a,b){var c,h=I(b);if(I(a)&&(a=d.compile(a)),!h&&!H(b)&&!K(b))throw new Error("invalid 'handler' in when()");var i={matcher:function(a,b){return h&&(c=d.compile(b),b=["$match",function(a){return c.format(a)}]),M(function(c,d){return g(c,b,a.exec(d.path(),d.search()))},{prefix:I(a.prefix)?a.prefix:""})},regex:function(a,b){if(a.global||a.sticky)throw new Error("when() RegExp must not be global or sticky");return h&&(c=b,b=["$match",function(a){return f(c,a)}]),M(function(c,d){return g(c,b,a.exec(d.path()))},{prefix:e(a)})}},j={matcher:d.isMatcher(a),regex:a instanceof RegExp};for(var k in j)if(j[k])return this.rule(i[k](a,b));throw new Error("invalid 'what' in when()")},this.deferIntercept=function(a){a===c&&(a=!0),l=a},this.$get=h,h.$inject=["$location","$rootScope","$injector","$browser"]}function u(a,e){function f(a){return 0===a.indexOf(".")||0===a.indexOf("^")}function l(a,b){if(!a)return c;var d=I(a),e=d?a:a.name,g=f(e);if(g){if(!b)throw new Error("No reference point given for path '"+e+"'");b=l(b);for(var h=e.split("."),i=0,j=h.length,k=b;j>i;i++)if(""!==h[i]||0!==i){if("^"!==h[i])break;if(!k.parent)throw new Error("Path '"+e+"' not valid for state '"+b.name+"'");k=k.parent}else k=b;h=h.slice(i).join("."),e=k.name+(k.name&&h?".":"")+h}var m=y[e];return!m||!d&&(d||m!==a&&m.self!==a)?c:m}function m(a,b){z[a]||(z[a]=[]),z[a].push(b)}function o(a){for(var b=z[a]||[];b.length;)p(b.shift())}function p(b){b=d(b,{self:b,resolve:b.resolve||{},toString:function(){return this.name}});var c=b.name;if(!I(c)||c.indexOf("@")>=0)throw new Error("State must have a valid name");if(y.hasOwnProperty(c))throw new Error("State '"+c+"'' is already defined");var e=-1!==c.indexOf(".")?c.substring(0,c.lastIndexOf(".")):I(b.parent)?b.parent:J(b.parent)&&I(b.parent.name)?b.parent.name:"";if(e&&!y[e])return m(e,b.self);for(var f in B)H(B[f])&&(b[f]=B[f](b,B.$delegates[f]));return y[c]=b,!b[A]&&b.url&&a.when(b.url,["$match","$stateParams",function(a,c){x.$current.navigable==b&&j(a,c)||x.transitionTo(b,a,{inherit:!0,location:!1})}]),o(c),b}function q(a){return a.indexOf("*")>-1}function r(a){var b=a.split("."),c=x.$current.name.split(".");if("**"===b[0]&&(c=c.slice(h(c,b[1])),c.unshift("**")),"**"===b[b.length-1]&&(c.splice(h(c,b[b.length-2])+1,Number.MAX_VALUE),c.push("**")),b.length!=c.length)return!1;for(var d=0,e=b.length;e>d;d++)"*"===b[d]&&(c[d]="*");return c.join("")===b.join("")}function s(a,b){return I(a)&&!G(b)?B[a]:H(b)&&I(a)?(B[a]&&!B.$delegates[a]&&(B.$delegates[a]=B[a]),B[a]=b,this):this}function t(a,b){return J(a)?b=a:b.name=a,p(b),this}function u(a,e,f,h,m,o,p){function s(b,c,d,f){var g=a.$broadcast("$stateNotFound",b,c,d);if(g.defaultPrevented)return p.update(),B;if(!g.retry)return null;if(f.$retry)return p.update(),C;var h=x.transition=e.when(g.retry);return h.then(function(){return h!==x.transition?u:(b.options.$retry=!0,x.transitionTo(b.to,b.toParams,b.options))},function(){return B}),p.update(),h}function t(a,c,d,g,i,j){var l=d?c:k(a.params.$$keys(),c),n={$stateParams:l};i.resolve=m.resolve(a.resolve,n,i.resolve,a);var o=[i.resolve.then(function(a){i.globals=a})];return g&&o.push(g),L(a.views,function(c,d){var e=c.resolve&&c.resolve!==a.resolve?c.resolve:{};e.$template=[function(){return f.load(d,{view:c,locals:n,params:l,notify:j.notify})||""}],o.push(m.resolve(e,n,i.resolve,a).then(function(f){if(H(c.controllerProvider)||K(c.controllerProvider)){var g=b.extend({},e,n);f.$$controller=h.invoke(c.controllerProvider,null,g)}else f.$$controller=c.controller;f.$$state=a,f.$$controllerAs=c.controllerAs,i[d]=f}))}),e.all(o).then(function(){return i})}var u=e.reject(new Error("transition superseded")),z=e.reject(new Error("transition prevented")),B=e.reject(new Error("transition aborted")),C=e.reject(new Error("transition failed"));return w.locals={resolve:null,globals:{$stateParams:{}}},x={params:{},current:w.self,$current:w,transition:null},x.reload=function(){return x.transitionTo(x.current,o,{reload:!0,inherit:!1,notify:!0})},x.go=function(a,b,c){return x.transitionTo(a,b,M({inherit:!0,relative:x.$current},c))},x.transitionTo=function(b,c,f){c=c||{},f=M({location:!0,inherit:!1,relative:null,notify:!0,reload:!1,$retry:!1},f||{});var g,j=x.$current,m=x.params,n=j.path,q=l(b,f.relative);if(!G(q)){var r={to:b,toParams:c,options:f},y=s(r,j.self,m,f);if(y)return y;if(b=r.to,c=r.toParams,f=r.options,q=l(b,f.relative),!G(q)){if(!f.relative)throw new Error("No such state '"+b+"'");throw new Error("Could not resolve '"+b+"' from state '"+f.relative+"'")}}if(q[A])throw new Error("Cannot transition to abstract state '"+b+"'");if(f.inherit&&(c=i(o,c||{},x.$current,q)),!q.params.$$validates(c))return C;c=q.params.$$values(c),b=q;var B=b.path,D=0,E=B[D],F=w.locals,H=[];if(!f.reload)for(;E&&E===n[D]&&E.ownParams.$$equals(c,m);)F=H[D]=E.locals,D++,E=B[D];if(v(b,j,F,f))return b.self.reloadOnSearch!==!1&&p.update(),x.transition=null,e.when(x.current);if(c=k(b.params.$$keys(),c||{}),f.notify&&a.$broadcast("$stateChangeStart",b.self,c,j.self,m).defaultPrevented)return p.update(),z;for(var I=e.when(F),J=D;J<B.length;J++,E=B[J])F=H[J]=d(F),I=t(E,c,E===b,I,F,f);var K=x.transition=I.then(function(){var d,e,g;if(x.transition!==K)return u;for(d=n.length-1;d>=D;d--)g=n[d],g.self.onExit&&h.invoke(g.self.onExit,g.self,g.locals.globals),g.locals=null;for(d=D;d<B.length;d++)e=B[d],e.locals=H[d],e.self.onEnter&&h.invoke(e.self.onEnter,e.self,e.locals.globals);return x.transition!==K?u:(x.$current=b,x.current=b.self,x.params=c,N(x.params,o),x.transition=null,f.location&&b.navigable&&p.push(b.navigable.url,b.navigable.locals.globals.$stateParams,{$$avoidResync:!0,replace:"replace"===f.location}),f.notify&&a.$broadcast("$stateChangeSuccess",b.self,c,j.self,m),p.update(!0),x.current)},function(d){return x.transition!==K?u:(x.transition=null,g=a.$broadcast("$stateChangeError",b.self,c,j.self,m,d),g.defaultPrevented||p.update(),e.reject(d))});return K},x.is=function(a,b,d){d=M({relative:x.$current},d||{});var e=l(a,d.relative);return G(e)?x.$current!==e?!1:b?j(e.params.$$values(b),o):!0:c},x.includes=function(a,b,d){if(d=M({relative:x.$current},d||{}),I(a)&&q(a)){if(!r(a))return!1;a=x.$current.name}var e=l(a,d.relative);return G(e)?G(x.$current.includes[e.name])?b?j(e.params.$$values(b),o,g(b)):!0:!1:c},x.href=function(a,b,d){d=M({lossy:!0,inherit:!0,absolute:!1,relative:x.$current},d||{});var e=l(a,d.relative);if(!G(e))return null;d.inherit&&(b=i(o,b||{},x.$current,e));var f=e&&d.lossy?e.navigable:e;return f&&f.url!==c&&null!==f.url?p.href(f.url,k(e.params.$$keys(),b||{}),{absolute:d.absolute}):null},x.get=function(a,b){if(0===arguments.length)return n(g(y),function(a){return y[a].self});var c=l(a,b||x.$current);return c&&c.self?c.self:null},x}function v(a,b,c,d){return a!==b||(c!==b.locals||d.reload)&&a.self.reloadOnSearch!==!1?void 0:!0}var w,x,y={},z={},A="abstract",B={parent:function(a){if(G(a.parent)&&a.parent)return l(a.parent);var b=/^(.+)\.[^.]+$/.exec(a.name);return b?l(b[1]):w},data:function(a){return a.parent&&a.parent.data&&(a.data=a.self.data=M({},a.parent.data,a.data)),a.data},url:function(a){var b=a.url,c={params:a.params||{}};if(I(b))return"^"==b.charAt(0)?e.compile(b.substring(1),c):(a.parent.navigable||w).url.concat(b,c);if(!b||e.isMatcher(b))return b;throw new Error("Invalid url '"+b+"' in state '"+a+"'")},navigable:function(a){return a.url?a:a.parent?a.parent.navigable:null},ownParams:function(a){var b=a.url&&a.url.params||new O.ParamSet;return L(a.params||{},function(a,c){b[c]||(b[c]=new O.Param(c,null,a,"config"))}),b},params:function(a){return a.parent&&a.parent.params?M(a.parent.params.$$new(),a.ownParams):new O.ParamSet},views:function(a){var b={};return L(G(a.views)?a.views:{"":a},function(c,d){d.indexOf("@")<0&&(d+="@"+a.parent.name),b[d]=c}),b},path:function(a){return a.parent?a.parent.path.concat(a):[]},includes:function(a){var b=a.parent?M({},a.parent.includes):{};return b[a.name]=!0,b},$delegates:{}};w=p({name:"",url:"^",views:null,"abstract":!0}),w.navigable=null,this.decorator=s,this.state=t,this.$get=u,u.$inject=["$rootScope","$q","$view","$injector","$resolve","$stateParams","$urlRouter","$location","$urlMatcherFactory"]}function v(){function a(a,b){return{load:function(c,d){var e,f={template:null,controller:null,view:null,locals:null,notify:!0,async:!0,params:{}};return d=M(f,d),d.view&&(e=b.fromConfig(d.view,d.params,d.locals)),e&&d.notify&&a.$broadcast("$viewContentLoading",d),e}}}this.$get=a,a.$inject=["$rootScope","$templateFactory"]}function w(){var a=!1;this.useAnchorScroll=function(){a=!0},this.$get=["$anchorScroll","$timeout",function(b,c){return a?b:function(a){c(function(){a[0].scrollIntoView()},0,!1)}}]}function x(a,c,d,e){function f(){return c.has?function(a){return c.has(a)?c.get(a):null}:function(a){try{return c.get(a)}catch(b){return null}}}function g(a,b){var c=function(){return{enter:function(a,b,c){b.after(a),c()},leave:function(a,b){a.remove(),b()}}};if(j)return{enter:function(a,b,c){var d=j.enter(a,null,b,c);d&&d.then&&d.then(c)},leave:function(a,b){var c=j.leave(a,b);c&&c.then&&c.then(b)}};if(i){var d=i&&i(b,a);return{enter:function(a,b,c){d.enter(a,null,b),c()},leave:function(a,b){d.leave(a),b()}}}return c()}var h=f(),i=h("$animator"),j=h("$animate"),k={restrict:"ECA",terminal:!0,priority:400,transclude:"element",compile:function(c,f,h){return function(c,f,i){function j(){l&&(l.remove(),l=null),n&&(n.$destroy(),n=null),m&&(r.leave(m,function(){l=null}),l=m,m=null)}function k(g){var k,l=z(c,i,f,e),s=l&&a.$current&&a.$current.locals[l];if(g||s!==o){k=c.$new(),o=a.$current.locals[l];var t=h(k,function(a){r.enter(a,f,function(){n&&n.$emit("$viewContentAnimationEnded"),(b.isDefined(q)&&!q||c.$eval(q))&&d(a)}),j()});m=t,n=k,n.$emit("$viewContentLoaded"),n.$eval(p)}}var l,m,n,o,p=i.onload||"",q=i.autoscroll,r=g(i,c);c.$on("$stateChangeSuccess",function(){k(!1)}),c.$on("$viewContentLoading",function(){k(!1)}),k(!0)}}};return k}function y(a,b,c,d){return{restrict:"ECA",priority:-400,compile:function(e){var f=e.html();return function(e,g,h){var i=c.$current,j=z(e,h,g,d),k=i&&i.locals[j];if(k){g.data("$uiView",{name:j,state:k.$$state}),g.html(k.$template?k.$template:f);var l=a(g.contents());if(k.$$controller){k.$scope=e;var m=b(k.$$controller,k);k.$$controllerAs&&(e[k.$$controllerAs]=m),g.data("$ngControllerController",m),g.children().data("$ngControllerController",m)}l(e)}}}}}function z(a,b,c,d){var e=d(b.uiView||b.name||"")(a),f=c.inheritedData("$uiView");return e.indexOf("@")>=0?e:e+"@"+(f?f.state.name:"")}function A(a,b){var c,d=a.match(/^\s*({[^}]*})\s*$/);if(d&&(a=b+"("+d[1]+")"),c=a.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/),!c||4!==c.length)throw new Error("Invalid state ref '"+a+"'");return{state:c[1],paramExpr:c[3]||null}}function B(a){var b=a.parent().inheritedData("$uiView");return b&&b.state&&b.state.name?b.state:void 0}function C(a,c){var d=["location","inherit","reload"];return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(e,f,g,h){var i=A(g.uiSref,a.current.name),j=null,k=B(f)||a.$current,l=null,m="A"===f.prop("tagName"),n="FORM"===f[0].nodeName,o=n?"action":"href",p=!0,q={relative:k,inherit:!0},r=e.$eval(g.uiSrefOpts)||{};b.forEach(d,function(a){a in r&&(q[a]=r[a])});var s=function(c){if(c&&(j=b.copy(c)),p){l=a.href(i.state,j,q);var d=h[1]||h[0];return d&&d.$$setStateInfo(i.state,j),null===l?(p=!1,!1):void g.$set(o,l)}};i.paramExpr&&(e.$watch(i.paramExpr,function(a){a!==j&&s(a)},!0),j=b.copy(e.$eval(i.paramExpr))),s(),n||f.bind("click",function(b){var d=b.which||b.button;if(!(d>1||b.ctrlKey||b.metaKey||b.shiftKey||f.attr("target"))){var e=c(function(){a.go(i.state,j,q)});b.preventDefault();var g=m&&!l?1:0;b.preventDefault=function(){g--<=0&&c.cancel(e)}}})}}}function D(a,b,c){return{restrict:"A",controller:["$scope","$element","$attrs",function(b,d,e){function f(){g()?d.addClass(j):d.removeClass(j)}function g(){return"undefined"!=typeof e.uiSrefActiveEq?h&&a.is(h.name,i):h&&a.includes(h.name,i)}var h,i,j;j=c(e.uiSrefActiveEq||e.uiSrefActive||"",!1)(b),this.$$setStateInfo=function(b,c){h=a.get(b,B(d)),i=c,f()},b.$on("$stateChangeSuccess",f)}]}}function E(a){var b=function(b){return a.is(b)};return b.$stateful=!0,b}function F(a){var b=function(b){return a.includes(b)};return b.$stateful=!0,b}var G=b.isDefined,H=b.isFunction,I=b.isString,J=b.isObject,K=b.isArray,L=b.forEach,M=b.extend,N=b.copy;b.module("ui.router.util",["ng"]),b.module("ui.router.router",["ui.router.util"]),b.module("ui.router.state",["ui.router.router","ui.router.util"]),b.module("ui.router",["ui.router.state"]),b.module("ui.router.compat",["ui.router"]),o.$inject=["$q","$injector"],b.module("ui.router.util").service("$resolve",o),p.$inject=["$http","$templateCache","$injector"],b.module("ui.router.util").service("$templateFactory",p);var O;q.prototype.concat=function(a,b){var c={caseInsensitive:O.caseInsensitive(),strict:O.strictMode(),squash:O.defaultSquashPolicy()};return new q(this.sourcePath+a+this.sourceSearch,M(c,b),this)},q.prototype.toString=function(){return this.source},q.prototype.exec=function(a,b){function c(a){function b(a){return a.split("").reverse().join("")}function c(a){return a.replace(/\\-/,"-")}var d=b(a).split(/-(?!\\)/),e=n(d,b);return n(e,c).reverse()}var d=this.regexp.exec(a);if(!d)return null;b=b||{};var e,f,g,h=this.parameters(),i=h.length,j=this.segments.length-1,k={};if(j!==d.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");for(e=0;j>e;e++){g=h[e];var l=this.params[g],m=d[e+1];for(f=0;f<l.replace;f++)l.replace[f].from===m&&(m=l.replace[f].to);m&&l.array===!0&&(m=c(m)),k[g]=l.value(m)}for(;i>e;e++)g=h[e],k[g]=this.params[g].value(b[g]);return k},q.prototype.parameters=function(a){return G(a)?this.params[a]||null:this.$$paramNames},q.prototype.validates=function(a){return this.params.$$validates(a)},q.prototype.format=function(a){function b(a){return encodeURIComponent(a).replace(/-/g,function(a){return"%5C%"+a.charCodeAt(0).toString(16).toUpperCase()})}a=a||{};var c=this.segments,d=this.parameters(),e=this.params;if(!this.validates(a))return null;var f,g=!1,h=c.length-1,i=d.length,j=c[0];for(f=0;i>f;f++){var k=h>f,l=d[f],m=e[l],o=m.value(a[l]),p=m.isOptional&&m.type.equals(m.value(),o),q=p?m.squash:!1,r=m.type.encode(o);if(k){var s=c[f+1];if(q===!1)null!=r&&(j+=K(r)?n(r,b).join("-"):encodeURIComponent(r)),j+=s;else if(q===!0){var t=j.match(/\/$/)?/\/?(.*)/:/(.*)/;j+=s.match(t)[1]}else I(q)&&(j+=q+s)}else{if(null==r||p&&q!==!1)continue;K(r)||(r=[r]),r=n(r,encodeURIComponent).join("&"+l+"="),j+=(g?"&":"?")+(l+"="+r),g=!0}}return j},r.prototype.is=function(){return!0},r.prototype.encode=function(a){return a},r.prototype.decode=function(a){return a},r.prototype.equals=function(a,b){return a==b},r.prototype.$subPattern=function(){var a=this.pattern.toString();return a.substr(1,a.length-2)},r.prototype.pattern=/.*/,r.prototype.toString=function(){return"{Type:"+this.name+"}"},r.prototype.$asArray=function(a,b){function d(a,b){function d(a,b){return function(){return a[b].apply(a,arguments)}}function e(a){return K(a)?a:G(a)?[a]:[]}function f(a){switch(a.length){case 0:return c;case 1:return"auto"===b?a[0]:a;default:return a}}function g(a){return!a}function h(a,b){return function(c){c=e(c);var d=n(c,a);return b===!0?0===m(d,g).length:f(d)}}function i(a){return function(b,c){var d=e(b),f=e(c);if(d.length!==f.length)return!1;for(var g=0;g<d.length;g++)if(!a(d[g],f[g]))return!1;return!0}}this.encode=h(d(a,"encode")),this.decode=h(d(a,"decode")),this.is=h(d(a,"is"),!0),this.equals=i(d(a,"equals")),this.pattern=a.pattern,this.$arrayMode=b}if(!a)return this;if("auto"===a&&!b)throw new Error("'auto' array mode is for query parameters only");return new d(this,a)},b.module("ui.router.util").provider("$urlMatcherFactory",s),b.module("ui.router.util").run(["$urlMatcherFactory",function(){}]),t.$inject=["$locationProvider","$urlMatcherFactoryProvider"],b.module("ui.router.router").provider("$urlRouter",t),u.$inject=["$urlRouterProvider","$urlMatcherFactoryProvider"],b.module("ui.router.state").value("$stateParams",{}).provider("$state",u),v.$inject=[],b.module("ui.router.state").provider("$view",v),b.module("ui.router.state").provider("$uiViewScroll",w),x.$inject=["$state","$injector","$uiViewScroll","$interpolate"],y.$inject=["$compile","$controller","$state","$interpolate"],b.module("ui.router.state").directive("uiView",x),b.module("ui.router.state").directive("uiView",y),C.$inject=["$state","$timeout"],D.$inject=["$state","$stateParams","$interpolate"],b.module("ui.router.state").directive("uiSref",C).directive("uiSrefActive",D).directive("uiSrefActiveEq",D),E.$inject=["$state"],F.$inject=["$state"],b.module("ui.router.state").filter("isState",E).filter("includedByState",F)}(window,window.angular);
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/common.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/common.js new file mode 100644 index 000000000..5f0500e06 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/common.js @@ -0,0 +1,292 @@ +/*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; + +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 = []; + + angular.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].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']); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/resolve.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/resolve.js new file mode 100644 index 000000000..f1c179009 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/resolve.js @@ -0,0 +1,252 @@ +/** + * @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 caues `$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); + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/state.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/state.js new file mode 100644 index 000000000..f55d9634b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/state.js @@ -0,0 +1,1373 @@ +/** + * @ngdoc object + * @name ui.router.state.$stateProvider + * + * @requires ui.router.router.$urlRouterProvider + * @requires ui.router.util.$urlMatcherFactoryProvider + * + * @description + * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely + * on state. + * + * A state corresponds to a "place" in the application in terms of the overall UI and + * navigation. A state describes (via the controller / template / view properties) what + * the UI looks like and does at that place. + * + * States often have things in common, and the primary way of factoring out these + * commonalities in this model is via the state hierarchy, i.e. parent/child states aka + * nested states. + * + * The `$stateProvider` provides interfaces to declare these states for your app. + */ +$StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider']; +function $StateProvider( $urlRouterProvider, $urlMatcherFactory) { + + var root, states = {}, $state, queue = {}, abstractKey = 'abstract'; + + // Builds state properties from definition passed to registerState() + var stateBuilder = { + + // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined. + // state.children = []; + // if (parent) parent.children.push(state); + parent: function(state) { + if (isDefined(state.parent) && state.parent) return findState(state.parent); + // regex matches any valid composite state name + // would match "contact.list" but not "contacts" + var compositeName = /^(.+)\.[^.]+$/.exec(state.name); + return compositeName ? findState(compositeName[1]) : root; + }, + + // inherit 'data' from parent and override by own values (if any) + data: function(state) { + if (state.parent && state.parent.data) { + state.data = state.self.data = extend({}, state.parent.data, state.data); + } + return state.data; + }, + + // Build a URLMatcher if necessary, either via a relative or absolute URL + url: function(state) { + var url = state.url, config = { params: state.params || {} }; + + if (isString(url)) { + if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config); + return (state.parent.navigable || root).url.concat(url, config); + } + + if (!url || $urlMatcherFactory.isMatcher(url)) return url; + throw new Error("Invalid url '" + url + "' in state '" + state + "'"); + }, + + // Keep track of the closest ancestor state that has a URL (i.e. is navigable) + navigable: function(state) { + return state.url ? state : (state.parent ? state.parent.navigable : null); + }, + + // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params + ownParams: function(state) { + var params = state.url && state.url.params || new $$UMFP.ParamSet(); + forEach(state.params || {}, function(config, id) { + if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config"); + }); + return params; + }, + + // Derive parameters for this state and ensure they're a super-set of parent's parameters + params: function(state) { + return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet(); + }, + + // If there is no explicit multi-view configuration, make one up so we don't have + // to handle both cases in the view directive later. Note that having an explicit + // 'views' property will mean the default unnamed view properties are ignored. This + // is also a good time to resolve view names to absolute names, so everything is a + // straight lookup at link time. + views: function(state) { + var views = {}; + + forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) { + if (name.indexOf('@') < 0) name += '@' + state.parent.name; + views[name] = view; + }); + return views; + }, + + // Keep a full path from the root down to this state as this is needed for state activation. + path: function(state) { + return state.parent ? state.parent.path.concat(state) : []; // exclude root from path + }, + + // Speed up $state.contains() as it's used a lot + includes: function(state) { + var includes = state.parent ? extend({}, state.parent.includes) : {}; + includes[state.name] = true; + return includes; + }, + + $delegates: {} + }; + + function isRelative(stateName) { + return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0; + } + + function findState(stateOrName, base) { + if (!stateOrName) return undefined; + + var isStr = isString(stateOrName), + name = isStr ? stateOrName : stateOrName.name, + path = isRelative(name); + + if (path) { + if (!base) throw new Error("No reference point given for path '" + name + "'"); + base = findState(base); + + var rel = name.split("."), i = 0, pathLength = rel.length, current = base; + + for (; i < pathLength; i++) { + if (rel[i] === "" && i === 0) { + current = base; + continue; + } + if (rel[i] === "^") { + if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'"); + current = current.parent; + continue; + } + break; + } + rel = rel.slice(i).join("."); + name = current.name + (current.name && rel ? "." : "") + rel; + } + var state = states[name]; + + if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) { + return state; + } + return undefined; + } + + function queueState(parentName, state) { + if (!queue[parentName]) { + queue[parentName] = []; + } + queue[parentName].push(state); + } + + function flushQueuedChildren(parentName) { + var queued = queue[parentName] || []; + while(queued.length) { + registerState(queued.shift()); + } + } + + function registerState(state) { + // Wrap a new object around the state so we can store our private details easily. + state = inherit(state, { + self: state, + resolve: state.resolve || {}, + toString: function() { return this.name; } + }); + + var name = state.name; + if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name"); + if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined"); + + // Get parent name + var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.')) + : (isString(state.parent)) ? state.parent + : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name + : ''; + + // If parent is not registered yet, add state to queue and register later + if (parentName && !states[parentName]) { + return queueState(parentName, state.self); + } + + for (var key in stateBuilder) { + if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]); + } + states[name] = state; + + // Register the state in the global state list and with $urlRouter if necessary. + if (!state[abstractKey] && state.url) { + $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) { + if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) { + $state.transitionTo(state, $match, { inherit: true, location: false }); + } + }]); + } + + // Register any queued children + flushQueuedChildren(name); + + return state; + } + + // Checks text to see if it looks like a glob. + function isGlob (text) { + return text.indexOf('*') > -1; + } + + // Returns true if glob matches current $state name. + function doesStateMatchGlob (glob) { + var globSegments = glob.split('.'), + segments = $state.$current.name.split('.'); + + //match greedy starts + if (globSegments[0] === '**') { + segments = segments.slice(indexOf(segments, globSegments[1])); + segments.unshift('**'); + } + //match greedy ends + if (globSegments[globSegments.length - 1] === '**') { + segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE); + segments.push('**'); + } + + if (globSegments.length != segments.length) { + return false; + } + + //match single stars + for (var i = 0, l = globSegments.length; i < l; i++) { + if (globSegments[i] === '*') { + segments[i] = '*'; + } + } + + return segments.join('') === globSegments.join(''); + } + + + // Implicit root state that is always active + root = registerState({ + name: '', + url: '^', + views: null, + 'abstract': true + }); + root.navigable = null; + + + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#decorator + * @methodOf ui.router.state.$stateProvider + * + * @description + * Allows you to extend (carefully) or override (at your own peril) the + * `stateBuilder` object used internally by `$stateProvider`. This can be used + * to add custom functionality to ui-router, for example inferring templateUrl + * based on the state name. + * + * When passing only a name, it returns the current (original or decorated) builder + * function that matches `name`. + * + * The builder functions that can be decorated are listed below. Though not all + * necessarily have a good use case for decoration, that is up to you to decide. + * + * In addition, users can attach custom decorators, which will generate new + * properties within the state's internal definition. There is currently no clear + * use-case for this beyond accessing internal states (i.e. $state.$current), + * however, expect this to become increasingly relevant as we introduce additional + * meta-programming features. + * + * **Warning**: Decorators should not be interdependent because the order of + * execution of the builder functions in non-deterministic. Builder functions + * should only be dependent on the state definition object and super function. + * + * + * Existing builder functions and current return values: + * + * - **parent** `{object}` - returns the parent state object. + * - **data** `{object}` - returns state data, including any inherited data that is not + * overridden by own values (if any). + * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher} + * or `null`. + * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is + * navigable). + * - **params** `{object}` - returns an array of state params that are ensured to + * be a super-set of parent's params. + * - **views** `{object}` - returns a views object where each key is an absolute view + * name (i.e. "viewName@stateName") and each value is the config object + * (template, controller) for the view. Even when you don't use the views object + * explicitly on a state config, one is still created for you internally. + * So by decorating this builder function you have access to decorating template + * and controller properties. + * - **ownParams** `{object}` - returns an array of params that belong to the state, + * not including any params defined by ancestor states. + * - **path** `{string}` - returns the full path from the root down to this state. + * Needed for state activation. + * - **includes** `{object}` - returns an object that includes every state that + * would pass a `$state.includes()` test. + * + * @example + * <pre> + * // Override the internal 'views' builder with a function that takes the state + * // definition, and a reference to the internal function being overridden: + * $stateProvider.decorator('views', function (state, parent) { + * var result = {}, + * views = parent(state); + * + * angular.forEach(views, function (config, name) { + * var autoName = (state.name + '.' + name).replace('.', '/'); + * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html'; + * result[name] = config; + * }); + * return result; + * }); + * + * $stateProvider.state('home', { + * views: { + * 'contact.list': { controller: 'ListController' }, + * 'contact.item': { controller: 'ItemController' } + * } + * }); + * + * // ... + * + * $state.go('home'); + * // Auto-populates list and item views with /partials/home/contact/list.html, + * // and /partials/home/contact/item.html, respectively. + * </pre> + * + * @param {string} name The name of the builder function to decorate. + * @param {object} func A function that is responsible for decorating the original + * builder function. The function receives two parameters: + * + * - `{object}` - state - The state config object. + * - `{object}` - super - The original builder function. + * + * @return {object} $stateProvider - $stateProvider instance + */ + this.decorator = decorator; + function decorator(name, func) { + /*jshint validthis: true */ + if (isString(name) && !isDefined(func)) { + return stateBuilder[name]; + } + if (!isFunction(func) || !isString(name)) { + return this; + } + if (stateBuilder[name] && !stateBuilder.$delegates[name]) { + stateBuilder.$delegates[name] = stateBuilder[name]; + } + stateBuilder[name] = func; + return this; + } + + /** + * @ngdoc function + * @name ui.router.state.$stateProvider#state + * @methodOf ui.router.state.$stateProvider + * + * @description + * Registers a state configuration under a given state name. The stateConfig object + * has the following acceptable properties. + * + * @param {string} name A unique state name, e.g. "home", "about", "contacts". + * To create a parent/child state use a dot, e.g. "about.sales", "home.newest". + * @param {object} stateConfig State configuration object. + * @param {string|function=} stateConfig.template + * <a id='template'></a> + * html template as a string or a function that returns + * an html template as a string which should be used by the uiView directives. This property + * takes precedence over templateUrl. + * + * If `template` is a function, it will be called with the following parameters: + * + * - {array.<object>} - state parameters extracted from the current $location.path() by + * applying the current state + * + * <pre>template: + * "<h1>inline template definition</h1>" + + * "<div ui-view></div>"</pre> + * <pre>template: function(params) { + * return "<h1>generated template</h1>"; }</pre> + * </div> + * + * @param {string|function=} stateConfig.templateUrl + * <a id='templateUrl'></a> + * + * path or function that returns a path to an html + * template that should be used by uiView. + * + * If `templateUrl` is a function, it will be called with the following parameters: + * + * - {array.<object>} - state parameters extracted from the current $location.path() by + * applying the current state + * + * <pre>templateUrl: "home.html"</pre> + * <pre>templateUrl: function(params) { + * return myTemplates[params.pageId]; }</pre> + * + * @param {function=} stateConfig.templateProvider + * <a id='templateProvider'></a> + * Provider function that returns HTML content string. + * <pre> templateProvider: + * function(MyTemplateService, params) { + * return MyTemplateService.getTemplate(params.pageId); + * }</pre> + * + * @param {string|function=} stateConfig.controller + * <a id='controller'></a> + * + * Controller fn that should be associated with newly + * related scope or the name of a registered controller if passed as a string. + * Optionally, the ControllerAs may be declared here. + * <pre>controller: "MyRegisteredController"</pre> + * <pre>controller: + * "MyRegisteredController as fooCtrl"}</pre> + * <pre>controller: function($scope, MyService) { + * $scope.data = MyService.getData(); }</pre> + * + * @param {function=} stateConfig.controllerProvider + * <a id='controllerProvider'></a> + * + * Injectable provider function that returns the actual controller or string. + * <pre>controllerProvider: + * function(MyResolveData) { + * if (MyResolveData.foo) + * return "FooCtrl" + * else if (MyResolveData.bar) + * return "BarCtrl"; + * else return function($scope) { + * $scope.baz = "Qux"; + * } + * }</pre> + * + * @param {string=} stateConfig.controllerAs + * <a id='controllerAs'></a> + * + * A controller alias name. If present the controller will be + * published to scope under the controllerAs name. + * <pre>controllerAs: "myCtrl"</pre> + * + * @param {object=} stateConfig.resolve + * <a id='resolve'></a> + * + * An optional map<string, function> of dependencies which + * should be injected into the controller. If any of these dependencies are promises, + * the router will wait for them all to be resolved before the controller is instantiated. + * If all the promises are resolved successfully, the $stateChangeSuccess event is fired + * and the values of the resolved promises are injected into any controllers that reference them. + * If any of the promises are rejected the $stateChangeError event is fired. + * + * The map object is: + * + * - key - {string}: name of dependency to be injected into controller + * - factory - {string|function}: If string then it is alias for service. Otherwise if function, + * it is injected and return value it treated as dependency. If result is a promise, it is + * resolved before its value is injected into controller. + * + * <pre>resolve: { + * myResolve1: + * function($http, $stateParams) { + * return $http.get("/api/foos/"+stateParams.fooID); + * } + * }</pre> + * + * @param {string=} stateConfig.url + * <a id='url'></a> + * + * A url fragment with optional parameters. When a state is navigated or + * transitioned to, the `$stateParams` service will be populated with any + * parameters that were passed. + * + * examples: + * <pre>url: "/home" + * url: "/users/:userid" + * url: "/books/{bookid:[a-zA-Z_-]}" + * url: "/books/{categoryid:int}" + * url: "/books/{publishername:string}/{categoryid:int}" + * url: "/messages?before&after" + * url: "/messages?{before:date}&{after:date}"</pre> + * url: "/messages/:mailboxid?{before:date}&{after:date}" + * + * @param {object=} stateConfig.views + * <a id='views'></a> + * an optional map<string, object> which defined multiple views, or targets views + * manually/explicitly. + * + * Examples: + * + * Targets three named `ui-view`s in the parent state's template + * <pre>views: { + * header: { + * controller: "headerCtrl", + * templateUrl: "header.html" + * }, body: { + * controller: "bodyCtrl", + * templateUrl: "body.html" + * }, footer: { + * controller: "footCtrl", + * templateUrl: "footer.html" + * } + * }</pre> + * + * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template. + * <pre>views: { + * 'header@top': { + * controller: "msgHeaderCtrl", + * templateUrl: "msgHeader.html" + * }, 'body': { + * controller: "messagesCtrl", + * templateUrl: "messages.html" + * } + * }</pre> + * + * @param {boolean=} [stateConfig.abstract=false] + * <a id='abstract'></a> + * An abstract state will never be directly activated, + * but can provide inherited properties to its common children states. + * <pre>abstract: true</pre> + * + * @param {function=} stateConfig.onEnter + * <a id='onEnter'></a> + * + * Callback function for when a state is entered. Good way + * to trigger an action or dispatch an event, such as opening a dialog. + * If minifying your scripts, make sure to explictly annotate this function, + * because it won't be automatically annotated by your build tools. + * + * <pre>onEnter: function(MyService, $stateParams) { + * MyService.foo($stateParams.myParam); + * }</pre> + * + * @param {function=} stateConfig.onExit + * <a id='onExit'></a> + * + * Callback function for when a state is exited. Good way to + * trigger an action or dispatch an event, such as opening a dialog. + * If minifying your scripts, make sure to explictly annotate this function, + * because it won't be automatically annotated by your build tools. + * + * <pre>onExit: function(MyService, $stateParams) { + * MyService.cleanup($stateParams.myParam); + * }</pre> + * + * @param {boolean=} [stateConfig.reloadOnSearch=true] + * <a id='reloadOnSearch'></a> + * + * If `false`, will not retrigger the same state + * just because a search/query parameter has changed (via $location.search() or $location.hash()). + * Useful for when you'd like to modify $location.search() without triggering a reload. + * <pre>reloadOnSearch: false</pre> + * + * @param {object=} stateConfig.data + * <a id='data'></a> + * + * Arbitrary data object, useful for custom configuration. The parent state's `data` is + * prototypally inherited. In other words, adding a data property to a state adds it to + * the entire subtree via prototypal inheritance. + * + * <pre>data: { + * requiredRole: 'foo' + * } </pre> + * + * @param {object=} stateConfig.params + * <a id='params'></a> + * + * A map which optionally configures parameters declared in the `url`, or + * defines additional non-url parameters. For each parameter being + * configured, add a configuration object keyed to the name of the parameter. + * + * Each parameter configuration object may contain the following properties: + * + * - ** value ** - {object|function=}: specifies the default value for this + * parameter. This implicitly sets this parameter as optional. + * + * When UI-Router routes to a state and no value is + * specified for this parameter in the URL or transition, the + * default value will be used instead. If `value` is a function, + * it will be injected and invoked, and the return value used. + * + * *Note*: `undefined` is treated as "no default value" while `null` + * is treated as "the default value is `null`". + * + * *Shorthand*: If you only need to configure the default value of the + * parameter, you may use a shorthand syntax. In the **`params`** + * map, instead mapping the param name to a full parameter configuration + * object, simply set map it to the default parameter value, e.g.: + * + * <pre>// define a parameter's default value + * params: { + * param1: { value: "defaultValue" } + * } + * // shorthand default values + * params: { + * param1: "defaultValue", + * param2: "param2Default" + * }</pre> + * + * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be + * treated as an array of values. If you specified a Type, the value will be + * treated as an array of the specified Type. Note: query parameter values + * default to a special `"auto"` mode. + * + * For query parameters in `"auto"` mode, if multiple values for a single parameter + * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values + * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if + * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single + * value (e.g.: `{ foo: '1' }`). + * + * <pre>params: { + * param1: { array: true } + * }</pre> + * + * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when + * the current parameter value is the same as the default value. If `squash` is not set, it uses the + * configured default squash policy. + * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`}) + * + * There are three squash settings: + * + * - false: The parameter's default value is not squashed. It is encoded and included in the URL + * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed + * by slashes in the state's `url` declaration, then one of those slashes are omitted. + * This can allow for cleaner looking URLs. + * - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of your choice. + * + * <pre>params: { + * param1: { + * value: "defaultId", + * squash: true + * } } + * // squash "defaultValue" to "~" + * params: { + * param1: { + * value: "defaultValue", + * squash: "~" + * } } + * </pre> + * + * + * @example + * <pre> + * // Some state name examples + * + * // stateName can be a single top-level name (must be unique). + * $stateProvider.state("home", {}); + * + * // Or it can be a nested state name. This state is a child of the + * // above "home" state. + * $stateProvider.state("home.newest", {}); + * + * // Nest states as deeply as needed. + * $stateProvider.state("home.newest.abc.xyz.inception", {}); + * + * // state() returns $stateProvider, so you can chain state declarations. + * $stateProvider + * .state("home", {}) + * .state("about", {}) + * .state("contacts", {}); + * </pre> + * + */ + this.state = state; + function state(name, definition) { + /*jshint validthis: true */ + if (isObject(name)) definition = name; + else definition.name = name; + registerState(definition); + return this; + } + + /** + * @ngdoc object + * @name ui.router.state.$state + * + * @requires $rootScope + * @requires $q + * @requires ui.router.state.$view + * @requires $injector + * @requires ui.router.util.$resolve + * @requires ui.router.state.$stateParams + * @requires ui.router.router.$urlRouter + * + * @property {object} params A param object, e.g. {sectionId: section.id)}, that + * you'd like to test against the current active state. + * @property {object} current A reference to the state's config object. However + * you passed it in. Useful for accessing custom data. + * @property {object} transition Currently pending transition. A promise that'll + * resolve or reject. + * + * @description + * `$state` service is responsible for representing states as well as transitioning + * between them. It also provides interfaces to ask for current state or even states + * you're coming from. + */ + this.$get = $get; + $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory']; + function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $urlRouter, $location, $urlMatcherFactory) { + + var TransitionSuperseded = $q.reject(new Error('transition superseded')); + var TransitionPrevented = $q.reject(new Error('transition prevented')); + var TransitionAborted = $q.reject(new Error('transition aborted')); + var TransitionFailed = $q.reject(new Error('transition failed')); + + // Handles the case where a state which is the target of a transition is not found, and the user + // can optionally retry or defer the transition + function handleRedirect(redirect, state, params, options) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateNotFound + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when a requested state **cannot be found** using the provided state name during transition. + * The event is broadcast allowing any handlers a single chance to deal with the error (usually by + * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler, + * you can see its three properties in the example. You can use `event.preventDefault()` to abort the + * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value. + * + * @param {Object} event Event object. + * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties. + * @param {State} fromState Current state object. + * @param {Object} fromParams Current state params. + * + * @example + * + * <pre> + * // somewhere, assume lazy.state has not been defined + * $state.go("lazy.state", {a:1, b:2}, {inherit:false}); + * + * // somewhere else + * $scope.$on('$stateNotFound', + * function(event, unfoundState, fromState, fromParams){ + * console.log(unfoundState.to); // "lazy.state" + * console.log(unfoundState.toParams); // {a:1, b:2} + * console.log(unfoundState.options); // {inherit:false} + default options + * }) + * </pre> + */ + var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params); + + if (evt.defaultPrevented) { + $urlRouter.update(); + return TransitionAborted; + } + + if (!evt.retry) { + return null; + } + + // Allow the handler to return a promise to defer state lookup retry + if (options.$retry) { + $urlRouter.update(); + return TransitionFailed; + } + var retryTransition = $state.transition = $q.when(evt.retry); + + retryTransition.then(function() { + if (retryTransition !== $state.transition) return TransitionSuperseded; + redirect.options.$retry = true; + return $state.transitionTo(redirect.to, redirect.toParams, redirect.options); + }, function() { + return TransitionAborted; + }); + $urlRouter.update(); + + return retryTransition; + } + + root.locals = { resolve: null, globals: { $stateParams: {} } }; + + $state = { + params: {}, + current: root.self, + $current: root, + transition: null + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#reload + * @methodOf ui.router.state.$state + * + * @description + * A method that force reloads the current state. All resolves are re-resolved, events are not re-fired, + * and controllers reinstantiated (bug with controllers reinstantiating right now, fixing soon). + * + * @example + * <pre> + * var app angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.reload = function(){ + * $state.reload(); + * } + * }); + * </pre> + * + * `reload()` is just an alias for: + * <pre> + * $state.transitionTo($state.current, $stateParams, { + * reload: true, inherit: false, notify: true + * }); + * </pre> + * + * @returns {promise} A promise representing the state of the new transition. See + * {@link ui.router.state.$state#methods_go $state.go}. + */ + $state.reload = function reload() { + return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true }); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#go + * @methodOf ui.router.state.$state + * + * @description + * Convenience method for transitioning to a new state. `$state.go` calls + * `$state.transitionTo` internally but automatically sets options to + * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. + * This allows you to easily use an absolute or relative to path and specify + * only the parameters you'd like to update (while letting unspecified parameters + * inherit from the currently active ancestor states). + * + * @example + * <pre> + * var app = angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.changeState = function () { + * $state.go('contact.detail'); + * }; + * }); + * </pre> + * <img src='../ngdoc_assets/StateGoExamples.png'/> + * + * @param {string} to Absolute state name or relative state path. Some examples: + * + * - `$state.go('contact.detail')` - will go to the `contact.detail` state + * - `$state.go('^')` - will go to a parent state + * - `$state.go('^.sibling')` - will go to a sibling state + * - `$state.go('.child.grandchild')` - will go to grandchild state + * + * @param {object=} params A map of the parameters that will be sent to the state, + * will populate $stateParams. Any parameters that are not specified will be inherited from currently + * defined parameters. This allows, for example, going to a sibling state that shares parameters + * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e. + * transitioning to a sibling will get you the parameters for all parents, transitioning to a child + * will get you all current parameters, etc. + * @param {object=} options Options object. The options are: + * + * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` + * will not. If string, must be `"replace"`, which will update url and also replace last history record. + * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. + * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params + * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd + * use this when you want to force a reload when *everything* is the same, including search params. + * + * @returns {promise} A promise representing the state of the new transition. + * + * Possible success values: + * + * - $state.current + * + * <br/>Possible rejection values: + * + * - 'transition superseded' - when a newer transition has been started after this one + * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener + * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or + * when a `$stateNotFound` `event.retry` promise errors. + * - 'transition failed' - when a state has been unsuccessfully found after 2 tries. + * - *resolve error* - when an error has occurred with a `resolve` + * + */ + $state.go = function go(to, params, options) { + return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options)); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#transitionTo + * @methodOf ui.router.state.$state + * + * @description + * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go} + * uses `transitionTo` internally. `$state.go` is recommended in most situations. + * + * @example + * <pre> + * var app = angular.module('app', ['ui.router']); + * + * app.controller('ctrl', function ($scope, $state) { + * $scope.changeState = function () { + * $state.transitionTo('contact.detail'); + * }; + * }); + * </pre> + * + * @param {string} to State name. + * @param {object=} toParams A map of the parameters that will be sent to the state, + * will populate $stateParams. + * @param {object=} options Options object. The options are: + * + * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false` + * will not. If string, must be `"replace"`, which will update url and also replace last history record. + * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events. + * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params + * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd + * use this when you want to force a reload when *everything* is the same, including search params. + * + * @returns {promise} A promise representing the state of the new transition. See + * {@link ui.router.state.$state#methods_go $state.go}. + */ + $state.transitionTo = function transitionTo(to, toParams, options) { + toParams = toParams || {}; + options = extend({ + location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false + }, options || {}); + + var from = $state.$current, fromParams = $state.params, fromPath = from.path; + var evt, toState = findState(to, options.relative); + + if (!isDefined(toState)) { + var redirect = { to: to, toParams: toParams, options: options }; + var redirectResult = handleRedirect(redirect, from.self, fromParams, options); + + if (redirectResult) { + return redirectResult; + } + + // Always retry once if the $stateNotFound was not prevented + // (handles either redirect changed or state lazy-definition) + to = redirect.to; + toParams = redirect.toParams; + options = redirect.options; + toState = findState(to, options.relative); + + if (!isDefined(toState)) { + if (!options.relative) throw new Error("No such state '" + to + "'"); + throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'"); + } + } + if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'"); + if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState); + if (!toState.params.$$validates(toParams)) return TransitionFailed; + + toParams = toState.params.$$values(toParams); + to = toState; + + var toPath = to.path; + + // Starting from the root of the path, keep all levels that haven't changed + var keep = 0, state = toPath[keep], locals = root.locals, toLocals = []; + + if (!options.reload) { + while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) { + locals = toLocals[keep] = state.locals; + keep++; + state = toPath[keep]; + } + } + + // If we're going to the same state and all locals are kept, we've got nothing to do. + // But clear 'transition', as we still want to cancel any other pending transitions. + // TODO: We may not want to bump 'transition' if we're called from a location change + // that we've initiated ourselves, because we might accidentally abort a legitimate + // transition initiated from code? + if (shouldTriggerReload(to, from, locals, options)) { + if (to.self.reloadOnSearch !== false) $urlRouter.update(); + $state.transition = null; + return $q.when($state.current); + } + + // Filter parameters before we pass them to event handlers etc. + toParams = filterByKeys(to.params.$$keys(), toParams || {}); + + // Broadcast start event and cancel the transition if requested + if (options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeStart + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when the state transition **begins**. You can use `event.preventDefault()` + * to prevent the transition from happening and then the transition promise will be + * rejected with a `'transition prevented'` value. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + * + * @example + * + * <pre> + * $rootScope.$on('$stateChangeStart', + * function(event, toState, toParams, fromState, fromParams){ + * event.preventDefault(); + * // transitionTo() promise will be rejected with + * // a 'transition prevented' error + * }) + * </pre> + */ + if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) { + $urlRouter.update(); + return TransitionPrevented; + } + } + + // Resolve locals for the remaining states, but don't update any global state just + // yet -- if anything fails to resolve the current state needs to remain untouched. + // We also set up an inheritance chain for the locals here. This allows the view directive + // to quickly look up the correct definition for each view in the current state. Even + // though we create the locals object itself outside resolveState(), it is initially + // empty and gets filled asynchronously. We need to keep track of the promise for the + // (fully resolved) current locals, and pass this down the chain. + var resolved = $q.when(locals); + + for (var l = keep; l < toPath.length; l++, state = toPath[l]) { + locals = toLocals[l] = inherit(locals); + resolved = resolveState(state, toParams, state === to, resolved, locals, options); + } + + // Once everything is resolved, we are ready to perform the actual transition + // and return a promise for the new state. We also keep track of what the + // current promise is, so that we can detect overlapping transitions and + // keep only the outcome of the last transition. + var transition = $state.transition = resolved.then(function () { + var l, entering, exiting; + + if ($state.transition !== transition) return TransitionSuperseded; + + // Exit 'from' states not kept + for (l = fromPath.length - 1; l >= keep; l--) { + exiting = fromPath[l]; + if (exiting.self.onExit) { + $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals); + } + exiting.locals = null; + } + + // Enter 'to' states not kept + for (l = keep; l < toPath.length; l++) { + entering = toPath[l]; + entering.locals = toLocals[l]; + if (entering.self.onEnter) { + $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals); + } + } + + // Run it again, to catch any transitions in callbacks + if ($state.transition !== transition) return TransitionSuperseded; + + // Update globals in $state + $state.$current = to; + $state.current = to.self; + $state.params = toParams; + copy($state.params, $stateParams); + $state.transition = null; + + if (options.location && to.navigable) { + $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, { + $$avoidResync: true, replace: options.location === 'replace' + }); + } + + if (options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeSuccess + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired once the state transition is **complete**. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + */ + $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams); + } + $urlRouter.update(true); + + return $state.current; + }, function (error) { + if ($state.transition !== transition) return TransitionSuperseded; + + $state.transition = null; + /** + * @ngdoc event + * @name ui.router.state.$state#$stateChangeError + * @eventOf ui.router.state.$state + * @eventType broadcast on root scope + * @description + * Fired when an **error occurs** during transition. It's important to note that if you + * have any errors in your resolve functions (javascript errors, non-existent services, etc) + * they will not throw traditionally. You must listen for this $stateChangeError event to + * catch **ALL** errors. + * + * @param {Object} event Event object. + * @param {State} toState The state being transitioned to. + * @param {Object} toParams The params supplied to the `toState`. + * @param {State} fromState The current state, pre-transition. + * @param {Object} fromParams The params supplied to the `fromState`. + * @param {Error} error The resolve error object. + */ + evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error); + + if (!evt.defaultPrevented) { + $urlRouter.update(); + } + + return $q.reject(error); + }); + + return transition; + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#is + * @methodOf ui.router.state.$state + * + * @description + * Similar to {@link ui.router.state.$state#methods_includes $state.includes}, + * but only checks for the full state name. If params is supplied then it will be + * tested for strict equality against the current active params object, so all params + * must match with none missing and no extras. + * + * @example + * <pre> + * $state.$current.name = 'contacts.details.item'; + * + * // absolute name + * $state.is('contact.details.item'); // returns true + * $state.is(contactDetailItemStateObject); // returns true + * + * // relative name (. and ^), typically from a template + * // E.g. from the 'contacts.details' template + * <div ng-class="{highlighted: $state.is('.item')}">Item</div> + * </pre> + * + * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check. + * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like + * to test against the current active state. + * @param {object=} options An options object. The options are: + * + * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will + * test relative to `options.relative` state (or name). + * + * @returns {boolean} Returns true if it is the state. + */ + $state.is = function is(stateOrName, params, options) { + options = extend({ relative: $state.$current }, options || {}); + var state = findState(stateOrName, options.relative); + + if (!isDefined(state)) { return undefined; } + if ($state.$current !== state) { return false; } + return params ? equalForKeys(state.params.$$values(params), $stateParams) : true; + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#includes + * @methodOf ui.router.state.$state + * + * @description + * A method to determine if the current active state is equal to or is the child of the + * state stateName. If any params are passed then they will be tested for a match as well. + * Not all the parameters need to be passed, just the ones you'd like to test for equality. + * + * @example + * Partial and relative names + * <pre> + * $state.$current.name = 'contacts.details.item'; + * + * // Using partial names + * $state.includes("contacts"); // returns true + * $state.includes("contacts.details"); // returns true + * $state.includes("contacts.details.item"); // returns true + * $state.includes("contacts.list"); // returns false + * $state.includes("about"); // returns false + * + * // Using relative names (. and ^), typically from a template + * // E.g. from the 'contacts.details' template + * <div ng-class="{highlighted: $state.includes('.item')}">Item</div> + * </pre> + * + * Basic globbing patterns + * <pre> + * $state.$current.name = 'contacts.details.item.url'; + * + * $state.includes("*.details.*.*"); // returns true + * $state.includes("*.details.**"); // returns true + * $state.includes("**.item.**"); // returns true + * $state.includes("*.details.item.url"); // returns true + * $state.includes("*.details.*.url"); // returns true + * $state.includes("*.details.*"); // returns false + * $state.includes("item.**"); // returns false + * </pre> + * + * @param {string} stateOrName A partial name, relative name, or glob pattern + * to be searched for within the current state name. + * @param {object=} params A param object, e.g. `{sectionId: section.id}`, + * that you'd like to test against the current active state. + * @param {object=} options An options object. The options are: + * + * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set, + * .includes will test relative to `options.relative` state (or name). + * + * @returns {boolean} Returns true if it does include the state + */ + $state.includes = function includes(stateOrName, params, options) { + options = extend({ relative: $state.$current }, options || {}); + if (isString(stateOrName) && isGlob(stateOrName)) { + if (!doesStateMatchGlob(stateOrName)) { + return false; + } + stateOrName = $state.$current.name; + } + + var state = findState(stateOrName, options.relative); + if (!isDefined(state)) { return undefined; } + if (!isDefined($state.$current.includes[state.name])) { return false; } + return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true; + }; + + + /** + * @ngdoc function + * @name ui.router.state.$state#href + * @methodOf ui.router.state.$state + * + * @description + * A url generation method that returns the compiled url for the given state populated with the given params. + * + * @example + * <pre> + * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob"); + * </pre> + * + * @param {string|object} stateOrName The state name or state object you'd like to generate a url from. + * @param {object=} params An object of parameter values to fill the state's required parameters. + * @param {object=} options Options object. The options are: + * + * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the + * first parameter, then the constructed href url will be built from the first navigable ancestor (aka + * ancestor with a valid url). + * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url. + * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), + * defines which state to be relative from. + * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". + * + * @returns {string} compiled state url + */ + $state.href = function href(stateOrName, params, options) { + options = extend({ + lossy: true, + inherit: true, + absolute: false, + relative: $state.$current + }, options || {}); + + var state = findState(stateOrName, options.relative); + + if (!isDefined(state)) return null; + if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state); + + var nav = (state && options.lossy) ? state.navigable : state; + + if (!nav || nav.url === undefined || nav.url === null) { + return null; + } + return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys(), params || {}), { + absolute: options.absolute + }); + }; + + /** + * @ngdoc function + * @name ui.router.state.$state#get + * @methodOf ui.router.state.$state + * + * @description + * Returns the state configuration object for any specific state or all states. + * + * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for + * the requested state. If not provided, returns an array of ALL state configs. + * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context. + * @returns {Object|Array} State configuration object or array of all objects. + */ + $state.get = function (stateOrName, context) { + if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; }); + var state = findState(stateOrName, context || $state.$current); + return (state && state.self) ? state.self : null; + }; + + function resolveState(state, params, paramsAreFiltered, inherited, dst, options) { + // Make a restricted $stateParams with only the parameters that apply to this state if + // necessary. In addition to being available to the controller and onEnter/onExit callbacks, + // we also need $stateParams to be available for any $injector calls we make during the + // dependency resolution process. + var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params); + var locals = { $stateParams: $stateParams }; + + // Resolve 'global' dependencies for the state, i.e. those not specific to a view. + // We're also including $stateParams in this; that way the parameters are restricted + // to the set that should be visible to the state, and are independent of when we update + // the global $state and $stateParams values. + dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state); + var promises = [dst.resolve.then(function (globals) { + dst.globals = globals; + })]; + if (inherited) promises.push(inherited); + + // Resolve template and dependencies for all views. + forEach(state.views, function (view, name) { + var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {}); + injectables.$template = [ function () { + return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: options.notify }) || ''; + }]; + + promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) { + // References to the controller (only instantiated at link time) + if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) { + var injectLocals = angular.extend({}, injectables, locals); + result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals); + } else { + result.$$controller = view.controller; + } + // Provide access to the state itself for internal use + result.$$state = state; + result.$$controllerAs = view.controllerAs; + dst[name] = result; + })); + }); + + // Wait for all the promises and then return the activation object + return $q.all(promises).then(function (values) { + return dst; + }); + } + + return $state; + } + + function shouldTriggerReload(to, from, locals, options) { + if (to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false))) { + return true; + } + } +} + +angular.module('ui.router.state') + .value('$stateParams', {}) + .provider('$state', $StateProvider); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/stateDirectives.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/stateDirectives.js new file mode 100644 index 000000000..4d9d527dd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/stateDirectives.js @@ -0,0 +1,268 @@ +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; + } +} + +/** + * @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#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#go $state.go()} + */ +$StateRefDirective.$inject = ['$state', '$timeout']; +function $StateRefDirective($state, $timeout) { + var allowedOptions = ['location', 'inherit', 'reload']; + + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function(scope, element, attrs, uiSrefActive) { + var ref = parseStateRef(attrs.uiSref, $state.current.name); + var params = null, url = null, base = stateContext(element) || $state.$current; + var newHref = null, isAnchor = element.prop("tagName") === "A"; + var isForm = element[0].nodeName === "FORM"; + var attr = isForm ? "action" : "href", nav = true; + + var options = { relative: base, inherit: true }; + var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {}; + + angular.forEach(allowedOptions, function(option) { + if (option in optionsOverride) { + options[option] = optionsOverride[option]; + } + }); + + var update = function(newVal) { + if (newVal) params = angular.copy(newVal); + if (!nav) return; + + newHref = $state.href(ref.state, params, options); + + var activeDirective = uiSrefActive[1] || uiSrefActive[0]; + if (activeDirective) { + activeDirective.$$setStateInfo(ref.state, params); + } + if (newHref === null) { + nav = false; + return false; + } + attrs.$set(attr, newHref); + }; + + if (ref.paramExpr) { + scope.$watch(ref.paramExpr, function(newVal, oldVal) { + if (newVal !== params) update(newVal); + }, true); + params = angular.copy(scope.$eval(ref.paramExpr)); + } + update(); + + if (isForm) return; + + element.bind("click", function(e) { + var button = e.which || e.button; + if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { + // HACK: This is to allow ng-clicks to be processed before the transition is initiated: + var transition = $timeout(function() { + $state.go(ref.state, params, options); + }); + e.preventDefault(); + + // if the state has no URL, ignore one preventDefault from the <a> directive. + var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0; + e.preventDefault = function() { + if (ignorePreventDefaultCount-- <= 0) + $timeout.cancel(transition); + }; + } + }); + } + }; +} + +/** + * @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> + */ + +/** + * @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', function ($scope, $element, $attrs) { + var state, params, activeClass; + + // There probably isn't much point in $observing this + // uiSrefActive and uiSrefActiveEq share the same directive object with some + // slight difference in logic routing + activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope); + + // Allow uiSref to communicate with uiSrefActive[Equals] + this.$$setStateInfo = function (newState, newParams) { + state = $state.get(newState, stateContext($element)); + params = newParams; + update(); + }; + + $scope.$on('$stateChangeSuccess', update); + + // Update route state + function update() { + if (isMatch()) { + $element.addClass(activeClass); + } else { + $element.removeClass(activeClass); + } + } + + function isMatch() { + if (typeof $attrs.uiSrefActiveEq !== 'undefined') { + return state && $state.is(state.name, params); + } else { + return state && $state.includes(state.name, params); + } + } + }] + }; +} + +angular.module('ui.router.state') + .directive('uiSref', $StateRefDirective) + .directive('uiSrefActive', $StateRefActiveDirective) + .directive('uiSrefActiveEq', $StateRefActiveDirective); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/stateFilters.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/stateFilters.js new file mode 100644 index 000000000..e0a117580 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/stateFilters.js @@ -0,0 +1,39 @@ +/** + * @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) { + return $state.is(state); + }; + 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) { + return $state.includes(state); + }; + includesFilter.$stateful = true; + return includesFilter; +} + +angular.module('ui.router.state') + .filter('isState', $IsStateFilter) + .filter('includedByState', $IncludedByStateFilter); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/templateFactory.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/templateFactory.js new file mode 100644 index 000000000..ca491a987 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/templateFactory.js @@ -0,0 +1,110 @@ +/** + * @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); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/urlMatcherFactory.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/urlMatcherFactory.js new file mode 100644 index 000000000..a16e728c5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/urlMatcherFactory.js @@ -0,0 +1,1036 @@ +var $$UMFP; // reference to $UrlMatcherFactoryProvider + +/** + * @ngdoc object + * @name ui.router.util.type:UrlMatcher + * + * @description + * Matches URLs against patterns and extracts named parameters from the path or the search + * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list + * of search parameters. Multiple search parameter names are separated by '&'. Search parameters + * do not influence whether or not a URL is matched, but their values are passed through into + * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}. + * + * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace + * syntax, which optionally allows a regular expression for the parameter to be specified: + * + * * `':'` name - colon placeholder + * * `'*'` name - catch-all placeholder + * * `'{' name '}'` - curly placeholder + * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the + * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash. + * + * Parameter names may contain only word characters (latin letters, digits, and underscore) and + * must be unique within the pattern (across both path and search parameters). For colon + * placeholders or curly placeholders without an explicit regexp, a path parameter matches any + * number of characters other than '/'. For catch-all placeholders the path parameter matches + * any number of characters. + * + * Examples: + * + * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for + * trailing slashes, and patterns have to match the entire path, not just a prefix. + * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or + * '/user/bob/details'. The second path segment will be captured as the parameter 'id'. + * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax. + * * `'/user/{id:[^/]*}'` - Same as the previous example. + * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id + * parameter consists of 1 to 8 hex digits. + * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the + * path into the parameter 'path'. + * * `'/files/*path'` - ditto. + * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined + * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start + * + * @param {string} pattern The pattern to compile into a matcher. + * @param {Object} config A configuration object hash: + * @param {Object=} parentMatcher Used to concatenate the pattern/config onto + * an existing UrlMatcher + * + * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`. + * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`. + * + * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any + * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns + * non-null) will start with this prefix. + * + * @property {string} source The pattern that was passed into the constructor + * + * @property {string} sourcePath The path portion of the source property + * + * @property {string} sourceSearch The search portion of the source property + * + * @property {string} regex The constructed regex that will be used to match against the url when + * it is time to determine which url will match. + * + * @returns {Object} New `UrlMatcher` object + */ +function UrlMatcher(pattern, config, parentMatcher) { + config = extend({ params: {} }, isObject(config) ? config : {}); + + // Find all placeholders and create a compiled pattern, using either classic or curly syntax: + // '*' name + // ':' name + // '{' name '}' + // '{' name ':' regexp '}' + // The regular expression is somewhat complicated due to the need to allow curly braces + // inside the regular expression. The placeholder regexp breaks down as follows: + // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case) + // \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case + // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either + // [^{}\\]+ - anything other than curly braces or backslash + // \\. - a backslash escape + // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms + var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + compiled = '^', last = 0, m, + segments = this.segments = [], + parentParams = parentMatcher ? parentMatcher.params : {}, + params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(), + paramNames = []; + + function addParameter(id, type, config, location) { + paramNames.push(id); + if (parentParams[id]) return parentParams[id]; + if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'"); + if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'"); + params[id] = new $$UMFP.Param(id, type, config, location); + return params[id]; + } + + function quoteRegExp(string, pattern, squash) { + var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); + if (!pattern) return result; + switch(squash) { + case false: surroundPattern = ['(', ')']; break; + case true: surroundPattern = ['?(', ')?']; break; + default: surroundPattern = ['(' + squash + "|", ')?']; break; + } + return result + surroundPattern[0] + pattern + surroundPattern[1]; + } + + this.source = pattern; + + // Split into static segments separated by path parameter placeholders. + // The number of segments is always 1 more than the number of parameters. + function matchDetails(m, isSearch) { + var id, regexp, segment, type, cfg, arrayMode; + id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null + cfg = config.params[id]; + segment = pattern.substring(last, m.index); + regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null); + type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp) }); + return { + id: id, regexp: regexp, segment: segment, type: type, cfg: cfg + }; + } + + var p, param, segment; + while ((m = placeholder.exec(pattern))) { + p = matchDetails(m, false); + if (p.segment.indexOf('?') >= 0) break; // we're into the search part + + param = addParameter(p.id, p.type, p.cfg, "path"); + compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash); + segments.push(p.segment); + last = placeholder.lastIndex; + } + segment = pattern.substring(last); + + // Find any search parameter names and remove them from the last segment + var i = segment.indexOf('?'); + + if (i >= 0) { + var search = this.sourceSearch = segment.substring(i); + segment = segment.substring(0, i); + this.sourcePath = pattern.substring(0, last + i); + + if (search.length > 0) { + last = 0; + while ((m = searchPlaceholder.exec(search))) { + p = matchDetails(m, true); + param = addParameter(p.id, p.type, p.cfg, "search"); + last = placeholder.lastIndex; + // check if ?& + } + } + } else { + this.sourcePath = pattern; + this.sourceSearch = ''; + } + + compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$'; + segments.push(segment); + + this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined); + this.prefix = segments[0]; + this.$$paramNames = paramNames; +} + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#concat + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Returns a new matcher for a pattern constructed by appending the path part and adding the + * search parameters of the specified pattern to this pattern. The current pattern is not + * modified. This can be understood as creating a pattern for URLs that are relative to (or + * suffixes of) the current pattern. + * + * @example + * The following two matchers are equivalent: + * <pre> + * new UrlMatcher('/user/{id}?q').concat('/details?date'); + * new UrlMatcher('/user/{id}/details?q&date'); + * </pre> + * + * @param {string} pattern The pattern to append. + * @param {Object} config An object hash of the configuration for the matcher. + * @returns {UrlMatcher} A matcher for the concatenated pattern. + */ +UrlMatcher.prototype.concat = function (pattern, config) { + // Because order of search parameters is irrelevant, we can add our own search + // parameters to the end of the new pattern. Parse the new pattern by itself + // and then join the bits together, but it's much easier to do this on a string level. + var defaultConfig = { + caseInsensitive: $$UMFP.caseInsensitive(), + strict: $$UMFP.strictMode(), + squash: $$UMFP.defaultSquashPolicy() + }; + return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this); +}; + +UrlMatcher.prototype.toString = function () { + return this.source; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#exec + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Tests the specified path against this matcher, and returns an object containing the captured + * parameter values, or null if the path does not match. The returned object contains the values + * of any search parameters that are mentioned in the pattern, but their value may be null if + * they are not present in `searchParams`. This means that search parameters are always treated + * as optional. + * + * @example + * <pre> + * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { + * x: '1', q: 'hello' + * }); + * // returns { id: 'bob', q: 'hello', r: null } + * </pre> + * + * @param {string} path The URL path to match, e.g. `$location.path()`. + * @param {Object} searchParams URL search parameters, e.g. `$location.search()`. + * @returns {Object} The captured parameter values. + */ +UrlMatcher.prototype.exec = function (path, searchParams) { + var m = this.regexp.exec(path); + if (!m) return null; + searchParams = searchParams || {}; + + var paramNames = this.parameters(), nTotal = paramNames.length, + nPath = this.segments.length - 1, + values = {}, i, j, cfg, paramName; + + if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'"); + + function decodePathArray(string) { + function reverseString(str) { return str.split("").reverse().join(""); } + function unquoteDashes(str) { return str.replace(/\\-/, "-"); } + + var split = reverseString(string).split(/-(?!\\)/); + var allReversed = map(split, reverseString); + return map(allReversed, unquoteDashes).reverse(); + } + + for (i = 0; i < nPath; i++) { + paramName = paramNames[i]; + var param = this.params[paramName]; + var paramVal = m[i+1]; + // if the param value matches a pre-replace pair, replace the value before decoding. + for (j = 0; j < param.replace; j++) { + if (param.replace[j].from === paramVal) paramVal = param.replace[j].to; + } + if (paramVal && param.array === true) paramVal = decodePathArray(paramVal); + values[paramName] = param.value(paramVal); + } + for (/**/; i < nTotal; i++) { + paramName = paramNames[i]; + values[paramName] = this.params[paramName].value(searchParams[paramName]); + } + + return values; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#parameters + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Returns the names of all path and search parameters of this pattern in an unspecified order. + * + * @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the + * pattern has no parameters, an empty array is returned. + */ +UrlMatcher.prototype.parameters = function (param) { + if (!isDefined(param)) return this.$$paramNames; + return this.params[param] || null; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#validate + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Checks an object hash of parameters to validate their correctness according to the parameter + * types of this `UrlMatcher`. + * + * @param {Object} params The object hash of parameters to validate. + * @returns {boolean} Returns `true` if `params` validates, otherwise `false`. + */ +UrlMatcher.prototype.validates = function (params) { + return this.params.$$validates(params); +}; + +/** + * @ngdoc function + * @name ui.router.util.type:UrlMatcher#format + * @methodOf ui.router.util.type:UrlMatcher + * + * @description + * Creates a URL that matches this pattern by substituting the specified values + * for the path and search parameters. Null values for path parameters are + * treated as empty strings. + * + * @example + * <pre> + * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' }); + * // returns '/user/bob?q=yes' + * </pre> + * + * @param {Object} values the values to substitute for the parameters in this pattern. + * @returns {string} the formatted URL (path and optionally search part). + */ +UrlMatcher.prototype.format = function (values) { + values = values || {}; + var segments = this.segments, params = this.parameters(), paramset = this.params; + if (!this.validates(values)) return null; + + var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0]; + + function encodeDashes(str) { // Replace dashes with encoded "\-" + return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); }); + } + + for (i = 0; i < nTotal; i++) { + var isPathParam = i < nPath; + var name = params[i], param = paramset[name], value = param.value(values[name]); + var isDefaultValue = param.isOptional && param.type.equals(param.value(), value); + var squash = isDefaultValue ? param.squash : false; + var encoded = param.type.encode(value); + + if (isPathParam) { + var nextSegment = segments[i + 1]; + if (squash === false) { + if (encoded != null) { + if (isArray(encoded)) { + result += map(encoded, encodeDashes).join("-"); + } else { + result += encodeURIComponent(encoded); + } + } + result += nextSegment; + } else if (squash === true) { + var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/; + result += nextSegment.match(capture)[1]; + } else if (isString(squash)) { + result += squash + nextSegment; + } + } else { + if (encoded == null || (isDefaultValue && squash !== false)) continue; + if (!isArray(encoded)) encoded = [ encoded ]; + encoded = map(encoded, encodeURIComponent).join('&' + name + '='); + result += (search ? '&' : '?') + (name + '=' + encoded); + search = true; + } + } + + return result; +}; + +/** + * @ngdoc object + * @name ui.router.util.type:Type + * + * @description + * Implements an interface to define custom parameter types that can be decoded from and encoded to + * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`} + * objects when matching or formatting URLs, or comparing or validating parameter values. + * + * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more + * information on registering custom types. + * + * @param {Object} config A configuration object which contains the custom type definition. The object's + * properties will override the default methods and/or pattern in `Type`'s public interface. + * @example + * <pre> + * { + * decode: function(val) { return parseInt(val, 10); }, + * encode: function(val) { return val && val.toString(); }, + * equals: function(a, b) { return this.is(a) && a === b; }, + * is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; }, + * pattern: /\d+/ + * } + * </pre> + * + * @property {RegExp} pattern The regular expression pattern used to match values of this type when + * coming from a substring of a URL. + * + * @returns {Object} Returns a new `Type` object. + */ +function Type(config) { + extend(this, config); +} + +/** + * @ngdoc function + * @name ui.router.util.type:Type#is + * @methodOf ui.router.util.type:Type + * + * @description + * Detects whether a value is of a particular type. Accepts a native (decoded) value + * and determines whether it matches the current `Type` object. + * + * @param {*} val The value to check. + * @param {string} key Optional. If the type check is happening in the context of a specific + * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the + * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects. + * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`. + */ +Type.prototype.is = function(val, key) { + return true; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#encode + * @methodOf ui.router.util.type:Type + * + * @description + * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the + * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it + * only needs to be a representation of `val` that has been coerced to a string. + * + * @param {*} val The value to encode. + * @param {string} key The name of the parameter in which `val` is stored. Can be used for + * meta-programming of `Type` objects. + * @returns {string} Returns a string representation of `val` that can be encoded in a URL. + */ +Type.prototype.encode = function(val, key) { + return val; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#decode + * @methodOf ui.router.util.type:Type + * + * @description + * Converts a parameter value (from URL string or transition param) to a custom/native value. + * + * @param {string} val The URL parameter value to decode. + * @param {string} key The name of the parameter in which `val` is stored. Can be used for + * meta-programming of `Type` objects. + * @returns {*} Returns a custom representation of the URL parameter value. + */ +Type.prototype.decode = function(val, key) { + return val; +}; + +/** + * @ngdoc function + * @name ui.router.util.type:Type#equals + * @methodOf ui.router.util.type:Type + * + * @description + * Determines whether two decoded values are equivalent. + * + * @param {*} a A value to compare against. + * @param {*} b A value to compare against. + * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`. + */ +Type.prototype.equals = function(a, b) { + return a == b; +}; + +Type.prototype.$subPattern = function() { + var sub = this.pattern.toString(); + return sub.substr(1, sub.length - 2); +}; + +Type.prototype.pattern = /.*/; + +Type.prototype.toString = function() { return "{Type:" + this.name + "}"; }; + +/* + * Wraps an existing custom Type as an array of Type, depending on 'mode'. + * e.g.: + * - urlmatcher pattern "/path?{queryParam[]:int}" + * - url: "/path?queryParam=1&queryParam=2 + * - $stateParams.queryParam will be [1, 2] + * if `mode` is "auto", then + * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1 + * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2] + */ +Type.prototype.$asArray = function(mode, isSearch) { + if (!mode) return this; + if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only"); + return new ArrayType(this, mode); + + function ArrayType(type, mode) { + function bindTo(type, callbackName) { + return function() { + return type[callbackName].apply(type, arguments); + }; + } + + // Wrap non-array value as array + function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); } + // Unwrap array value for "auto" mode. Return undefined for empty array. + function arrayUnwrap(val) { + switch(val.length) { + case 0: return undefined; + case 1: return mode === "auto" ? val[0] : val; + default: return val; + } + } + function falsey(val) { return !val; } + + // Wraps type (.is/.encode/.decode) functions to operate on each value of an array + function arrayHandler(callback, allTruthyMode) { + return function handleArray(val) { + val = arrayWrap(val); + var result = map(val, callback); + if (allTruthyMode === true) + return filter(result, falsey).length === 0; + return arrayUnwrap(result); + }; + } + + // Wraps type (.equals) functions to operate on each value of an array + function arrayEqualsHandler(callback) { + return function handleArray(val1, val2) { + var left = arrayWrap(val1), right = arrayWrap(val2); + if (left.length !== right.length) return false; + for (var i = 0; i < left.length; i++) { + if (!callback(left[i], right[i])) return false; + } + return true; + }; + } + + this.encode = arrayHandler(bindTo(type, 'encode')); + this.decode = arrayHandler(bindTo(type, 'decode')); + this.is = arrayHandler(bindTo(type, 'is'), true); + this.equals = arrayEqualsHandler(bindTo(type, 'equals')); + this.pattern = type.pattern; + this.$arrayMode = mode; + } +}; + + + +/** + * @ngdoc object + * @name ui.router.util.$urlMatcherFactory + * + * @description + * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory + * is also available to providers under the name `$urlMatcherFactoryProvider`. + */ +function $UrlMatcherFactory() { + $$UMFP = this; + + var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false; + + function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; } + function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; } +// TODO: in 1.0, make string .is() return false if value is undefined by default. +// function regexpMatches(val) { /*jshint validthis:true */ return isDefined(val) && this.pattern.test(val); } + function regexpMatches(val) { /*jshint validthis:true */ return this.pattern.test(val); } + + var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = { + string: { + encode: valToString, + decode: valFromString, + is: regexpMatches, + pattern: /[^/]*/ + }, + int: { + encode: valToString, + decode: function(val) { return parseInt(val, 10); }, + is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; }, + pattern: /\d+/ + }, + bool: { + encode: function(val) { return val ? 1 : 0; }, + decode: function(val) { return parseInt(val, 10) !== 0; }, + is: function(val) { return val === true || val === false; }, + pattern: /0|1/ + }, + date: { + encode: function (val) { + if (!this.is(val)) + return undefined; + return [ val.getFullYear(), + ('0' + (val.getMonth() + 1)).slice(-2), + ('0' + val.getDate()).slice(-2) + ].join("-"); + }, + decode: function (val) { + if (this.is(val)) return val; + var match = this.capture.exec(val); + return match ? new Date(match[1], match[2] - 1, match[3]) : undefined; + }, + is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); }, + equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); }, + pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/, + capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/ + }, + json: { + encode: angular.toJson, + decode: angular.fromJson, + is: angular.isObject, + equals: angular.equals, + pattern: /[^/]*/ + }, + any: { // does not encode/decode + encode: angular.identity, + decode: angular.identity, + is: angular.identity, + equals: angular.equals, + pattern: /.*/ + } + }; + + function getDefaultConfig() { + return { + strict: isStrictMode, + caseInsensitive: isCaseInsensitive + }; + } + + function isInjectable(value) { + return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1]))); + } + + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + $UrlMatcherFactory.$$getDefaultValue = function(config) { + if (!isInjectable(config.value)) return config.value; + if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); + return injector.invoke(config.value); + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#caseInsensitive + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Defines whether URL matching should be case sensitive (the default behavior), or not. + * + * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`; + * @returns {boolean} the current value of caseInsensitive + */ + this.caseInsensitive = function(value) { + if (isDefined(value)) + isCaseInsensitive = value; + return isCaseInsensitive; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#strictMode + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Defines whether URLs should match trailing slashes, or not (the default behavior). + * + * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`. + * @returns {boolean} the current value of strictMode + */ + this.strictMode = function(value) { + if (isDefined(value)) + isStrictMode = value; + return isStrictMode; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Sets the default behavior when generating or matching URLs with default parameter values. + * + * @param {string} value A string that defines the default parameter URL squashing behavior. + * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL + * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the + * parameter is surrounded by slashes, squash (remove) one slash from the URL + * any other string, e.g. "~": When generating an href with a default parameter value, squash (remove) + * the parameter value from the URL and replace it with this string. + */ + this.defaultSquashPolicy = function(value) { + if (!isDefined(value)) return defaultSquashPolicy; + if (value !== true && value !== false && !isString(value)) + throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string"); + defaultSquashPolicy = value; + return value; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#compile + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern. + * + * @param {string} pattern The URL pattern. + * @param {Object} config The config object hash. + * @returns {UrlMatcher} The UrlMatcher. + */ + this.compile = function (pattern, config) { + return new UrlMatcher(pattern, extend(getDefaultConfig(), config)); + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#isMatcher + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Returns true if the specified object is a `UrlMatcher`, or false otherwise. + * + * @param {Object} object The object to perform the type check against. + * @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by + * implementing all the same methods. + */ + this.isMatcher = function (o) { + if (!isObject(o)) return false; + var result = true; + + forEach(UrlMatcher.prototype, function(val, name) { + if (isFunction(val)) { + result = result && (isDefined(o[name]) && isFunction(o[name])); + } + }); + return result; + }; + + /** + * @ngdoc function + * @name ui.router.util.$urlMatcherFactory#type + * @methodOf ui.router.util.$urlMatcherFactory + * + * @description + * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to + * generate URLs with typed parameters. + * + * @param {string} name The type name. + * @param {Object|Function} definition The type definition. See + * {@link ui.router.util.type:Type `Type`} for information on the values accepted. + * @param {Object|Function} definitionFn (optional) A function that is injected before the app + * runtime starts. The result of this function is merged into the existing `definition`. + * See {@link ui.router.util.type:Type `Type`} for information on the values accepted. + * + * @returns {Object} Returns `$urlMatcherFactoryProvider`. + * + * @example + * This is a simple example of a custom type that encodes and decodes items from an + * array, using the array index as the URL-encoded value: + * + * <pre> + * var list = ['John', 'Paul', 'George', 'Ringo']; + * + * $urlMatcherFactoryProvider.type('listItem', { + * encode: function(item) { + * // Represent the list item in the URL using its corresponding index + * return list.indexOf(item); + * }, + * decode: function(item) { + * // Look up the list item by index + * return list[parseInt(item, 10)]; + * }, + * is: function(item) { + * // Ensure the item is valid by checking to see that it appears + * // in the list + * return list.indexOf(item) > -1; + * } + * }); + * + * $stateProvider.state('list', { + * url: "/list/{item:listItem}", + * controller: function($scope, $stateParams) { + * console.log($stateParams.item); + * } + * }); + * + * // ... + * + * // Changes URL to '/list/3', logs "Ringo" to the console + * $state.go('list', { item: "Ringo" }); + * </pre> + * + * This is a more complex example of a type that relies on dependency injection to + * interact with services, and uses the parameter name from the URL to infer how to + * handle encoding and decoding parameter values: + * + * <pre> + * // Defines a custom type that gets a value from a service, + * // where each service gets different types of values from + * // a backend API: + * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) { + * + * // Matches up services to URL parameter names + * var services = { + * user: Users, + * post: Posts + * }; + * + * return { + * encode: function(object) { + * // Represent the object in the URL using its unique ID + * return object.id; + * }, + * decode: function(value, key) { + * // Look up the object by ID, using the parameter + * // name (key) to call the correct service + * return services[key].findById(value); + * }, + * is: function(object, key) { + * // Check that object is a valid dbObject + * return angular.isObject(object) && object.id && services[key]; + * } + * equals: function(a, b) { + * // Check the equality of decoded objects by comparing + * // their unique IDs + * return a.id === b.id; + * } + * }; + * }); + * + * // In a config() block, you can then attach URLs with + * // type-annotated parameters: + * $stateProvider.state('users', { + * url: "/users", + * // ... + * }).state('users.item', { + * url: "/{user:dbObject}", + * controller: function($scope, $stateParams) { + * // $stateParams.user will now be an object returned from + * // the Users service + * }, + * // ... + * }); + * </pre> + */ + this.type = function (name, definition, definitionFn) { + if (!isDefined(definition)) return $types[name]; + if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined."); + + $types[name] = new Type(extend({ name: name }, definition)); + if (definitionFn) { + typeQueue.push({ name: name, def: definitionFn }); + if (!enqueue) flushTypeQueue(); + } + return this; + }; + + // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s + function flushTypeQueue() { + while(typeQueue.length) { + var type = typeQueue.shift(); + if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime."); + angular.extend($types[type.name], injector.invoke(type.def)); + } + } + + // Register default types. Store them in the prototype of $types. + forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); }); + $types = inherit($types, {}); + + /* No need to document $get, since it returns this */ + this.$get = ['$injector', function ($injector) { + injector = $injector; + enqueue = false; + flushTypeQueue(); + + forEach(defaultTypes, function(type, name) { + if (!$types[name]) $types[name] = new Type(type); + }); + return this; + }]; + + this.Param = function Param(id, type, config, location) { + var self = this; + config = unwrapShorthand(config); + type = getType(config, type, location); + var arrayMode = getArrayMode(); + type = arrayMode ? type.$asArray(arrayMode, location === "search") : type; + if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined) + config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to "" + var isOptional = config.value !== undefined; + var squash = getSquashPolicy(config, isOptional); + var replace = getReplace(config, arrayMode, isOptional, squash); + + function unwrapShorthand(config) { + var keys = isObject(config) ? objectKeys(config) : []; + var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 && + indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1; + if (isShorthand) config = { value: config }; + config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; }; + return config; + } + + function getType(config, urlType, location) { + if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations."); + if (urlType) return urlType; + if (!config.type) return (location === "config" ? $types.any : $types.string); + return config.type instanceof Type ? config.type : new Type(config.type); + } + + // array config: param name (param[]) overrides default settings. explicit config overrides param name. + function getArrayMode() { + var arrayDefaults = { array: (location === "search" ? "auto" : false) }; + var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {}; + return extend(arrayDefaults, arrayParamNomenclature, config).array; + } + + /** + * returns false, true, or the squash value to indicate the "default parameter url squash policy". + */ + function getSquashPolicy(config, isOptional) { + var squash = config.squash; + if (!isOptional || squash === false) return false; + if (!isDefined(squash) || squash == null) return defaultSquashPolicy; + if (squash === true || isString(squash)) return squash; + throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string"); + } + + function getReplace(config, arrayMode, isOptional, squash) { + var replace, configuredKeys, defaultPolicy = [ + { from: "", to: (isOptional || arrayMode ? undefined : "") }, + { from: null, to: (isOptional || arrayMode ? undefined : "") } + ]; + replace = isArray(config.replace) ? config.replace : []; + if (isString(squash)) + replace.push({ from: squash, to: undefined }); + configuredKeys = map(replace, function(item) { return item.from; } ); + return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace); + } + + /** + * [Internal] Get the default value of a parameter, which may be an injectable function. + */ + function $$getDefaultValue() { + if (!injector) throw new Error("Injectable functions cannot be called at configuration time"); + return injector.invoke(config.$$fn); + } + + /** + * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the + * default value, which may be the result of an injectable function. + */ + function $value(value) { + function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; } + function $replace(value) { + var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; }); + return replacement.length ? replacement[0] : value; + } + value = $replace(value); + return isDefined(value) ? self.type.decode(value) : $$getDefaultValue(); + } + + function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; } + + extend(this, { + id: id, + type: type, + location: location, + array: arrayMode, + squash: squash, + replace: replace, + isOptional: isOptional, + value: $value, + dynamic: undefined, + config: config, + toString: toString + }); + }; + + function ParamSet(params) { + extend(this, params || {}); + } + + ParamSet.prototype = { + $$new: function() { + return inherit(this, extend(new ParamSet(), { $$parent: this})); + }, + $$keys: function () { + var keys = [], chain = [], parent = this, + ignore = objectKeys(ParamSet.prototype); + while (parent) { chain.push(parent); parent = parent.$$parent; } + chain.reverse(); + forEach(chain, function(paramset) { + forEach(objectKeys(paramset), function(key) { + if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key); + }); + }); + return keys; + }, + $$values: function(paramValues) { + var values = {}, self = this; + forEach(self.$$keys(), function(key) { + values[key] = self[key].value(paramValues && paramValues[key]); + }); + return values; + }, + $$equals: function(paramValues1, paramValues2) { + var equal = true, self = this; + forEach(self.$$keys(), function(key) { + var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key]; + if (!self[key].type.equals(left, right)) equal = false; + }); + return equal; + }, + $$validates: function $$validate(paramValues) { + var result = true, isOptional, val, param, self = this; + + forEach(this.$$keys(), function(key) { + param = self[key]; + val = paramValues[key]; + isOptional = !val && param.isOptional; + result = result && (isOptional || !!param.type.is(val)); + }); + return result; + }, + $$parent: undefined + }; + + this.ParamSet = ParamSet; +} + +// Register as a provider so it's available to other providers +angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory); +angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/urlRouter.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/urlRouter.js new file mode 100644 index 000000000..2b2293762 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/urlRouter.js @@ -0,0 +1,413 @@ +/** + * @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 {object} 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|object} 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 handle 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|object} 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']; + function $get( $location, $rootScope, $injector, $browser) { + + 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; + 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) { + $location.url(urlMatcher.format(params || {})); + 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; + } + + var url = urlMatcher.format(params); + options = options || {}; + + if (!isHtml5 && url !== null) { + url = "#" + $locationProvider.hashPrefix() + url; + } + 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); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/view.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/view.js new file mode 100644 index 000000000..f19a3c569 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/view.js @@ -0,0 +1,71 @@ + +$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); + } + if (result && options.notify) { + /** + * @ngdoc event + * @name ui.router.state.$state#$viewContentLoading + * @eventOf ui.router.state.$view + * @eventType broadcast on root scope + * @description + * + * Fired once the view **begins loading**, *before* the DOM is rendered. + * + * @param {Object} event Event object. + * @param {Object} viewConfig The view config properties (template, controller, etc). + * + * @example + * + * <pre> + * $scope.$on('$viewContentLoading', + * function(event, viewConfig){ + * // Access to all the view config properties. + * // and one special property 'targetView' + * // viewConfig.targetView + * }); + * </pre> + */ + $rootScope.$broadcast('$viewContentLoading', options); + } + return result; + } + }; + } +} + +angular.module('ui.router.state').provider('$view', $ViewProvider); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/viewDirective.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/viewDirective.js new file mode 100644 index 000000000..d3cf100a2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/viewDirective.js @@ -0,0 +1,302 @@ +/** + * @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#views `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> + */ +$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate']; +function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) { + + 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) { + var promise = $animate.enter(element, null, target, cb); + if (promise && promise.then) promise.then(cb); + }, + leave: function(element, cb) { + var promise = $animate.leave(element, cb); + if (promise && promise.then) promise.then(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); + + scope.$on('$stateChangeSuccess', function() { + updateView(false); + }); + scope.$on('$viewContentLoading', function() { + updateView(false); + }); + + updateView(true); + + function cleanupLastView() { + if (previousEl) { + previousEl.remove(); + previousEl = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentEl) { + renderer.leave(currentEl, function() { + 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]; + + var clone = $transclude(newScope, function(clone) { + renderer.enter(clone, $element, function onUiViewEnter() { + 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. + */ + currentScope.$emit('$viewContentLoaded'); + 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 link = $compile($element.contents()); + + if (locals.$$controller) { + locals.$scope = scope; + var controller = $controller(locals.$$controller, locals); + if (locals.$$controllerAs) { + scope[locals.$$controllerAs] = controller; + } + $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 inherited = element.inheritedData('$uiView'); + return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : '')); +} + +angular.module('ui.router.state').directive('uiView', $ViewDirective); +angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/viewScroll.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/viewScroll.js new file mode 100644 index 000000000..dfe0a030d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular-ui-router/src/viewScroll.js @@ -0,0 +1,52 @@ +/** + * @ngdoc object + * @name ui.router.state.$uiViewScrollProvider + * + * @description + * Provider that returns the {@link ui.router.state.$uiViewScroll} service function. + */ +function $ViewScrollProvider() { + + var useAnchorScroll = false; + + /** + * @ngdoc function + * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll + * @methodOf ui.router.state.$uiViewScrollProvider + * + * @description + * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for + * scrolling based on the url anchor. + */ + this.useAnchorScroll = function () { + useAnchorScroll = true; + }; + + /** + * @ngdoc object + * @name ui.router.state.$uiViewScroll + * + * @requires $anchorScroll + * @requires $timeout + * + * @description + * When called with a jqLite element, it scrolls the element into view (after a + * `$timeout` so the DOM has time to refresh). + * + * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor, + * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}. + */ + this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) { + if (useAnchorScroll) { + return $anchorScroll; + } + + return function ($element) { + $timeout(function () { + $element[0].scrollIntoView(); + }, 0, false); + }; + }]; +} + +angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/.bower.json new file mode 100644 index 000000000..565d4f2f6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/.bower.json @@ -0,0 +1,17 @@ +{ + "name": "angular", + "version": "1.3.15", + "main": "./angular.js", + "ignore": [], + "dependencies": {}, + "homepage": "https://github.com/angular/bower-angular", + "_release": "1.3.15", + "_resolution": { + "type": "version", + "tag": "v1.3.15", + "commit": "ba7abcfa409ba852146e6ba206693cf7bac3e359" + }, + "_source": "https://github.com/angular/bower-angular.git", + "_target": "1.3.15", + "_originalSource": "angular" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/README.md new file mode 100644 index 000000000..d1bc0eddf --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/README.md @@ -0,0 +1,64 @@ +# packaged angular + +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). +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 +``` + +Then add a `<script>` to your `index.html`: + +```html +<script src="/node_modules/angular/angular.js"></script> +``` + +Or `require('angular')` from your code. + +### bower + +```shell +bower install angular +``` + +Then add a `<script>` to your `index.html`: + +```html +<script src="/bower_components/angular/angular.js"></script> +``` + +## Documentation + +Documentation is available on the +[AngularJS docs site](http://docs.angularjs.org/). + +## 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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular-csp.css b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular-csp.css new file mode 100644 index 000000000..0ce9d864c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular-csp.css @@ -0,0 +1,13 @@ +/* Include this file in your html if you are using the CSP mode. */ + +@charset "UTF-8"; + +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], +.ng-cloak, .x-ng-cloak, +.ng-hide:not(.ng-hide-animate) { + display: none !important; +} + +ng\:form { + display: block; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.js new file mode 100644 index 000000000..e488352b2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.js @@ -0,0 +1,26309 @@ +/** + * @license AngularJS v1.3.15 + * (c) 2010-2014 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, document, undefined) {'use strict'; + +/** + * @description + * + * This object provides a utility for producing rich Error messages within + * Angular. It can be called as follows: + * + * var exampleMinErr = minErr('example'); + * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); + * + * The above creates an instance of minErr in the example namespace. The + * resulting error will have a namespaced error code of example.one. The + * resulting error will replace {0} with the value of foo, and {1} with the + * value of bar. The object is not restricted in the number of arguments it can + * take. + * + * If fewer arguments are specified than necessary for interpolation, the extra + * interpolation markers will be preserved in the final string. + * + * Since data will be parsed statically during a build step, some restrictions + * are applied with respect to how minErr instances are created and called. + * Instances should have names of the form namespaceMinErr for a minErr created + * using minErr('namespace') . Error codes, namespaces and template strings + * should all be static strings, not variables or general expressions. + * + * @param {string} module The namespace to use for the new minErr instance. + * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning + * error from returned function, for cases when a particular type of error is useful. + * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance + */ + +function minErr(module, ErrorConstructor) { + ErrorConstructor = ErrorConstructor || Error; + return function() { + var code = arguments[0], + prefix = '[' + (module ? module + ':' : '') + code + '] ', + template = arguments[1], + templateArgs = arguments, + + message, i; + + message = prefix + template.replace(/\{\d+\}/g, function(match) { + var index = +match.slice(1, -1), arg; + + if (index + 2 < templateArgs.length) { + return toDebugString(templateArgs[index + 2]); + } + return match; + }); + + message = message + '\nhttp://errors.angularjs.org/1.3.15/' + + (module ? module + '/' : '') + code; + for (i = 2; i < arguments.length; i++) { + message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' + + encodeURIComponent(toDebugString(arguments[i])); + } + return new ErrorConstructor(message); + }; +} + +/* We need to tell jshint what variables are being exported */ +/* global angular: true, + msie: true, + jqLite: true, + jQuery: true, + slice: true, + splice: true, + push: true, + toString: true, + ngMinErr: true, + angularModule: true, + uid: true, + REGEX_STRING_REGEXP: true, + VALIDITY_STATE_PROPERTY: true, + + lowercase: true, + uppercase: true, + manualLowercase: true, + manualUppercase: true, + nodeName_: true, + isArrayLike: true, + forEach: true, + sortedKeys: true, + forEachSorted: true, + reverseParams: true, + nextUid: true, + setHashKey: true, + extend: true, + int: true, + inherit: true, + noop: true, + identity: true, + valueFn: true, + isUndefined: true, + isDefined: true, + isObject: true, + isString: true, + isNumber: true, + isDate: true, + isArray: true, + isFunction: true, + isRegExp: true, + isWindow: true, + isScope: true, + isFile: true, + isFormData: true, + isBlob: true, + isBoolean: true, + isPromiseLike: true, + trim: true, + escapeForRegexp: true, + isElement: true, + makeMap: true, + includes: true, + arrayRemove: true, + copy: true, + shallowCopy: true, + equals: true, + csp: true, + concat: true, + sliceArgs: true, + bind: true, + toJsonReplacer: true, + toJson: true, + fromJson: true, + startingTag: true, + tryDecodeURIComponent: true, + parseKeyValue: true, + toKeyValue: true, + encodeUriSegment: true, + encodeUriQuery: true, + angularInit: true, + bootstrap: true, + getTestability: true, + snake_case: true, + bindJQuery: true, + assertArg: true, + assertArgFn: true, + assertNotHasOwnProperty: true, + getter: true, + getBlockNodes: true, + hasOwnProperty: true, + createMap: true, + + NODE_TYPE_ELEMENT: true, + NODE_TYPE_TEXT: true, + NODE_TYPE_COMMENT: true, + NODE_TYPE_DOCUMENT: true, + NODE_TYPE_DOCUMENT_FRAGMENT: true, +*/ + +//////////////////////////////////// + +/** + * @ngdoc module + * @name ng + * @module ng + * @description + * + * # ng (core module) + * The ng module is loaded by default when an AngularJS application is started. The module itself + * contains the essential components for an AngularJS application to function. The table below + * lists a high level breakdown of each of the services/factories, filters, directives and testing + * components available within this core module. + * + * <div doc-module-components="ng"></div> + */ + +var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/; + +// The name of a form control's ValidityState property. +// This is used so that it's possible for internal tests to create mock ValidityStates. +var VALIDITY_STATE_PROPERTY = 'validity'; + +/** + * @ngdoc function + * @name angular.lowercase + * @module ng + * @kind function + * + * @description Converts the specified string to lowercase. + * @param {string} string String to be converted to lowercase. + * @returns {string} Lowercased string. + */ +var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;}; +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * @ngdoc function + * @name angular.uppercase + * @module ng + * @kind function + * + * @description Converts the specified string to uppercase. + * @param {string} string String to be converted to uppercase. + * @returns {string} Uppercased string. + */ +var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;}; + + +var manualLowercase = function(s) { + /* jshint bitwise: false */ + return isString(s) + ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) + : s; +}; +var manualUppercase = function(s) { + /* jshint bitwise: false */ + return isString(s) + ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) + : s; +}; + + +// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish +// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods +// with correct but slower alternatives. +if ('i' !== 'I'.toLowerCase()) { + lowercase = manualLowercase; + uppercase = manualUppercase; +} + + +var + msie, // holds major version number for IE, or NaN if UA is not IE. + jqLite, // delay binding since jQuery could be loaded after us. + jQuery, // delay binding + slice = [].slice, + splice = [].splice, + push = [].push, + toString = Object.prototype.toString, + ngMinErr = minErr('ng'), + + /** @name angular */ + angular = window.angular || (window.angular = {}), + angularModule, + uid = 0; + +/** + * documentMode is an IE-only property + * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx + */ +msie = document.documentMode; + + +/** + * @private + * @param {*} obj + * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, + * String ...) + */ +function isArrayLike(obj) { + if (obj == null || isWindow(obj)) { + return false; + } + + var length = obj.length; + + if (obj.nodeType === NODE_TYPE_ELEMENT && length) { + return true; + } + + return isString(obj) || isArray(obj) || length === 0 || + typeof length === 'number' && length > 0 && (length - 1) in obj; +} + +/** + * @ngdoc function + * @name angular.forEach + * @module ng + * @kind function + * + * @description + * Invokes the `iterator` function once for each item in `obj` collection, which can be either an + * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value` + * is the value of an object property or an array element, `key` is the object property key or + * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional. + * + * It is worth noting that `.forEach` does not iterate over inherited properties because it filters + * using the `hasOwnProperty` method. + * + * Unlike ES262's + * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18), + * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just + * return the value provided. + * + ```js + var values = {name: 'misko', gender: 'male'}; + var log = []; + angular.forEach(values, function(value, key) { + this.push(key + ': ' + value); + }, log); + expect(log).toEqual(['name: misko', 'gender: male']); + ``` + * + * @param {Object|Array} obj Object to iterate over. + * @param {Function} iterator Iterator function. + * @param {Object=} context Object to become context (`this`) for the iterator function. + * @returns {Object|Array} Reference to `obj`. + */ + +function forEach(obj, iterator, context) { + var key, length; + if (obj) { + if (isFunction(obj)) { + for (key in obj) { + // Need to check if hasOwnProperty exists, + // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function + if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { + iterator.call(context, obj[key], key, obj); + } + } + } else if (isArray(obj) || isArrayLike(obj)) { + var isPrimitive = typeof obj !== 'object'; + for (key = 0, length = obj.length; key < length; key++) { + if (isPrimitive || key in obj) { + iterator.call(context, obj[key], key, obj); + } + } + } else if (obj.forEach && obj.forEach !== forEach) { + obj.forEach(iterator, context, obj); + } else { + for (key in obj) { + if (obj.hasOwnProperty(key)) { + iterator.call(context, obj[key], key, obj); + } + } + } + } + return obj; +} + +function sortedKeys(obj) { + return Object.keys(obj).sort(); +} + +function forEachSorted(obj, iterator, context) { + var keys = sortedKeys(obj); + for (var i = 0; i < keys.length; i++) { + iterator.call(context, obj[keys[i]], keys[i]); + } + return keys; +} + + +/** + * when using forEach the params are value, key, but it is often useful to have key, value. + * @param {function(string, *)} iteratorFn + * @returns {function(*, string)} + */ +function reverseParams(iteratorFn) { + return function(value, key) { iteratorFn(key, value); }; +} + +/** + * A consistent way of creating unique IDs in angular. + * + * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before + * we hit number precision issues in JavaScript. + * + * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M + * + * @returns {number} an unique alpha-numeric string + */ +function nextUid() { + return ++uid; +} + + +/** + * Set or clear the hashkey for an object. + * @param obj object + * @param h the hashkey (!truthy to delete the hashkey) + */ +function setHashKey(obj, h) { + if (h) { + obj.$$hashKey = h; + } else { + delete obj.$$hashKey; + } +} + +/** + * @ngdoc function + * @name angular.extend + * @module ng + * @kind function + * + * @description + * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) + * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so + * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`. + * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy). + * + * @param {Object} dst Destination object. + * @param {...Object} src Source object(s). + * @returns {Object} Reference to `dst`. + */ +function extend(dst) { + var h = dst.$$hashKey; + + for (var i = 1, ii = arguments.length; i < ii; i++) { + var obj = arguments[i]; + if (obj) { + var keys = Object.keys(obj); + for (var j = 0, jj = keys.length; j < jj; j++) { + var key = keys[j]; + dst[key] = obj[key]; + } + } + } + + setHashKey(dst, h); + return dst; +} + +function int(str) { + return parseInt(str, 10); +} + + +function inherit(parent, extra) { + return extend(Object.create(parent), extra); +} + +/** + * @ngdoc function + * @name angular.noop + * @module ng + * @kind function + * + * @description + * A function that performs no operations. This function can be useful when writing code in the + * functional style. + ```js + function foo(callback) { + var result = calculateResult(); + (callback || angular.noop)(result); + } + ``` + */ +function noop() {} +noop.$inject = []; + + +/** + * @ngdoc function + * @name angular.identity + * @module ng + * @kind function + * + * @description + * A function that returns its first argument. This function is useful when writing code in the + * functional style. + * + ```js + function transformer(transformationFn, value) { + return (transformationFn || angular.identity)(value); + }; + ``` + * @param {*} value to be returned. + * @returns {*} the value passed in. + */ +function identity($) {return $;} +identity.$inject = []; + + +function valueFn(value) {return function() {return value;};} + +/** + * @ngdoc function + * @name angular.isUndefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is undefined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is undefined. + */ +function isUndefined(value) {return typeof value === 'undefined';} + + +/** + * @ngdoc function + * @name angular.isDefined + * @module ng + * @kind function + * + * @description + * Determines if a reference is defined. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is defined. + */ +function isDefined(value) {return typeof value !== 'undefined';} + + +/** + * @ngdoc function + * @name angular.isObject + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not + * considered to be objects. Note that JavaScript arrays are objects. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Object` but not `null`. + */ +function isObject(value) { + // http://jsperf.com/isobject4 + return value !== null && typeof value === 'object'; +} + + +/** + * @ngdoc function + * @name angular.isString + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `String`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `String`. + */ +function isString(value) {return typeof value === 'string';} + + +/** + * @ngdoc function + * @name angular.isNumber + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Number`. + * + * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`. + * + * If you wish to exclude these then you can use the native + * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite) + * method. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Number`. + */ +function isNumber(value) {return typeof value === 'number';} + + +/** + * @ngdoc function + * @name angular.isDate + * @module ng + * @kind function + * + * @description + * Determines if a value is a date. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Date`. + */ +function isDate(value) { + return toString.call(value) === '[object Date]'; +} + + +/** + * @ngdoc function + * @name angular.isArray + * @module ng + * @kind function + * + * @description + * Determines if a reference is an `Array`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is an `Array`. + */ +var isArray = Array.isArray; + +/** + * @ngdoc function + * @name angular.isFunction + * @module ng + * @kind function + * + * @description + * Determines if a reference is a `Function`. + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `Function`. + */ +function isFunction(value) {return typeof value === 'function';} + + +/** + * Determines if a value is a regular expression object. + * + * @private + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a `RegExp`. + */ +function isRegExp(value) { + return toString.call(value) === '[object RegExp]'; +} + + +/** + * Checks if `obj` is a window object. + * + * @private + * @param {*} obj Object to check + * @returns {boolean} True if `obj` is a window obj. + */ +function isWindow(obj) { + return obj && obj.window === obj; +} + + +function isScope(obj) { + return obj && obj.$evalAsync && obj.$watch; +} + + +function isFile(obj) { + return toString.call(obj) === '[object File]'; +} + + +function isFormData(obj) { + return toString.call(obj) === '[object FormData]'; +} + + +function isBlob(obj) { + return toString.call(obj) === '[object Blob]'; +} + + +function isBoolean(value) { + return typeof value === 'boolean'; +} + + +function isPromiseLike(obj) { + return obj && isFunction(obj.then); +} + + +var trim = function(value) { + return isString(value) ? value.trim() : value; +}; + +// Copied from: +// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021 +// Prereq: s is a string. +var escapeForRegexp = function(s) { + return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). + replace(/\x08/g, '\\x08'); +}; + + +/** + * @ngdoc function + * @name angular.isElement + * @module ng + * @kind function + * + * @description + * Determines if a reference is a DOM element (or wrapped jQuery element). + * + * @param {*} value Reference to check. + * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). + */ +function isElement(node) { + return !!(node && + (node.nodeName // we are a direct element + || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API +} + +/** + * @param str 'key1,key2,...' + * @returns {object} in the form of {key1:true, key2:true, ...} + */ +function makeMap(str) { + var obj = {}, items = str.split(","), i; + for (i = 0; i < items.length; i++) + obj[items[i]] = true; + return obj; +} + + +function nodeName_(element) { + return lowercase(element.nodeName || (element[0] && element[0].nodeName)); +} + +function includes(array, obj) { + return Array.prototype.indexOf.call(array, obj) != -1; +} + +function arrayRemove(array, value) { + var index = array.indexOf(value); + if (index >= 0) + array.splice(index, 1); + return value; +} + +/** + * @ngdoc function + * @name angular.copy + * @module ng + * @kind function + * + * @description + * Creates a deep copy of `source`, which should be an object or an array. + * + * * If no destination is supplied, a copy of the object or array is created. + * * If a destination is provided, all of its elements (for arrays) or properties (for objects) + * are deleted and then all elements/properties from the source are copied to it. + * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. + * * If `source` is identical to 'destination' an exception will be thrown. + * + * @param {*} source The source that will be used to make a copy. + * Can be any type, including primitives, `null`, and `undefined`. + * @param {(Object|Array)=} destination Destination into which the source is copied. If + * provided, must be of the same type as `source`. + * @returns {*} The copy or updated `destination`, if `destination` was specified. + * + * @example + <example module="copyExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <form novalidate class="simple-form"> + Name: <input type="text" ng-model="user.name" /><br /> + E-mail: <input type="email" ng-model="user.email" /><br /> + Gender: <input type="radio" ng-model="user.gender" value="male" />male + <input type="radio" ng-model="user.gender" value="female" />female<br /> + <button ng-click="reset()">RESET</button> + <button ng-click="update(user)">SAVE</button> + </form> + <pre>form = {{user | json}}</pre> + <pre>master = {{master | json}}</pre> + </div> + + <script> + angular.module('copyExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.master= {}; + + $scope.update = function(user) { + // Example with 1 argument + $scope.master= angular.copy(user); + }; + + $scope.reset = function() { + // Example with 2 arguments + angular.copy($scope.master, $scope.user); + }; + + $scope.reset(); + }]); + </script> + </file> + </example> + */ +function copy(source, destination, stackSource, stackDest) { + if (isWindow(source) || isScope(source)) { + throw ngMinErr('cpws', + "Can't copy! Making copies of Window or Scope instances is not supported."); + } + + if (!destination) { + destination = source; + if (source) { + if (isArray(source)) { + destination = copy(source, [], stackSource, stackDest); + } else if (isDate(source)) { + destination = new Date(source.getTime()); + } else if (isRegExp(source)) { + destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); + destination.lastIndex = source.lastIndex; + } else if (isObject(source)) { + var emptyObject = Object.create(Object.getPrototypeOf(source)); + destination = copy(source, emptyObject, stackSource, stackDest); + } + } + } else { + if (source === destination) throw ngMinErr('cpi', + "Can't copy! Source and destination are identical."); + + stackSource = stackSource || []; + stackDest = stackDest || []; + + if (isObject(source)) { + var index = stackSource.indexOf(source); + if (index !== -1) return stackDest[index]; + + stackSource.push(source); + stackDest.push(destination); + } + + var result; + if (isArray(source)) { + destination.length = 0; + for (var i = 0; i < source.length; i++) { + result = copy(source[i], null, stackSource, stackDest); + if (isObject(source[i])) { + stackSource.push(source[i]); + stackDest.push(result); + } + destination.push(result); + } + } else { + var h = destination.$$hashKey; + if (isArray(destination)) { + destination.length = 0; + } else { + forEach(destination, function(value, key) { + delete destination[key]; + }); + } + for (var key in source) { + if (source.hasOwnProperty(key)) { + result = copy(source[key], null, stackSource, stackDest); + if (isObject(source[key])) { + stackSource.push(source[key]); + stackDest.push(result); + } + destination[key] = result; + } + } + setHashKey(destination,h); + } + + } + return destination; +} + +/** + * Creates a shallow copy of an object, an array or a primitive. + * + * Assumes that there are no proto properties for objects. + */ +function shallowCopy(src, dst) { + if (isArray(src)) { + dst = dst || []; + + for (var i = 0, ii = src.length; i < ii; i++) { + dst[i] = src[i]; + } + } else if (isObject(src)) { + dst = dst || {}; + + for (var key in src) { + if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + } + + return dst || src; +} + + +/** + * @ngdoc function + * @name angular.equals + * @module ng + * @kind function + * + * @description + * Determines if two objects or two values are equivalent. Supports value types, regular + * expressions, arrays and objects. + * + * Two objects or values are considered equivalent if at least one of the following is true: + * + * * Both objects or values pass `===` comparison. + * * Both objects or values are of the same type and all of their properties are equal by + * comparing them with `angular.equals`. + * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) + * * Both values represent the same regular expression (In JavaScript, + * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual + * representation matches). + * + * During a property comparison, properties of `function` type and properties with names + * that begin with `$` are ignored. + * + * Scope and DOMWindow objects are being compared only by identify (`===`). + * + * @param {*} o1 Object or value to compare. + * @param {*} o2 Object or value to compare. + * @returns {boolean} True if arguments are equal. + */ +function equals(o1, o2) { + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN + var t1 = typeof o1, t2 = typeof o2, length, key, keySet; + if (t1 == t2) { + if (t1 == 'object') { + if (isArray(o1)) { + if (!isArray(o2)) return false; + if ((length = o1.length) == o2.length) { + for (key = 0; key < length; key++) { + if (!equals(o1[key], o2[key])) return false; + } + return true; + } + } else if (isDate(o1)) { + if (!isDate(o2)) return false; + return equals(o1.getTime(), o2.getTime()); + } else if (isRegExp(o1)) { + return isRegExp(o2) ? o1.toString() == o2.toString() : false; + } else { + if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || + isArray(o2) || isDate(o2) || isRegExp(o2)) return false; + keySet = {}; + for (key in o1) { + if (key.charAt(0) === '$' || isFunction(o1[key])) continue; + if (!equals(o1[key], o2[key])) return false; + keySet[key] = true; + } + for (key in o2) { + if (!keySet.hasOwnProperty(key) && + key.charAt(0) !== '$' && + o2[key] !== undefined && + !isFunction(o2[key])) return false; + } + return true; + } + } + } + return false; +} + +var csp = function() { + if (isDefined(csp.isActive_)) return csp.isActive_; + + var active = !!(document.querySelector('[ng-csp]') || + document.querySelector('[data-ng-csp]')); + + if (!active) { + try { + /* jshint -W031, -W054 */ + new Function(''); + /* jshint +W031, +W054 */ + } catch (e) { + active = true; + } + } + + return (csp.isActive_ = active); +}; + + + +function concat(array1, array2, index) { + return array1.concat(slice.call(array2, index)); +} + +function sliceArgs(args, startIndex) { + return slice.call(args, startIndex || 0); +} + + +/* jshint -W101 */ +/** + * @ngdoc function + * @name angular.bind + * @module ng + * @kind function + * + * @description + * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for + * `fn`). You can supply optional `args` that are prebound to the function. This feature is also + * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as + * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application). + * + * @param {Object} self Context which `fn` should be evaluated in. + * @param {function()} fn Function to be bound. + * @param {...*} args Optional arguments to be prebound to the `fn` function call. + * @returns {function()} Function that wraps the `fn` with all the specified bindings. + */ +/* jshint +W101 */ +function bind(self, fn) { + var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; + if (isFunction(fn) && !(fn instanceof RegExp)) { + return curryArgs.length + ? function() { + return arguments.length + ? fn.apply(self, concat(curryArgs, arguments, 0)) + : fn.apply(self, curryArgs); + } + : function() { + return arguments.length + ? fn.apply(self, arguments) + : fn.call(self); + }; + } else { + // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) + return fn; + } +} + + +function toJsonReplacer(key, value) { + var val = value; + + if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') { + val = undefined; + } else if (isWindow(value)) { + val = '$WINDOW'; + } else if (value && document === value) { + val = '$DOCUMENT'; + } else if (isScope(value)) { + val = '$SCOPE'; + } + + return val; +} + + +/** + * @ngdoc function + * @name angular.toJson + * @module ng + * @kind function + * + * @description + * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be + * stripped since angular uses this notation internally. + * + * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. + * @param {boolean|number=} pretty If set to true, the JSON output will contain newlines and whitespace. + * If set to an integer, the JSON output will contain that many spaces per indentation (the default is 2). + * @returns {string|undefined} JSON-ified string representing `obj`. + */ +function toJson(obj, pretty) { + if (typeof obj === 'undefined') return undefined; + if (!isNumber(pretty)) { + pretty = pretty ? 2 : null; + } + return JSON.stringify(obj, toJsonReplacer, pretty); +} + + +/** + * @ngdoc function + * @name angular.fromJson + * @module ng + * @kind function + * + * @description + * Deserializes a JSON string. + * + * @param {string} json JSON string to deserialize. + * @returns {Object|Array|string|number} Deserialized JSON string. + */ +function fromJson(json) { + return isString(json) + ? JSON.parse(json) + : json; +} + + +/** + * @returns {string} Returns the string representation of the element. + */ +function startingTag(element) { + element = jqLite(element).clone(); + try { + // turns out IE does not let you set .html() on elements which + // are not allowed to have children. So we just ignore it. + element.empty(); + } catch (e) {} + var elemHtml = jqLite('<div>').append(element).html(); + try { + return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) : + elemHtml. + match(/^(<[^>]+>)/)[1]. + replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); + } catch (e) { + return lowercase(elemHtml); + } + +} + + +///////////////////////////////////////////////// + +/** + * Tries to decode the URI component without throwing an exception. + * + * @private + * @param str value potential URI component to check. + * @returns {boolean} True if `value` can be decoded + * with the decodeURIComponent function. + */ +function tryDecodeURIComponent(value) { + try { + return decodeURIComponent(value); + } catch (e) { + // Ignore any invalid uri component + } +} + + +/** + * Parses an escaped url query string into key-value pairs. + * @returns {Object.<string,boolean|Array>} + */ +function parseKeyValue(/**string*/keyValue) { + var obj = {}, key_value, key; + forEach((keyValue || "").split('&'), function(keyValue) { + if (keyValue) { + key_value = keyValue.replace(/\+/g,'%20').split('='); + key = tryDecodeURIComponent(key_value[0]); + if (isDefined(key)) { + var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; + if (!hasOwnProperty.call(obj, key)) { + obj[key] = val; + } else if (isArray(obj[key])) { + obj[key].push(val); + } else { + obj[key] = [obj[key],val]; + } + } + } + }); + return obj; +} + +function toKeyValue(obj) { + var parts = []; + forEach(obj, function(value, key) { + if (isArray(value)) { + forEach(value, function(arrayValue) { + parts.push(encodeUriQuery(key, true) + + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); + }); + } else { + parts.push(encodeUriQuery(key, true) + + (value === true ? '' : '=' + encodeUriQuery(value, true))); + } + }); + return parts.length ? parts.join('&') : ''; +} + + +/** + * 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(/%3B/gi, ';'). + replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); +} + +var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-']; + +function getNgAttribute(element, ngAttr) { + var attr, i, ii = ngAttrPrefixes.length; + element = jqLite(element); + for (i = 0; i < ii; ++i) { + attr = ngAttrPrefixes[i] + ngAttr; + if (isString(attr = element.attr(attr))) { + return attr; + } + } + return null; +} + +/** + * @ngdoc directive + * @name ngApp + * @module ng + * + * @element ANY + * @param {angular.Module} ngApp an optional application + * {@link angular.module module} name to load. + * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be + * created in "strict-di" mode. This means that the application will fail to invoke functions which + * do not use explicit function annotation (and are thus unsuitable for minification), as described + * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in + * tracking down the root of these bugs. + * + * @description + * + * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive + * designates the **root element** of the application and is typically placed near the root element + * of the page - e.g. on the `<body>` or `<html>` tags. + * + * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` + * found in the document will be used to define the root element to auto-bootstrap as an + * application. To run multiple applications in an HTML document you must manually bootstrap them using + * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. + * + * You can specify an **AngularJS module** to be used as the root module for the application. This + * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It + * should contain the application code needed or have dependencies on other modules that will + * contain the code. See {@link angular.module} for more information. + * + * In the example below if the `ngApp` directive were not placed on the `html` element then the + * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` + * would not be resolved to `3`. + * + * `ngApp` is the easiest, and most common way to bootstrap an application. + * + <example module="ngAppDemo"> + <file name="index.html"> + <div ng-controller="ngAppDemoController"> + I can add: {{a}} + {{b}} = {{ a+b }} + </div> + </file> + <file name="script.js"> + angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }); + </file> + </example> + * + * Using `ngStrictDi`, you would see something like this: + * + <example ng-app-included="true"> + <file name="index.html"> + <div ng-app="ngAppStrictDemo" ng-strict-di> + <div ng-controller="GoodController1"> + I can add: {{a}} + {{b}} = {{ a+b }} + + <p>This renders because the controller does not fail to + instantiate, by using explicit annotation style (see + script.js for details) + </p> + </div> + + <div ng-controller="GoodController2"> + Name: <input ng-model="name"><br /> + Hello, {{name}}! + + <p>This renders because the controller does not fail to + instantiate, by using explicit annotation style + (see script.js for details) + </p> + </div> + + <div ng-controller="BadController"> + I can add: {{a}} + {{b}} = {{ a+b }} + + <p>The controller could not be instantiated, due to relying + on automatic function annotations (which are disabled in + strict mode). As such, the content of this section is not + interpolated, and there should be an error in your web console. + </p> + </div> + </div> + </file> + <file name="script.js"> + angular.module('ngAppStrictDemo', []) + // BadController will fail to instantiate, due to relying on automatic function annotation, + // rather than an explicit annotation + .controller('BadController', function($scope) { + $scope.a = 1; + $scope.b = 2; + }) + // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated, + // due to using explicit annotations using the array style and $inject property, respectively. + .controller('GoodController1', ['$scope', function($scope) { + $scope.a = 1; + $scope.b = 2; + }]) + .controller('GoodController2', GoodController2); + function GoodController2($scope) { + $scope.name = "World"; + } + GoodController2.$inject = ['$scope']; + </file> + <file name="style.css"> + div[ng-controller] { + margin-bottom: 1em; + -webkit-border-radius: 4px; + border-radius: 4px; + border: 1px solid; + padding: .5em; + } + div[ng-controller^=Good] { + border-color: #d6e9c6; + background-color: #dff0d8; + color: #3c763d; + } + div[ng-controller^=Bad] { + border-color: #ebccd1; + background-color: #f2dede; + color: #a94442; + margin-bottom: 0; + } + </file> + </example> + */ +function angularInit(element, bootstrap) { + var appElement, + module, + config = {}; + + // The element `element` has priority over any other element + forEach(ngAttrPrefixes, function(prefix) { + var name = prefix + 'app'; + + if (!appElement && element.hasAttribute && element.hasAttribute(name)) { + appElement = element; + module = element.getAttribute(name); + } + }); + forEach(ngAttrPrefixes, function(prefix) { + var name = prefix + 'app'; + var candidate; + + if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) { + appElement = candidate; + module = candidate.getAttribute(name); + } + }); + if (appElement) { + config.strictDi = getNgAttribute(appElement, "strict-di") !== null; + bootstrap(appElement, module ? [module] : [], config); + } +} + +/** + * @ngdoc function + * @name angular.bootstrap + * @module ng + * @description + * Use this function to manually start up angular application. + * + * See: {@link guide/bootstrap Bootstrap} + * + * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually. + * They must use {@link ng.directive:ngApp ngApp}. + * + * Angular will detect if it has been loaded into the browser more than once and only allow the + * first loaded script to be bootstrapped and will report a warning to the browser console for + * each of the subsequent scripts. This prevents strange results in applications, where otherwise + * multiple instances of Angular try to work on the DOM. + * + * ```html + * <!doctype html> + * <html> + * <body> + * <div ng-controller="WelcomeController"> + * {{greeting}} + * </div> + * + * <script src="angular.js"></script> + * <script> + * var app = angular.module('demo', []) + * .controller('WelcomeController', function($scope) { + * $scope.greeting = 'Welcome!'; + * }); + * angular.bootstrap(document, ['demo']); + * </script> + * </body> + * </html> + * ``` + * + * @param {DOMElement} element DOM element which is the root of angular application. + * @param {Array<String|Function|Array>=} modules an array of modules to load into the application. + * Each item in the array should be the name of a predefined module or a (DI annotated) + * function that will be invoked by the injector as a `config` block. + * See: {@link angular.module modules} + * @param {Object=} config an object for defining configuration options for the application. The + * following keys are supported: + * + * * `strictDi` - disable automatic function annotation for the application. This is meant to + * assist in finding bugs which break minified code. Defaults to `false`. + * + * @returns {auto.$injector} Returns the newly created injector for this app. + */ +function bootstrap(element, modules, config) { + if (!isObject(config)) config = {}; + var defaultConfig = { + strictDi: false + }; + config = extend(defaultConfig, config); + var doBootstrap = function() { + element = jqLite(element); + + if (element.injector()) { + var tag = (element[0] === document) ? 'document' : startingTag(element); + //Encode angle brackets to prevent input from being sanitized to empty string #8683 + throw ngMinErr( + 'btstrpd', + "App Already Bootstrapped with this Element '{0}'", + tag.replace(/</,'<').replace(/>/,'>')); + } + + modules = modules || []; + modules.unshift(['$provide', function($provide) { + $provide.value('$rootElement', element); + }]); + + if (config.debugInfoEnabled) { + // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`. + modules.push(['$compileProvider', function($compileProvider) { + $compileProvider.debugInfoEnabled(true); + }]); + } + + modules.unshift('ng'); + var injector = createInjector(modules, config.strictDi); + injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', + function bootstrapApply(scope, element, compile, injector) { + scope.$apply(function() { + element.data('$injector', injector); + compile(element)(scope); + }); + }] + ); + return injector; + }; + + var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/; + var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; + + if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) { + config.debugInfoEnabled = true; + window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, ''); + } + + if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { + return doBootstrap(); + } + + window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); + angular.resumeBootstrap = function(extraModules) { + forEach(extraModules, function(module) { + modules.push(module); + }); + return doBootstrap(); + }; + + if (isFunction(angular.resumeDeferredBootstrap)) { + angular.resumeDeferredBootstrap(); + } +} + +/** + * @ngdoc function + * @name angular.reloadWithDebugInfo + * @module ng + * @description + * Use this function to reload the current application with debug information turned on. + * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`. + * + * See {@link ng.$compileProvider#debugInfoEnabled} for more. + */ +function reloadWithDebugInfo() { + window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name; + window.location.reload(); +} + +/** + * @name angular.getTestability + * @module ng + * @description + * Get the testability service for the instance of Angular on the given + * element. + * @param {DOMElement} element DOM element which is the root of angular application. + */ +function getTestability(rootElement) { + var injector = angular.element(rootElement).injector(); + if (!injector) { + throw ngMinErr('test', + 'no injector found for element argument to getTestability'); + } + return injector.get('$$testability'); +} + +var SNAKE_CASE_REGEXP = /[A-Z]/g; +function snake_case(name, separator) { + separator = separator || '_'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); +} + +var bindJQueryFired = false; +var skipDestroyOnNextJQueryCleanData; +function bindJQuery() { + var originalCleanData; + + if (bindJQueryFired) { + return; + } + + // bind to jQuery if present; + jQuery = window.jQuery; + // Use jQuery if it exists with proper functionality, otherwise default to us. + // Angular 1.2+ requires jQuery 1.7+ for on()/off() support. + // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older + // versions. It will not work for sure with jQuery <1.7, though. + if (jQuery && jQuery.fn.on) { + jqLite = jQuery; + extend(jQuery.fn, { + scope: JQLitePrototype.scope, + isolateScope: JQLitePrototype.isolateScope, + controller: JQLitePrototype.controller, + injector: JQLitePrototype.injector, + inheritedData: JQLitePrototype.inheritedData + }); + + // All nodes removed from the DOM via various jQuery APIs like .remove() + // are passed through jQuery.cleanData. Monkey-patch this method to fire + // the $destroy event on all removed nodes. + originalCleanData = jQuery.cleanData; + jQuery.cleanData = function(elems) { + var events; + if (!skipDestroyOnNextJQueryCleanData) { + for (var i = 0, elem; (elem = elems[i]) != null; i++) { + events = jQuery._data(elem, "events"); + if (events && events.$destroy) { + jQuery(elem).triggerHandler('$destroy'); + } + } + } else { + skipDestroyOnNextJQueryCleanData = false; + } + originalCleanData(elems); + }; + } else { + jqLite = JQLite; + } + + angular.element = jqLite; + + // Prevent double-proxying. + bindJQueryFired = true; +} + +/** + * throw error if the argument is falsy. + */ +function assertArg(arg, name, reason) { + if (!arg) { + throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); + } + return arg; +} + +function assertArgFn(arg, name, acceptArrayAnnotation) { + if (acceptArrayAnnotation && isArray(arg)) { + arg = arg[arg.length - 1]; + } + + assertArg(isFunction(arg), name, 'not a function, got ' + + (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); + return arg; +} + +/** + * throw error if the name given is hasOwnProperty + * @param {String} name the name to test + * @param {String} context the context in which the name is used, such as module or directive + */ +function assertNotHasOwnProperty(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); + } +} + +/** + * Return the value accessible from the object by path. Any undefined traversals are ignored + * @param {Object} obj starting object + * @param {String} path path to traverse + * @param {boolean} [bindFnToScope=true] + * @returns {Object} value as accessible by path + */ +//TODO(misko): this function needs to be removed +function getter(obj, path, bindFnToScope) { + if (!path) return obj; + var keys = path.split('.'); + var key; + var lastInstance = obj; + var len = keys.length; + + for (var i = 0; i < len; i++) { + key = keys[i]; + if (obj) { + obj = (lastInstance = obj)[key]; + } + } + if (!bindFnToScope && isFunction(obj)) { + return bind(lastInstance, obj); + } + return obj; +} + +/** + * Return the DOM siblings between the first and last node in the given array. + * @param {Array} array like object + * @returns {jqLite} jqLite collection containing the nodes + */ +function getBlockNodes(nodes) { + // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original + // collection, otherwise update the original collection. + var node = nodes[0]; + var endNode = nodes[nodes.length - 1]; + var blockNodes = [node]; + + do { + node = node.nextSibling; + if (!node) break; + blockNodes.push(node); + } while (node !== endNode); + + return jqLite(blockNodes); +} + + +/** + * Creates a new object without a prototype. This object is useful for lookup without having to + * guard against prototypically inherited properties via hasOwnProperty. + * + * Related micro-benchmarks: + * - http://jsperf.com/object-create2 + * - http://jsperf.com/proto-map-lookup/2 + * - http://jsperf.com/for-in-vs-object-keys2 + * + * @returns {Object} + */ +function createMap() { + return Object.create(null); +} + +var NODE_TYPE_ELEMENT = 1; +var NODE_TYPE_TEXT = 3; +var NODE_TYPE_COMMENT = 8; +var NODE_TYPE_DOCUMENT = 9; +var NODE_TYPE_DOCUMENT_FRAGMENT = 11; + +/** + * @ngdoc type + * @name angular.Module + * @module ng + * @description + * + * Interface for configuring angular {@link angular.module modules}. + */ + +function setupModuleLoader(window) { + + var $injectorMinErr = minErr('$injector'); + var ngMinErr = minErr('ng'); + + function ensure(obj, name, factory) { + return obj[name] || (obj[name] = factory()); + } + + var angular = ensure(window, 'angular', Object); + + // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap + angular.$$minErr = angular.$$minErr || minErr; + + return ensure(angular, 'module', function() { + /** @type {Object.<string, angular.Module>} */ + var modules = {}; + + /** + * @ngdoc function + * @name angular.module + * @module ng + * @description + * + * The `angular.module` is a global place for creating, registering and retrieving Angular + * modules. + * All modules (angular core or 3rd party) that should be available to an application must be + * registered using this mechanism. + * + * When passed two or more arguments, a new module is created. If passed only one argument, an + * existing module (the name passed as the first argument to `module`) is retrieved. + * + * + * # Module + * + * A module is a collection of services, directives, controllers, filters, and configuration information. + * `angular.module` is used to configure the {@link auto.$injector $injector}. + * + * ```js + * // Create a new module + * var myModule = angular.module('myModule', []); + * + * // register a new service + * myModule.value('appName', 'MyCoolApp'); + * + * // configure existing services inside initialization blocks. + * myModule.config(['$locationProvider', function($locationProvider) { + * // Configure existing providers + * $locationProvider.hashPrefix('!'); + * }]); + * ``` + * + * Then you can create an injector and load your modules like this: + * + * ```js + * var injector = angular.injector(['ng', 'myModule']) + * ``` + * + * However it's more likely that you'll just use + * {@link ng.directive:ngApp ngApp} or + * {@link angular.bootstrap} to simplify this process for you. + * + * @param {!string} name The name of the module to create or retrieve. + * @param {!Array.<string>=} requires If specified then new module is being created. If + * unspecified then the module is being retrieved for further configuration. + * @param {Function=} configFn Optional configuration function for the module. Same as + * {@link angular.Module#config Module#config()}. + * @returns {module} new module with the {@link angular.Module} api. + */ + return function module(name, requires, configFn) { + var assertNotHasOwnProperty = function(name, context) { + if (name === 'hasOwnProperty') { + throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); + } + }; + + assertNotHasOwnProperty(name, 'module'); + if (requires && modules.hasOwnProperty(name)) { + modules[name] = null; + } + return ensure(modules, name, function() { + if (!requires) { + throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + + "the module name or forgot to load it. If registering a module ensure that you " + + "specify the dependencies as the second argument.", name); + } + + /** @type {!Array.<Array.<*>>} */ + var invokeQueue = []; + + /** @type {!Array.<Function>} */ + var configBlocks = []; + + /** @type {!Array.<Function>} */ + var runBlocks = []; + + var config = invokeLater('$injector', 'invoke', 'push', configBlocks); + + /** @type {angular.Module} */ + var moduleInstance = { + // Private state + _invokeQueue: invokeQueue, + _configBlocks: configBlocks, + _runBlocks: runBlocks, + + /** + * @ngdoc property + * @name angular.Module#requires + * @module ng + * + * @description + * Holds the list of modules which the injector will load before the current module is + * loaded. + */ + requires: requires, + + /** + * @ngdoc property + * @name angular.Module#name + * @module ng + * + * @description + * Name of the module. + */ + name: name, + + + /** + * @ngdoc method + * @name angular.Module#provider + * @module ng + * @param {string} name service name + * @param {Function} providerType Construction function for creating new instance of the + * service. + * @description + * See {@link auto.$provide#provider $provide.provider()}. + */ + provider: invokeLater('$provide', 'provider'), + + /** + * @ngdoc method + * @name angular.Module#factory + * @module ng + * @param {string} name service name + * @param {Function} providerFunction Function for creating new instance of the service. + * @description + * See {@link auto.$provide#factory $provide.factory()}. + */ + factory: invokeLater('$provide', 'factory'), + + /** + * @ngdoc method + * @name angular.Module#service + * @module ng + * @param {string} name service name + * @param {Function} constructor A constructor function that will be instantiated. + * @description + * See {@link auto.$provide#service $provide.service()}. + */ + service: invokeLater('$provide', 'service'), + + /** + * @ngdoc method + * @name angular.Module#value + * @module ng + * @param {string} name service name + * @param {*} object Service instance object. + * @description + * See {@link auto.$provide#value $provide.value()}. + */ + value: invokeLater('$provide', 'value'), + + /** + * @ngdoc method + * @name angular.Module#constant + * @module ng + * @param {string} name constant name + * @param {*} object Constant value. + * @description + * Because the constant are fixed, they get applied before other provide methods. + * See {@link auto.$provide#constant $provide.constant()}. + */ + constant: invokeLater('$provide', 'constant', 'unshift'), + + /** + * @ngdoc method + * @name angular.Module#animation + * @module ng + * @param {string} name animation name + * @param {Function} animationFactory Factory function for creating new instance of an + * animation. + * @description + * + * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. + * + * + * Defines an animation hook that can be later used with + * {@link ngAnimate.$animate $animate} service and directives that use this service. + * + * ```js + * module.animation('.animation-name', function($inject1, $inject2) { + * return { + * eventName : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction(element) { + * //code to cancel the animation + * } + * } + * } + * }) + * ``` + * + * See {@link ng.$animateProvider#register $animateProvider.register()} and + * {@link ngAnimate ngAnimate module} for more information. + */ + animation: invokeLater('$animateProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#filter + * @module ng + * @param {string} name Filter name. + * @param {Function} filterFactory Factory function for creating new instance of filter. + * @description + * See {@link ng.$filterProvider#register $filterProvider.register()}. + */ + filter: invokeLater('$filterProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#controller + * @module ng + * @param {string|Object} name Controller name, or an object map of controllers where the + * keys are the names and the values are the constructors. + * @param {Function} constructor Controller constructor function. + * @description + * See {@link ng.$controllerProvider#register $controllerProvider.register()}. + */ + controller: invokeLater('$controllerProvider', 'register'), + + /** + * @ngdoc method + * @name angular.Module#directive + * @module ng + * @param {string|Object} name Directive name, or an object map of directives where the + * keys are the names and the values are the factories. + * @param {Function} directiveFactory Factory function for creating new instance of + * directives. + * @description + * See {@link ng.$compileProvider#directive $compileProvider.directive()}. + */ + directive: invokeLater('$compileProvider', 'directive'), + + /** + * @ngdoc method + * @name angular.Module#config + * @module ng + * @param {Function} configFn Execute this function on module load. Useful for service + * configuration. + * @description + * Use this method to register work which needs to be performed on module loading. + * For more about how to configure services, see + * {@link providers#provider-recipe Provider Recipe}. + */ + config: config, + + /** + * @ngdoc method + * @name angular.Module#run + * @module ng + * @param {Function} initializationFn Execute this function after injector creation. + * Useful for application initialization. + * @description + * Use this method to register work which should be performed when the injector is done + * loading all modules. + */ + run: function(block) { + runBlocks.push(block); + return this; + } + }; + + if (configFn) { + config(configFn); + } + + return moduleInstance; + + /** + * @param {string} provider + * @param {string} method + * @param {String=} insertMethod + * @returns {angular.Module} + */ + function invokeLater(provider, method, insertMethod, queue) { + if (!queue) queue = invokeQueue; + return function() { + queue[insertMethod || 'push']([provider, method, arguments]); + return moduleInstance; + }; + } + }); + }; + }); + +} + +/* global: toDebugString: true */ + +function serializeObject(obj) { + var seen = []; + + return JSON.stringify(obj, function(key, val) { + val = toJsonReplacer(key, val); + if (isObject(val)) { + + if (seen.indexOf(val) >= 0) return '<<already seen>>'; + + seen.push(val); + } + return val; + }); +} + +function toDebugString(obj) { + if (typeof obj === 'function') { + return obj.toString().replace(/ \{[\s\S]*$/, ''); + } else if (typeof obj === 'undefined') { + return 'undefined'; + } else if (typeof obj !== 'string') { + return serializeObject(obj); + } + return obj; +} + +/* global angularModule: true, + version: true, + + $LocaleProvider, + $CompileProvider, + + htmlAnchorDirective, + inputDirective, + inputDirective, + formDirective, + scriptDirective, + selectDirective, + styleDirective, + optionDirective, + ngBindDirective, + ngBindHtmlDirective, + ngBindTemplateDirective, + ngClassDirective, + ngClassEvenDirective, + ngClassOddDirective, + ngCspDirective, + ngCloakDirective, + ngControllerDirective, + ngFormDirective, + ngHideDirective, + ngIfDirective, + ngIncludeDirective, + ngIncludeFillContentDirective, + ngInitDirective, + ngNonBindableDirective, + ngPluralizeDirective, + ngRepeatDirective, + ngShowDirective, + ngStyleDirective, + ngSwitchDirective, + ngSwitchWhenDirective, + ngSwitchDefaultDirective, + ngOptionsDirective, + ngTranscludeDirective, + ngModelDirective, + ngListDirective, + ngChangeDirective, + patternDirective, + patternDirective, + requiredDirective, + requiredDirective, + minlengthDirective, + minlengthDirective, + maxlengthDirective, + maxlengthDirective, + ngValueDirective, + ngModelOptionsDirective, + ngAttributeAliasDirectives, + ngEventDirectives, + + $AnchorScrollProvider, + $AnimateProvider, + $BrowserProvider, + $CacheFactoryProvider, + $ControllerProvider, + $DocumentProvider, + $ExceptionHandlerProvider, + $FilterProvider, + $InterpolateProvider, + $IntervalProvider, + $HttpProvider, + $HttpBackendProvider, + $LocationProvider, + $LogProvider, + $ParseProvider, + $RootScopeProvider, + $QProvider, + $$QProvider, + $$SanitizeUriProvider, + $SceProvider, + $SceDelegateProvider, + $SnifferProvider, + $TemplateCacheProvider, + $TemplateRequestProvider, + $$TestabilityProvider, + $TimeoutProvider, + $$RAFProvider, + $$AsyncCallbackProvider, + $WindowProvider, + $$jqLiteProvider +*/ + + +/** + * @ngdoc object + * @name angular.version + * @module ng + * @description + * An object that contains information about the current AngularJS version. This object has the + * following properties: + * + * - `full` – `{string}` – Full version string, such as "0.9.18". + * - `major` – `{number}` – Major version number, such as "0". + * - `minor` – `{number}` – Minor version number, such as "9". + * - `dot` – `{number}` – Dot version number, such as "18". + * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". + */ +var version = { + full: '1.3.15', // all of these placeholder strings will be replaced by grunt's + major: 1, // package task + minor: 3, + dot: 15, + codeName: 'locality-filtration' +}; + + +function publishExternalAPI(angular) { + extend(angular, { + 'bootstrap': bootstrap, + 'copy': copy, + 'extend': extend, + 'equals': equals, + 'element': jqLite, + 'forEach': forEach, + 'injector': createInjector, + 'noop': noop, + 'bind': bind, + 'toJson': toJson, + 'fromJson': fromJson, + 'identity': identity, + 'isUndefined': isUndefined, + 'isDefined': isDefined, + 'isString': isString, + 'isFunction': isFunction, + 'isObject': isObject, + 'isNumber': isNumber, + 'isElement': isElement, + 'isArray': isArray, + 'version': version, + 'isDate': isDate, + 'lowercase': lowercase, + 'uppercase': uppercase, + 'callbacks': {counter: 0}, + 'getTestability': getTestability, + '$$minErr': minErr, + '$$csp': csp, + 'reloadWithDebugInfo': reloadWithDebugInfo + }); + + angularModule = setupModuleLoader(window); + try { + angularModule('ngLocale'); + } catch (e) { + angularModule('ngLocale', []).provider('$locale', $LocaleProvider); + } + + angularModule('ng', ['ngLocale'], ['$provide', + function ngModule($provide) { + // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. + $provide.provider({ + $$sanitizeUri: $$SanitizeUriProvider + }); + $provide.provider('$compile', $CompileProvider). + directive({ + a: htmlAnchorDirective, + input: inputDirective, + textarea: inputDirective, + form: formDirective, + script: scriptDirective, + select: selectDirective, + style: styleDirective, + option: optionDirective, + ngBind: ngBindDirective, + ngBindHtml: ngBindHtmlDirective, + ngBindTemplate: ngBindTemplateDirective, + ngClass: ngClassDirective, + ngClassEven: ngClassEvenDirective, + ngClassOdd: ngClassOddDirective, + ngCloak: ngCloakDirective, + ngController: ngControllerDirective, + ngForm: ngFormDirective, + ngHide: ngHideDirective, + ngIf: ngIfDirective, + ngInclude: ngIncludeDirective, + ngInit: ngInitDirective, + ngNonBindable: ngNonBindableDirective, + ngPluralize: ngPluralizeDirective, + ngRepeat: ngRepeatDirective, + ngShow: ngShowDirective, + ngStyle: ngStyleDirective, + ngSwitch: ngSwitchDirective, + ngSwitchWhen: ngSwitchWhenDirective, + ngSwitchDefault: ngSwitchDefaultDirective, + ngOptions: ngOptionsDirective, + ngTransclude: ngTranscludeDirective, + ngModel: ngModelDirective, + ngList: ngListDirective, + ngChange: ngChangeDirective, + pattern: patternDirective, + ngPattern: patternDirective, + required: requiredDirective, + ngRequired: requiredDirective, + minlength: minlengthDirective, + ngMinlength: minlengthDirective, + maxlength: maxlengthDirective, + ngMaxlength: maxlengthDirective, + ngValue: ngValueDirective, + ngModelOptions: ngModelOptionsDirective + }). + directive({ + ngInclude: ngIncludeFillContentDirective + }). + directive(ngAttributeAliasDirectives). + directive(ngEventDirectives); + $provide.provider({ + $anchorScroll: $AnchorScrollProvider, + $animate: $AnimateProvider, + $browser: $BrowserProvider, + $cacheFactory: $CacheFactoryProvider, + $controller: $ControllerProvider, + $document: $DocumentProvider, + $exceptionHandler: $ExceptionHandlerProvider, + $filter: $FilterProvider, + $interpolate: $InterpolateProvider, + $interval: $IntervalProvider, + $http: $HttpProvider, + $httpBackend: $HttpBackendProvider, + $location: $LocationProvider, + $log: $LogProvider, + $parse: $ParseProvider, + $rootScope: $RootScopeProvider, + $q: $QProvider, + $$q: $$QProvider, + $sce: $SceProvider, + $sceDelegate: $SceDelegateProvider, + $sniffer: $SnifferProvider, + $templateCache: $TemplateCacheProvider, + $templateRequest: $TemplateRequestProvider, + $$testability: $$TestabilityProvider, + $timeout: $TimeoutProvider, + $window: $WindowProvider, + $$rAF: $$RAFProvider, + $$asyncCallback: $$AsyncCallbackProvider, + $$jqLite: $$jqLiteProvider + }); + } + ]); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* global JQLitePrototype: true, + addEventListenerFn: true, + removeEventListenerFn: true, + BOOLEAN_ATTR: true, + ALIASED_ATTR: true, +*/ + +////////////////////////////////// +//JQLite +////////////////////////////////// + +/** + * @ngdoc function + * @name angular.element + * @module ng + * @kind function + * + * @description + * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. + * + * If jQuery is available, `angular.element` is an alias for the + * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` + * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." + * + * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows + * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most + * commonly needed functionality with the goal of having a very small footprint.</div> + * + * To use jQuery, simply load it before `DOMContentLoaded` event fired. + * + * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or + * jqLite; they are never raw DOM references.</div> + * + * ## Angular's jqLite + * jqLite provides only the following jQuery methods: + * + * - [`addClass()`](http://api.jquery.com/addClass/) + * - [`after()`](http://api.jquery.com/after/) + * - [`append()`](http://api.jquery.com/append/) + * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters + * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData + * - [`children()`](http://api.jquery.com/children/) - Does not support selectors + * - [`clone()`](http://api.jquery.com/clone/) + * - [`contents()`](http://api.jquery.com/contents/) + * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()` + * - [`data()`](http://api.jquery.com/data/) + * - [`detach()`](http://api.jquery.com/detach/) + * - [`empty()`](http://api.jquery.com/empty/) + * - [`eq()`](http://api.jquery.com/eq/) + * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name + * - [`hasClass()`](http://api.jquery.com/hasClass/) + * - [`html()`](http://api.jquery.com/html/) + * - [`next()`](http://api.jquery.com/next/) - Does not support selectors + * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData + * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors + * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors + * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors + * - [`prepend()`](http://api.jquery.com/prepend/) + * - [`prop()`](http://api.jquery.com/prop/) + * - [`ready()`](http://api.jquery.com/ready/) + * - [`remove()`](http://api.jquery.com/remove/) + * - [`removeAttr()`](http://api.jquery.com/removeAttr/) + * - [`removeClass()`](http://api.jquery.com/removeClass/) + * - [`removeData()`](http://api.jquery.com/removeData/) + * - [`replaceWith()`](http://api.jquery.com/replaceWith/) + * - [`text()`](http://api.jquery.com/text/) + * - [`toggleClass()`](http://api.jquery.com/toggleClass/) + * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. + * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces + * - [`val()`](http://api.jquery.com/val/) + * - [`wrap()`](http://api.jquery.com/wrap/) + * + * ## jQuery/jqLite Extras + * Angular also provides the following additional methods and events to both jQuery and jqLite: + * + * ### Events + * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event + * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM + * element before it is removed. + * + * ### Methods + * - `controller(name)` - retrieves the controller of the current element or its parent. By default + * retrieves controller associated with the `ngController` directive. If `name` is provided as + * camelCase directive name, then the controller for this directive will be retrieved (e.g. + * `'ngModel'`). + * - `injector()` - retrieves the injector of the current element or its parent. + * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current + * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to + * be enabled. + * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the + * current element. This getter should be used only on elements that contain a directive which starts a new isolate + * scope. Calling `scope()` on this element always returns the original non-isolate scope. + * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled. + * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top + * parent element is reached. + * + * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. + * @returns {Object} jQuery object. + */ + +JQLite.expando = 'ng339'; + +var jqCache = JQLite.cache = {}, + jqId = 1, + addEventListenerFn = function(element, type, fn) { + element.addEventListener(type, fn, false); + }, + removeEventListenerFn = function(element, type, fn) { + element.removeEventListener(type, fn, false); + }; + +/* + * !!! This is an undocumented "private" function !!! + */ +JQLite._data = function(node) { + //jQuery always returns an object on cache miss + return this.cache[node[this.expando]] || {}; +}; + +function jqNextId() { return ++jqId; } + + +var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; +var MOZ_HACK_REGEXP = /^moz([A-Z])/; +var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"}; +var jqLiteMinErr = minErr('jqLite'); + +/** + * Converts snake_case to camelCase. + * Also there is special case for Moz prefix starting with upper case letter. + * @param name Name to normalize + */ +function camelCase(name) { + return name. + replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { + return offset ? letter.toUpperCase() : letter; + }). + replace(MOZ_HACK_REGEXP, 'Moz$1'); +} + +var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; +var HTML_REGEXP = /<|&#?\w+;/; +var TAG_NAME_REGEXP = /<([\w:]+)/; +var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; + +var wrapMap = { + 'option': [1, '<select multiple="multiple">', '</select>'], + + 'thead': [1, '<table>', '</table>'], + 'col': [2, '<table><colgroup>', '</colgroup></table>'], + 'tr': [2, '<table><tbody>', '</tbody></table>'], + 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'], + '_default': [0, "", ""] +}; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function jqLiteIsTextNode(html) { + return !HTML_REGEXP.test(html); +} + +function jqLiteAcceptsData(node) { + // The window object can accept data but has no nodeType + // Otherwise we are only interested in elements (1) and documents (9) + var nodeType = node.nodeType; + return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT; +} + +function jqLiteBuildFragment(html, context) { + var tmp, tag, wrap, + fragment = context.createDocumentFragment(), + nodes = [], i; + + if (jqLiteIsTextNode(html)) { + // Convert non-html into a text node + nodes.push(context.createTextNode(html)); + } else { + // Convert html into DOM nodes + tmp = tmp || fragment.appendChild(context.createElement("div")); + tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); + wrap = wrapMap[tag] || wrapMap._default; + tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2]; + + // Descend through wrappers to the right content + i = wrap[0]; + while (i--) { + tmp = tmp.lastChild; + } + + nodes = concat(nodes, tmp.childNodes); + + tmp = fragment.firstChild; + tmp.textContent = ""; + } + + // Remove wrapper from fragment + fragment.textContent = ""; + fragment.innerHTML = ""; // Clear inner HTML + forEach(nodes, function(node) { + fragment.appendChild(node); + }); + + return fragment; +} + +function jqLiteParseHTML(html, context) { + context = context || document; + var parsed; + + if ((parsed = SINGLE_TAG_REGEXP.exec(html))) { + return [context.createElement(parsed[1])]; + } + + if ((parsed = jqLiteBuildFragment(html, context))) { + return parsed.childNodes; + } + + return []; +} + +///////////////////////////////////////////// +function JQLite(element) { + if (element instanceof JQLite) { + return element; + } + + var argIsString; + + if (isString(element)) { + element = trim(element); + argIsString = true; + } + if (!(this instanceof JQLite)) { + if (argIsString && element.charAt(0) != '<') { + throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element'); + } + return new JQLite(element); + } + + if (argIsString) { + jqLiteAddNodes(this, jqLiteParseHTML(element)); + } else { + jqLiteAddNodes(this, element); + } +} + +function jqLiteClone(element) { + return element.cloneNode(true); +} + +function jqLiteDealoc(element, onlyDescendants) { + if (!onlyDescendants) jqLiteRemoveData(element); + + if (element.querySelectorAll) { + var descendants = element.querySelectorAll('*'); + for (var i = 0, l = descendants.length; i < l; i++) { + jqLiteRemoveData(descendants[i]); + } + } +} + +function jqLiteOff(element, type, fn, unsupported) { + if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument'); + + var expandoStore = jqLiteExpandoStore(element); + var events = expandoStore && expandoStore.events; + var handle = expandoStore && expandoStore.handle; + + if (!handle) return; //no listeners registered + + if (!type) { + for (type in events) { + if (type !== '$destroy') { + removeEventListenerFn(element, type, handle); + } + delete events[type]; + } + } else { + forEach(type.split(' '), function(type) { + if (isDefined(fn)) { + var listenerFns = events[type]; + arrayRemove(listenerFns || [], fn); + if (listenerFns && listenerFns.length > 0) { + return; + } + } + + removeEventListenerFn(element, type, handle); + delete events[type]; + }); + } +} + +function jqLiteRemoveData(element, name) { + var expandoId = element.ng339; + var expandoStore = expandoId && jqCache[expandoId]; + + if (expandoStore) { + if (name) { + delete expandoStore.data[name]; + return; + } + + if (expandoStore.handle) { + if (expandoStore.events.$destroy) { + expandoStore.handle({}, '$destroy'); + } + jqLiteOff(element); + } + delete jqCache[expandoId]; + element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it + } +} + + +function jqLiteExpandoStore(element, createIfNecessary) { + var expandoId = element.ng339, + expandoStore = expandoId && jqCache[expandoId]; + + if (createIfNecessary && !expandoStore) { + element.ng339 = expandoId = jqNextId(); + expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined}; + } + + return expandoStore; +} + + +function jqLiteData(element, key, value) { + if (jqLiteAcceptsData(element)) { + + var isSimpleSetter = isDefined(value); + var isSimpleGetter = !isSimpleSetter && key && !isObject(key); + var massGetter = !key; + var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter); + var data = expandoStore && expandoStore.data; + + if (isSimpleSetter) { // data('key', value) + data[key] = value; + } else { + if (massGetter) { // data() + return data; + } else { + if (isSimpleGetter) { // data('key') + // don't force creation of expandoStore if it doesn't exist yet + return data && data[key]; + } else { // mass-setter: data({key1: val1, key2: val2}) + extend(data, key); + } + } + } + } +} + +function jqLiteHasClass(element, selector) { + if (!element.getAttribute) return false; + return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " "). + indexOf(" " + selector + " ") > -1); +} + +function jqLiteRemoveClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + forEach(cssClasses.split(' '), function(cssClass) { + element.setAttribute('class', trim( + (" " + (element.getAttribute('class') || '') + " ") + .replace(/[\n\t]/g, " ") + .replace(" " + trim(cssClass) + " ", " ")) + ); + }); + } +} + +function jqLiteAddClass(element, cssClasses) { + if (cssClasses && element.setAttribute) { + var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') + .replace(/[\n\t]/g, " "); + + forEach(cssClasses.split(' '), function(cssClass) { + cssClass = trim(cssClass); + if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { + existingClasses += cssClass + ' '; + } + }); + + element.setAttribute('class', trim(existingClasses)); + } +} + + +function jqLiteAddNodes(root, elements) { + // THIS CODE IS VERY HOT. Don't make changes without benchmarking. + + if (elements) { + + // if a Node (the most common case) + if (elements.nodeType) { + root[root.length++] = elements; + } else { + var length = elements.length; + + // if an Array or NodeList and not a Window + if (typeof length === 'number' && elements.window !== elements) { + if (length) { + for (var i = 0; i < length; i++) { + root[root.length++] = elements[i]; + } + } + } else { + root[root.length++] = elements; + } + } + } +} + + +function jqLiteController(element, name) { + return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller'); +} + +function jqLiteInheritedData(element, name, value) { + // if element is the document object work with the html element instead + // this makes $(document).scope() possible + if (element.nodeType == NODE_TYPE_DOCUMENT) { + element = element.documentElement; + } + var names = isArray(name) ? name : [name]; + + while (element) { + for (var i = 0, ii = names.length; i < ii; i++) { + if ((value = jqLite.data(element, names[i])) !== undefined) return value; + } + + // If dealing with a document fragment node with a host element, and no parent, use the host + // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM + // to lookup parent controllers. + element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host); + } +} + +function jqLiteEmpty(element) { + jqLiteDealoc(element, true); + while (element.firstChild) { + element.removeChild(element.firstChild); + } +} + +function jqLiteRemove(element, keepData) { + if (!keepData) jqLiteDealoc(element); + var parent = element.parentNode; + if (parent) parent.removeChild(element); +} + + +function jqLiteDocumentLoaded(action, win) { + win = win || window; + if (win.document.readyState === 'complete') { + // Force the action to be run async for consistent behaviour + // from the action's point of view + // i.e. it will definitely not be in a $apply + win.setTimeout(action); + } else { + // No need to unbind this handler as load is only ever called once + jqLite(win).on('load', action); + } +} + +////////////////////////////////////////// +// Functions which are declared directly. +////////////////////////////////////////// +var JQLitePrototype = JQLite.prototype = { + ready: function(fn) { + var fired = false; + + function trigger() { + if (fired) return; + fired = true; + fn(); + } + + // check if document is already loaded + if (document.readyState === 'complete') { + setTimeout(trigger); + } else { + this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 + // we can not use jqLite since we are not done loading and jQuery could be loaded later. + // jshint -W064 + JQLite(window).on('load', trigger); // fallback to window.onload for others + // jshint +W064 + } + }, + toString: function() { + var value = []; + forEach(this, function(e) { value.push('' + e);}); + return '[' + value.join(', ') + ']'; + }, + + eq: function(index) { + return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); + }, + + length: 0, + push: push, + sort: [].sort, + splice: [].splice +}; + +////////////////////////////////////////// +// Functions iterating getter/setters. +// these functions return self on setter and +// value on get. +////////////////////////////////////////// +var BOOLEAN_ATTR = {}; +forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { + BOOLEAN_ATTR[lowercase(value)] = value; +}); +var BOOLEAN_ELEMENTS = {}; +forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { + BOOLEAN_ELEMENTS[value] = true; +}); +var ALIASED_ATTR = { + 'ngMinlength': 'minlength', + 'ngMaxlength': 'maxlength', + 'ngMin': 'min', + 'ngMax': 'max', + 'ngPattern': 'pattern' +}; + +function getBooleanAttrName(element, name) { + // check dom last since we will most likely fail on name + var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; + + // booleanAttr is here twice to minimize DOM access + return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr; +} + +function getAliasedAttrName(element, name) { + var nodeName = element.nodeName; + return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name]; +} + +forEach({ + data: jqLiteData, + removeData: jqLiteRemoveData +}, function(fn, name) { + JQLite[name] = fn; +}); + +forEach({ + data: jqLiteData, + inheritedData: jqLiteInheritedData, + + scope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); + }, + + isolateScope: function(element) { + // Can't use jqLiteData here directly so we stay compatible with jQuery! + return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); + }, + + controller: jqLiteController, + + injector: function(element) { + return jqLiteInheritedData(element, '$injector'); + }, + + removeAttr: function(element, name) { + element.removeAttribute(name); + }, + + hasClass: jqLiteHasClass, + + css: function(element, name, value) { + name = camelCase(name); + + if (isDefined(value)) { + element.style[name] = value; + } else { + return element.style[name]; + } + }, + + attr: function(element, name, value) { + var lowercasedName = lowercase(name); + if (BOOLEAN_ATTR[lowercasedName]) { + if (isDefined(value)) { + if (!!value) { + element[name] = true; + element.setAttribute(name, lowercasedName); + } else { + element[name] = false; + element.removeAttribute(lowercasedName); + } + } else { + return (element[name] || + (element.attributes.getNamedItem(name) || noop).specified) + ? lowercasedName + : undefined; + } + } else if (isDefined(value)) { + element.setAttribute(name, value); + } else if (element.getAttribute) { + // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code + // some elements (e.g. Document) don't have get attribute, so return undefined + var ret = element.getAttribute(name, 2); + // normalize non-existing attributes to undefined (as jQuery) + return ret === null ? undefined : ret; + } + }, + + prop: function(element, name, value) { + if (isDefined(value)) { + element[name] = value; + } else { + return element[name]; + } + }, + + text: (function() { + getText.$dv = ''; + return getText; + + function getText(element, value) { + if (isUndefined(value)) { + var nodeType = element.nodeType; + return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : ''; + } + element.textContent = value; + } + })(), + + val: function(element, value) { + if (isUndefined(value)) { + if (element.multiple && nodeName_(element) === 'select') { + var result = []; + forEach(element.options, function(option) { + if (option.selected) { + result.push(option.value || option.text); + } + }); + return result.length === 0 ? null : result; + } + return element.value; + } + element.value = value; + }, + + html: function(element, value) { + if (isUndefined(value)) { + return element.innerHTML; + } + jqLiteDealoc(element, true); + element.innerHTML = value; + }, + + empty: jqLiteEmpty +}, function(fn, name) { + /** + * Properties: writes return selection, reads return first value + */ + JQLite.prototype[name] = function(arg1, arg2) { + var i, key; + var nodeCount = this.length; + + // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it + // in a way that survives minification. + // jqLiteEmpty takes no arguments but is a setter. + if (fn !== jqLiteEmpty && + (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) { + if (isObject(arg1)) { + + // we are a write, but the object properties are the key/values + for (i = 0; i < nodeCount; i++) { + if (fn === jqLiteData) { + // data() takes the whole object in jQuery + fn(this[i], arg1); + } else { + for (key in arg1) { + fn(this[i], key, arg1[key]); + } + } + } + // return self for chaining + return this; + } else { + // we are a read, so read the first child. + // TODO: do we still need this? + var value = fn.$dv; + // Only if we have $dv do we iterate over all, otherwise it is just the first element. + var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount; + for (var j = 0; j < jj; j++) { + var nodeValue = fn(this[j], arg1, arg2); + value = value ? value + nodeValue : nodeValue; + } + return value; + } + } else { + // we are a write, so apply to all children + for (i = 0; i < nodeCount; i++) { + fn(this[i], arg1, arg2); + } + // return self for chaining + return this; + } + }; +}); + +function createEventHandler(element, events) { + var eventHandler = function(event, type) { + // jQuery specific api + event.isDefaultPrevented = function() { + return event.defaultPrevented; + }; + + var eventFns = events[type || event.type]; + var eventFnsLength = eventFns ? eventFns.length : 0; + + if (!eventFnsLength) return; + + if (isUndefined(event.immediatePropagationStopped)) { + var originalStopImmediatePropagation = event.stopImmediatePropagation; + event.stopImmediatePropagation = function() { + event.immediatePropagationStopped = true; + + if (event.stopPropagation) { + event.stopPropagation(); + } + + if (originalStopImmediatePropagation) { + originalStopImmediatePropagation.call(event); + } + }; + } + + event.isImmediatePropagationStopped = function() { + return event.immediatePropagationStopped === true; + }; + + // Copy event handlers in case event handlers array is modified during execution. + if ((eventFnsLength > 1)) { + eventFns = shallowCopy(eventFns); + } + + for (var i = 0; i < eventFnsLength; i++) { + if (!event.isImmediatePropagationStopped()) { + eventFns[i].call(element, event); + } + } + }; + + // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all + // events on `element` + eventHandler.elem = element; + return eventHandler; +} + +////////////////////////////////////////// +// Functions iterating traversal. +// These functions chain results into a single +// selector. +////////////////////////////////////////// +forEach({ + removeData: jqLiteRemoveData, + + on: function jqLiteOn(element, type, fn, unsupported) { + if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); + + // Do not add event handlers to non-elements because they will not be cleaned up. + if (!jqLiteAcceptsData(element)) { + return; + } + + var expandoStore = jqLiteExpandoStore(element, true); + var events = expandoStore.events; + var handle = expandoStore.handle; + + if (!handle) { + handle = expandoStore.handle = createEventHandler(element, events); + } + + // http://jsperf.com/string-indexof-vs-split + var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type]; + var i = types.length; + + while (i--) { + type = types[i]; + var eventFns = events[type]; + + if (!eventFns) { + events[type] = []; + + if (type === 'mouseenter' || type === 'mouseleave') { + // Refer to jQuery's implementation of mouseenter & mouseleave + // Read about mouseenter and mouseleave: + // http://www.quirksmode.org/js/events_mouse.html#link8 + + jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) { + var target = this, related = event.relatedTarget; + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if (!related || (related !== target && !target.contains(related))) { + handle(event, type); + } + }); + + } else { + if (type !== '$destroy') { + addEventListenerFn(element, type, handle); + } + } + eventFns = events[type]; + } + eventFns.push(fn); + } + }, + + off: jqLiteOff, + + one: function(element, type, fn) { + element = jqLite(element); + + //add the listener twice so that when it is called + //you can remove the original function and still be + //able to call element.off(ev, fn) normally + element.on(type, function onFn() { + element.off(type, fn); + element.off(type, onFn); + }); + element.on(type, fn); + }, + + replaceWith: function(element, replaceNode) { + var index, parent = element.parentNode; + jqLiteDealoc(element); + forEach(new JQLite(replaceNode), function(node) { + if (index) { + parent.insertBefore(node, index.nextSibling); + } else { + parent.replaceChild(node, element); + } + index = node; + }); + }, + + children: function(element) { + var children = []; + forEach(element.childNodes, function(element) { + if (element.nodeType === NODE_TYPE_ELEMENT) + children.push(element); + }); + return children; + }, + + contents: function(element) { + return element.contentDocument || element.childNodes || []; + }, + + append: function(element, node) { + var nodeType = element.nodeType; + if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return; + + node = new JQLite(node); + + for (var i = 0, ii = node.length; i < ii; i++) { + var child = node[i]; + element.appendChild(child); + } + }, + + prepend: function(element, node) { + if (element.nodeType === NODE_TYPE_ELEMENT) { + var index = element.firstChild; + forEach(new JQLite(node), function(child) { + element.insertBefore(child, index); + }); + } + }, + + wrap: function(element, wrapNode) { + wrapNode = jqLite(wrapNode).eq(0).clone()[0]; + var parent = element.parentNode; + if (parent) { + parent.replaceChild(wrapNode, element); + } + wrapNode.appendChild(element); + }, + + remove: jqLiteRemove, + + detach: function(element) { + jqLiteRemove(element, true); + }, + + after: function(element, newElement) { + var index = element, parent = element.parentNode; + newElement = new JQLite(newElement); + + for (var i = 0, ii = newElement.length; i < ii; i++) { + var node = newElement[i]; + parent.insertBefore(node, index.nextSibling); + index = node; + } + }, + + addClass: jqLiteAddClass, + removeClass: jqLiteRemoveClass, + + toggleClass: function(element, selector, condition) { + if (selector) { + forEach(selector.split(' '), function(className) { + var classCondition = condition; + if (isUndefined(classCondition)) { + classCondition = !jqLiteHasClass(element, className); + } + (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); + }); + } + }, + + parent: function(element) { + var parent = element.parentNode; + return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null; + }, + + next: function(element) { + return element.nextElementSibling; + }, + + find: function(element, selector) { + if (element.getElementsByTagName) { + return element.getElementsByTagName(selector); + } else { + return []; + } + }, + + clone: jqLiteClone, + + triggerHandler: function(element, event, extraParameters) { + + var dummyEvent, eventFnsCopy, handlerArgs; + var eventName = event.type || event; + var expandoStore = jqLiteExpandoStore(element); + var events = expandoStore && expandoStore.events; + var eventFns = events && events[eventName]; + + if (eventFns) { + // Create a dummy event to pass to the handlers + dummyEvent = { + preventDefault: function() { this.defaultPrevented = true; }, + isDefaultPrevented: function() { return this.defaultPrevented === true; }, + stopImmediatePropagation: function() { this.immediatePropagationStopped = true; }, + isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; }, + stopPropagation: noop, + type: eventName, + target: element + }; + + // If a custom event was provided then extend our dummy event with it + if (event.type) { + dummyEvent = extend(dummyEvent, event); + } + + // Copy event handlers in case event handlers array is modified during execution. + eventFnsCopy = shallowCopy(eventFns); + handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; + + forEach(eventFnsCopy, function(fn) { + if (!dummyEvent.isImmediatePropagationStopped()) { + fn.apply(element, handlerArgs); + } + }); + } + } +}, function(fn, name) { + /** + * chaining functions + */ + JQLite.prototype[name] = function(arg1, arg2, arg3) { + var value; + + for (var i = 0, ii = this.length; i < ii; i++) { + if (isUndefined(value)) { + value = fn(this[i], arg1, arg2, arg3); + if (isDefined(value)) { + // any function which returns a value needs to be wrapped + value = jqLite(value); + } + } else { + jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); + } + } + return isDefined(value) ? value : this; + }; + + // bind legacy bind/unbind to on/off + JQLite.prototype.bind = JQLite.prototype.on; + JQLite.prototype.unbind = JQLite.prototype.off; +}); + + +// Provider for private $$jqLite service +function $$jqLiteProvider() { + this.$get = function $$jqLite() { + return extend(JQLite, { + hasClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteHasClass(node, classes); + }, + addClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteAddClass(node, classes); + }, + removeClass: function(node, classes) { + if (node.attr) node = node[0]; + return jqLiteRemoveClass(node, classes); + } + }); + }; +} + +/** + * Computes a hash of an 'obj'. + * Hash of a: + * string is string + * number is number as string + * object is either result of calling $$hashKey function on the object or uniquely generated id, + * that is also assigned to the $$hashKey property of the object. + * + * @param obj + * @returns {string} hash string such that the same input will have the same hash string. + * The resulting string key is in 'type:hashKey' format. + */ +function hashKey(obj, nextUidFn) { + var key = obj && obj.$$hashKey; + + if (key) { + if (typeof key === 'function') { + key = obj.$$hashKey(); + } + return key; + } + + var objType = typeof obj; + if (objType == 'function' || (objType == 'object' && obj !== null)) { + key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)(); + } else { + key = objType + ':' + obj; + } + + return key; +} + +/** + * HashMap which can use objects as keys + */ +function HashMap(array, isolatedUid) { + if (isolatedUid) { + var uid = 0; + this.nextUid = function() { + return ++uid; + }; + } + forEach(array, this.put, this); +} +HashMap.prototype = { + /** + * Store key value pair + * @param key key to store can be any type + * @param value value to store can be any type + */ + put: function(key, value) { + this[hashKey(key, this.nextUid)] = value; + }, + + /** + * @param key + * @returns {Object} the value for the key + */ + get: function(key) { + return this[hashKey(key, this.nextUid)]; + }, + + /** + * Remove the key/value pair + * @param key + */ + remove: function(key) { + var value = this[key = hashKey(key, this.nextUid)]; + delete this[key]; + return value; + } +}; + +/** + * @ngdoc function + * @module ng + * @name angular.injector + * @kind function + * + * @description + * Creates an injector object that can be used for retrieving services as well as for + * dependency injection (see {@link guide/di dependency injection}). + * + * @param {Array.<string|Function>} modules A list of module functions or their aliases. See + * {@link angular.module}. The `ng` module must be explicitly added. + * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which + * disallows argument name annotation inference. + * @returns {injector} Injector object. See {@link auto.$injector $injector}. + * + * @example + * Typical usage + * ```js + * // create an injector + * var $injector = angular.injector(['ng']); + * + * // use the injector to kick off your application + * // use the type inference to auto inject arguments, or use implicit injection + * $injector.invoke(function($rootScope, $compile, $document) { + * $compile($document)($rootScope); + * $rootScope.$digest(); + * }); + * ``` + * + * Sometimes you want to get access to the injector of a currently running Angular app + * from outside Angular. Perhaps, you want to inject and compile some markup after the + * application has been bootstrapped. You can do this using the extra `injector()` added + * to JQuery/jqLite elements. See {@link angular.element}. + * + * *This is fairly rare but could be the case if a third party library is injecting the + * markup.* + * + * In the following example a new block of HTML containing a `ng-controller` + * directive is added to the end of the document body by JQuery. We then compile and link + * it into the current AngularJS scope. + * + * ```js + * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>'); + * $(document.body).append($div); + * + * angular.element(document).injector().invoke(function($compile) { + * var scope = angular.element($div).scope(); + * $compile($div)(scope); + * }); + * ``` + */ + + +/** + * @ngdoc module + * @name auto + * @description + * + * Implicit module which gets automatically added to each {@link auto.$injector $injector}. + */ + +var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; +var FN_ARG_SPLIT = /,/; +var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; +var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; +var $injectorMinErr = minErr('$injector'); + +function anonFn(fn) { + // For anonymous functions, showing at the very least the function signature can help in + // debugging. + var fnText = fn.toString().replace(STRIP_COMMENTS, ''), + args = fnText.match(FN_ARGS); + if (args) { + return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; + } + return 'fn'; +} + +function annotate(fn, strictDi, name) { + var $inject, + fnText, + argDecl, + last; + + if (typeof fn === 'function') { + if (!($inject = fn.$inject)) { + $inject = []; + if (fn.length) { + if (strictDi) { + if (!isString(name) || !name) { + name = fn.name || anonFn(fn); + } + throw $injectorMinErr('strictdi', + '{0} is not using explicit annotation and cannot be invoked in strict mode', name); + } + fnText = fn.toString().replace(STRIP_COMMENTS, ''); + argDecl = fnText.match(FN_ARGS); + forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) { + arg.replace(FN_ARG, function(all, underscore, name) { + $inject.push(name); + }); + }); + } + fn.$inject = $inject; + } + } else if (isArray(fn)) { + last = fn.length - 1; + assertArgFn(fn[last], 'fn'); + $inject = fn.slice(0, last); + } else { + assertArgFn(fn, 'fn', true); + } + return $inject; +} + +/////////////////////////////////////// + +/** + * @ngdoc service + * @name $injector + * + * @description + * + * `$injector` is used to retrieve object instances as defined by + * {@link auto.$provide provider}, instantiate types, invoke methods, + * and load modules. + * + * The following always holds true: + * + * ```js + * var $injector = angular.injector(); + * expect($injector.get('$injector')).toBe($injector); + * expect($injector.invoke(function($injector) { + * return $injector; + * })).toBe($injector); + * ``` + * + * # Injection Function Annotation + * + * JavaScript does not have annotations, and annotations are needed for dependency injection. The + * following are all valid ways of annotating function with injection arguments and are equivalent. + * + * ```js + * // inferred (only works if code not minified/obfuscated) + * $injector.invoke(function(serviceA){}); + * + * // annotated + * function explicit(serviceA) {}; + * explicit.$inject = ['serviceA']; + * $injector.invoke(explicit); + * + * // inline + * $injector.invoke(['serviceA', function(serviceA){}]); + * ``` + * + * ## Inference + * + * In JavaScript calling `toString()` on a function returns the function definition. The definition + * can then be parsed and the function arguments can be extracted. This method of discovering + * annotations is disallowed when the injector is in strict mode. + * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the + * argument names. + * + * ## `$inject` Annotation + * By adding an `$inject` property onto a function the injection parameters can be specified. + * + * ## Inline + * As an array of injection names, where the last item in the array is the function to call. + */ + +/** + * @ngdoc method + * @name $injector#get + * + * @description + * Return an instance of the service. + * + * @param {string} name The name of the instance to retrieve. + * @param {string} caller An optional string to provide the origin of the function call for error messages. + * @return {*} The instance. + */ + +/** + * @ngdoc method + * @name $injector#invoke + * + * @description + * Invoke the method and supply the method arguments from the `$injector`. + * + * @param {!Function} fn The function to invoke. Function parameters are injected according to the + * {@link guide/di $inject Annotation} rules. + * @param {Object=} self The `this` for the invoked method. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {*} the value returned by the invoked `fn` function. + */ + +/** + * @ngdoc method + * @name $injector#has + * + * @description + * Allows the user to query if the particular service exists. + * + * @param {string} name Name of the service to query. + * @returns {boolean} `true` if injector has given service. + */ + +/** + * @ngdoc method + * @name $injector#instantiate + * @description + * Create a new instance of JS type. The method takes a constructor function, invokes the new + * operator, and supplies all of the arguments to the constructor function as specified by the + * constructor annotation. + * + * @param {Function} Type Annotated constructor function. + * @param {Object=} locals Optional object. If preset then any argument names are read from this + * object first, before the `$injector` is consulted. + * @returns {Object} new instance of `Type`. + */ + +/** + * @ngdoc method + * @name $injector#annotate + * + * @description + * Returns an array of service names which the function is requesting for injection. This API is + * used by the injector to determine which services need to be injected into the function when the + * function is invoked. There are three ways in which the function can be annotated with the needed + * dependencies. + * + * # Argument names + * + * The simplest form is to extract the dependencies from the arguments of the function. This is done + * by converting the function into a string using `toString()` method and extracting the argument + * names. + * ```js + * // Given + * function MyController($scope, $route) { + * // ... + * } + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * You can disallow this method by using strict injection mode. + * + * This method does not work with code minification / obfuscation. For this reason the following + * annotation strategies are supported. + * + * # The `$inject` property + * + * If a function has an `$inject` property and its value is an array of strings, then the strings + * represent names of services to be injected into the function. + * ```js + * // Given + * var MyController = function(obfuscatedScope, obfuscatedRoute) { + * // ... + * } + * // Define function dependencies + * MyController['$inject'] = ['$scope', '$route']; + * + * // Then + * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); + * ``` + * + * # The array notation + * + * It is often desirable to inline Injected functions and that's when setting the `$inject` property + * is very inconvenient. In these situations using the array notation to specify the dependencies in + * a way that survives minification is a better choice: + * + * ```js + * // We wish to write this (not minification / obfuscation safe) + * injector.invoke(function($compile, $rootScope) { + * // ... + * }); + * + * // We are forced to write break inlining + * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { + * // ... + * }; + * tmpFn.$inject = ['$compile', '$rootScope']; + * injector.invoke(tmpFn); + * + * // To better support inline function the inline annotation is supported + * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { + * // ... + * }]); + * + * // Therefore + * expect(injector.annotate( + * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) + * ).toEqual(['$compile', '$rootScope']); + * ``` + * + * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to + * be retrieved as described above. + * + * @param {boolean=} [strictDi=false] Disallow argument name annotation inference. + * + * @returns {Array.<string>} The names of the services which the function requires. + */ + + + + +/** + * @ngdoc service + * @name $provide + * + * @description + * + * The {@link auto.$provide $provide} service has a number of methods for registering components + * with the {@link auto.$injector $injector}. Many of these functions are also exposed on + * {@link angular.Module}. + * + * An Angular **service** is a singleton object created by a **service factory**. These **service + * factories** are functions which, in turn, are created by a **service provider**. + * The **service providers** are constructor functions. When instantiated they must contain a + * property called `$get`, which holds the **service factory** function. + * + * When you request a service, the {@link auto.$injector $injector} is responsible for finding the + * correct **service provider**, instantiating it and then calling its `$get` **service factory** + * function to get the instance of the **service**. + * + * Often services have no configuration options and there is no need to add methods to the service + * provider. The provider will be no more than a constructor function with a `$get` property. For + * these cases the {@link auto.$provide $provide} service has additional helper methods to register + * services without specifying a provider. + * + * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the + * {@link auto.$injector $injector} + * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by + * providers and services. + * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by + * services, not providers. + * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, + * that will be wrapped in a **service provider** object, whose `$get` property will contain the + * given factory function. + * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` + * that will be wrapped in a **service provider** object, whose `$get` property will instantiate + * a new object using the given constructor function. + * + * See the individual methods for more information and examples. + */ + +/** + * @ngdoc method + * @name $provide#provider + * @description + * + * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions + * are constructor functions, whose instances are responsible for "providing" a factory for a + * service. + * + * Service provider names start with the name of the service they provide followed by `Provider`. + * For example, the {@link ng.$log $log} service has a provider called + * {@link ng.$logProvider $logProvider}. + * + * Service provider objects can have additional methods which allow configuration of the provider + * and its service. Importantly, you can configure what kind of service is created by the `$get` + * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a + * method {@link ng.$logProvider#debugEnabled debugEnabled} + * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the + * console or not. + * + * @param {string} name The name of the instance. NOTE: the provider will be available under `name + + 'Provider'` key. + * @param {(Object|function())} provider If the provider is: + * + * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using + * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. + * - `Constructor`: a new instance of the provider will be created using + * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. + * + * @returns {Object} registered provider instance + + * @example + * + * The following example shows how to create a simple event tracking service and register it using + * {@link auto.$provide#provider $provide.provider()}. + * + * ```js + * // Define the eventTracker provider + * function EventTrackerProvider() { + * var trackingUrl = '/track'; + * + * // A provider method for configuring where the tracked events should been saved + * this.setTrackingUrl = function(url) { + * trackingUrl = url; + * }; + * + * // The service factory function + * this.$get = ['$http', function($http) { + * var trackedEvents = {}; + * return { + * // Call this to track an event + * event: function(event) { + * var count = trackedEvents[event] || 0; + * count += 1; + * trackedEvents[event] = count; + * return count; + * }, + * // Call this to save the tracked events to the trackingUrl + * save: function() { + * $http.post(trackingUrl, trackedEvents); + * } + * }; + * }]; + * } + * + * describe('eventTracker', function() { + * var postSpy; + * + * beforeEach(module(function($provide) { + * // Register the eventTracker provider + * $provide.provider('eventTracker', EventTrackerProvider); + * })); + * + * beforeEach(module(function(eventTrackerProvider) { + * // Configure eventTracker provider + * eventTrackerProvider.setTrackingUrl('/custom-track'); + * })); + * + * it('tracks events', inject(function(eventTracker) { + * expect(eventTracker.event('login')).toEqual(1); + * expect(eventTracker.event('login')).toEqual(2); + * })); + * + * it('saves to the tracking url', inject(function(eventTracker, $http) { + * postSpy = spyOn($http, 'post'); + * eventTracker.event('login'); + * eventTracker.save(); + * expect(postSpy).toHaveBeenCalled(); + * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); + * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); + * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); + * })); + * }); + * ``` + */ + +/** + * @ngdoc method + * @name $provide#factory + * @description + * + * Register a **service factory**, which will be called to return the service instance. + * This is short for registering a service where its provider consists of only a `$get` property, + * which is the given service factory function. + * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to + * configure your service in a provider. + * + * @param {string} name The name of the instance. + * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand + * for `$provide.provider(name, {$get: $getFn})`. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service + * ```js + * $provide.factory('ping', ['$http', function($http) { + * return function ping() { + * return $http.send('/ping'); + * }; + * }]); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#service + * @description + * + * Register a **service constructor**, which will be invoked with `new` to create the service + * instance. + * This is short for registering a service where its provider's `$get` property is the service + * constructor function that will be used to instantiate the service instance. + * + * You should use {@link auto.$provide#service $provide.service(class)} if you define your service + * as a type/class. + * + * @param {string} name The name of the instance. + * @param {Function} constructor A class (constructor function) that will be instantiated. + * @returns {Object} registered provider instance + * + * @example + * Here is an example of registering a service using + * {@link auto.$provide#service $provide.service(class)}. + * ```js + * var Ping = function($http) { + * this.$http = $http; + * }; + * + * Ping.$inject = ['$http']; + * + * Ping.prototype.send = function() { + * return this.$http.get('/ping'); + * }; + * $provide.service('ping', Ping); + * ``` + * You would then inject and use this service like this: + * ```js + * someModule.controller('Ctrl', ['ping', function(ping) { + * ping.send(); + * }]); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#value + * @description + * + * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a + * number, an array, an object or a function. This is short for registering a service where its + * provider's `$get` property is a factory function that takes no arguments and returns the **value + * service**. + * + * Value services are similar to constant services, except that they cannot be injected into a + * module configuration function (see {@link angular.Module#config}) but they can be overridden by + * an Angular + * {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the instance. + * @param {*} value The value. + * @returns {Object} registered provider instance + * + * @example + * Here are some examples of creating value services. + * ```js + * $provide.value('ADMIN_USER', 'admin'); + * + * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); + * + * $provide.value('halfOf', function(value) { + * return value / 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#constant + * @description + * + * Register a **constant service**, such as a string, a number, an array, an object or a function, + * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be + * injected into a module configuration function (see {@link angular.Module#config}) and it cannot + * be overridden by an Angular {@link auto.$provide#decorator decorator}. + * + * @param {string} name The name of the constant. + * @param {*} value The constant value. + * @returns {Object} registered instance + * + * @example + * Here a some examples of creating constants: + * ```js + * $provide.constant('SHARD_HEIGHT', 306); + * + * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); + * + * $provide.constant('double', function(value) { + * return value * 2; + * }); + * ``` + */ + + +/** + * @ngdoc method + * @name $provide#decorator + * @description + * + * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator + * intercepts the creation of a service, allowing it to override or modify the behaviour of the + * service. The object returned by the decorator may be the original service, or a new service + * object which replaces or wraps and delegates to the original service. + * + * @param {string} name The name of the service to decorate. + * @param {function()} decorator This function will be invoked when the service needs to be + * instantiated and should return the decorated service instance. The function is called using + * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. + * Local injection arguments: + * + * * `$delegate` - The original service instance, which can be monkey patched, configured, + * decorated or delegated to. + * + * @example + * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting + * calls to {@link ng.$log#error $log.warn()}. + * ```js + * $provide.decorator('$log', ['$delegate', function($delegate) { + * $delegate.warn = $delegate.error; + * return $delegate; + * }]); + * ``` + */ + + +function createInjector(modulesToLoad, strictDi) { + strictDi = (strictDi === true); + var INSTANTIATING = {}, + providerSuffix = 'Provider', + path = [], + loadedModules = new HashMap([], true), + providerCache = { + $provide: { + provider: supportObject(provider), + factory: supportObject(factory), + service: supportObject(service), + value: supportObject(value), + constant: supportObject(constant), + decorator: decorator + } + }, + providerInjector = (providerCache.$injector = + createInternalInjector(providerCache, function(serviceName, caller) { + if (angular.isString(caller)) { + path.push(caller); + } + throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); + })), + instanceCache = {}, + instanceInjector = (instanceCache.$injector = + createInternalInjector(instanceCache, function(serviceName, caller) { + var provider = providerInjector.get(serviceName + providerSuffix, caller); + return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); + })); + + + forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); + + return instanceInjector; + + //////////////////////////////////// + // $provider + //////////////////////////////////// + + function supportObject(delegate) { + return function(key, value) { + if (isObject(key)) { + forEach(key, reverseParams(delegate)); + } else { + return delegate(key, value); + } + }; + } + + function provider(name, provider_) { + assertNotHasOwnProperty(name, 'service'); + if (isFunction(provider_) || isArray(provider_)) { + provider_ = providerInjector.instantiate(provider_); + } + if (!provider_.$get) { + throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); + } + return providerCache[name + providerSuffix] = provider_; + } + + function enforceReturnValue(name, factory) { + return function enforcedReturnValue() { + var result = instanceInjector.invoke(factory, this); + if (isUndefined(result)) { + throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name); + } + return result; + }; + } + + function factory(name, factoryFn, enforce) { + return provider(name, { + $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn + }); + } + + function service(name, constructor) { + return factory(name, ['$injector', function($injector) { + return $injector.instantiate(constructor); + }]); + } + + function value(name, val) { return factory(name, valueFn(val), false); } + + function constant(name, value) { + assertNotHasOwnProperty(name, 'constant'); + providerCache[name] = value; + instanceCache[name] = value; + } + + function decorator(serviceName, decorFn) { + var origProvider = providerInjector.get(serviceName + providerSuffix), + orig$get = origProvider.$get; + + origProvider.$get = function() { + var origInstance = instanceInjector.invoke(orig$get, origProvider); + return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); + }; + } + + //////////////////////////////////// + // Module Loading + //////////////////////////////////// + function loadModules(modulesToLoad) { + var runBlocks = [], moduleFn; + forEach(modulesToLoad, function(module) { + if (loadedModules.get(module)) return; + loadedModules.put(module, true); + + function runInvokeQueue(queue) { + var i, ii; + for (i = 0, ii = queue.length; i < ii; i++) { + var invokeArgs = queue[i], + provider = providerInjector.get(invokeArgs[0]); + + provider[invokeArgs[1]].apply(provider, invokeArgs[2]); + } + } + + try { + if (isString(module)) { + moduleFn = angularModule(module); + runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); + runInvokeQueue(moduleFn._invokeQueue); + runInvokeQueue(moduleFn._configBlocks); + } else if (isFunction(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else if (isArray(module)) { + runBlocks.push(providerInjector.invoke(module)); + } else { + assertArgFn(module, 'module'); + } + } catch (e) { + if (isArray(module)) { + module = module[module.length - 1]; + } + if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { + // Safari & FF's stack traces don't contain error.message content + // unlike those of Chrome and IE + // So if stack doesn't contain message, we create a new string that contains both. + // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. + /* jshint -W022 */ + e = e.message + '\n' + e.stack; + } + throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", + module, e.stack || e.message || e); + } + }); + return runBlocks; + } + + //////////////////////////////////// + // internal Injector + //////////////////////////////////// + + function createInternalInjector(cache, factory) { + + function getService(serviceName, caller) { + if (cache.hasOwnProperty(serviceName)) { + if (cache[serviceName] === INSTANTIATING) { + throw $injectorMinErr('cdep', 'Circular dependency found: {0}', + serviceName + ' <- ' + path.join(' <- ')); + } + return cache[serviceName]; + } else { + try { + path.unshift(serviceName); + cache[serviceName] = INSTANTIATING; + return cache[serviceName] = factory(serviceName, caller); + } catch (err) { + if (cache[serviceName] === INSTANTIATING) { + delete cache[serviceName]; + } + throw err; + } finally { + path.shift(); + } + } + } + + function invoke(fn, self, locals, serviceName) { + if (typeof locals === 'string') { + serviceName = locals; + locals = null; + } + + var args = [], + $inject = createInjector.$$annotate(fn, strictDi, serviceName), + length, i, + key; + + for (i = 0, length = $inject.length; i < length; i++) { + key = $inject[i]; + if (typeof key !== 'string') { + throw $injectorMinErr('itkn', + 'Incorrect injection token! Expected service name as string, got {0}', key); + } + args.push( + locals && locals.hasOwnProperty(key) + ? locals[key] + : getService(key, serviceName) + ); + } + if (isArray(fn)) { + fn = fn[length]; + } + + // http://jsperf.com/angularjs-invoke-apply-vs-switch + // #5388 + return fn.apply(self, args); + } + + function instantiate(Type, locals, serviceName) { + // Check if Type is annotated and use just the given function at n-1 as parameter + // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); + // Object creation: http://jsperf.com/create-constructor/2 + var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null); + var returnedValue = invoke(Type, instance, locals, serviceName); + + return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; + } + + return { + invoke: invoke, + instantiate: instantiate, + get: getService, + annotate: createInjector.$$annotate, + has: function(name) { + return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); + } + }; + } +} + +createInjector.$$annotate = annotate; + +/** + * @ngdoc provider + * @name $anchorScrollProvider + * + * @description + * Use `$anchorScrollProvider` to disable automatic scrolling whenever + * {@link ng.$location#hash $location.hash()} changes. + */ +function $AnchorScrollProvider() { + + var autoScrollingEnabled = true; + + /** + * @ngdoc method + * @name $anchorScrollProvider#disableAutoScrolling + * + * @description + * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to + * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br /> + * Use this method to disable automatic scrolling. + * + * If automatic scrolling is disabled, one must explicitly call + * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the + * current hash. + */ + this.disableAutoScrolling = function() { + autoScrollingEnabled = false; + }; + + /** + * @ngdoc service + * @name $anchorScroll + * @kind function + * @requires $window + * @requires $location + * @requires $rootScope + * + * @description + * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and + * scrolls to the related element, according to the rules specified in the + * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). + * + * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to + * match any anchor whenever it changes. This can be disabled by calling + * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}. + * + * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a + * vertical scroll-offset (either fixed or dynamic). + * + * @property {(number|function|jqLite)} yOffset + * If set, specifies a vertical scroll-offset. This is often useful when there are fixed + * positioned elements at the top of the page, such as navbars, headers etc. + * + * `yOffset` can be specified in various ways: + * - **number**: A fixed number of pixels to be used as offset.<br /><br /> + * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return + * a number representing the offset (in pixels).<br /><br /> + * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from + * the top of the page to the element's bottom will be used as offset.<br /> + * **Note**: The element will be taken into account only as long as its `position` is set to + * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust + * their height and/or positioning according to the viewport's size. + * + * <br /> + * <div class="alert alert-warning"> + * In order for `yOffset` to work properly, scrolling should take place on the document's root and + * not some child element. + * </div> + * + * @example + <example module="anchorScrollExample"> + <file name="index.html"> + <div id="scrollArea" ng-controller="ScrollController"> + <a ng-click="gotoBottom()">Go to bottom</a> + <a id="bottom"></a> You're at the bottom! + </div> + </file> + <file name="script.js"> + angular.module('anchorScrollExample', []) + .controller('ScrollController', ['$scope', '$location', '$anchorScroll', + function ($scope, $location, $anchorScroll) { + $scope.gotoBottom = function() { + // set the location.hash to the id of + // the element you wish to scroll to. + $location.hash('bottom'); + + // call $anchorScroll() + $anchorScroll(); + }; + }]); + </file> + <file name="style.css"> + #scrollArea { + height: 280px; + overflow: auto; + } + + #bottom { + display: block; + margin-top: 2000px; + } + </file> + </example> + * + * <hr /> + * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value). + * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details. + * + * @example + <example module="anchorScrollOffsetExample"> + <file name="index.html"> + <div class="fixed-header" ng-controller="headerCtrl"> + <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]"> + Go to anchor {{x}} + </a> + </div> + <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]"> + Anchor {{x}} of 5 + </div> + </file> + <file name="script.js"> + angular.module('anchorScrollOffsetExample', []) + .run(['$anchorScroll', function($anchorScroll) { + $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels + }]) + .controller('headerCtrl', ['$anchorScroll', '$location', '$scope', + function ($anchorScroll, $location, $scope) { + $scope.gotoAnchor = function(x) { + var newHash = 'anchor' + x; + if ($location.hash() !== newHash) { + // set the $location.hash to `newHash` and + // $anchorScroll will automatically scroll to it + $location.hash('anchor' + x); + } else { + // call $anchorScroll() explicitly, + // since $location.hash hasn't changed + $anchorScroll(); + } + }; + } + ]); + </file> + <file name="style.css"> + body { + padding-top: 50px; + } + + .anchor { + border: 2px dashed DarkOrchid; + padding: 10px 10px 200px 10px; + } + + .fixed-header { + background-color: rgba(0, 0, 0, 0.2); + height: 50px; + position: fixed; + top: 0; left: 0; right: 0; + } + + .fixed-header > a { + display: inline-block; + margin: 5px 15px; + } + </file> + </example> + */ + this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { + var document = $window.document; + + // Helper function to get first anchor from a NodeList + // (using `Array#some()` instead of `angular#forEach()` since it's more performant + // and working in all supported browsers.) + function getFirstAnchor(list) { + var result = null; + Array.prototype.some.call(list, function(element) { + if (nodeName_(element) === 'a') { + result = element; + return true; + } + }); + return result; + } + + function getYOffset() { + + var offset = scroll.yOffset; + + if (isFunction(offset)) { + offset = offset(); + } else if (isElement(offset)) { + var elem = offset[0]; + var style = $window.getComputedStyle(elem); + if (style.position !== 'fixed') { + offset = 0; + } else { + offset = elem.getBoundingClientRect().bottom; + } + } else if (!isNumber(offset)) { + offset = 0; + } + + return offset; + } + + function scrollTo(elem) { + if (elem) { + elem.scrollIntoView(); + + var offset = getYOffset(); + + if (offset) { + // `offset` is the number of pixels we should scroll UP in order to align `elem` properly. + // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the + // top of the viewport. + // + // IF the number of pixels from the top of `elem` to the end of the page's content is less + // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some + // way down the page. + // + // This is often the case for elements near the bottom of the page. + // + // In such cases we do not need to scroll the whole `offset` up, just the difference between + // the top of the element and the offset, which is enough to align the top of `elem` at the + // desired position. + var elemTop = elem.getBoundingClientRect().top; + $window.scrollBy(0, elemTop - offset); + } + } else { + $window.scrollTo(0, 0); + } + } + + function scroll() { + var hash = $location.hash(), elm; + + // empty hash, scroll to the top of the page + if (!hash) scrollTo(null); + + // element with given id + else if ((elm = document.getElementById(hash))) scrollTo(elm); + + // first anchor with given name :-D + else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm); + + // no element and hash == 'top', scroll to the top of the page + else if (hash === 'top') scrollTo(null); + } + + // does not scroll when user clicks on anchor link that is currently on + // (no url change, no $location.hash() change), browser native does scroll + if (autoScrollingEnabled) { + $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, + function autoScrollWatchAction(newVal, oldVal) { + // skip the initial scroll if $location.hash is empty + if (newVal === oldVal && newVal === '') return; + + jqLiteDocumentLoaded(function() { + $rootScope.$evalAsync(scroll); + }); + }); + } + + return scroll; + }]; +} + +var $animateMinErr = minErr('$animate'); + +/** + * @ngdoc provider + * @name $animateProvider + * + * @description + * Default implementation of $animate that doesn't perform any animations, instead just + * synchronously performs DOM + * updates and calls done() callbacks. + * + * In order to enable animations the ngAnimate module has to be loaded. + * + * To see the functional implementation check out src/ngAnimate/animate.js + */ +var $AnimateProvider = ['$provide', function($provide) { + + + this.$$selectors = {}; + + + /** + * @ngdoc method + * @name $animateProvider#register + * + * @description + * Registers a new injectable animation factory function. The factory function produces the + * animation object which contains callback functions for each event that is expected to be + * animated. + * + * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` + * must be called once the element animation is complete. If a function is returned then the + * animation service will use this function to cancel the animation whenever a cancel event is + * triggered. + * + * + * ```js + * return { + * eventFn : function(element, done) { + * //code to run the animation + * //once complete, then run done() + * return function cancellationFunction() { + * //code to cancel the animation + * } + * } + * } + * ``` + * + * @param {string} name The name of the animation. + * @param {Function} factory The factory function that will be executed to return the animation + * object. + */ + this.register = function(name, factory) { + var key = name + '-animation'; + if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', + "Expecting class selector starting with '.' got '{0}'.", name); + this.$$selectors[name.substr(1)] = key; + $provide.factory(key, factory); + }; + + /** + * @ngdoc method + * @name $animateProvider#classNameFilter + * + * @description + * Sets and/or returns the CSS class regular expression that is checked when performing + * an animation. Upon bootstrap the classNameFilter value is not set at all and will + * therefore enable $animate to attempt to perform an animation on any element. + * When setting the classNameFilter value, animations will only be performed on elements + * that successfully match the filter expression. This in turn can boost performance + * for low-powered devices as well as applications containing a lot of structural operations. + * @param {RegExp=} expression The className expression which will be checked against all animations + * @return {RegExp} The current CSS className expression value. If null then there is no expression value + */ + this.classNameFilter = function(expression) { + if (arguments.length === 1) { + this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; + } + return this.$$classNameFilter; + }; + + this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) { + + var currentDefer; + + function runAnimationPostDigest(fn) { + var cancelFn, defer = $$q.defer(); + defer.promise.$$cancelFn = function ngAnimateMaybeCancel() { + cancelFn && cancelFn(); + }; + + $rootScope.$$postDigest(function ngAnimatePostDigest() { + cancelFn = fn(function ngAnimateNotifyComplete() { + defer.resolve(); + }); + }); + + return defer.promise; + } + + function resolveElementClasses(element, classes) { + var toAdd = [], toRemove = []; + + var hasClasses = createMap(); + forEach((element.attr('class') || '').split(/\s+/), function(className) { + hasClasses[className] = true; + }); + + forEach(classes, function(status, className) { + var hasClass = hasClasses[className]; + + // If the most recent class manipulation (via $animate) was to remove the class, and the + // element currently has the class, the class is scheduled for removal. Otherwise, if + // the most recent class manipulation (via $animate) was to add the class, and the + // element does not currently have the class, the class is scheduled to be added. + if (status === false && hasClass) { + toRemove.push(className); + } else if (status === true && !hasClass) { + toAdd.push(className); + } + }); + + return (toAdd.length + toRemove.length) > 0 && + [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null]; + } + + function cachedClassManipulation(cache, classes, op) { + for (var i=0, ii = classes.length; i < ii; ++i) { + var className = classes[i]; + cache[className] = op; + } + } + + function asyncPromise() { + // only serve one instance of a promise in order to save CPU cycles + if (!currentDefer) { + currentDefer = $$q.defer(); + $$asyncCallback(function() { + currentDefer.resolve(); + currentDefer = null; + }); + } + return currentDefer.promise; + } + + function applyStyles(element, options) { + if (angular.isObject(options)) { + var styles = extend(options.from || {}, options.to || {}); + element.css(styles); + } + } + + /** + * + * @ngdoc service + * @name $animate + * @description The $animate service provides rudimentary DOM manipulation functions to + * insert, remove and move elements within the DOM, as well as adding and removing classes. + * This service is the core service used by the ngAnimate $animator service which provides + * high-level animation hooks for CSS and JavaScript. + * + * $animate is available in the AngularJS core, however, the ngAnimate module must be included + * to enable full out animation support. Otherwise, $animate will only perform simple DOM + * manipulation operations. + * + * To learn more about enabling animation support, click here to visit the {@link ngAnimate + * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service + * page}. + */ + return { + animate: function(element, from, to) { + applyStyles(element, { from: from, to: to }); + return asyncPromise(); + }, + + /** + * + * @ngdoc method + * @name $animate#enter + * @kind function + * @description Inserts the element into the DOM either after the `after` element or + * as the first child within the `parent` element. When the function is called a promise + * is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will be inserted into the DOM + * @param {DOMElement} parent the parent element which will append the element as + * a child (if the after element is not present) + * @param {DOMElement} after the sibling element which will append the element + * after itself + * @param {object=} options an optional collection of styles that will be applied to the element. + * @return {Promise} the animation callback promise + */ + enter: function(element, parent, after, options) { + applyStyles(element, options); + after ? after.after(element) + : parent.prepend(element); + return asyncPromise(); + }, + + /** + * + * @ngdoc method + * @name $animate#leave + * @kind function + * @description Removes the element from the DOM. When the function is called a promise + * is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will be removed from the DOM + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise + */ + leave: function(element, options) { + applyStyles(element, options); + element.remove(); + return asyncPromise(); + }, + + /** + * + * @ngdoc method + * @name $animate#move + * @kind function + * @description Moves the position of the provided element within the DOM to be placed + * either after the `after` element or inside of the `parent` element. When the function + * is called a promise is returned that will be resolved at a later time. + * + * @param {DOMElement} element the element which will be moved around within the + * DOM + * @param {DOMElement} parent the parent element where the element will be + * inserted into (if the after element is not present) + * @param {DOMElement} after the sibling element where the element will be + * positioned next to + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise + */ + move: function(element, parent, after, options) { + // Do not remove element before insert. Removing will cause data associated with the + // element to be dropped. Insert will implicitly do the remove. + return this.enter(element, parent, after, options); + }, + + /** + * + * @ngdoc method + * @name $animate#addClass + * @kind function + * @description Adds the provided className CSS class value to the provided element. + * When the function is called a promise is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will have the className value + * added to it + * @param {string} className the CSS class which will be added to the element + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise + */ + addClass: function(element, className, options) { + return this.setClass(element, className, [], options); + }, + + $$addClassImmediately: function(element, className, options) { + element = jqLite(element); + className = !isString(className) + ? (isArray(className) ? className.join(' ') : '') + : className; + forEach(element, function(element) { + jqLiteAddClass(element, className); + }); + applyStyles(element, options); + return asyncPromise(); + }, + + /** + * + * @ngdoc method + * @name $animate#removeClass + * @kind function + * @description Removes the provided className CSS class value from the provided element. + * When the function is called a promise is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will have the className value + * removed from it + * @param {string} className the CSS class which will be removed from the element + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise + */ + removeClass: function(element, className, options) { + return this.setClass(element, [], className, options); + }, + + $$removeClassImmediately: function(element, className, options) { + element = jqLite(element); + className = !isString(className) + ? (isArray(className) ? className.join(' ') : '') + : className; + forEach(element, function(element) { + jqLiteRemoveClass(element, className); + }); + applyStyles(element, options); + return asyncPromise(); + }, + + /** + * + * @ngdoc method + * @name $animate#setClass + * @kind function + * @description Adds and/or removes the given CSS classes to and from the element. + * When the function is called a promise is returned that will be resolved at a later time. + * @param {DOMElement} element the element which will have its CSS classes changed + * removed from it + * @param {string} add the CSS classes which will be added to the element + * @param {string} remove the CSS class which will be removed from the element + * @param {object=} options an optional collection of options that will be applied to the element. + * @return {Promise} the animation callback promise + */ + setClass: function(element, add, remove, options) { + var self = this; + var STORAGE_KEY = '$$animateClasses'; + var createdCache = false; + element = jqLite(element); + + var cache = element.data(STORAGE_KEY); + if (!cache) { + cache = { + classes: {}, + options: options + }; + createdCache = true; + } else if (options && cache.options) { + cache.options = angular.extend(cache.options || {}, options); + } + + var classes = cache.classes; + + add = isArray(add) ? add : add.split(' '); + remove = isArray(remove) ? remove : remove.split(' '); + cachedClassManipulation(classes, add, true); + cachedClassManipulation(classes, remove, false); + + if (createdCache) { + cache.promise = runAnimationPostDigest(function(done) { + var cache = element.data(STORAGE_KEY); + element.removeData(STORAGE_KEY); + + // in the event that the element is removed before postDigest + // is run then the cache will be undefined and there will be + // no need anymore to add or remove and of the element classes + if (cache) { + var classes = resolveElementClasses(element, cache.classes); + if (classes) { + self.$$setClassImmediately(element, classes[0], classes[1], cache.options); + } + } + + done(); + }); + element.data(STORAGE_KEY, cache); + } + + return cache.promise; + }, + + $$setClassImmediately: function(element, add, remove, options) { + add && this.$$addClassImmediately(element, add); + remove && this.$$removeClassImmediately(element, remove); + applyStyles(element, options); + return asyncPromise(); + }, + + enabled: noop, + cancel: noop + }; + }]; +}]; + +function $$AsyncCallbackProvider() { + this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { + return $$rAF.supported + ? function(fn) { return $$rAF(fn); } + : function(fn) { + return $timeout(fn, 0, false); + }; + }]; +} + +/* global stripHash: true */ + +/** + * ! This is a private undocumented service ! + * + * @name $browser + * @requires $log + * @description + * This object has two goals: + * + * - hide all the global state in the browser caused by the window object + * - abstract away all the browser specific features and inconsistencies + * + * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` + * service, which can be used for convenient testing of the application without the interaction with + * the real browser apis. + */ +/** + * @param {object} window The global window object. + * @param {object} document jQuery wrapped document. + * @param {object} $log window.console or an object with the same interface. + * @param {object} $sniffer $sniffer service + */ +function Browser(window, document, $log, $sniffer) { + var self = this, + rawDocument = document[0], + location = window.location, + history = window.history, + setTimeout = window.setTimeout, + clearTimeout = window.clearTimeout, + pendingDeferIds = {}; + + self.isMock = false; + + var outstandingRequestCount = 0; + var outstandingRequestCallbacks = []; + + // TODO(vojta): remove this temporary api + self.$$completeOutstandingRequest = completeOutstandingRequest; + self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; + + /** + * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, sliceArgs(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while (outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + + function getHash(url) { + var index = url.indexOf('#'); + return index === -1 ? '' : url.substr(index + 1); + } + + /** + * @private + * Note: this method is used only by scenario runner + * TODO(vojta): prefix this method with $$ ? + * @param {function()} callback Function that will be called when no outstanding request + */ + self.notifyWhenNoOutstandingRequests = function(callback) { + // force browser to execute all pollFns - this is needed so that cookies and other pollers fire + // at some deterministic time in respect to the test runner's actions. Leaving things up to the + // regular poller would result in flaky tests. + forEach(pollFns, function(pollFn) { pollFn(); }); + + if (outstandingRequestCount === 0) { + callback(); + } else { + outstandingRequestCallbacks.push(callback); + } + }; + + ////////////////////////////////////////////////////////////// + // Poll Watcher API + ////////////////////////////////////////////////////////////// + var pollFns = [], + pollTimeout; + + /** + * @name $browser#addPollFn + * + * @param {function()} fn Poll function to add + * + * @description + * Adds a function to the list of functions that poller periodically executes, + * and starts polling if not started yet. + * + * @returns {function()} the added function + */ + self.addPollFn = function(fn) { + if (isUndefined(pollTimeout)) startPoller(100, setTimeout); + pollFns.push(fn); + return fn; + }; + + /** + * @param {number} interval How often should browser call poll functions (ms) + * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. + * + * @description + * Configures the poller to run in the specified intervals, using the specified + * setTimeout fn and kicks it off. + */ + function startPoller(interval, setTimeout) { + (function check() { + forEach(pollFns, function(pollFn) { pollFn(); }); + pollTimeout = setTimeout(check, interval); + })(); + } + + ////////////////////////////////////////////////////////////// + // URL API + ////////////////////////////////////////////////////////////// + + var cachedState, lastHistoryState, + lastBrowserUrl = location.href, + baseElement = document.find('base'), + reloadLocation = null; + + cacheState(); + lastHistoryState = cachedState; + + /** + * @name $browser#url + * + * @description + * GETTER: + * Without any argument, this method just returns current value of location.href. + * + * SETTER: + * With at least one argument, this method sets url to new value. + * If html5 history api supported, pushState/replaceState is used, otherwise + * location.href/location.replace is used. + * Returns its own instance to allow chaining + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to change url. + * + * @param {string} url New url (when used as setter) + * @param {boolean=} replace Should new url replace current history record? + * @param {object=} state object to use with pushState/replaceState + */ + self.url = function(url, replace, state) { + // In modern browsers `history.state` is `null` by default; treating it separately + // from `undefined` would cause `$browser.url('/foo')` to change `history.state` + // to undefined via `pushState`. Instead, let's change `undefined` to `null` here. + if (isUndefined(state)) { + state = null; + } + + // Android Browser BFCache causes location, history reference to become stale. + if (location !== window.location) location = window.location; + if (history !== window.history) history = window.history; + + // setter + if (url) { + var sameState = lastHistoryState === state; + + // Don't change anything if previous and current URLs and states match. This also prevents + // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode. + // See https://github.com/angular/angular.js/commit/ffb2701 + if (lastBrowserUrl === url && (!$sniffer.history || sameState)) { + return self; + } + var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); + lastBrowserUrl = url; + lastHistoryState = state; + // Don't use history API if only the hash changed + // due to a bug in IE10/IE11 which leads + // to not firing a `hashchange` nor `popstate` event + // in some cases (see #9143). + if ($sniffer.history && (!sameBase || !sameState)) { + history[replace ? 'replaceState' : 'pushState'](state, '', url); + cacheState(); + // Do the assignment again so that those two variables are referentially identical. + lastHistoryState = cachedState; + } else { + if (!sameBase) { + reloadLocation = url; + } + if (replace) { + location.replace(url); + } else if (!sameBase) { + location.href = url; + } else { + location.hash = getHash(url); + } + } + return self; + // getter + } else { + // - reloadLocation is needed as browsers don't allow to read out + // the new location.href if a reload happened. + // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 + return reloadLocation || location.href.replace(/%27/g,"'"); + } + }; + + /** + * @name $browser#state + * + * @description + * This method is a getter. + * + * Return history.state or null if history.state is undefined. + * + * @returns {object} state + */ + self.state = function() { + return cachedState; + }; + + var urlChangeListeners = [], + urlChangeInit = false; + + function cacheStateAndFireUrlChange() { + cacheState(); + fireUrlChange(); + } + + function getCurrentState() { + try { + return history.state; + } catch (e) { + // MSIE can reportedly throw when there is no state (UNCONFIRMED). + } + } + + // This variable should be used *only* inside the cacheState function. + var lastCachedState = null; + function cacheState() { + // This should be the only place in $browser where `history.state` is read. + cachedState = getCurrentState(); + cachedState = isUndefined(cachedState) ? null : cachedState; + + // Prevent callbacks fo fire twice if both hashchange & popstate were fired. + if (equals(cachedState, lastCachedState)) { + cachedState = lastCachedState; + } + lastCachedState = cachedState; + } + + function fireUrlChange() { + if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) { + return; + } + + lastBrowserUrl = self.url(); + lastHistoryState = cachedState; + forEach(urlChangeListeners, function(listener) { + listener(self.url(), cachedState); + }); + } + + /** + * @name $browser#onUrlChange + * + * @description + * Register callback function that will be called, when url changes. + * + * It's only called when the url is changed from outside of angular: + * - user types different url into address bar + * - user clicks on history (forward/back) button + * - user clicks on a link + * + * It's not called when url is changed by $browser.url() method + * + * The listener gets called with new url as parameter. + * + * NOTE: this api is intended for use only by the $location service. Please use the + * {@link ng.$location $location service} to monitor url changes in angular apps. + * + * @param {function(string)} listener Listener function to be called when url changes. + * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. + */ + self.onUrlChange = function(callback) { + // TODO(vojta): refactor to use node's syntax for events + if (!urlChangeInit) { + // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) + // don't fire popstate when user change the address bar and don't fire hashchange when url + // changed by push/replaceState + + // html5 history api - popstate event + if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange); + // hashchange event + jqLite(window).on('hashchange', cacheStateAndFireUrlChange); + + urlChangeInit = true; + } + + urlChangeListeners.push(callback); + return callback; + }; + + /** + * Checks whether the url has changed outside of Angular. + * Needs to be exported to be able to check for changes that have been done in sync, + * as hashchange/popstate events fire in async. + */ + self.$$checkUrlChange = fireUrlChange; + + ////////////////////////////////////////////////////////////// + // Misc API + ////////////////////////////////////////////////////////////// + + /** + * @name $browser#baseHref + * + * @description + * Returns current <base href> + * (always relative - without domain) + * + * @returns {string} The current base href + */ + self.baseHref = function() { + var href = baseElement.attr('href'); + return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; + }; + + ////////////////////////////////////////////////////////////// + // Cookies API + ////////////////////////////////////////////////////////////// + var lastCookies = {}; + var lastCookieString = ''; + var cookiePath = self.baseHref(); + + function safeDecodeURIComponent(str) { + try { + return decodeURIComponent(str); + } catch (e) { + return str; + } + } + + /** + * @name $browser#cookies + * + * @param {string=} name Cookie name + * @param {string=} value Cookie value + * + * @description + * The cookies method provides a 'private' low level access to browser cookies. + * It is not meant to be used directly, use the $cookie service instead. + * + * The return values vary depending on the arguments that the method was called with as follows: + * + * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify + * it + * - cookies(name, value) -> set name to value, if value is undefined delete the cookie + * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that + * way) + * + * @returns {Object} Hash of all cookies (if called without any parameter) + */ + self.cookies = function(name, value) { + var cookieLength, cookieArray, cookie, i, index; + + if (name) { + if (value === undefined) { + rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath + + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; + } else { + if (isString(value)) { + cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) + + ';path=' + cookiePath).length + 1; + + // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: + // - 300 cookies + // - 20 cookies per unique domain + // - 4096 bytes per cookie + if (cookieLength > 4096) { + $log.warn("Cookie '" + name + + "' possibly not set or overflowed because it was too large (" + + cookieLength + " > 4096 bytes)!"); + } + } + } + } else { + if (rawDocument.cookie !== lastCookieString) { + lastCookieString = rawDocument.cookie; + cookieArray = lastCookieString.split("; "); + lastCookies = {}; + + for (i = 0; i < cookieArray.length; i++) { + cookie = cookieArray[i]; + index = cookie.indexOf('='); + if (index > 0) { //ignore nameless cookies + name = safeDecodeURIComponent(cookie.substring(0, index)); + // the first value that is seen for a cookie is the most + // specific one. values for the same cookie name that + // follow are for less specific paths. + if (lastCookies[name] === undefined) { + lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1)); + } + } + } + } + return lastCookies; + } + }; + + + /** + * @name $browser#defer + * @param {function()} fn A function, who's execution should be deferred. + * @param {number=} [delay=0] of milliseconds to defer the function execution. + * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. + * + * @description + * Executes a fn asynchronously via `setTimeout(fn, delay)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed + * via `$browser.defer.flush()`. + * + */ + self.defer = function(fn, delay) { + var timeoutId; + outstandingRequestCount++; + timeoutId = setTimeout(function() { + delete pendingDeferIds[timeoutId]; + completeOutstandingRequest(fn); + }, delay || 0); + pendingDeferIds[timeoutId] = true; + return timeoutId; + }; + + + /** + * @name $browser#defer.cancel + * + * @description + * Cancels a deferred task identified with `deferId`. + * + * @param {*} deferId Token returned by the `$browser.defer` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + self.defer.cancel = function(deferId) { + if (pendingDeferIds[deferId]) { + delete pendingDeferIds[deferId]; + clearTimeout(deferId); + completeOutstandingRequest(noop); + return true; + } + return false; + }; + +} + +function $BrowserProvider() { + this.$get = ['$window', '$log', '$sniffer', '$document', + function($window, $log, $sniffer, $document) { + return new Browser($window, $document, $log, $sniffer); + }]; +} + +/** + * @ngdoc service + * @name $cacheFactory + * + * @description + * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to + * them. + * + * ```js + * + * var cache = $cacheFactory('cacheId'); + * expect($cacheFactory.get('cacheId')).toBe(cache); + * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); + * + * cache.put("key", "value"); + * cache.put("another key", "another value"); + * + * // We've specified no options on creation + * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); + * + * ``` + * + * + * @param {string} cacheId Name or id of the newly created cache. + * @param {object=} options Options object that specifies the cache behavior. Properties: + * + * - `{number=}` `capacity` — turns the cache into LRU cache. + * + * @returns {object} Newly created cache object with the following set of methods: + * + * - `{object}` `info()` — Returns id, size, and options of cache. + * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns + * it. + * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. + * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. + * - `{void}` `removeAll()` — Removes all cached values. + * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. + * + * @example + <example module="cacheExampleApp"> + <file name="index.html"> + <div ng-controller="CacheController"> + <input ng-model="newCacheKey" placeholder="Key"> + <input ng-model="newCacheValue" placeholder="Value"> + <button ng-click="put(newCacheKey, newCacheValue)">Cache</button> + + <p ng-if="keys.length">Cached Values</p> + <div ng-repeat="key in keys"> + <span ng-bind="key"></span> + <span>: </span> + <b ng-bind="cache.get(key)"></b> + </div> + + <p>Cache Info</p> + <div ng-repeat="(key, value) in cache.info()"> + <span ng-bind="key"></span> + <span>: </span> + <b ng-bind="value"></b> + </div> + </div> + </file> + <file name="script.js"> + angular.module('cacheExampleApp', []). + controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { + $scope.keys = []; + $scope.cache = $cacheFactory('cacheId'); + $scope.put = function(key, value) { + if ($scope.cache.get(key) === undefined) { + $scope.keys.push(key); + } + $scope.cache.put(key, value === undefined ? null : value); + }; + }]); + </file> + <file name="style.css"> + p { + margin: 10px 0 3px; + } + </file> + </example> + */ +function $CacheFactoryProvider() { + + this.$get = function() { + var caches = {}; + + function cacheFactory(cacheId, options) { + if (cacheId in caches) { + throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); + } + + var size = 0, + stats = extend({}, options, {id: cacheId}), + data = {}, + capacity = (options && options.capacity) || Number.MAX_VALUE, + lruHash = {}, + freshEnd = null, + staleEnd = null; + + /** + * @ngdoc type + * @name $cacheFactory.Cache + * + * @description + * A cache object used to store and retrieve data, primarily used by + * {@link $http $http} and the {@link ng.directive:script script} directive to cache + * templates and other data. + * + * ```js + * angular.module('superCache') + * .factory('superCache', ['$cacheFactory', function($cacheFactory) { + * return $cacheFactory('super-cache'); + * }]); + * ``` + * + * Example test: + * + * ```js + * it('should behave like a cache', inject(function(superCache) { + * superCache.put('key', 'value'); + * superCache.put('another key', 'another value'); + * + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 2 + * }); + * + * superCache.remove('another key'); + * expect(superCache.get('another key')).toBeUndefined(); + * + * superCache.removeAll(); + * expect(superCache.info()).toEqual({ + * id: 'super-cache', + * size: 0 + * }); + * })); + * ``` + */ + return caches[cacheId] = { + + /** + * @ngdoc method + * @name $cacheFactory.Cache#put + * @kind function + * + * @description + * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be + * retrieved later, and incrementing the size of the cache if the key was not already + * present in the cache. If behaving like an LRU cache, it will also remove stale + * entries from the set. + * + * It will not insert undefined values into the cache. + * + * @param {string} key the key under which the cached data is stored. + * @param {*} value the value to store alongside the key. If it is undefined, the key + * will not be stored. + * @returns {*} the value stored. + */ + put: function(key, value) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); + + refresh(lruEntry); + } + + if (isUndefined(value)) return; + if (!(key in data)) size++; + data[key] = value; + + if (size > capacity) { + this.remove(staleEnd.key); + } + + return value; + }, + + /** + * @ngdoc method + * @name $cacheFactory.Cache#get + * @kind function + * + * @description + * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the data to be retrieved + * @returns {*} the value stored. + */ + get: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + refresh(lruEntry); + } + + return data[key]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#remove + * @kind function + * + * @description + * Removes an entry from the {@link $cacheFactory.Cache Cache} object. + * + * @param {string} key the key of the entry to be removed + */ + remove: function(key) { + if (capacity < Number.MAX_VALUE) { + var lruEntry = lruHash[key]; + + if (!lruEntry) return; + + if (lruEntry == freshEnd) freshEnd = lruEntry.p; + if (lruEntry == staleEnd) staleEnd = lruEntry.n; + link(lruEntry.n,lruEntry.p); + + delete lruHash[key]; + } + + delete data[key]; + size--; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#removeAll + * @kind function + * + * @description + * Clears the cache object of any entries. + */ + removeAll: function() { + data = {}; + size = 0; + lruHash = {}; + freshEnd = staleEnd = null; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#destroy + * @kind function + * + * @description + * Destroys the {@link $cacheFactory.Cache Cache} object entirely, + * removing it from the {@link $cacheFactory $cacheFactory} set. + */ + destroy: function() { + data = null; + stats = null; + lruHash = null; + delete caches[cacheId]; + }, + + + /** + * @ngdoc method + * @name $cacheFactory.Cache#info + * @kind function + * + * @description + * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. + * + * @returns {object} an object with the following properties: + * <ul> + * <li>**id**: the id of the cache instance</li> + * <li>**size**: the number of entries kept in the cache instance</li> + * <li>**...**: any additional properties from the options object when creating the + * cache.</li> + * </ul> + */ + info: function() { + return extend({}, stats, {size: size}); + } + }; + + + /** + * makes the `entry` the freshEnd of the LRU linked list + */ + function refresh(entry) { + if (entry != freshEnd) { + if (!staleEnd) { + staleEnd = entry; + } else if (staleEnd == entry) { + staleEnd = entry.n; + } + + link(entry.n, entry.p); + link(entry, freshEnd); + freshEnd = entry; + freshEnd.n = null; + } + } + + + /** + * bidirectionally links two entries of the LRU linked list + */ + function link(nextEntry, prevEntry) { + if (nextEntry != prevEntry) { + if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify + if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify + } + } + } + + + /** + * @ngdoc method + * @name $cacheFactory#info + * + * @description + * Get information about all the caches that have been created + * + * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` + */ + cacheFactory.info = function() { + var info = {}; + forEach(caches, function(cache, cacheId) { + info[cacheId] = cache.info(); + }); + return info; + }; + + + /** + * @ngdoc method + * @name $cacheFactory#get + * + * @description + * Get access to a cache object by the `cacheId` used when it was created. + * + * @param {string} cacheId Name or id of a cache to access. + * @returns {object} Cache object identified by the cacheId or undefined if no such cache. + */ + cacheFactory.get = function(cacheId) { + return caches[cacheId]; + }; + + + return cacheFactory; + }; +} + +/** + * @ngdoc service + * @name $templateCache + * + * @description + * The first time a template is used, it is loaded in the template cache for quick retrieval. You + * can load templates directly into the cache in a `script` tag, or by consuming the + * `$templateCache` service directly. + * + * Adding via the `script` tag: + * + * ```html + * <script type="text/ng-template" id="templateId.html"> + * <p>This is the content of the template</p> + * </script> + * ``` + * + * **Note:** the `script` tag containing the template does not need to be included in the `head` of + * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, + * element with ng-app attribute), otherwise the template will be ignored. + * + * Adding via the `$templateCache` service: + * + * ```js + * var myApp = angular.module('myApp', []); + * myApp.run(function($templateCache) { + * $templateCache.put('templateId.html', 'This is the content of the template'); + * }); + * ``` + * + * To retrieve the template later, simply use it in your HTML: + * ```html + * <div ng-include=" 'templateId.html' "></div> + * ``` + * + * or get it via Javascript: + * ```js + * $templateCache.get('templateId.html') + * ``` + * + * See {@link ng.$cacheFactory $cacheFactory}. + * + */ +function $TemplateCacheProvider() { + this.$get = ['$cacheFactory', function($cacheFactory) { + return $cacheFactory('templates'); + }]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! + * + * DOM-related variables: + * + * - "node" - DOM Node + * - "element" - DOM Element or Node + * - "$node" or "$element" - jqLite-wrapped node or element + * + * + * Compiler related stuff: + * + * - "linkFn" - linking fn of a single directive + * - "nodeLinkFn" - function that aggregates all linking fns for a particular node + * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node + * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) + */ + + +/** + * @ngdoc service + * @name $compile + * @kind function + * + * @description + * Compiles an HTML string or DOM into a template and produces a template function, which + * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. + * + * The compilation is a process of walking the DOM tree and matching DOM elements to + * {@link ng.$compileProvider#directive directives}. + * + * <div class="alert alert-warning"> + * **Note:** This document is an in-depth reference of all directive options. + * For a gentle introduction to directives with examples of common use cases, + * see the {@link guide/directive directive guide}. + * </div> + * + * ## Comprehensive Directive API + * + * There are many different options for a directive. + * + * The difference resides in the return value of the factory function. + * You can either return a "Directive Definition Object" (see below) that defines the directive properties, + * or just the `postLink` function (all other properties will have the default values). + * + * <div class="alert alert-success"> + * **Best Practice:** It's recommended to use the "directive definition object" form. + * </div> + * + * Here's an example directive declared with a Directive Definition Object: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * priority: 0, + * template: '<div></div>', // or // function(tElement, tAttrs) { ... }, + * // or + * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, + * transclude: false, + * restrict: 'A', + * templateNamespace: 'html', + * scope: false, + * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, + * controllerAs: 'stringAlias', + * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], + * compile: function compile(tElement, tAttrs, transclude) { + * return { + * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * post: function postLink(scope, iElement, iAttrs, controller) { ... } + * } + * // or + * // return function postLink( ... ) { ... } + * }, + * // or + * // link: { + * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, + * // post: function postLink(scope, iElement, iAttrs, controller) { ... } + * // } + * // or + * // link: function postLink( ... ) { ... } + * }; + * return directiveDefinitionObject; + * }); + * ``` + * + * <div class="alert alert-warning"> + * **Note:** Any unspecified options will use the default value. You can see the default values below. + * </div> + * + * Therefore the above can be simplified as: + * + * ```js + * var myModule = angular.module(...); + * + * myModule.directive('directiveName', function factory(injectables) { + * var directiveDefinitionObject = { + * link: function postLink(scope, iElement, iAttrs) { ... } + * }; + * return directiveDefinitionObject; + * // or + * // return function postLink(scope, iElement, iAttrs) { ... } + * }); + * ``` + * + * + * + * ### Directive Definition Object + * + * The directive definition object provides instructions to the {@link ng.$compile + * compiler}. The attributes are: + * + * #### `multiElement` + * When this property is set to true, the HTML compiler will collect DOM nodes between + * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them + * together as the directive elements. It is recommended that this feature be used on directives + * which are not strictly behavioural (such as {@link ngClick}), and which + * do not manipulate or replace child nodes (such as {@link ngInclude}). + * + * #### `priority` + * When there are multiple directives defined on a single DOM element, sometimes it + * is necessary to specify the order in which the directives are applied. The `priority` is used + * to sort the directives before their `compile` functions get called. Priority is defined as a + * number. Directives with greater numerical `priority` are compiled first. Pre-link functions + * are also run in priority order, but post-link functions are run in reverse order. The order + * of directives with the same priority is undefined. The default priority is `0`. + * + * #### `terminal` + * If set to true then the current `priority` will be the last set of directives + * which will execute (any directives at the current priority will still execute + * as the order of execution on same `priority` is undefined). Note that expressions + * and other directives used in the directive's template will also be excluded from execution. + * + * #### `scope` + * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the + * same element request a new scope, only one new scope is created. The new scope rule does not + * apply for the root of the template since the root of the template always gets a new scope. + * + * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from + * normal scope in that it does not prototypically inherit from the parent scope. This is useful + * when creating reusable components, which should not accidentally read or modify data in the + * parent scope. + * + * The 'isolate' scope takes an object hash which defines a set of local scope properties + * derived from the parent scope. These local properties are useful for aliasing values for + * templates. Locals definition is a hash of local scope property to its source: + * + * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is + * always a string since DOM attributes are strings. If no `attr` name is specified then the + * attribute name is assumed to be the same as the local name. + * Given `<widget my-attr="hello {{name}}">` and widget definition + * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect + * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the + * `localName` property on the widget scope. The `name` is read from the parent scope (not + * component scope). + * + * * `=` or `=attr` - set up bi-directional binding between a local scope property and the + * parent scope property of name defined via the value of the `attr` attribute. If no `attr` + * name is specified then the attribute name is assumed to be the same as the local name. + * Given `<widget my-attr="parentModel">` and widget definition of + * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the + * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected + * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent + * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You + * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If + * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use + * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional). + * + * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. + * If no `attr` name is specified then the attribute name is assumed to be the same as the + * local name. Given `<widget my-attr="count = count + value">` and widget definition of + * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to + * a function wrapper for the `count = count + value` expression. Often it's desirable to + * pass data from the isolated scope via an expression to the parent scope, this can be + * done by passing a map of local variable names and values into the expression wrapper fn. + * For example, if the expression is `increment(amount)` then we can specify the amount value + * by calling the `localFn` as `localFn({amount: 22})`. + * + * + * #### `bindToController` + * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will + * allow a component to have its properties bound to the controller, rather than to scope. When the controller + * is instantiated, the initial values of the isolate scope bindings are already available. + * + * #### `controller` + * Controller constructor function. The controller is instantiated before the + * pre-linking phase and it is shared with other directives (see + * `require` attribute). This allows the directives to communicate with each other and augment + * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: + * + * * `$scope` - Current scope associated with the element + * * `$element` - Current element + * * `$attrs` - Current attributes object for the element + * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: + * `function([scope], cloneLinkingFn, futureParentElement)`. + * * `scope`: optional argument to override the scope. + * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content. + * * `futureParentElement`: + * * defines the parent to which the `cloneLinkingFn` will add the cloned elements. + * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`. + * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements) + * and when the `cloneLinkinFn` is passed, + * as those elements need to created and cloned in a special way when they are defined outside their + * usual containers (e.g. like `<svg>`). + * * See also the `directive.templateNamespace` property. + * + * + * #### `require` + * Require another directive and inject its controller as the fourth argument to the linking function. The + * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the + * injected argument will be an array in corresponding order. If no such directive can be + * found, or if the directive does not have a controller, then an error is raised (unless no link function + * is specified, in which case error checking is skipped). The name can be prefixed with: + * + * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. + * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. + * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. + * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found. + * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass + * `null` to the `link` fn if not found. + * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass + * `null` to the `link` fn if not found. + * + * + * #### `controllerAs` + * Controller alias at the directive scope. An alias for the controller so it + * can be referenced at the directive template. The directive needs to define a scope for this + * configuration to be used. Useful in the case when directive is used as component. + * + * + * #### `restrict` + * String of subset of `EACM` which restricts the directive to a specific directive + * declaration style. If omitted, the defaults (elements and attributes) are used. + * + * * `E` - Element name (default): `<my-directive></my-directive>` + * * `A` - Attribute (default): `<div my-directive="exp"></div>` + * * `C` - Class: `<div class="my-directive: exp;"></div>` + * * `M` - Comment: `<!-- directive: my-directive exp -->` + * + * + * #### `templateNamespace` + * String representing the document type used by the markup in the template. + * AngularJS needs this information as those elements need to be created and cloned + * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`. + * + * * `html` - All root nodes in the template are HTML. Root nodes may also be + * top-level elements such as `<svg>` or `<math>`. + * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`). + * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`). + * + * If no `templateNamespace` is specified, then the namespace is considered to be `html`. + * + * #### `template` + * HTML markup that may: + * * Replace the contents of the directive's element (default). + * * Replace the directive's element itself (if `replace` is true - DEPRECATED). + * * Wrap the contents of the directive's element (if `transclude` is true). + * + * Value may be: + * + * * A string. For example `<div red-on-hover>{{delete_str}}</div>`. + * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` + * function api below) and returns a string value. + * + * + * #### `templateUrl` + * This is similar to `template` but the template is loaded from the specified URL, asynchronously. + * + * Because template loading is asynchronous the compiler will suspend compilation of directives on that element + * for later when the template has been resolved. In the meantime it will continue to compile and link + * sibling and parent elements as though this element had not contained any directives. + * + * The compiler does not suspend the entire compilation to wait for templates to be loaded because this + * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the + * case when only one deeply nested directive has `templateUrl`. + * + * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache} + * + * You can specify `templateUrl` as a string representing the URL or as a function which takes two + * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns + * a string value representing the url. In either case, the template URL is passed through {@link + * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. + * + * + * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0) + * specify what the template should replace. Defaults to `false`. + * + * * `true` - the template will replace the directive's element. + * * `false` - the template will replace the contents of the directive's element. + * + * The replacement process migrates all of the attributes / classes from the old element to the new + * one. See the {@link guide/directive#template-expanding-directive + * Directives Guide} for an example. + * + * There are very few scenarios where element replacement is required for the application function, + * the main one being reusable custom components that are used within SVG contexts + * (because SVG doesn't work with custom elements in the DOM tree). + * + * #### `transclude` + * Extract the contents of the element where the directive appears and make it available to the directive. + * The contents are compiled and provided to the directive as a **transclusion function**. See the + * {@link $compile#transclusion Transclusion} section below. + * + * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the + * directive's element or the entire element: + * + * * `true` - transclude the content (i.e. the child nodes) of the directive's element. + * * `'element'` - transclude the whole of the directive's element including any directives on this + * element that defined at a lower priority than this directive. When used, the `template` + * property is ignored. + * + * + * #### `compile` + * + * ```js + * function compile(tElement, tAttrs, transclude) { ... } + * ``` + * + * The compile function deals with transforming the template DOM. Since most directives do not do + * template transformation, it is not used often. The compile function takes the following arguments: + * + * * `tElement` - template element - The element where the directive has been declared. It is + * safe to do template transformation on the element and child elements only. + * + * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared + * between all directive compile functions. + * + * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` + * + * <div class="alert alert-warning"> + * **Note:** The template instance and the link instance may be different objects if the template has + * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that + * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration + * should be done in a linking function rather than in a compile function. + * </div> + + * <div class="alert alert-warning"> + * **Note:** The compile function cannot handle directives that recursively use themselves in their + * own templates or compile functions. Compiling these directives results in an infinite loop and a + * stack overflow errors. + * + * This can be avoided by manually using $compile in the postLink function to imperatively compile + * a directive's template instead of relying on automatic template compilation via `template` or + * `templateUrl` declaration or manual compilation inside the compile function. + * </div> + * + * <div class="alert alert-error"> + * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it + * e.g. does not know about the right outer scope. Please use the transclude function that is passed + * to the link function instead. + * </div> + + * A compile function can have a return value which can be either a function or an object. + * + * * returning a (post-link) function - is equivalent to registering the linking function via the + * `link` property of the config object when the compile function is empty. + * + * * returning an object with function(s) registered via `pre` and `post` properties - allows you to + * control when a linking function should be called during the linking phase. See info about + * pre-linking and post-linking functions below. + * + * + * #### `link` + * This property is used only if the `compile` property is not defined. + * + * ```js + * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } + * ``` + * + * The link function is responsible for registering DOM listeners as well as updating the DOM. It is + * executed after the template has been cloned. This is where most of the directive logic will be + * put. + * + * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the + * directive for registering {@link ng.$rootScope.Scope#$watch watches}. + * + * * `iElement` - instance element - The element where the directive is to be used. It is safe to + * manipulate the children of the element only in `postLink` function since the children have + * already been linked. + * + * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared + * between all directive linking functions. + * + * * `controller` - a controller instance - A controller instance if at least one directive on the + * element defines a controller. The controller is shared among all the directives, which allows + * the directives to use the controllers as a communication channel. + * + * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. + * This is the same as the `$transclude` + * parameter of directive controllers, see there for details. + * `function([scope], cloneLinkingFn, futureParentElement)`. + * + * #### Pre-linking function + * + * Executed before the child elements are linked. Not safe to do DOM transformation since the + * compiler linking function will fail to locate the correct elements for linking. + * + * #### Post-linking function + * + * Executed after the child elements are linked. + * + * Note that child elements that contain `templateUrl` directives will not have been compiled + * and linked since they are waiting for their template to load asynchronously and their own + * compilation and linking has been suspended until that occurs. + * + * It is safe to do DOM transformation in the post-linking function on elements that are not waiting + * for their async templates to be resolved. + * + * + * ### Transclusion + * + * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and + * copying them to another part of the DOM, while maintaining their connection to the original AngularJS + * scope from where they were taken. + * + * Transclusion is used (often with {@link ngTransclude}) to insert the + * original contents of a directive's element into a specified place in the template of the directive. + * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded + * content has access to the properties on the scope from which it was taken, even if the directive + * has isolated scope. + * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}. + * + * This makes it possible for the widget to have private state for its template, while the transcluded + * content has access to its originating scope. + * + * <div class="alert alert-warning"> + * **Note:** When testing an element transclude directive you must not place the directive at the root of the + * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives + * Testing Transclusion Directives}. + * </div> + * + * #### Transclusion Functions + * + * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion + * function** to the directive's `link` function and `controller`. This transclusion function is a special + * **linking function** that will return the compiled contents linked to a new transclusion scope. + * + * <div class="alert alert-info"> + * If you are just using {@link ngTransclude} then you don't need to worry about this function, since + * ngTransclude will deal with it for us. + * </div> + * + * If you want to manually control the insertion and removal of the transcluded content in your directive + * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery + * object that contains the compiled DOM, which is linked to the correct transclusion scope. + * + * When you call a transclusion function you can pass in a **clone attach function**. This function accepts + * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded + * content and the `scope` is the newly created transclusion scope, to which the clone is bound. + * + * <div class="alert alert-info"> + * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function + * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope. + * </div> + * + * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone + * attach function**: + * + * ```js + * var transcludedContent, transclusionScope; + * + * $transclude(function(clone, scope) { + * element.append(clone); + * transcludedContent = clone; + * transclusionScope = scope; + * }); + * ``` + * + * Later, if you want to remove the transcluded content from your DOM then you should also destroy the + * associated transclusion scope: + * + * ```js + * transcludedContent.remove(); + * transclusionScope.$destroy(); + * ``` + * + * <div class="alert alert-info"> + * **Best Practice**: if you intend to add and remove transcluded content manually in your directive + * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it), + * then you are also responsible for calling `$destroy` on the transclusion scope. + * </div> + * + * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat} + * automatically destroy their transluded clones as necessary so you do not need to worry about this if + * you are simply using {@link ngTransclude} to inject the transclusion into your directive. + * + * + * #### Transclusion Scopes + * + * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion + * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed + * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it + * was taken. + * + * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look + * like this: + * + * ```html + * <div ng-app> + * <div isolate> + * <div transclusion> + * </div> + * </div> + * </div> + * ``` + * + * The `$parent` scope hierarchy will look like this: + * + * ``` + * - $rootScope + * - isolate + * - transclusion + * ``` + * + * but the scopes will inherit prototypically from different scopes to their `$parent`. + * + * ``` + * - $rootScope + * - transclusion + * - isolate + * ``` + * + * + * ### Attributes + * + * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the + * `link()` or `compile()` functions. It has a variety of uses. + * + * accessing *Normalized attribute names:* + * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. + * the attributes object allows for normalized access to + * the attributes. + * + * * *Directive inter-communication:* All directives share the same instance of the attributes + * object which allows the directives to use the attributes object as inter directive + * communication. + * + * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object + * allowing other directives to read the interpolated value. + * + * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes + * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also + * the only way to easily get the actual value because during the linking phase the interpolation + * hasn't been evaluated yet and so the value is at this time set to `undefined`. + * + * ```js + * function linkingFn(scope, elm, attrs, ctrl) { + * // get the attribute value + * console.log(attrs.ngModel); + * + * // change the attribute + * attrs.$set('ngModel', 'new value'); + * + * // observe changes to interpolated attribute + * attrs.$observe('ngModel', function(value) { + * console.log('ngModel has changed value to ' + value); + * }); + * } + * ``` + * + * ## Example + * + * <div class="alert alert-warning"> + * **Note**: Typically directives are registered with `module.directive`. The example below is + * to illustrate how `$compile` works. + * </div> + * + <example module="compileExample"> + <file name="index.html"> + <script> + angular.module('compileExample', [], function($compileProvider) { + // configure new 'compile' directive by passing a directive + // factory function. The factory function injects the '$compile' + $compileProvider.directive('compile', function($compile) { + // directive factory creates a link function + return function(scope, element, attrs) { + scope.$watch( + function(scope) { + // watch the 'compile' expression for changes + return scope.$eval(attrs.compile); + }, + function(value) { + // when the 'compile' expression changes + // assign it into the current DOM + element.html(value); + + // compile the new DOM and link it to the current + // scope. + // NOTE: we only compile .childNodes so that + // we don't get into infinite loop compiling ourselves + $compile(element.contents())(scope); + } + ); + }; + }); + }) + .controller('GreeterController', ['$scope', function($scope) { + $scope.name = 'Angular'; + $scope.html = 'Hello {{name}}'; + }]); + </script> + <div ng-controller="GreeterController"> + <input ng-model="name"> <br> + <textarea ng-model="html"></textarea> <br> + <div compile="html"></div> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should auto compile', function() { + var textarea = $('textarea'); + var output = $('div[compile]'); + // The initial state reads 'Hello Angular'. + expect(output.getText()).toBe('Hello Angular'); + textarea.clear(); + textarea.sendKeys('{{name}}!'); + expect(output.getText()).toBe('Angular!'); + }); + </file> + </example> + + * + * + * @param {string|DOMElement} element Element or HTML string to compile into a template function. + * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED. + * + * <div class="alert alert-error"> + * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it + * e.g. will not use the right outer scope. Please pass the transclude function as a + * `parentBoundTranscludeFn` to the link function instead. + * </div> + * + * @param {number} maxPriority only apply directives lower than given priority (Only effects the + * root element(s), not their children) + * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template + * (a DOM element/tree) to a scope. Where: + * + * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. + * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the + * `template` and call the `cloneAttachFn` function allowing the caller to attach the + * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is + * called as: <br> `cloneAttachFn(clonedElement, scope)` where: + * + * * `clonedElement` - is a clone of the original `element` passed into the compiler. + * * `scope` - is the current scope with which the linking function is working with. + * + * * `options` - An optional object hash with linking options. If `options` is provided, then the following + * keys may be used to control linking behavior: + * + * * `parentBoundTranscludeFn` - the transclude function made available to + * directives; if given, it will be passed through to the link functions of + * directives found in `element` during compilation. + * * `transcludeControllers` - an object hash with keys that map controller names + * to controller instances; if given, it will make the controllers + * available to directives. + * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add + * the cloned elements; only needed for transcludes that are allowed to contain non html + * elements (e.g. SVG elements). See also the directive.controller property. + * + * Calling the linking function returns the element of the template. It is either the original + * element passed in, or the clone of the element if the `cloneAttachFn` is provided. + * + * After linking the view is not updated until after a call to $digest which typically is done by + * Angular automatically. + * + * If you need access to the bound view, there are two ways to do it: + * + * - If you are not asking the linking function to clone the template, create the DOM element(s) + * before you send them to the compiler and keep this reference around. + * ```js + * var element = $compile('<p>{{total}}</p>')(scope); + * ``` + * + * - if on the other hand, you need the element to be cloned, the view reference from the original + * example would not point to the clone, but rather to the original template that was cloned. In + * this case, you can access the clone via the cloneAttachFn: + * ```js + * var templateElement = angular.element('<p>{{total}}</p>'), + * scope = ....; + * + * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { + * //attach the clone to DOM document at the right place + * }); + * + * //now we have reference to the cloned DOM via `clonedElement` + * ``` + * + * + * For information on how the compiler works, see the + * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. + */ + +var $compileMinErr = minErr('$compile'); + +/** + * @ngdoc provider + * @name $compileProvider + * + * @description + */ +$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; +function $CompileProvider($provide, $$sanitizeUriProvider) { + var hasDirectives = {}, + Suffix = 'Directive', + COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/, + CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/, + ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'), + REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/; + + // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes + // The assumption is that future DOM event attribute names will begin with + // 'on' and be composed of only English letters. + var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; + + function parseIsolateBindings(scope, directiveName) { + var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/; + + var bindings = {}; + + forEach(scope, function(definition, scopeName) { + var match = definition.match(LOCAL_REGEXP); + + if (!match) { + throw $compileMinErr('iscp', + "Invalid isolate scope definition for directive '{0}'." + + " Definition: {... {1}: '{2}' ...}", + directiveName, scopeName, definition); + } + + bindings[scopeName] = { + mode: match[1][0], + collection: match[2] === '*', + optional: match[3] === '?', + attrName: match[4] || scopeName + }; + }); + + return bindings; + } + + /** + * @ngdoc method + * @name $compileProvider#directive + * @kind function + * + * @description + * Register a new directive with the compiler. + * + * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which + * will match as <code>ng-bind</code>), or an object map of directives where the keys are the + * names and the values are the factories. + * @param {Function|Array} directiveFactory An injectable directive factory function. See + * {@link guide/directive} for more info. + * @returns {ng.$compileProvider} Self for chaining. + */ + this.directive = function registerDirective(name, directiveFactory) { + assertNotHasOwnProperty(name, 'directive'); + if (isString(name)) { + assertArg(directiveFactory, 'directiveFactory'); + if (!hasDirectives.hasOwnProperty(name)) { + hasDirectives[name] = []; + $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', + function($injector, $exceptionHandler) { + var directives = []; + forEach(hasDirectives[name], function(directiveFactory, index) { + try { + var directive = $injector.invoke(directiveFactory); + if (isFunction(directive)) { + directive = { compile: valueFn(directive) }; + } else if (!directive.compile && directive.link) { + directive.compile = valueFn(directive.link); + } + directive.priority = directive.priority || 0; + directive.index = index; + directive.name = directive.name || name; + directive.require = directive.require || (directive.controller && directive.name); + directive.restrict = directive.restrict || 'EA'; + if (isObject(directive.scope)) { + directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name); + } + directives.push(directive); + } catch (e) { + $exceptionHandler(e); + } + }); + return directives; + }]); + } + hasDirectives[name].push(directiveFactory); + } else { + forEach(name, reverseParams(registerDirective)); + } + return this; + }; + + + /** + * @ngdoc method + * @name $compileProvider#aHrefSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at preventing XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); + } + }; + + + /** + * @ngdoc method + * @name $compileProvider#imgSrcSanitizationWhitelist + * @kind function + * + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); + return this; + } else { + return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); + } + }; + + /** + * @ngdoc method + * @name $compileProvider#debugInfoEnabled + * + * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the + * current debugInfoEnabled state + * @returns {*} current value if used as getter or itself (chaining) if used as setter + * + * @kind function + * + * @description + * Call this method to enable/disable various debug runtime information in the compiler such as adding + * binding information and a reference to the current scope on to DOM elements. + * If enabled, the compiler will add the following to DOM elements that have been bound to the scope + * * `ng-binding` CSS class + * * `$binding` data property containing an array of the binding expressions + * + * You may want to disable this in production for a significant performance boost. See + * {@link guide/production#disabling-debug-data Disabling Debug Data} for more. + * + * The default value is true. + */ + var debugInfoEnabled = true; + this.debugInfoEnabled = function(enabled) { + if (isDefined(enabled)) { + debugInfoEnabled = enabled; + return this; + } + return debugInfoEnabled; + }; + + this.$get = [ + '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse', + '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri', + function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse, + $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) { + + var Attributes = function(element, attributesToCopy) { + if (attributesToCopy) { + var keys = Object.keys(attributesToCopy); + var i, l, key; + + for (i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + this[key] = attributesToCopy[key]; + } + } else { + this.$attr = {}; + } + + this.$$element = element; + }; + + Attributes.prototype = { + /** + * @ngdoc method + * @name $compile.directive.Attributes#$normalize + * @kind function + * + * @description + * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or + * `data-`) to its normalized, camelCase form. + * + * Also there is special case for Moz prefix starting with upper case letter. + * + * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} + * + * @param {string} name Name to normalize + */ + $normalize: directiveNormalize, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$addClass + * @kind function + * + * @description + * Adds the CSS class value specified by the classVal parameter to the element. If animations + * are enabled then an animation will be triggered for the class addition. + * + * @param {string} classVal The className value that will be added to the element + */ + $addClass: function(classVal) { + if (classVal && classVal.length > 0) { + $animate.addClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$removeClass + * @kind function + * + * @description + * Removes the CSS class value specified by the classVal parameter from the element. If + * animations are enabled then an animation will be triggered for the class removal. + * + * @param {string} classVal The className value that will be removed from the element + */ + $removeClass: function(classVal) { + if (classVal && classVal.length > 0) { + $animate.removeClass(this.$$element, classVal); + } + }, + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$updateClass + * @kind function + * + * @description + * Adds and removes the appropriate CSS class values to the element based on the difference + * between the new and old CSS class values (specified as newClasses and oldClasses). + * + * @param {string} newClasses The current CSS className value + * @param {string} oldClasses The former CSS className value + */ + $updateClass: function(newClasses, oldClasses) { + var toAdd = tokenDifference(newClasses, oldClasses); + if (toAdd && toAdd.length) { + $animate.addClass(this.$$element, toAdd); + } + + var toRemove = tokenDifference(oldClasses, newClasses); + if (toRemove && toRemove.length) { + $animate.removeClass(this.$$element, toRemove); + } + }, + + /** + * Set a normalized attribute on the element in a way such that all directives + * can share the attribute. This function properly handles boolean attributes. + * @param {string} key Normalized key. (ie ngAttribute) + * @param {string|boolean} value The value to set. If `null` attribute will be deleted. + * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. + * Defaults to true. + * @param {string=} attrName Optional none normalized name. Defaults to key. + */ + $set: function(key, value, writeAttr, attrName) { + // TODO: decide whether or not to throw an error if "class" + //is set through this function since it may cause $updateClass to + //become unstable. + + var node = this.$$element[0], + booleanKey = getBooleanAttrName(node, key), + aliasedKey = getAliasedAttrName(node, key), + observer = key, + nodeName; + + if (booleanKey) { + this.$$element.prop(key, value); + attrName = booleanKey; + } else if (aliasedKey) { + this[aliasedKey] = value; + observer = aliasedKey; + } + + this[key] = value; + + // translate normalized key to actual key + if (attrName) { + this.$attr[key] = attrName; + } else { + attrName = this.$attr[key]; + if (!attrName) { + this.$attr[key] = attrName = snake_case(key, '-'); + } + } + + nodeName = nodeName_(this.$$element); + + if ((nodeName === 'a' && key === 'href') || + (nodeName === 'img' && key === 'src')) { + // sanitize a[href] and img[src] values + this[key] = value = $$sanitizeUri(value, key === 'src'); + } else if (nodeName === 'img' && key === 'srcset') { + // sanitize img[srcset] values + var result = ""; + + // first check if there are spaces because it's not the same pattern + var trimmedSrcset = trim(value); + // ( 999x ,| 999w ,| ,|, ) + var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/; + var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/; + + // split srcset into tuple of uri and descriptor except for the last item + var rawUris = trimmedSrcset.split(pattern); + + // for each tuples + var nbrUrisWith2parts = Math.floor(rawUris.length / 2); + for (var i = 0; i < nbrUrisWith2parts; i++) { + var innerIdx = i * 2; + // sanitize the uri + result += $$sanitizeUri(trim(rawUris[innerIdx]), true); + // add the descriptor + result += (" " + trim(rawUris[innerIdx + 1])); + } + + // split the last item into uri and descriptor + var lastTuple = trim(rawUris[i * 2]).split(/\s/); + + // sanitize the last uri + result += $$sanitizeUri(trim(lastTuple[0]), true); + + // and add the last descriptor if any + if (lastTuple.length === 2) { + result += (" " + trim(lastTuple[1])); + } + this[key] = value = result; + } + + if (writeAttr !== false) { + if (value === null || value === undefined) { + this.$$element.removeAttr(attrName); + } else { + this.$$element.attr(attrName, value); + } + } + + // fire observers + var $$observers = this.$$observers; + $$observers && forEach($$observers[observer], function(fn) { + try { + fn(value); + } catch (e) { + $exceptionHandler(e); + } + }); + }, + + + /** + * @ngdoc method + * @name $compile.directive.Attributes#$observe + * @kind function + * + * @description + * Observes an interpolated attribute. + * + * The observer function will be invoked once during the next `$digest` following + * compilation. The observer is then invoked whenever the interpolated value + * changes. + * + * @param {string} key Normalized key. (ie ngAttribute) . + * @param {function(interpolatedValue)} fn Function that will be called whenever + the interpolated value of the attribute changes. + * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info. + * @returns {function()} Returns a deregistration function for this observer. + */ + $observe: function(key, fn) { + var attrs = this, + $$observers = (attrs.$$observers || (attrs.$$observers = createMap())), + listeners = ($$observers[key] || ($$observers[key] = [])); + + listeners.push(fn); + $rootScope.$evalAsync(function() { + if (!listeners.$$inter && attrs.hasOwnProperty(key)) { + // no one registered attribute interpolation function, so lets call it manually + fn(attrs[key]); + } + }); + + return function() { + arrayRemove(listeners, fn); + }; + } + }; + + + function safeAddClass($element, className) { + try { + $element.addClass(className); + } catch (e) { + // ignore, since it means that we are trying to set class on + // SVG element, where class name is read-only. + } + } + + + var startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') + ? identity + : function denormalizeTemplate(template) { + return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); + }, + NG_ATTR_BINDING = /^ngAttr[A-Z]/; + + compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) { + var bindings = $element.data('$binding') || []; + + if (isArray(binding)) { + bindings = bindings.concat(binding); + } else { + bindings.push(binding); + } + + $element.data('$binding', bindings); + } : noop; + + compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) { + safeAddClass($element, 'ng-binding'); + } : noop; + + compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) { + var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope'; + $element.data(dataName, scope); + } : noop; + + compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) { + safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope'); + } : noop; + + return compile; + + //================================ + + function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, + previousCompileContext) { + if (!($compileNodes instanceof jqLite)) { + // jquery always rewraps, whereas we need to preserve the original selector so that we can + // modify it. + $compileNodes = jqLite($compileNodes); + } + // We can not compile top level text elements since text nodes can be merged and we will + // not be able to attach scope data to them, so we will wrap them in <span> + forEach($compileNodes, function(node, index) { + if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) { + $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0]; + } + }); + var compositeLinkFn = + compileNodes($compileNodes, transcludeFn, $compileNodes, + maxPriority, ignoreDirective, previousCompileContext); + compile.$$addScopeClass($compileNodes); + var namespace = null; + return function publicLinkFn(scope, cloneConnectFn, options) { + assertArg(scope, 'scope'); + + options = options || {}; + var parentBoundTranscludeFn = options.parentBoundTranscludeFn, + transcludeControllers = options.transcludeControllers, + futureParentElement = options.futureParentElement; + + // When `parentBoundTranscludeFn` is passed, it is a + // `controllersBoundTransclude` function (it was previously passed + // as `transclude` to directive.link) so we must unwrap it to get + // its `boundTranscludeFn` + if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) { + parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude; + } + + if (!namespace) { + namespace = detectNamespaceForChildElements(futureParentElement); + } + var $linkNode; + if (namespace !== 'html') { + // When using a directive with replace:true and templateUrl the $compileNodes + // (or a child element inside of them) + // might change, so we need to recreate the namespace adapted compileNodes + // for call to the link function. + // Note: This will already clone the nodes... + $linkNode = jqLite( + wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html()) + ); + } else if (cloneConnectFn) { + // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart + // and sometimes changes the structure of the DOM. + $linkNode = JQLitePrototype.clone.call($compileNodes); + } else { + $linkNode = $compileNodes; + } + + if (transcludeControllers) { + for (var controllerName in transcludeControllers) { + $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance); + } + } + + compile.$$addScopeInfo($linkNode, scope); + + if (cloneConnectFn) cloneConnectFn($linkNode, scope); + if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn); + return $linkNode; + }; + } + + function detectNamespaceForChildElements(parentElement) { + // TODO: Make this detect MathML as well... + var node = parentElement && parentElement[0]; + if (!node) { + return 'html'; + } else { + return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html'; + } + } + + /** + * Compile function matches each node in nodeList against the directives. Once all directives + * for a particular node are collected their compile functions are executed. The compile + * functions return values - the linking functions - are combined into a composite linking + * function, which is the a linking function for the node. + * + * @param {NodeList} nodeList an array of nodes or NodeList to compile + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new child of the transcluded parent scope. + * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then + * the rootElement must be set the jqLite collection of the compile root. This is + * needed so that the jqLite collection items can be replaced with widgets. + * @param {number=} maxPriority Max directive priority. + * @returns {Function} A composite linking function of all of the matched directives or null. + */ + function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective, + previousCompileContext) { + var linkFns = [], + attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound; + + for (var i = 0; i < nodeList.length; i++) { + attrs = new Attributes(); + + // we must always refer to nodeList[i] since the nodes can be replaced underneath us. + directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined, + ignoreDirective); + + nodeLinkFn = (directives.length) + ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement, + null, [], [], previousCompileContext) + : null; + + if (nodeLinkFn && nodeLinkFn.scope) { + compile.$$addScopeClass(attrs.$$element); + } + + childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || + !(childNodes = nodeList[i].childNodes) || + !childNodes.length) + ? null + : compileNodes(childNodes, + nodeLinkFn ? ( + (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement) + && nodeLinkFn.transclude) : transcludeFn); + + if (nodeLinkFn || childLinkFn) { + linkFns.push(i, nodeLinkFn, childLinkFn); + linkFnFound = true; + nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn; + } + + //use the previous context only for the first element in the virtual group + previousCompileContext = null; + } + + // return a linking function if we have found anything, null otherwise + return linkFnFound ? compositeLinkFn : null; + + function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) { + var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn; + var stableNodeList; + + + if (nodeLinkFnFound) { + // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our + // offsets don't get screwed up + var nodeListLength = nodeList.length; + stableNodeList = new Array(nodeListLength); + + // create a sparse array by only copying the elements which have a linkFn + for (i = 0; i < linkFns.length; i+=3) { + idx = linkFns[i]; + stableNodeList[idx] = nodeList[idx]; + } + } else { + stableNodeList = nodeList; + } + + for (i = 0, ii = linkFns.length; i < ii;) { + node = stableNodeList[linkFns[i++]]; + nodeLinkFn = linkFns[i++]; + childLinkFn = linkFns[i++]; + + if (nodeLinkFn) { + if (nodeLinkFn.scope) { + childScope = scope.$new(); + compile.$$addScopeInfo(jqLite(node), childScope); + } else { + childScope = scope; + } + + if (nodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn( + scope, nodeLinkFn.transclude, parentBoundTranscludeFn, + nodeLinkFn.elementTranscludeOnThisElement); + + } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) { + childBoundTranscludeFn = parentBoundTranscludeFn; + + } else if (!parentBoundTranscludeFn && transcludeFn) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn); + + } else { + childBoundTranscludeFn = null; + } + + nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn); + + } else if (childLinkFn) { + childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn); + } + } + } + } + + function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) { + + var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) { + + if (!transcludedScope) { + transcludedScope = scope.$new(false, containingScope); + transcludedScope.$$transcluded = true; + } + + return transcludeFn(transcludedScope, cloneFn, { + parentBoundTranscludeFn: previousBoundTranscludeFn, + transcludeControllers: controllers, + futureParentElement: futureParentElement + }); + }; + + return boundTranscludeFn; + } + + /** + * Looks for directives on the given node and adds them to the directive collection which is + * sorted. + * + * @param node Node to search. + * @param directives An array to which the directives are added to. This array is sorted before + * the function returns. + * @param attrs The shared attrs object which is used to populate the normalized attributes. + * @param {number=} maxPriority Max directive priority. + */ + function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) { + var nodeType = node.nodeType, + attrsMap = attrs.$attr, + match, + className; + + switch (nodeType) { + case NODE_TYPE_ELEMENT: /* Element */ + // use the node name: <directive> + addDirective(directives, + directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective); + + // iterate over the attributes + for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, + j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { + var attrStartName = false; + var attrEndName = false; + + attr = nAttrs[j]; + name = attr.name; + value = trim(attr.value); + + // support ngAttr attribute binding + ngAttrName = directiveNormalize(name); + if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { + name = name.replace(PREFIX_REGEXP, '') + .substr(8).replace(/_(.)/g, function(match, letter) { + return letter.toUpperCase(); + }); + } + + var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); + if (directiveIsMultiElement(directiveNName)) { + if (ngAttrName === directiveNName + 'Start') { + attrStartName = name; + attrEndName = name.substr(0, name.length - 5) + 'end'; + name = name.substr(0, name.length - 6); + } + } + + nName = directiveNormalize(name.toLowerCase()); + attrsMap[nName] = name; + if (isNgAttr || !attrs.hasOwnProperty(nName)) { + attrs[nName] = value; + if (getBooleanAttrName(node, nName)) { + attrs[nName] = true; // presence means true + } + } + addAttrInterpolateDirective(node, directives, value, nName, isNgAttr); + addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, + attrEndName); + } + + // use class as directive + className = node.className; + if (isObject(className)) { + // Maybe SVGAnimatedString + className = className.animVal; + } + if (isString(className) && className !== '') { + while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { + nName = directiveNormalize(match[2]); + if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[3]); + } + className = className.substr(match.index + match[0].length); + } + } + break; + case NODE_TYPE_TEXT: /* Text Node */ + addTextInterpolateDirective(directives, node.nodeValue); + break; + case NODE_TYPE_COMMENT: /* Comment */ + try { + match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); + if (match) { + nName = directiveNormalize(match[1]); + if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { + attrs[nName] = trim(match[2]); + } + } + } catch (e) { + // turns out that under some circumstances IE9 throws errors when one attempts to read + // comment's node value. + // Just ignore it and continue. (Can't seem to reproduce in test case.) + } + break; + } + + directives.sort(byPriority); + return directives; + } + + /** + * Given a node with an directive-start it collects all of the siblings until it finds + * directive-end. + * @param node + * @param attrStart + * @param attrEnd + * @returns {*} + */ + function groupScan(node, attrStart, attrEnd) { + var nodes = []; + var depth = 0; + if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { + do { + if (!node) { + throw $compileMinErr('uterdir', + "Unterminated attribute, found '{0}' but no matching '{1}' found.", + attrStart, attrEnd); + } + if (node.nodeType == NODE_TYPE_ELEMENT) { + if (node.hasAttribute(attrStart)) depth++; + if (node.hasAttribute(attrEnd)) depth--; + } + nodes.push(node); + node = node.nextSibling; + } while (depth > 0); + } else { + nodes.push(node); + } + + return jqLite(nodes); + } + + /** + * Wrapper for linking function which converts normal linking function into a grouped + * linking function. + * @param linkFn + * @param attrStart + * @param attrEnd + * @returns {Function} + */ + function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { + return function(scope, element, attrs, controllers, transcludeFn) { + element = groupScan(element[0], attrStart, attrEnd); + return linkFn(scope, element, attrs, controllers, transcludeFn); + }; + } + + /** + * Once the directives have been collected, their compile functions are executed. This method + * is responsible for inlining directive templates as well as terminating the application + * of the directives if the terminal directive has been reached. + * + * @param {Array} directives Array of collected directives to execute their compile function. + * this needs to be pre-sorted by priority order. + * @param {Node} compileNode The raw DOM node to apply the compile functions to + * @param {Object} templateAttrs The shared attribute function + * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the + * scope argument is auto-generated to the new + * child of the transcluded parent scope. + * @param {JQLite} jqCollection If we are working on the root of the compile tree then this + * argument has the root jqLite array so that we can replace nodes + * on it. + * @param {Object=} originalReplaceDirective An optional directive that will be ignored when + * compiling the transclusion. + * @param {Array.<Function>} preLinkFns + * @param {Array.<Function>} postLinkFns + * @param {Object} previousCompileContext Context used for previous compilation of the current + * node + * @returns {Function} linkFn + */ + function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, + jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, + previousCompileContext) { + previousCompileContext = previousCompileContext || {}; + + var terminalPriority = -Number.MAX_VALUE, + newScopeDirective, + controllerDirectives = previousCompileContext.controllerDirectives, + controllers, + newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, + templateDirective = previousCompileContext.templateDirective, + nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, + hasTranscludeDirective = false, + hasTemplate = false, + hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, + $compileNode = templateAttrs.$$element = jqLite(compileNode), + directive, + directiveName, + $template, + replaceDirective = originalReplaceDirective, + childTranscludeFn = transcludeFn, + linkFn, + directiveValue; + + // executes all directives on the current element + for (var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + var attrStart = directive.$$start; + var attrEnd = directive.$$end; + + // collect multiblock sections + if (attrStart) { + $compileNode = groupScan(compileNode, attrStart, attrEnd); + } + $template = undefined; + + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + if (directiveValue = directive.scope) { + + // skip the check for directives with async templates, we'll check the derived sync + // directive when the template arrives + if (!directive.templateUrl) { + if (isObject(directiveValue)) { + // This directive is trying to add an isolated scope. + // Check that there is no scope of any kind already + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective, + directive, $compileNode); + newIsolateScopeDirective = directive; + } else { + // This directive is trying to add a child scope. + // Check that there is no isolated scope already + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, + $compileNode); + } + } + + newScopeDirective = newScopeDirective || directive; + } + + directiveName = directive.name; + + if (!directive.templateUrl && directive.controller) { + directiveValue = directive.controller; + controllerDirectives = controllerDirectives || {}; + assertNoDuplicate("'" + directiveName + "' controller", + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + if (directiveValue = directive.transclude) { + hasTranscludeDirective = true; + + // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. + // This option should only be used by directives that know how to safely handle element transclusion, + // where the transcluded nodes are added or replaced after linking. + if (!directive.$$tlb) { + assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); + nonTlbTranscludeDirective = directive; + } + + if (directiveValue == 'element') { + hasElementTranscludeDirective = true; + terminalPriority = directive.priority; + $template = $compileNode; + $compileNode = templateAttrs.$$element = + jqLite(document.createComment(' ' + directiveName + ': ' + + templateAttrs[directiveName] + ' ')); + compileNode = $compileNode[0]; + replaceWith(jqCollection, sliceArgs($template), compileNode); + + childTranscludeFn = compile($template, transcludeFn, terminalPriority, + replaceDirective && replaceDirective.name, { + // Don't pass in: + // - controllerDirectives - otherwise we'll create duplicates controllers + // - newIsolateScopeDirective or templateDirective - combining templates with + // element transclusion doesn't make sense. + // + // We need only nonTlbTranscludeDirective so that we prevent putting transclusion + // on the same element more than once. + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + } else { + $template = jqLite(jqLiteClone(compileNode)).contents(); + $compileNode.empty(); // clear contents + childTranscludeFn = compile($template, transcludeFn); + } + } + + if (directive.template) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + directiveValue = (isFunction(directive.template)) + ? directive.template($compileNode, templateAttrs) + : directive.template; + + directiveValue = denormalizeTemplate(directiveValue); + + if (directive.replace) { + replaceDirective = directive; + if (jqLiteIsTextNode(directiveValue)) { + $template = []; + } else { + $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue))); + } + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + directiveName, ''); + } + + replaceWith(jqCollection, $compileNode, compileNode); + + var newTemplateAttrs = {$attr: {}}; + + // combine directives from the original node and from the template: + // - take the array of directives for this element + // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) + // - collect directives from the template and sort them by priority + // - combine directives as: processed + template + unprocessed + var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); + var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); + + if (newIsolateScopeDirective) { + markDirectivesAsIsolate(templateDirectives); + } + directives = directives.concat(templateDirectives).concat(unprocessedDirectives); + mergeTemplateAttributes(templateAttrs, newTemplateAttrs); + + ii = directives.length; + } else { + $compileNode.html(directiveValue); + } + } + + if (directive.templateUrl) { + hasTemplate = true; + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + + if (directive.replace) { + replaceDirective = directive; + } + + nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, + templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { + controllerDirectives: controllerDirectives, + newIsolateScopeDirective: newIsolateScopeDirective, + templateDirective: templateDirective, + nonTlbTranscludeDirective: nonTlbTranscludeDirective + }); + ii = directives.length; + } else if (directive.compile) { + try { + linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); + if (isFunction(linkFn)) { + addLinkFns(null, linkFn, attrStart, attrEnd); + } else if (linkFn) { + addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); + } + } catch (e) { + $exceptionHandler(e, startingTag($compileNode)); + } + } + + if (directive.terminal) { + nodeLinkFn.terminal = true; + terminalPriority = Math.max(terminalPriority, directive.priority); + } + + } + + nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; + nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; + nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective; + nodeLinkFn.templateOnThisElement = hasTemplate; + nodeLinkFn.transclude = childTranscludeFn; + + previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; + + // might be normal or delayed nodeLinkFn depending on if templateUrl is present + return nodeLinkFn; + + //////////////////// + + function addLinkFns(pre, post, attrStart, attrEnd) { + if (pre) { + if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); + pre.require = directive.require; + pre.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + pre = cloneAndAnnotateFn(pre, {isolateScope: true}); + } + preLinkFns.push(pre); + } + if (post) { + if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); + post.require = directive.require; + post.directiveName = directiveName; + if (newIsolateScopeDirective === directive || directive.$$isolateScope) { + post = cloneAndAnnotateFn(post, {isolateScope: true}); + } + postLinkFns.push(post); + } + } + + + function getControllers(directiveName, require, $element, elementControllers) { + var value, retrievalMethod = 'data', optional = false; + var $searchElement = $element; + var match; + if (isString(require)) { + match = require.match(REQUIRE_PREFIX_REGEXP); + require = require.substring(match[0].length); + + if (match[3]) { + if (match[1]) match[3] = null; + else match[1] = match[3]; + } + if (match[1] === '^') { + retrievalMethod = 'inheritedData'; + } else if (match[1] === '^^') { + retrievalMethod = 'inheritedData'; + $searchElement = $element.parent(); + } + if (match[2] === '?') { + optional = true; + } + + value = null; + + if (elementControllers && retrievalMethod === 'data') { + if (value = elementControllers[require]) { + value = value.instance; + } + } + value = value || $searchElement[retrievalMethod]('$' + require + 'Controller'); + + if (!value && !optional) { + throw $compileMinErr('ctreq', + "Controller '{0}', required by directive '{1}', can't be found!", + require, directiveName); + } + return value || null; + } else if (isArray(require)) { + value = []; + forEach(require, function(require) { + value.push(getControllers(directiveName, require, $element, elementControllers)); + }); + } + return value; + } + + + function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { + var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element, + attrs; + + if (compileNode === linkNode) { + attrs = templateAttrs; + $element = templateAttrs.$$element; + } else { + $element = jqLite(linkNode); + attrs = new Attributes($element, templateAttrs); + } + + if (newIsolateScopeDirective) { + isolateScope = scope.$new(true); + } + + if (boundTranscludeFn) { + // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn` + // is later passed as `parentBoundTranscludeFn` to `publicLinkFn` + transcludeFn = controllersBoundTransclude; + transcludeFn.$$boundTransclude = boundTranscludeFn; + } + + if (controllerDirectives) { + // TODO: merge `controllers` and `elementControllers` into single object. + controllers = {}; + elementControllers = {}; + forEach(controllerDirectives, function(directive) { + var locals = { + $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, + $element: $element, + $attrs: attrs, + $transclude: transcludeFn + }, controllerInstance; + + controller = directive.controller; + if (controller == '@') { + controller = attrs[directive.name]; + } + + controllerInstance = $controller(controller, locals, true, directive.controllerAs); + + // For directives with element transclusion the element is a comment, + // but jQuery .data doesn't support attaching data to comment nodes as it's hard to + // clean up (http://bugs.jquery.com/ticket/8335). + // Instead, we save the controllers for the element in a local hash and attach to .data + // later, once we have the actual element. + elementControllers[directive.name] = controllerInstance; + if (!hasElementTranscludeDirective) { + $element.data('$' + directive.name + 'Controller', controllerInstance.instance); + } + + controllers[directive.name] = controllerInstance; + }); + } + + if (newIsolateScopeDirective) { + compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective || + templateDirective === newIsolateScopeDirective.$$originalDirective))); + compile.$$addScopeClass($element, true); + + var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name]; + var isolateBindingContext = isolateScope; + if (isolateScopeController && isolateScopeController.identifier && + newIsolateScopeDirective.bindToController === true) { + isolateBindingContext = isolateScopeController.instance; + } + + forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) { + var attrName = definition.attrName, + optional = definition.optional, + mode = definition.mode, // @, =, or & + lastValue, + parentGet, parentSet, compare; + + switch (mode) { + + case '@': + attrs.$observe(attrName, function(value) { + isolateBindingContext[scopeName] = value; + }); + attrs.$$observers[attrName].$$scope = scope; + if (attrs[attrName]) { + // If the attribute has been provided then we trigger an interpolation to ensure + // the value is there for use in the link fn + isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope); + } + break; + + case '=': + if (optional && !attrs[attrName]) { + return; + } + parentGet = $parse(attrs[attrName]); + if (parentGet.literal) { + compare = equals; + } else { + compare = function(a, b) { return a === b || (a !== a && b !== b); }; + } + parentSet = parentGet.assign || function() { + // reset the change, or we will throw this exception on every $digest + lastValue = isolateBindingContext[scopeName] = parentGet(scope); + throw $compileMinErr('nonassign', + "Expression '{0}' used with directive '{1}' is non-assignable!", + attrs[attrName], newIsolateScopeDirective.name); + }; + lastValue = isolateBindingContext[scopeName] = parentGet(scope); + var parentValueWatch = function parentValueWatch(parentValue) { + if (!compare(parentValue, isolateBindingContext[scopeName])) { + // we are out of sync and need to copy + if (!compare(parentValue, lastValue)) { + // parent changed and it has precedence + isolateBindingContext[scopeName] = parentValue; + } else { + // if the parent can be assigned then do so + parentSet(scope, parentValue = isolateBindingContext[scopeName]); + } + } + return lastValue = parentValue; + }; + parentValueWatch.$stateful = true; + var unwatch; + if (definition.collection) { + unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch); + } else { + unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal); + } + isolateScope.$on('$destroy', unwatch); + break; + + case '&': + parentGet = $parse(attrs[attrName]); + isolateBindingContext[scopeName] = function(locals) { + return parentGet(scope, locals); + }; + break; + } + }); + } + if (controllers) { + forEach(controllers, function(controller) { + controller(); + }); + controllers = null; + } + + // PRELINKING + for (i = 0, ii = preLinkFns.length; i < ii; i++) { + linkFn = preLinkFns[i]; + invokeLinkFn(linkFn, + linkFn.isolateScope ? isolateScope : scope, + $element, + attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), + transcludeFn + ); + } + + // RECURSION + // We only pass the isolate scope, if the isolate directive has a template, + // otherwise the child elements do not belong to the isolate directive. + var scopeToChild = scope; + if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { + scopeToChild = isolateScope; + } + childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); + + // POSTLINKING + for (i = postLinkFns.length - 1; i >= 0; i--) { + linkFn = postLinkFns[i]; + invokeLinkFn(linkFn, + linkFn.isolateScope ? isolateScope : scope, + $element, + attrs, + linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), + transcludeFn + ); + } + + // This is the function that is injected as `$transclude`. + // Note: all arguments are optional! + function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) { + var transcludeControllers; + + // No scope passed in: + if (!isScope(scope)) { + futureParentElement = cloneAttachFn; + cloneAttachFn = scope; + scope = undefined; + } + + if (hasElementTranscludeDirective) { + transcludeControllers = elementControllers; + } + if (!futureParentElement) { + futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element; + } + return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild); + } + } + } + + function markDirectivesAsIsolate(directives) { + // mark all directives as needing isolate scope. + for (var j = 0, jj = directives.length; j < jj; j++) { + directives[j] = inherit(directives[j], {$$isolateScope: true}); + } + } + + /** + * looks up the directive and decorates it with exception handling and proper parameters. We + * call this the boundDirective. + * + * @param {string} name name of the directive to look up. + * @param {string} location The directive must be found in specific format. + * String containing any of theses characters: + * + * * `E`: element name + * * `A': attribute + * * `C`: class + * * `M`: comment + * @returns {boolean} true if directive was added. + */ + function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, + endAttrName) { + if (name === ignoreDirective) return null; + var match = null; + if (hasDirectives.hasOwnProperty(name)) { + for (var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i < ii; i++) { + try { + directive = directives[i]; + if ((maxPriority === undefined || maxPriority > directive.priority) && + directive.restrict.indexOf(location) != -1) { + if (startAttrName) { + directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); + } + tDirectives.push(directive); + match = directive; + } + } catch (e) { $exceptionHandler(e); } + } + } + return match; + } + + + /** + * looks up the directive and returns true if it is a multi-element directive, + * and therefore requires DOM nodes between -start and -end markers to be grouped + * together. + * + * @param {string} name name of the directive to look up. + * @returns true if directive was registered as multi-element. + */ + function directiveIsMultiElement(name) { + if (hasDirectives.hasOwnProperty(name)) { + for (var directive, directives = $injector.get(name + Suffix), + i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + if (directive.multiElement) { + return true; + } + } + } + return false; + } + + /** + * When the element is replaced with HTML template then the new attributes + * on the template need to be merged with the existing attributes in the DOM. + * The desired effect is to have both of the attributes present. + * + * @param {object} dst destination attributes (original DOM) + * @param {object} src source attributes (from the directive template) + */ + function mergeTemplateAttributes(dst, src) { + var srcAttr = src.$attr, + dstAttr = dst.$attr, + $element = dst.$$element; + + // reapply the old attributes to the new element + forEach(dst, function(value, key) { + if (key.charAt(0) != '$') { + if (src[key] && src[key] !== value) { + value += (key === 'style' ? ';' : ' ') + src[key]; + } + dst.$set(key, value, true, srcAttr[key]); + } + }); + + // copy the new attributes on the old attrs object + forEach(src, function(value, key) { + if (key == 'class') { + safeAddClass($element, value); + dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; + } else if (key == 'style') { + $element.attr('style', $element.attr('style') + ';' + value); + dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; + // `dst` will never contain hasOwnProperty as DOM parser won't let it. + // You will get an "InvalidCharacterError: DOM Exception 5" error if you + // have an attribute like "has-own-property" or "data-has-own-property", etc. + } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { + dst[key] = value; + dstAttr[key] = srcAttr[key]; + } + }); + } + + + function compileTemplateUrl(directives, $compileNode, tAttrs, + $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { + var linkQueue = [], + afterTemplateNodeLinkFn, + afterTemplateChildLinkFn, + beforeTemplateCompileNode = $compileNode[0], + origAsyncDirective = directives.shift(), + derivedSyncDirective = inherit(origAsyncDirective, { + templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective + }), + templateUrl = (isFunction(origAsyncDirective.templateUrl)) + ? origAsyncDirective.templateUrl($compileNode, tAttrs) + : origAsyncDirective.templateUrl, + templateNamespace = origAsyncDirective.templateNamespace; + + $compileNode.empty(); + + $templateRequest($sce.getTrustedResourceUrl(templateUrl)) + .then(function(content) { + var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; + + content = denormalizeTemplate(content); + + if (origAsyncDirective.replace) { + if (jqLiteIsTextNode(content)) { + $template = []; + } else { + $template = removeComments(wrapTemplate(templateNamespace, trim(content))); + } + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) { + throw $compileMinErr('tplrt', + "Template for directive '{0}' must have exactly one root element. {1}", + origAsyncDirective.name, templateUrl); + } + + tempTemplateAttrs = {$attr: {}}; + replaceWith($rootElement, $compileNode, compileNode); + var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); + + if (isObject(origAsyncDirective.scope)) { + markDirectivesAsIsolate(templateDirectives); + } + directives = templateDirectives.concat(directives); + mergeTemplateAttributes(tAttrs, tempTemplateAttrs); + } else { + compileNode = beforeTemplateCompileNode; + $compileNode.html(content); + } + + directives.unshift(derivedSyncDirective); + + afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, + childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, + previousCompileContext); + forEach($rootElement, function(node, i) { + if (node == compileNode) { + $rootElement[i] = $compileNode[0]; + } + }); + afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); + + while (linkQueue.length) { + var scope = linkQueue.shift(), + beforeTemplateLinkNode = linkQueue.shift(), + linkRootElement = linkQueue.shift(), + boundTranscludeFn = linkQueue.shift(), + linkNode = $compileNode[0]; + + if (scope.$$destroyed) continue; + + if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { + var oldClasses = beforeTemplateLinkNode.className; + + if (!(previousCompileContext.hasElementTranscludeDirective && + origAsyncDirective.replace)) { + // it was cloned therefore we have to clone as well. + linkNode = jqLiteClone(compileNode); + } + replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); + + // Copy in CSS classes from original node + safeAddClass(jqLite(linkNode), oldClasses); + } + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } else { + childBoundTranscludeFn = boundTranscludeFn; + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, + childBoundTranscludeFn); + } + linkQueue = null; + }); + + return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { + var childBoundTranscludeFn = boundTranscludeFn; + if (scope.$$destroyed) return; + if (linkQueue) { + linkQueue.push(scope, + node, + rootElement, + childBoundTranscludeFn); + } else { + if (afterTemplateNodeLinkFn.transcludeOnThisElement) { + childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); + } + afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); + } + }; + } + + + /** + * Sorting function for bound directives. + */ + function byPriority(a, b) { + var diff = b.priority - a.priority; + if (diff !== 0) return diff; + if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; + return a.index - b.index; + } + + + function assertNoDuplicate(what, previousDirective, directive, element) { + if (previousDirective) { + throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}', + previousDirective.name, directive.name, what, startingTag(element)); + } + } + + + function addTextInterpolateDirective(directives, text) { + var interpolateFn = $interpolate(text, true); + if (interpolateFn) { + directives.push({ + priority: 0, + compile: function textInterpolateCompileFn(templateNode) { + var templateNodeParent = templateNode.parent(), + hasCompileParent = !!templateNodeParent.length; + + // When transcluding a template that has bindings in the root + // we don't have a parent and thus need to add the class during linking fn. + if (hasCompileParent) compile.$$addBindingClass(templateNodeParent); + + return function textInterpolateLinkFn(scope, node) { + var parent = node.parent(); + if (!hasCompileParent) compile.$$addBindingClass(parent); + compile.$$addBindingInfo(parent, interpolateFn.expressions); + scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { + node[0].nodeValue = value; + }); + }; + } + }); + } + } + + + function wrapTemplate(type, template) { + type = lowercase(type || 'html'); + switch (type) { + case 'svg': + case 'math': + var wrapper = document.createElement('div'); + wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>'; + return wrapper.childNodes[0].childNodes; + default: + return template; + } + } + + + function getTrustedContext(node, attrNormalizedName) { + if (attrNormalizedName == "srcdoc") { + return $sce.HTML; + } + var tag = nodeName_(node); + // maction[xlink:href] can source SVG. It's not limited to <maction>. + if (attrNormalizedName == "xlinkHref" || + (tag == "form" && attrNormalizedName == "action") || + (tag != "img" && (attrNormalizedName == "src" || + attrNormalizedName == "ngSrc"))) { + return $sce.RESOURCE_URL; + } + } + + + function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) { + var trustedContext = getTrustedContext(node, name); + allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing; + + var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing); + + // no interpolation found -> ignore + if (!interpolateFn) return; + + + if (name === "multiple" && nodeName_(node) === "select") { + throw $compileMinErr("selmulti", + "Binding to the 'multiple' attribute is not supported. Element: {0}", + startingTag(node)); + } + + directives.push({ + priority: 100, + compile: function() { + return { + pre: function attrInterpolatePreLinkFn(scope, element, attr) { + var $$observers = (attr.$$observers || (attr.$$observers = {})); + + if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { + throw $compileMinErr('nodomevents', + "Interpolations for HTML DOM event attributes are disallowed. Please use the " + + "ng- versions (such as ng-click instead of onclick) instead."); + } + + // If the attribute has changed since last $interpolate()ed + var newValue = attr[name]; + if (newValue !== value) { + // we need to interpolate again since the attribute value has been updated + // (e.g. by another directive's compile function) + // ensure unset/empty values make interpolateFn falsy + interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing); + value = newValue; + } + + // if attribute was updated so that there is no interpolation going on we don't want to + // register any observers + if (!interpolateFn) return; + + // initialize attr object so that it's ready in case we need the value for isolate + // scope initialization, otherwise the value would not be available from isolate + // directive's linking fn during linking phase + attr[name] = interpolateFn(scope); + + ($$observers[name] || ($$observers[name] = [])).$$inter = true; + (attr.$$observers && attr.$$observers[name].$$scope || scope). + $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { + //special case for class attribute addition + removal + //so that class changes can tap into the animation + //hooks provided by the $animate service. Be sure to + //skip animations when the first digest occurs (when + //both the new and the old values are the same) since + //the CSS classes are the non-interpolated values + if (name === 'class' && newValue != oldValue) { + attr.$updateClass(newValue, oldValue); + } else { + attr.$set(name, newValue); + } + }); + } + }; + } + }); + } + + + /** + * This is a special jqLite.replaceWith, which can replace items which + * have no parents, provided that the containing jqLite collection is provided. + * + * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes + * in the root of the tree. + * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep + * the shell, but replace its DOM node reference. + * @param {Node} newNode The new DOM node. + */ + function replaceWith($rootElement, elementsToRemove, newNode) { + var firstElementToRemove = elementsToRemove[0], + removeCount = elementsToRemove.length, + parent = firstElementToRemove.parentNode, + i, ii; + + if ($rootElement) { + for (i = 0, ii = $rootElement.length; i < ii; i++) { + if ($rootElement[i] == firstElementToRemove) { + $rootElement[i++] = newNode; + for (var j = i, j2 = j + removeCount - 1, + jj = $rootElement.length; + j < jj; j++, j2++) { + if (j2 < jj) { + $rootElement[j] = $rootElement[j2]; + } else { + delete $rootElement[j]; + } + } + $rootElement.length -= removeCount - 1; + + // If the replaced element is also the jQuery .context then replace it + // .context is a deprecated jQuery api, so we should set it only when jQuery set it + // http://api.jquery.com/context/ + if ($rootElement.context === firstElementToRemove) { + $rootElement.context = newNode; + } + break; + } + } + } + + if (parent) { + parent.replaceChild(newNode, firstElementToRemove); + } + + // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it? + var fragment = document.createDocumentFragment(); + fragment.appendChild(firstElementToRemove); + + // Copy over user data (that includes Angular's $scope etc.). Don't copy private + // data here because there's no public interface in jQuery to do that and copying over + // event listeners (which is the main use of private data) wouldn't work anyway. + jqLite(newNode).data(jqLite(firstElementToRemove).data()); + + // Remove data of the replaced element. We cannot just call .remove() + // on the element it since that would deallocate scope that is needed + // for the new node. Instead, remove the data "manually". + if (!jQuery) { + delete jqLite.cache[firstElementToRemove[jqLite.expando]]; + } else { + // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after + // the replaced element. The cleanData version monkey-patched by Angular would cause + // the scope to be trashed and we do need the very same scope to work with the new + // element. However, we cannot just cache the non-patched version and use it here as + // that would break if another library patches the method after Angular does (one + // example is jQuery UI). Instead, set a flag indicating scope destroying should be + // skipped this one time. + skipDestroyOnNextJQueryCleanData = true; + jQuery.cleanData([firstElementToRemove]); + } + + for (var k = 1, kk = elementsToRemove.length; k < kk; k++) { + var element = elementsToRemove[k]; + jqLite(element).remove(); // must do this way to clean up expando + fragment.appendChild(element); + delete elementsToRemove[k]; + } + + elementsToRemove[0] = newNode; + elementsToRemove.length = 1; + } + + + function cloneAndAnnotateFn(fn, annotation) { + return extend(function() { return fn.apply(null, arguments); }, fn, annotation); + } + + + function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) { + try { + linkFn(scope, $element, attrs, controllers, transcludeFn); + } catch (e) { + $exceptionHandler(e, startingTag($element)); + } + } + }]; +} + +var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; +/** + * Converts all accepted directives format into proper directive name. + * @param name Name to normalize + */ +function directiveNormalize(name) { + return camelCase(name.replace(PREFIX_REGEXP, '')); +} + +/** + * @ngdoc type + * @name $compile.directive.Attributes + * + * @description + * A shared object between directive compile / linking functions which contains normalized DOM + * element attributes. The values reflect current binding state `{{ }}`. The normalization is + * needed since all of these are treated as equivalent in Angular: + * + * ``` + * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> + * ``` + */ + +/** + * @ngdoc property + * @name $compile.directive.Attributes#$attr + * + * @description + * A map of DOM element attribute names to the normalized name. This is + * needed to do reverse lookup from normalized name back to actual name. + */ + + +/** + * @ngdoc method + * @name $compile.directive.Attributes#$set + * @kind function + * + * @description + * Set DOM element attribute value. + * + * + * @param {string} name Normalized element attribute name of the property to modify. The name is + * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} + * property to the original name. + * @param {string} value Value to set the attribute to. The value can be an interpolated string. + */ + + + +/** + * Closure compiler type information + */ + +function nodesetLinkingFn( + /* angular.Scope */ scope, + /* NodeList */ nodeList, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +) {} + +function directiveLinkingFn( + /* nodesetLinkingFn */ nodesetLinkingFn, + /* angular.Scope */ scope, + /* Node */ node, + /* Element */ rootElement, + /* function(Function) */ boundTranscludeFn +) {} + +function tokenDifference(str1, str2) { + var values = '', + tokens1 = str1.split(/\s+/), + tokens2 = str2.split(/\s+/); + + outer: + for (var i = 0; i < tokens1.length; i++) { + var token = tokens1[i]; + for (var j = 0; j < tokens2.length; j++) { + if (token == tokens2[j]) continue outer; + } + values += (values.length > 0 ? ' ' : '') + token; + } + return values; +} + +function removeComments(jqNodes) { + jqNodes = jqLite(jqNodes); + var i = jqNodes.length; + + if (i <= 1) { + return jqNodes; + } + + while (i--) { + var node = jqNodes[i]; + if (node.nodeType === NODE_TYPE_COMMENT) { + splice.call(jqNodes, i, 1); + } + } + return jqNodes; +} + +var $controllerMinErr = minErr('$controller'); + +/** + * @ngdoc provider + * @name $controllerProvider + * @description + * The {@link ng.$controller $controller service} is used by Angular to create new + * controllers. + * + * This provider allows controller registration via the + * {@link ng.$controllerProvider#register register} method. + */ +function $ControllerProvider() { + var controllers = {}, + globals = false, + CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; + + + /** + * @ngdoc method + * @name $controllerProvider#register + * @param {string|Object} name Controller name, or an object map of controllers where the keys are + * the names and the values are the constructors. + * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI + * annotations in the array notation). + */ + this.register = function(name, constructor) { + assertNotHasOwnProperty(name, 'controller'); + if (isObject(name)) { + extend(controllers, name); + } else { + controllers[name] = constructor; + } + }; + + /** + * @ngdoc method + * @name $controllerProvider#allowGlobals + * @description If called, allows `$controller` to find controller constructors on `window` + */ + this.allowGlobals = function() { + globals = true; + }; + + + this.$get = ['$injector', '$window', function($injector, $window) { + + /** + * @ngdoc service + * @name $controller + * @requires $injector + * + * @param {Function|string} constructor If called with a function then it's considered to be the + * controller constructor function. Otherwise it's considered to be a string which is used + * to retrieve the controller constructor using the following steps: + * + * * check if a controller with given name is registered via `$controllerProvider` + * * check if evaluating the string on the current scope returns a constructor + * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global + * `window` object (not recommended) + * + * The string can use the `controller as property` syntax, where the controller instance is published + * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this + * to work correctly. + * + * @param {Object} locals Injection locals for Controller. + * @return {Object} Instance of given controller. + * + * @description + * `$controller` service is responsible for instantiating controllers. + * + * It's just a simple call to {@link auto.$injector $injector}, but extracted into + * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). + */ + return function(expression, locals, later, ident) { + // PRIVATE API: + // param `later` --- indicates that the controller's constructor is invoked at a later time. + // If true, $controller will allocate the object with the correct + // prototype chain, but will not invoke the controller until a returned + // callback is invoked. + // param `ident` --- An optional label which overrides the label parsed from the controller + // expression, if any. + var instance, match, constructor, identifier; + later = later === true; + if (ident && isString(ident)) { + identifier = ident; + } + + if (isString(expression)) { + match = expression.match(CNTRL_REG); + if (!match) { + throw $controllerMinErr('ctrlfmt', + "Badly formed controller string '{0}'. " + + "Must match `__name__ as __id__` or `__name__`.", expression); + } + constructor = match[1], + identifier = identifier || match[3]; + expression = controllers.hasOwnProperty(constructor) + ? controllers[constructor] + : getter(locals.$scope, constructor, true) || + (globals ? getter($window, constructor, true) : undefined); + + assertArgFn(expression, constructor, true); + } + + if (later) { + // Instantiate controller later: + // This machinery is used to create an instance of the object before calling the + // controller's constructor itself. + // + // This allows properties to be added to the controller before the constructor is + // invoked. Primarily, this is used for isolate scope bindings in $compile. + // + // This feature is not intended for use by applications, and is thus not documented + // publicly. + // Object creation: http://jsperf.com/create-constructor/2 + var controllerPrototype = (isArray(expression) ? + expression[expression.length - 1] : expression).prototype; + instance = Object.create(controllerPrototype || null); + + if (identifier) { + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + + return extend(function() { + $injector.invoke(expression, instance, locals, constructor); + return instance; + }, { + instance: instance, + identifier: identifier + }); + } + + instance = $injector.instantiate(expression, locals, constructor); + + if (identifier) { + addIdentifier(locals, identifier, instance, constructor || expression.name); + } + + return instance; + }; + + function addIdentifier(locals, identifier, instance, name) { + if (!(locals && isObject(locals.$scope))) { + throw minErr('$controller')('noscp', + "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", + name, identifier); + } + + locals.$scope[identifier] = instance; + } + }]; +} + +/** + * @ngdoc service + * @name $document + * @requires $window + * + * @description + * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. + * + * @example + <example module="documentExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <p>$document title: <b ng-bind="title"></b></p> + <p>window.document title: <b ng-bind="windowTitle"></b></p> + </div> + </file> + <file name="script.js"> + angular.module('documentExample', []) + .controller('ExampleController', ['$scope', '$document', function($scope, $document) { + $scope.title = $document[0].title; + $scope.windowTitle = angular.element(window.document)[0].title; + }]); + </file> + </example> + */ +function $DocumentProvider() { + this.$get = ['$window', function(window) { + return jqLite(window.document); + }]; +} + +/** + * @ngdoc service + * @name $exceptionHandler + * @requires ng.$log + * + * @description + * Any uncaught exception in angular expressions is delegated to this service. + * The default implementation simply delegates to `$log.error` which logs it into + * the browser console. + * + * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by + * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. + * + * ## Example: + * + * ```js + * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() { + * return function(exception, cause) { + * exception.message += ' (caused by "' + cause + '")'; + * throw exception; + * }; + * }); + * ``` + * + * This example will override the normal action of `$exceptionHandler`, to make angular + * exceptions fail hard when they happen, instead of just logging to the console. + * + * <hr /> + * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind` + * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler} + * (unless executed during a digest). + * + * If you wish, you can manually delegate exceptions, e.g. + * `try { ... } catch(e) { $exceptionHandler(e); }` + * + * @param {Error} exception Exception associated with the error. + * @param {string=} cause optional information about the context in which + * the error was thrown. + * + */ +function $ExceptionHandlerProvider() { + this.$get = ['$log', function($log) { + return function(exception, cause) { + $log.error.apply($log, arguments); + }; + }]; +} + +var APPLICATION_JSON = 'application/json'; +var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'}; +var JSON_START = /^\[|^\{(?!\{)/; +var JSON_ENDS = { + '[': /]$/, + '{': /}$/ +}; +var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/; + +function defaultHttpResponseTransform(data, headers) { + if (isString(data)) { + // Strip json vulnerability protection prefix and trim whitespace + var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim(); + + if (tempData) { + var contentType = headers('Content-Type'); + if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) { + data = fromJson(tempData); + } + } + } + + return data; +} + +function isJsonLike(str) { + var jsonStart = str.match(JSON_START); + return jsonStart && JSON_ENDS[jsonStart[0]].test(str); +} + +/** + * Parse headers into key value object + * + * @param {string} headers Raw headers as a string + * @returns {Object} Parsed headers as key value object + */ +function parseHeaders(headers) { + var parsed = createMap(), key, val, i; + + if (!headers) return parsed; + + forEach(headers.split('\n'), function(line) { + i = line.indexOf(':'); + key = lowercase(trim(line.substr(0, i))); + val = trim(line.substr(i + 1)); + + if (key) { + parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; + } + }); + + return parsed; +} + + +/** + * Returns a function that provides access to parsed headers. + * + * Headers are lazy parsed when first requested. + * @see parseHeaders + * + * @param {(string|Object)} headers Headers to provide access to. + * @returns {function(string=)} Returns a getter function which if called with: + * + * - if called with single an argument returns a single header value or null + * - if called with no arguments returns an object containing all headers. + */ +function headersGetter(headers) { + var headersObj = isObject(headers) ? headers : undefined; + + return function(name) { + if (!headersObj) headersObj = parseHeaders(headers); + + if (name) { + var value = headersObj[lowercase(name)]; + if (value === void 0) { + value = null; + } + return value; + } + + return headersObj; + }; +} + + +/** + * Chain all given functions + * + * This function is used for both request and response transforming + * + * @param {*} data Data to transform. + * @param {function(string=)} headers HTTP headers getter fn. + * @param {number} status HTTP status code of the response. + * @param {(Function|Array.<Function>)} fns Function or an array of functions. + * @returns {*} Transformed data. + */ +function transformData(data, headers, status, fns) { + if (isFunction(fns)) + return fns(data, headers, status); + + forEach(fns, function(fn) { + data = fn(data, headers, status); + }); + + return data; +} + + +function isSuccess(status) { + return 200 <= status && status < 300; +} + + +/** + * @ngdoc provider + * @name $httpProvider + * @description + * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. + * */ +function $HttpProvider() { + /** + * @ngdoc property + * @name $httpProvider#defaults + * @description + * + * Object containing default values for all {@link ng.$http $http} requests. + * + * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`} + * that will provide the cache for all requests who set their `cache` property to `true`. + * If you set the `default.cache = false` then only requests that specify their own custom + * cache object will be cached. See {@link $http#caching $http Caching} for more information. + * + * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. + * Defaults value is `'XSRF-TOKEN'`. + * + * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the + * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. + * + * - **`defaults.headers`** - {Object} - Default headers for all $http requests. + * Refer to {@link ng.$http#setting-http-headers $http} for documentation on + * setting default headers. + * - **`defaults.headers.common`** + * - **`defaults.headers.post`** + * - **`defaults.headers.put`** + * - **`defaults.headers.patch`** + * + **/ + var defaults = this.defaults = { + // transform incoming response data + transformResponse: [defaultHttpResponseTransform], + + // transform outgoing request data + transformRequest: [function(d) { + return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d; + }], + + // default headers + headers: { + common: { + 'Accept': 'application/json, text/plain, */*' + }, + post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), + patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) + }, + + xsrfCookieName: 'XSRF-TOKEN', + xsrfHeaderName: 'X-XSRF-TOKEN' + }; + + var useApplyAsync = false; + /** + * @ngdoc method + * @name $httpProvider#useApplyAsync + * @description + * + * Configure $http service to combine processing of multiple http responses received at around + * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in + * significant performance improvement for bigger applications that make many HTTP requests + * concurrently (common during application bootstrap). + * + * Defaults to false. If no value is specifed, returns the current configured value. + * + * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred + * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window + * to load and share the same digest cycle. + * + * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining. + * otherwise, returns the current configured value. + **/ + this.useApplyAsync = function(value) { + if (isDefined(value)) { + useApplyAsync = !!value; + return this; + } + return useApplyAsync; + }; + + /** + * @ngdoc property + * @name $httpProvider#interceptors + * @description + * + * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} + * pre-processing of request or postprocessing of responses. + * + * These service factories are ordered by request, i.e. they are applied in the same order as the + * array, on request, but reverse order, on response. + * + * {@link ng.$http#interceptors Interceptors detailed info} + **/ + var interceptorFactories = this.interceptors = []; + + this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', + function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { + + var defaultCache = $cacheFactory('$http'); + + /** + * Interceptors stored in reverse order. Inner interceptors before outer interceptors. + * The reversal is needed so that we can build up the interception chain around the + * server request. + */ + var reversedInterceptors = []; + + forEach(interceptorFactories, function(interceptorFactory) { + reversedInterceptors.unshift(isString(interceptorFactory) + ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); + }); + + /** + * @ngdoc service + * @kind function + * @name $http + * @requires ng.$httpBackend + * @requires $cacheFactory + * @requires $rootScope + * @requires $q + * @requires $injector + * + * @description + * The `$http` service is a core Angular service that facilitates communication with the remote + * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) + * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). + * + * For unit testing applications that use `$http` service, see + * {@link ngMock.$httpBackend $httpBackend mock}. + * + * For a higher level of abstraction, please check out the {@link ngResource.$resource + * $resource} service. + * + * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by + * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage + * it is important to familiarize yourself with these APIs and the guarantees they provide. + * + * + * ## General usage + * The `$http` service is a function which takes a single argument — a configuration object — + * that is used to generate an HTTP request and returns a {@link ng.$q promise} + * with two $http specific methods: `success` and `error`. + * + * ```js + * // Simple GET request example : + * $http.get('/someUrl'). + * success(function(data, status, headers, config) { + * // this callback will be called asynchronously + * // when the response is available + * }). + * error(function(data, status, headers, config) { + * // called asynchronously if an error occurs + * // or server returns response with an error status. + * }); + * ``` + * + * ```js + * // Simple POST request example (passing data) : + * $http.post('/someUrl', {msg:'hello word!'}). + * success(function(data, status, headers, config) { + * // this callback will be called asynchronously + * // when the response is available + * }). + * error(function(data, status, headers, config) { + * // called asynchronously if an error occurs + * // or server returns response with an error status. + * }); + * ``` + * + * + * Since the returned value of calling the $http function is a `promise`, you can also use + * the `then` method to register callbacks, and these callbacks will receive a single argument – + * an object representing the response. See the API signature and type info below for more + * details. + * + * A response status code between 200 and 299 is considered a success status and + * will result in the success callback being called. Note that if the response is a redirect, + * XMLHttpRequest will transparently follow it, meaning that the error callback will not be + * called for such responses. + * + * ## Writing Unit Tests that use $http + * When unit testing (using {@link ngMock ngMock}), it is necessary to call + * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending + * request using trained responses. + * + * ``` + * $httpBackend.expectGET(...); + * $http.get(...); + * $httpBackend.flush(); + * ``` + * + * ## Shortcut methods + * + * Shortcut methods are also available. All shortcut methods require passing in the URL, and + * request data must be passed in for POST/PUT requests. + * + * ```js + * $http.get('/someUrl').success(successCallback); + * $http.post('/someUrl', data).success(successCallback); + * ``` + * + * Complete list of shortcut methods: + * + * - {@link ng.$http#get $http.get} + * - {@link ng.$http#head $http.head} + * - {@link ng.$http#post $http.post} + * - {@link ng.$http#put $http.put} + * - {@link ng.$http#delete $http.delete} + * - {@link ng.$http#jsonp $http.jsonp} + * - {@link ng.$http#patch $http.patch} + * + * + * ## Setting HTTP Headers + * + * The $http service will automatically add certain HTTP headers to all requests. These defaults + * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration + * object, which currently contains this default configuration: + * + * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): + * - `Accept: application/json, text/plain, * / *` + * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) + * - `Content-Type: application/json` + * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) + * - `Content-Type: application/json` + * + * To add or overwrite these defaults, simply add or remove a property from these configuration + * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object + * with the lowercased HTTP method name as the key, e.g. + * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }. + * + * The defaults can also be set at runtime via the `$http.defaults` object in the same + * fashion. For example: + * + * ``` + * module.run(function($http) { + * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w' + * }); + * ``` + * + * In addition, you can supply a `headers` property in the config object passed when + * calling `$http(config)`, which overrides the defaults without changing them globally. + * + * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, + * Use the `headers` property, setting the desired header to `undefined`. For example: + * + * ```js + * var req = { + * method: 'POST', + * url: 'http://example.com', + * headers: { + * 'Content-Type': undefined + * }, + * data: { test: 'test' }, + * } + * + * $http(req).success(function(){...}).error(function(){...}); + * ``` + * + * ## Transforming Requests and Responses + * + * Both requests and responses can be transformed using transformation functions: `transformRequest` + * and `transformResponse`. These properties can be a single function that returns + * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions, + * which allows you to `push` or `unshift` a new transformation function into the transformation chain. + * + * ### Default Transformations + * + * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and + * `defaults.transformResponse` properties. If a request does not provide its own transformations + * then these will be applied. + * + * You can augment or replace the default transformations by modifying these properties by adding to or + * replacing the array. + * + * Angular provides the following default transformations: + * + * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`): + * + * - If the `data` property of the request configuration object contains an object, serialize it + * into JSON format. + * + * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`): + * + * - If XSRF prefix is detected, strip it (see Security Considerations section below). + * - If JSON response is detected, deserialize it using a JSON parser. + * + * + * ### Overriding the Default Transformations Per Request + * + * If you wish override the request/response transformations only for a single request then provide + * `transformRequest` and/or `transformResponse` properties on the configuration object passed + * into `$http`. + * + * Note that if you provide these properties on the config object the default transformations will be + * overwritten. If you wish to augment the default transformations then you must include them in your + * local transformation array. + * + * The following code demonstrates adding a new response transformation to be run after the default response + * transformations have been run. + * + * ```js + * function appendTransform(defaults, transform) { + * + * // We can't guarantee that the default transformation is an array + * defaults = angular.isArray(defaults) ? defaults : [defaults]; + * + * // Append the new transformation to the defaults + * return defaults.concat(transform); + * } + * + * $http({ + * url: '...', + * method: 'GET', + * transformResponse: appendTransform($http.defaults.transformResponse, function(value) { + * return doTransform(value); + * }) + * }); + * ``` + * + * + * ## Caching + * + * To enable caching, set the request configuration `cache` property to `true` (to use default + * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). + * When the cache is enabled, `$http` stores the response from the server in the specified + * cache. The next time the same request is made, the response is served from the cache without + * sending a request to the server. + * + * Note that even if the response is served from cache, delivery of the data is asynchronous in + * the same way that real requests are. + * + * If there are multiple GET requests for the same URL that should be cached using the same + * cache, but the cache is not populated yet, only one request to the server will be made and + * the remaining requests will be fulfilled using the response from the first request. + * + * You can change the default cache to a new object (built with + * {@link ng.$cacheFactory `$cacheFactory`}) by updating the + * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set + * their `cache` property to `true` will now use this cache object. + * + * If you set the default cache to `false` then only requests that specify their own custom + * cache object will be cached. + * + * ## Interceptors + * + * Before you start creating interceptors, be sure to understand the + * {@link ng.$q $q and deferred/promise APIs}. + * + * For purposes of global error handling, authentication, or any kind of synchronous or + * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be + * able to intercept requests before they are handed to the server and + * responses before they are handed over to the application code that + * initiated these requests. The interceptors leverage the {@link ng.$q + * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. + * + * The interceptors are service factories that are registered with the `$httpProvider` by + * adding them to the `$httpProvider.interceptors` array. The factory is called and + * injected with dependencies (if specified) and returns the interceptor. + * + * There are two kinds of interceptors (and two kinds of rejection interceptors): + * + * * `request`: interceptors get called with a http `config` object. The function is free to + * modify the `config` object or create a new one. The function needs to return the `config` + * object directly, or a promise containing the `config` or a new `config` object. + * * `requestError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * * `response`: interceptors get called with http `response` object. The function is free to + * modify the `response` object or create a new one. The function needs to return the `response` + * object directly, or as a promise containing the `response` or a new `response` object. + * * `responseError`: interceptor gets called when a previous interceptor threw an error or + * resolved with a rejection. + * + * + * ```js + * // register the interceptor as a service + * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { + * return { + * // optional method + * 'request': function(config) { + * // do something on success + * return config; + * }, + * + * // optional method + * 'requestError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * }, + * + * + * + * // optional method + * 'response': function(response) { + * // do something on success + * return response; + * }, + * + * // optional method + * 'responseError': function(rejection) { + * // do something on error + * if (canRecover(rejection)) { + * return responseOrNewPromise + * } + * return $q.reject(rejection); + * } + * }; + * }); + * + * $httpProvider.interceptors.push('myHttpInterceptor'); + * + * + * // alternatively, register the interceptor via an anonymous factory + * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { + * return { + * 'request': function(config) { + * // same as above + * }, + * + * 'response': function(response) { + * // same as above + * } + * }; + * }); + * ``` + * + * ## Security Considerations + * + * When designing web applications, consider security threats from: + * + * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) + * + * Both server and the client must cooperate in order to eliminate these threats. Angular comes + * pre-configured with strategies that address these issues, but for this to work backend server + * cooperation is required. + * + * ### JSON Vulnerability Protection + * + * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) + * allows third party website to turn your JSON resource URL into + * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To + * counter this your server can prefix all JSON requests with following string `")]}',\n"`. + * Angular will automatically strip the prefix before processing it as JSON. + * + * For example if your server needs to return: + * ```js + * ['one','two'] + * ``` + * + * which is vulnerable to attack, your server can return: + * ```js + * )]}', + * ['one','two'] + * ``` + * + * Angular will strip the prefix, before processing the JSON. + * + * + * ### Cross Site Request Forgery (XSRF) Protection + * + * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which + * an unauthorized site can gain your user's private data. Angular provides a mechanism + * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie + * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only + * JavaScript that runs on your domain could read the cookie, your server can be assured that + * the XHR came from JavaScript running on your domain. The header will not be set for + * cross-domain requests. + * + * To take advantage of this, your server needs to set a token in a JavaScript readable session + * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the + * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure + * that only JavaScript running on your domain could have sent the request. The token must be + * unique for each user and must be verifiable by the server (to prevent the JavaScript from + * making up its own tokens). We recommend that the token is a digest of your site's + * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) + * for added security. + * + * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName + * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, + * or the per-request config object. + * + * + * @param {object} config Object describing the request to be made and how it should be + * processed. The object has following properties: + * + * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) + * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. + * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned + * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be + * JSONified. + * - **data** – `{string|Object}` – Data to be sent as the request message data. + * - **headers** – `{Object}` – Map of strings or functions which return strings representing + * HTTP headers to send to the server. If the return value of a function is null, the + * header will not be sent. + * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. + * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. + * - **transformRequest** – + * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – + * transform function or an array of such functions. The transform function takes the http + * request body and headers and returns its transformed (typically serialized) version. + * See {@link ng.$http#overriding-the-default-transformations-per-request + * Overriding the Default Transformations} + * - **transformResponse** – + * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` – + * transform function or an array of such functions. The transform function takes the http + * response body, headers and status and returns its transformed (typically deserialized) version. + * See {@link ng.$http#overriding-the-default-transformations-per-request + * Overriding the Default Transformations} + * - **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|Promise}` – timeout in milliseconds, or {@link ng.$q promise} + * that should abort the request when resolved. + * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the + * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) + * for more information. + * - **responseType** - `{string}` - see + * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType). + * + * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the + * standard `then` method and two http specific methods: `success` and `error`. The `then` + * method takes two arguments a success and an error callback which will be called with a + * response object. The `success` and `error` methods take a single argument - a function that + * will be called when the request succeeds or fails respectively. The arguments passed into + * these functions are destructured representation of the response object passed into the + * `then` method. The response object has these properties: + * + * - **data** – `{string|Object}` – The response body transformed with the transform + * functions. + * - **status** – `{number}` – HTTP status code of the response. + * - **headers** – `{function([headerName])}` – Header getter function. + * - **config** – `{Object}` – The configuration object that was used to generate the request. + * - **statusText** – `{string}` – HTTP status text of the response. + * + * @property {Array.<Object>} pendingRequests Array of config objects for currently pending + * requests. This is primarily meant to be used for debugging purposes. + * + * + * @example +<example module="httpExample"> +<file name="index.html"> + <div ng-controller="FetchController"> + <select ng-model="method"> + <option>GET</option> + <option>JSONP</option> + </select> + <input type="text" ng-model="url" size="80"/> + <button id="fetchbtn" ng-click="fetch()">fetch</button><br> + <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> + <button id="samplejsonpbtn" + ng-click="updateModel('JSONP', + 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')"> + Sample JSONP + </button> + <button id="invalidjsonpbtn" + ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')"> + Invalid JSONP + </button> + <pre>http status code: {{status}}</pre> + <pre>http response data: {{data}}</pre> + </div> +</file> +<file name="script.js"> + angular.module('httpExample', []) + .controller('FetchController', ['$scope', '$http', '$templateCache', + function($scope, $http, $templateCache) { + $scope.method = 'GET'; + $scope.url = 'http-hello.html'; + + $scope.fetch = function() { + $scope.code = null; + $scope.response = null; + + $http({method: $scope.method, url: $scope.url, cache: $templateCache}). + success(function(data, status) { + $scope.status = status; + $scope.data = data; + }). + error(function(data, status) { + $scope.data = data || "Request failed"; + $scope.status = status; + }); + }; + + $scope.updateModel = function(method, url) { + $scope.method = method; + $scope.url = url; + }; + }]); +</file> +<file name="http-hello.html"> + Hello, $http! +</file> +<file name="protractor.js" type="protractor"> + var status = element(by.binding('status')); + var data = element(by.binding('data')); + var fetchBtn = element(by.id('fetchbtn')); + var sampleGetBtn = element(by.id('samplegetbtn')); + var sampleJsonpBtn = element(by.id('samplejsonpbtn')); + var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); + + it('should make an xhr GET request', function() { + sampleGetBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('200'); + expect(data.getText()).toMatch(/Hello, \$http!/); + }); + +// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 +// it('should make a JSONP request to angularjs.org', function() { +// sampleJsonpBtn.click(); +// fetchBtn.click(); +// expect(status.getText()).toMatch('200'); +// expect(data.getText()).toMatch(/Super Hero!/); +// }); + + it('should make JSONP request to invalid URL and invoke the error handler', + function() { + invalidJsonpBtn.click(); + fetchBtn.click(); + expect(status.getText()).toMatch('0'); + expect(data.getText()).toMatch('Request failed'); + }); +</file> +</example> + */ + function $http(requestConfig) { + + if (!angular.isObject(requestConfig)) { + throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig); + } + + var config = extend({ + method: 'get', + transformRequest: defaults.transformRequest, + transformResponse: defaults.transformResponse + }, requestConfig); + + config.headers = mergeHeaders(requestConfig); + config.method = uppercase(config.method); + + var serverRequest = function(config) { + var headers = config.headers; + var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest); + + // strip content-type if data is undefined + if (isUndefined(reqData)) { + forEach(headers, function(value, header) { + if (lowercase(header) === 'content-type') { + delete headers[header]; + } + }); + } + + if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { + config.withCredentials = defaults.withCredentials; + } + + // send request + return sendReq(config, reqData).then(transformResponse, transformResponse); + }; + + var chain = [serverRequest, undefined]; + var promise = $q.when(config); + + // apply interceptors + forEach(reversedInterceptors, function(interceptor) { + if (interceptor.request || interceptor.requestError) { + chain.unshift(interceptor.request, interceptor.requestError); + } + if (interceptor.response || interceptor.responseError) { + chain.push(interceptor.response, interceptor.responseError); + } + }); + + while (chain.length) { + var thenFn = chain.shift(); + var rejectFn = chain.shift(); + + promise = promise.then(thenFn, rejectFn); + } + + promise.success = function(fn) { + promise.then(function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + promise.error = function(fn) { + promise.then(null, function(response) { + fn(response.data, response.status, response.headers, config); + }); + return promise; + }; + + return promise; + + function transformResponse(response) { + // make a copy since the response must be cacheable + var resp = extend({}, response); + if (!response.data) { + resp.data = response.data; + } else { + resp.data = transformData(response.data, response.headers, response.status, config.transformResponse); + } + return (isSuccess(response.status)) + ? resp + : $q.reject(resp); + } + + function executeHeaderFns(headers) { + var headerContent, processedHeaders = {}; + + forEach(headers, function(headerFn, header) { + if (isFunction(headerFn)) { + headerContent = headerFn(); + if (headerContent != null) { + processedHeaders[header] = headerContent; + } + } else { + processedHeaders[header] = headerFn; + } + }); + + return processedHeaders; + } + + function mergeHeaders(config) { + var defHeaders = defaults.headers, + reqHeaders = extend({}, config.headers), + defHeaderName, lowercaseDefHeaderName, reqHeaderName; + + defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); + + // using for-in instead of forEach to avoid unecessary iteration after header has been found + defaultHeadersIteration: + for (defHeaderName in defHeaders) { + lowercaseDefHeaderName = lowercase(defHeaderName); + + for (reqHeaderName in reqHeaders) { + if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { + continue defaultHeadersIteration; + } + } + + reqHeaders[defHeaderName] = defHeaders[defHeaderName]; + } + + // execute if header value is a function for merged headers + return executeHeaderFns(reqHeaders); + } + } + + $http.pendingRequests = []; + + /** + * @ngdoc method + * @name $http#get + * + * @description + * Shortcut method to perform `GET` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#delete + * + * @description + * Shortcut method to perform `DELETE` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#head + * + * @description + * Shortcut method to perform `HEAD` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#jsonp + * + * @description + * Shortcut method to perform `JSONP` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request. + * The name of the callback should be the string `JSON_CALLBACK`. + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethods('get', 'delete', 'head', 'jsonp'); + + /** + * @ngdoc method + * @name $http#post + * + * @description + * Shortcut method to perform `POST` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#put + * + * @description + * Shortcut method to perform `PUT` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + + /** + * @ngdoc method + * @name $http#patch + * + * @description + * Shortcut method to perform `PATCH` request. + * + * @param {string} url Relative or absolute URL specifying the destination of the request + * @param {*} data Request content + * @param {Object=} config Optional configuration object + * @returns {HttpPromise} Future object + */ + createShortMethodsWithData('post', 'put', 'patch'); + + /** + * @ngdoc property + * @name $http#defaults + * + * @description + * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of + * default headers, withCredentials as well as request and response transformations. + * + * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. + */ + $http.defaults = defaults; + + + return $http; + + + function createShortMethods(names) { + forEach(arguments, function(name) { + $http[name] = function(url, config) { + return $http(extend(config || {}, { + method: name, + url: url + })); + }; + }); + } + + + function createShortMethodsWithData(name) { + forEach(arguments, function(name) { + $http[name] = function(url, data, config) { + return $http(extend(config || {}, { + method: name, + url: url, + data: data + })); + }; + }); + } + + + /** + * Makes the request. + * + * !!! ACCESSES CLOSURE VARS: + * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests + */ + function sendReq(config, reqData) { + var deferred = $q.defer(), + promise = deferred.promise, + cache, + cachedResp, + reqHeaders = config.headers, + url = buildUrl(config.url, config.params); + + $http.pendingRequests.push(config); + promise.then(removePendingReq, removePendingReq); + + + if ((config.cache || defaults.cache) && config.cache !== false && + (config.method === 'GET' || config.method === 'JSONP')) { + cache = isObject(config.cache) ? config.cache + : isObject(defaults.cache) ? defaults.cache + : defaultCache; + } + + if (cache) { + cachedResp = cache.get(url); + if (isDefined(cachedResp)) { + if (isPromiseLike(cachedResp)) { + // cached request has already been sent, but there is no response yet + cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult); + } else { + // serving from cache + if (isArray(cachedResp)) { + resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); + } else { + resolvePromise(cachedResp, 200, {}, 'OK'); + } + } + } else { + // put the promise for the non-transformed response into cache as a placeholder + cache.put(url, promise); + } + } + + + // if we won't have the response in cache, set the xsrf headers and + // send the request to the backend + if (isUndefined(cachedResp)) { + var xsrfValue = urlIsSameOrigin(config.url) + ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] + : undefined; + if (xsrfValue) { + reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; + } + + $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, + config.withCredentials, config.responseType); + } + + return promise; + + + /** + * Callback registered to $httpBackend(): + * - caches the response if desired + * - resolves the raw $http promise + * - calls $apply + */ + function done(status, response, headersString, statusText) { + if (cache) { + if (isSuccess(status)) { + cache.put(url, [status, response, parseHeaders(headersString), statusText]); + } else { + // remove promise from the cache + cache.remove(url); + } + } + + function resolveHttpPromise() { + resolvePromise(response, status, headersString, statusText); + } + + if (useApplyAsync) { + $rootScope.$applyAsync(resolveHttpPromise); + } else { + resolveHttpPromise(); + if (!$rootScope.$$phase) $rootScope.$apply(); + } + } + + + /** + * Resolves the raw $http promise. + */ + function resolvePromise(response, status, headers, statusText) { + // normalize internal statuses to 0 + status = Math.max(status, 0); + + (isSuccess(status) ? deferred.resolve : deferred.reject)({ + data: response, + status: status, + headers: headersGetter(headers), + config: config, + statusText: statusText + }); + } + + function resolvePromiseWithResult(result) { + resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText); + } + + function removePendingReq() { + var idx = $http.pendingRequests.indexOf(config); + if (idx !== -1) $http.pendingRequests.splice(idx, 1); + } + } + + + function buildUrl(url, params) { + if (!params) return url; + var parts = []; + forEachSorted(params, function(value, key) { + if (value === null || isUndefined(value)) return; + if (!isArray(value)) value = [value]; + + forEach(value, function(v) { + if (isObject(v)) { + if (isDate(v)) { + v = v.toISOString(); + } else { + v = toJson(v); + } + } + parts.push(encodeUriQuery(key) + '=' + + encodeUriQuery(v)); + }); + }); + if (parts.length > 0) { + url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); + } + return url; + } + }]; +} + +function createXhr() { + return new window.XMLHttpRequest(); +} + +/** + * @ngdoc service + * @name $httpBackend + * @requires $window + * @requires $document + * + * @description + * HTTP backend used by the {@link ng.$http service} that delegates to + * XMLHttpRequest object or JSONP and deals with browser incompatibilities. + * + * You should never need to use this service directly, instead use the higher-level abstractions: + * {@link ng.$http $http} or {@link ngResource.$resource $resource}. + * + * During testing this implementation is swapped with {@link ngMock.$httpBackend mock + * $httpBackend} which can be trained with responses. + */ +function $HttpBackendProvider() { + this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { + return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); + }]; +} + +function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { + // TODO(vojta): fix the signature + return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { + $browser.$$incOutstandingRequestCount(); + url = url || $browser.url(); + + if (lowercase(method) == 'jsonp') { + var callbackId = '_' + (callbacks.counter++).toString(36); + callbacks[callbackId] = function(data) { + callbacks[callbackId].data = data; + callbacks[callbackId].called = true; + }; + + var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), + callbackId, function(status, text) { + completeRequest(callback, status, callbacks[callbackId].data, "", text); + callbacks[callbackId] = noop; + }); + } else { + + var xhr = createXhr(); + + xhr.open(method, url, true); + forEach(headers, function(value, key) { + if (isDefined(value)) { + xhr.setRequestHeader(key, value); + } + }); + + xhr.onload = function requestLoaded() { + var statusText = xhr.statusText || ''; + + // responseText is the old-school way of retrieving response (supported by IE8 & 9) + // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) + var response = ('response' in xhr) ? xhr.response : xhr.responseText; + + // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) + var status = xhr.status === 1223 ? 204 : xhr.status; + + // fix status code when it is 0 (0 status is undocumented). + // Occurs when accessing file resources or on Android 4.1 stock browser + // while retrieving files from application cache. + if (status === 0) { + status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; + } + + completeRequest(callback, + status, + response, + xhr.getAllResponseHeaders(), + statusText); + }; + + var requestError = function() { + // The response is always empty + // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error + completeRequest(callback, -1, null, null, ''); + }; + + xhr.onerror = requestError; + xhr.onabort = requestError; + + if (withCredentials) { + xhr.withCredentials = true; + } + + if (responseType) { + try { + xhr.responseType = responseType; + } catch (e) { + // WebKit added support for the json responseType value on 09/03/2013 + // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are + // known to throw when setting the value "json" as the response type. Other older + // browsers implementing the responseType + // + // The json response type can be ignored if not supported, because JSON payloads are + // parsed on the client-side regardless. + if (responseType !== 'json') { + throw e; + } + } + } + + xhr.send(post || null); + } + + if (timeout > 0) { + var timeoutId = $browserDefer(timeoutRequest, timeout); + } else if (isPromiseLike(timeout)) { + timeout.then(timeoutRequest); + } + + + function timeoutRequest() { + jsonpDone && jsonpDone(); + xhr && xhr.abort(); + } + + function completeRequest(callback, status, response, headersString, statusText) { + // cancel timeout and subsequent timeout promise resolution + if (timeoutId !== undefined) { + $browserDefer.cancel(timeoutId); + } + jsonpDone = xhr = null; + + callback(status, response, headersString, statusText); + $browser.$$completeOutstandingRequest(noop); + } + }; + + function jsonpReq(url, callbackId, done) { + // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: + // - fetches local scripts via XHR and evals them + // - adds and immediately removes script elements from the document + var script = rawDocument.createElement('script'), callback = null; + script.type = "text/javascript"; + script.src = url; + script.async = true; + + callback = function(event) { + removeEventListenerFn(script, "load", callback); + removeEventListenerFn(script, "error", callback); + rawDocument.body.removeChild(script); + script = null; + var status = -1; + var text = "unknown"; + + if (event) { + if (event.type === "load" && !callbacks[callbackId].called) { + event = { type: "error" }; + } + text = event.type; + status = event.type === "error" ? 404 : 200; + } + + if (done) { + done(status, text); + } + }; + + addEventListenerFn(script, "load", callback); + addEventListenerFn(script, "error", callback); + rawDocument.body.appendChild(script); + return callback; + } +} + +var $interpolateMinErr = minErr('$interpolate'); + +/** + * @ngdoc provider + * @name $interpolateProvider + * + * @description + * + * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. + * + * @example +<example module="customInterpolationApp"> +<file name="index.html"> +<script> + var customInterpolationApp = angular.module('customInterpolationApp', []); + + customInterpolationApp.config(function($interpolateProvider) { + $interpolateProvider.startSymbol('//'); + $interpolateProvider.endSymbol('//'); + }); + + + customInterpolationApp.controller('DemoController', function() { + this.label = "This binding is brought you by // interpolation symbols."; + }); +</script> +<div ng-app="App" ng-controller="DemoController as demo"> + //demo.label// +</div> +</file> +<file name="protractor.js" type="protractor"> + it('should interpolate binding with custom symbols', function() { + expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); + }); +</file> +</example> + */ +function $InterpolateProvider() { + var startSymbol = '{{'; + var endSymbol = '}}'; + + /** + * @ngdoc method + * @name $interpolateProvider#startSymbol + * @description + * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. + * + * @param {string=} value new value to set the starting symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.startSymbol = function(value) { + if (value) { + startSymbol = value; + return this; + } else { + return startSymbol; + } + }; + + /** + * @ngdoc method + * @name $interpolateProvider#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * @param {string=} value new value to set the ending symbol to. + * @returns {string|self} Returns the symbol when used as getter and self if used as setter. + */ + this.endSymbol = function(value) { + if (value) { + endSymbol = value; + return this; + } else { + return endSymbol; + } + }; + + + this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { + var startSymbolLength = startSymbol.length, + endSymbolLength = endSymbol.length, + escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'), + escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g'); + + function escape(ch) { + return '\\\\\\' + ch; + } + + /** + * @ngdoc service + * @name $interpolate + * @kind function + * + * @requires $parse + * @requires $sce + * + * @description + * + * Compiles a string with markup into an interpolation function. This service is used by the + * HTML {@link ng.$compile $compile} service for data binding. See + * {@link ng.$interpolateProvider $interpolateProvider} for configuring the + * interpolation markup. + * + * + * ```js + * var $interpolate = ...; // injected + * var exp = $interpolate('Hello {{name | uppercase}}!'); + * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!'); + * ``` + * + * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is + * `true`, the interpolation function will return `undefined` unless all embedded expressions + * evaluate to a value other than `undefined`. + * + * ```js + * var $interpolate = ...; // injected + * var context = {greeting: 'Hello', name: undefined }; + * + * // default "forgiving" mode + * var exp = $interpolate('{{greeting}} {{name}}!'); + * expect(exp(context)).toEqual('Hello !'); + * + * // "allOrNothing" mode + * exp = $interpolate('{{greeting}} {{name}}!', false, null, true); + * expect(exp(context)).toBeUndefined(); + * context.name = 'Angular'; + * expect(exp(context)).toEqual('Hello Angular!'); + * ``` + * + * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior. + * + * ####Escaped Interpolation + * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers + * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash). + * It will be rendered as a regular start/end marker, and will not be interpreted as an expression + * or binding. + * + * This enables web-servers to prevent script injection attacks and defacing attacks, to some + * degree, while also enabling code examples to work without relying on the + * {@link ng.directive:ngNonBindable ngNonBindable} directive. + * + * **For security purposes, it is strongly encouraged that web servers escape user-supplied data, + * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all + * interpolation start/end markers with their escaped counterparts.** + * + * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered + * output when the $interpolate service processes the text. So, for HTML elements interpolated + * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter + * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such, + * this is typically useful only when user-data is used in rendering a template from the server, or + * when otherwise untrusted data is used by a directive. + * + * <example> + * <file name="index.html"> + * <div ng-init="username='A user'"> + * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\} + * </p> + * <p><strong>{{username}}</strong> attempts to inject code which will deface the + * application, but fails to accomplish their task, because the server has correctly + * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash) + * characters.</p> + * <p>Instead, the result of the attempted script injection is visible, and can be removed + * from the database by an administrator.</p> + * </div> + * </file> + * </example> + * + * @param {string} text The text with markup to interpolate. + * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have + * embedded expression in order to return an interpolation function. Strings with no + * embedded expression will return null for the interpolation function. + * @param {string=} trustedContext when provided, the returned function passes the interpolated + * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, + * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that + * provides Strict Contextual Escaping for details. + * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined + * unless all embedded expressions evaluate to a value other than `undefined`. + * @returns {function(context)} an interpolation function which is used to compute the + * interpolated string. The function has these parameters: + * + * - `context`: evaluation context for all expressions embedded in the interpolated text + */ + function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) { + allOrNothing = !!allOrNothing; + var startIndex, + endIndex, + index = 0, + expressions = [], + parseFns = [], + textLength = text.length, + exp, + concat = [], + expressionPositions = []; + + while (index < textLength) { + if (((startIndex = text.indexOf(startSymbol, index)) != -1) && + ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) { + if (index !== startIndex) { + concat.push(unescapeText(text.substring(index, startIndex))); + } + exp = text.substring(startIndex + startSymbolLength, endIndex); + expressions.push(exp); + parseFns.push($parse(exp, parseStringifyInterceptor)); + index = endIndex + endSymbolLength; + expressionPositions.push(concat.length); + concat.push(''); + } else { + // we did not find an interpolation, so we have to add the remainder to the separators array + if (index !== textLength) { + concat.push(unescapeText(text.substring(index))); + } + break; + } + } + + // Concatenating expressions makes it hard to reason about whether some combination of + // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a + // single expression be used for iframe[src], object[src], etc., we ensure that the value + // that's used is assigned or constructed by some JS code somewhere that is more testable or + // make it obvious that you bound the value to some user controlled value. This helps reduce + // the load when auditing for XSS issues. + if (trustedContext && concat.length > 1) { + throw $interpolateMinErr('noconcat', + "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + + "interpolations that concatenate multiple expressions when a trusted value is " + + "required. See http://docs.angularjs.org/api/ng.$sce", text); + } + + if (!mustHaveExpression || expressions.length) { + var compute = function(values) { + for (var i = 0, ii = expressions.length; i < ii; i++) { + if (allOrNothing && isUndefined(values[i])) return; + concat[expressionPositions[i]] = values[i]; + } + return concat.join(''); + }; + + var getValue = function(value) { + return trustedContext ? + $sce.getTrusted(trustedContext, value) : + $sce.valueOf(value); + }; + + var stringify = function(value) { + if (value == null) { // null || undefined + return ''; + } + switch (typeof value) { + case 'string': + break; + case 'number': + value = '' + value; + break; + default: + value = toJson(value); + } + + return value; + }; + + return extend(function interpolationFn(context) { + var i = 0; + var ii = expressions.length; + var values = new Array(ii); + + try { + for (; i < ii; i++) { + values[i] = parseFns[i](context); + } + + return compute(values); + } catch (err) { + var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, + err.toString()); + $exceptionHandler(newErr); + } + + }, { + // all of these properties are undocumented for now + exp: text, //just for compatibility with regular watchers created via $watch + expressions: expressions, + $$watchDelegate: function(scope, listener, objectEquality) { + var lastValue; + return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) { + var currValue = compute(values); + if (isFunction(listener)) { + listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope); + } + lastValue = currValue; + }, objectEquality); + } + }); + } + + function unescapeText(text) { + return text.replace(escapedStartRegexp, startSymbol). + replace(escapedEndRegexp, endSymbol); + } + + function parseStringifyInterceptor(value) { + try { + value = getValue(value); + return allOrNothing && !isDefined(value) ? value : stringify(value); + } catch (err) { + var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, + err.toString()); + $exceptionHandler(newErr); + } + } + } + + + /** + * @ngdoc method + * @name $interpolate#startSymbol + * @description + * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. + * + * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change + * the symbol. + * + * @returns {string} start symbol. + */ + $interpolate.startSymbol = function() { + return startSymbol; + }; + + + /** + * @ngdoc method + * @name $interpolate#endSymbol + * @description + * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. + * + * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change + * the symbol. + * + * @returns {string} end symbol. + */ + $interpolate.endSymbol = function() { + return endSymbol; + }; + + return $interpolate; + }]; +} + +function $IntervalProvider() { + this.$get = ['$rootScope', '$window', '$q', '$$q', + function($rootScope, $window, $q, $$q) { + var intervals = {}; + + + /** + * @ngdoc service + * @name $interval + * + * @description + * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay` + * milliseconds. + * + * The return value of registering an interval function is a promise. This promise will be + * notified upon each tick of the interval, and will be resolved after `count` iterations, or + * run indefinitely if `count` is not defined. The value of the notification will be the + * number of iterations that have run. + * To cancel an interval, call `$interval.cancel(promise)`. + * + * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to + * move forward by `millis` milliseconds and trigger any functions scheduled to run in that + * time. + * + * <div class="alert alert-warning"> + * **Note**: Intervals created by this service must be explicitly destroyed when you are finished + * with them. In particular they are not automatically destroyed when a controller's scope or a + * directive's element are destroyed. + * You should take this into consideration and make sure to always cancel the interval at the + * appropriate moment. See the example below for more details on how and when to do this. + * </div> + * + * @param {function()} fn A function that should be called repeatedly. + * @param {number} delay Number of milliseconds between each function call. + * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat + * indefinitely. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {promise} A promise which will be notified on each iteration. + * + * @example + * <example module="intervalExample"> + * <file name="index.html"> + * <script> + * angular.module('intervalExample', []) + * .controller('ExampleController', ['$scope', '$interval', + * function($scope, $interval) { + * $scope.format = 'M/d/yy h:mm:ss a'; + * $scope.blood_1 = 100; + * $scope.blood_2 = 120; + * + * var stop; + * $scope.fight = function() { + * // Don't start a new fight if we are already fighting + * if ( angular.isDefined(stop) ) return; + * + * stop = $interval(function() { + * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) { + * $scope.blood_1 = $scope.blood_1 - 3; + * $scope.blood_2 = $scope.blood_2 - 4; + * } else { + * $scope.stopFight(); + * } + * }, 100); + * }; + * + * $scope.stopFight = function() { + * if (angular.isDefined(stop)) { + * $interval.cancel(stop); + * stop = undefined; + * } + * }; + * + * $scope.resetFight = function() { + * $scope.blood_1 = 100; + * $scope.blood_2 = 120; + * }; + * + * $scope.$on('$destroy', function() { + * // Make sure that the interval is destroyed too + * $scope.stopFight(); + * }); + * }]) + * // Register the 'myCurrentTime' directive factory method. + * // We inject $interval and dateFilter service since the factory method is DI. + * .directive('myCurrentTime', ['$interval', 'dateFilter', + * function($interval, dateFilter) { + * // return the directive link function. (compile function not needed) + * return function(scope, element, attrs) { + * var format, // date format + * stopTime; // so that we can cancel the time updates + * + * // used to update the UI + * function updateTime() { + * element.text(dateFilter(new Date(), format)); + * } + * + * // watch the expression, and update the UI on change. + * scope.$watch(attrs.myCurrentTime, function(value) { + * format = value; + * updateTime(); + * }); + * + * stopTime = $interval(updateTime, 1000); + * + * // listen on DOM destroy (removal) event, and cancel the next UI update + * // to prevent updating time after the DOM element was removed. + * element.on('$destroy', function() { + * $interval.cancel(stopTime); + * }); + * } + * }]); + * </script> + * + * <div> + * <div ng-controller="ExampleController"> + * Date format: <input ng-model="format"> <hr/> + * Current time is: <span my-current-time="format"></span> + * <hr/> + * Blood 1 : <font color='red'>{{blood_1}}</font> + * Blood 2 : <font color='red'>{{blood_2}}</font> + * <button type="button" data-ng-click="fight()">Fight</button> + * <button type="button" data-ng-click="stopFight()">StopFight</button> + * <button type="button" data-ng-click="resetFight()">resetFight</button> + * </div> + * </div> + * + * </file> + * </example> + */ + function interval(fn, delay, count, invokeApply) { + var setInterval = $window.setInterval, + clearInterval = $window.clearInterval, + iteration = 0, + skipApply = (isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise; + + count = isDefined(count) ? count : 0; + + promise.then(null, null, fn); + + promise.$$intervalId = setInterval(function tick() { + deferred.notify(iteration++); + + if (count > 0 && iteration >= count) { + deferred.resolve(iteration); + clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + } + + if (!skipApply) $rootScope.$apply(); + + }, delay); + + intervals[promise.$$intervalId] = deferred; + + return promise; + } + + + /** + * @ngdoc method + * @name $interval#cancel + * + * @description + * Cancels a task associated with the `promise`. + * + * @param {promise} promise returned by the `$interval` function. + * @returns {boolean} Returns `true` if the task was successfully canceled. + */ + interval.cancel = function(promise) { + if (promise && promise.$$intervalId in intervals) { + intervals[promise.$$intervalId].reject('canceled'); + $window.clearInterval(promise.$$intervalId); + delete intervals[promise.$$intervalId]; + return true; + } + return false; + }; + + return interval; + }]; +} + +/** + * @ngdoc service + * @name $locale + * + * @description + * $locale service provides localization rules for various Angular components. As of right now the + * only public api is: + * + * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) + */ +function $LocaleProvider() { + this.$get = function() { + return { + id: 'en-us', + + NUMBER_FORMATS: { + DECIMAL_SEP: '.', + GROUP_SEP: ',', + PATTERNS: [ + { // Decimal Pattern + minInt: 1, + minFrac: 0, + maxFrac: 3, + posPre: '', + posSuf: '', + negPre: '-', + negSuf: '', + gSize: 3, + lgSize: 3 + },{ //Currency Pattern + minInt: 1, + minFrac: 2, + maxFrac: 2, + posPre: '\u00A4', + posSuf: '', + negPre: '(\u00A4', + negSuf: ')', + gSize: 3, + lgSize: 3 + } + ], + CURRENCY_SYM: '$' + }, + + DATETIME_FORMATS: { + MONTH: + 'January,February,March,April,May,June,July,August,September,October,November,December' + .split(','), + SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), + DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), + SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), + AMPMS: ['AM','PM'], + medium: 'MMM d, y h:mm:ss a', + 'short': 'M/d/yy h:mm a', + fullDate: 'EEEE, MMMM d, y', + longDate: 'MMMM d, y', + mediumDate: 'MMM d, y', + shortDate: 'M/d/yy', + mediumTime: 'h:mm:ss a', + shortTime: 'h:mm a', + ERANAMES: [ + "Before Christ", + "Anno Domini" + ], + ERAS: [ + "BC", + "AD" + ] + }, + + pluralCat: function(num) { + if (num === 1) { + return 'one'; + } + return 'other'; + } + }; + }; +} + +var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, + DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; +var $locationMinErr = minErr('$location'); + + +/** + * Encode path using encodeUriSegment, ignoring forward slashes + * + * @param {string} path Path to encode + * @returns {string} + */ +function encodePath(path) { + var segments = path.split('/'), + i = segments.length; + + while (i--) { + segments[i] = encodeUriSegment(segments[i]); + } + + return segments.join('/'); +} + +function parseAbsoluteUrl(absoluteUrl, locationObj) { + var parsedUrl = urlResolve(absoluteUrl); + + locationObj.$$protocol = parsedUrl.protocol; + locationObj.$$host = parsedUrl.hostname; + locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; +} + + +function parseAppUrl(relativeUrl, locationObj) { + var prefixed = (relativeUrl.charAt(0) !== '/'); + if (prefixed) { + relativeUrl = '/' + relativeUrl; + } + var match = urlResolve(relativeUrl); + locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? + match.pathname.substring(1) : match.pathname); + locationObj.$$search = parseKeyValue(match.search); + locationObj.$$hash = decodeURIComponent(match.hash); + + // make sure path starts with '/'; + if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { + locationObj.$$path = '/' + locationObj.$$path; + } +} + + +/** + * + * @param {string} begin + * @param {string} whole + * @returns {string} returns text from whole after begin or undefined if it does not begin with + * expected string. + */ +function beginsWith(begin, whole) { + if (whole.indexOf(begin) === 0) { + return whole.substr(begin.length); + } +} + + +function stripHash(url) { + var index = url.indexOf('#'); + return index == -1 ? url : url.substr(0, index); +} + +function trimEmptyHash(url) { + return url.replace(/(#.+)|#$/, '$1'); +} + + +function stripFile(url) { + return url.substr(0, stripHash(url).lastIndexOf('/') + 1); +} + +/* return the server only (scheme://host:port) */ +function serverBase(url) { + return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); +} + + +/** + * LocationHtml5Url represents an url + * This object is exposed as $location service when HTML5 mode is enabled and supported + * + * @constructor + * @param {string} appBase application base URL + * @param {string} basePrefix url path prefix + */ +function LocationHtml5Url(appBase, basePrefix) { + this.$$html5 = true; + basePrefix = basePrefix || ''; + var appBaseNoFile = stripFile(appBase); + parseAbsoluteUrl(appBase, this); + + + /** + * Parse given html5 (regular) url string into properties + * @param {string} url HTML5 url + * @private + */ + this.$$parse = function(url) { + var pathUrl = beginsWith(appBaseNoFile, url); + if (!isString(pathUrl)) { + throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, + appBaseNoFile); + } + + parseAppUrl(pathUrl, this); + + if (!this.$$path) { + this.$$path = '/'; + } + + this.$$compose(); + }; + + /** + * Compose url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' + }; + + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + var appUrl, prevAppUrl; + var rewrittenUrl; + + if ((appUrl = beginsWith(appBase, url)) !== undefined) { + prevAppUrl = appUrl; + if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) { + rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); + } else { + rewrittenUrl = appBase + prevAppUrl; + } + } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) { + rewrittenUrl = appBaseNoFile + appUrl; + } else if (appBaseNoFile == url + '/') { + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; + }; +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when developer doesn't opt into html5 mode. + * It also serves as the base class for html5 mode fallback on legacy browsers. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangUrl(appBase, hashPrefix) { + var appBaseNoFile = stripFile(appBase); + + parseAbsoluteUrl(appBase, this); + + + /** + * Parse given hashbang url into properties + * @param {string} url Hashbang url + * @private + */ + this.$$parse = function(url) { + var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); + var withoutHashUrl; + + if (withoutBaseUrl.charAt(0) === '#') { + + // The rest of the url starts with a hash so we have + // got either a hashbang path or a plain hash fragment + withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl); + if (isUndefined(withoutHashUrl)) { + // There was no hashbang prefix so we just have a hash fragment + withoutHashUrl = withoutBaseUrl; + } + + } else { + // There was no hashbang path nor hash fragment: + // If we are in HTML5 mode we use what is left as the path; + // Otherwise we ignore what is left + withoutHashUrl = this.$$html5 ? withoutBaseUrl : ''; + } + + parseAppUrl(withoutHashUrl, this); + + this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); + + this.$$compose(); + + /* + * In Windows, on an anchor node on documents loaded from + * the filesystem, the browser will return a pathname + * prefixed with the drive name ('/C:/path') when a + * pathname without a drive is set: + * * a.setAttribute('href', '/foo') + * * a.pathname === '/C:/foo' //true + * + * Inside of Angular, we're always using pathnames that + * do not include drive names for routing. + */ + function removeWindowsDriveName(path, url, base) { + /* + Matches paths for file protocol on windows, + such as /C:/foo/bar, and captures only /foo/bar. + */ + var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; + + var firstPathSegmentMatch; + + //Get the relative path from the input URL. + if (url.indexOf(base) === 0) { + url = url.replace(base, ''); + } + + // The input URL intentionally contains a first path segment that ends with a colon. + if (windowsFilePathExp.exec(url)) { + return path; + } + + firstPathSegmentMatch = windowsFilePathExp.exec(path); + return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; + } + }; + + /** + * Compose hashbang url and update `absUrl` property + * @private + */ + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); + }; + + this.$$parseLinkUrl = function(url, relHref) { + if (stripHash(appBase) == stripHash(url)) { + this.$$parse(url); + return true; + } + return false; + }; +} + + +/** + * LocationHashbangUrl represents url + * This object is exposed as $location service when html5 history api is enabled but the browser + * does not support it. + * + * @constructor + * @param {string} appBase application base URL + * @param {string} hashPrefix hashbang prefix + */ +function LocationHashbangInHtml5Url(appBase, hashPrefix) { + this.$$html5 = true; + LocationHashbangUrl.apply(this, arguments); + + var appBaseNoFile = stripFile(appBase); + + this.$$parseLinkUrl = function(url, relHref) { + if (relHref && relHref[0] === '#') { + // special case for links to hash fragments: + // keep the old url and only replace the hash fragment + this.hash(relHref.slice(1)); + return true; + } + + var rewrittenUrl; + var appUrl; + + if (appBase == stripHash(url)) { + rewrittenUrl = url; + } else if ((appUrl = beginsWith(appBaseNoFile, url))) { + rewrittenUrl = appBase + hashPrefix + appUrl; + } else if (appBaseNoFile === url + '/') { + rewrittenUrl = appBaseNoFile; + } + if (rewrittenUrl) { + this.$$parse(rewrittenUrl); + } + return !!rewrittenUrl; + }; + + this.$$compose = function() { + var search = toKeyValue(this.$$search), + hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; + + this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; + // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#' + this.$$absUrl = appBase + hashPrefix + this.$$url; + }; + +} + + +var locationPrototype = { + + /** + * Are we in html5 mode? + * @private + */ + $$html5: false, + + /** + * Has any change been replacing? + * @private + */ + $$replace: false, + + /** + * @ngdoc method + * @name $location#absUrl + * + * @description + * This method is getter only. + * + * Return full url representation with all segments encoded according to rules specified in + * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var absUrl = $location.absUrl(); + * // => "http://example.com/#/some/path?foo=bar&baz=xoxo" + * ``` + * + * @return {string} full url + */ + absUrl: locationGetter('$$absUrl'), + + /** + * @ngdoc method + * @name $location#url + * + * @description + * This method is getter / setter. + * + * Return url (e.g. `/path?a=b#hash`) when called without any parameter. + * + * Change path, search and hash, when called with parameter and return `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var url = $location.url(); + * // => "/some/path?foo=bar&baz=xoxo" + * ``` + * + * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) + * @return {string} url + */ + url: function(url) { + if (isUndefined(url)) + return this.$$url; + + var match = PATH_MATCH.exec(url); + if (match[1] || url === '') this.path(decodeURIComponent(match[1])); + if (match[2] || match[1] || url === '') this.search(match[3] || ''); + this.hash(match[5] || ''); + + return this; + }, + + /** + * @ngdoc method + * @name $location#protocol + * + * @description + * This method is getter only. + * + * Return protocol of current url. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var protocol = $location.protocol(); + * // => "http" + * ``` + * + * @return {string} protocol of current url + */ + protocol: locationGetter('$$protocol'), + + /** + * @ngdoc method + * @name $location#host + * + * @description + * This method is getter only. + * + * Return host of current url. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var host = $location.host(); + * // => "example.com" + * ``` + * + * @return {string} host of current url. + */ + host: locationGetter('$$host'), + + /** + * @ngdoc method + * @name $location#port + * + * @description + * This method is getter only. + * + * Return port of current url. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var port = $location.port(); + * // => 80 + * ``` + * + * @return {Number} port + */ + port: locationGetter('$$port'), + + /** + * @ngdoc method + * @name $location#path + * + * @description + * This method is getter / setter. + * + * Return path of current url when called without any parameter. + * + * Change path when called with parameter and return `$location`. + * + * Note: Path should always begin with forward slash (/), this method will add the forward slash + * if it is missing. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var path = $location.path(); + * // => "/some/path" + * ``` + * + * @param {(string|number)=} path New path + * @return {string} path + */ + path: locationGetterSetter('$$path', function(path) { + path = path !== null ? path.toString() : ''; + return path.charAt(0) == '/' ? path : '/' + path; + }), + + /** + * @ngdoc method + * @name $location#search + * + * @description + * This method is getter / setter. + * + * Return search part (as object) of current url when called without any parameter. + * + * Change search part when called with parameter and return `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo + * var searchObject = $location.search(); + * // => {foo: 'bar', baz: 'xoxo'} + * + * // set foo to 'yipee' + * $location.search('foo', 'yipee'); + * // $location.search() => {foo: 'yipee', baz: 'xoxo'} + * ``` + * + * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or + * hash object. + * + * When called with a single argument the method acts as a setter, setting the `search` component + * of `$location` to the specified value. + * + * If the argument is a hash object containing an array of values, these values will be encoded + * as duplicate search parameters in the url. + * + * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue` + * will override only a single search property. + * + * If `paramValue` is an array, it will override the property of the `search` component of + * `$location` specified via the first argument. + * + * If `paramValue` is `null`, the property specified via the first argument will be deleted. + * + * If `paramValue` is `true`, the property specified via the first argument will be added with no + * value nor trailing equal sign. + * + * @return {Object} If called with no arguments returns the parsed `search` object. If called with + * one or more arguments returns `$location` object itself. + */ + search: function(search, paramValue) { + switch (arguments.length) { + case 0: + return this.$$search; + case 1: + if (isString(search) || isNumber(search)) { + search = search.toString(); + this.$$search = parseKeyValue(search); + } else if (isObject(search)) { + search = copy(search, {}); + // remove object undefined or null properties + forEach(search, function(value, key) { + if (value == null) delete search[key]; + }); + + this.$$search = search; + } else { + throw $locationMinErr('isrcharg', + 'The first argument of the `$location#search()` call must be a string or an object.'); + } + break; + default: + if (isUndefined(paramValue) || paramValue === null) { + delete this.$$search[search]; + } else { + this.$$search[search] = paramValue; + } + } + + this.$$compose(); + return this; + }, + + /** + * @ngdoc method + * @name $location#hash + * + * @description + * This method is getter / setter. + * + * Return hash fragment when called without any parameter. + * + * Change hash fragment when called with parameter and return `$location`. + * + * + * ```js + * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue + * var hash = $location.hash(); + * // => "hashValue" + * ``` + * + * @param {(string|number)=} hash New hash fragment + * @return {string} hash + */ + hash: locationGetterSetter('$$hash', function(hash) { + return hash !== null ? hash.toString() : ''; + }), + + /** + * @ngdoc method + * @name $location#replace + * + * @description + * If called, all changes to $location during current `$digest` will be replacing current history + * record, instead of adding new one. + */ + replace: function() { + this.$$replace = true; + return this; + } +}; + +forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) { + Location.prototype = Object.create(locationPrototype); + + /** + * @ngdoc method + * @name $location#state + * + * @description + * This method is getter / setter. + * + * Return the history state object when called without any parameter. + * + * Change the history state object when called with one parameter and return `$location`. + * The state object is later passed to `pushState` or `replaceState`. + * + * NOTE: This method is supported only in HTML5 mode and only in browsers supporting + * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support + * older browsers (like IE9 or Android < 4.0), don't use this method. + * + * @param {object=} state State object for pushState or replaceState + * @return {object} state + */ + Location.prototype.state = function(state) { + if (!arguments.length) + return this.$$state; + + if (Location !== LocationHtml5Url || !this.$$html5) { + throw $locationMinErr('nostate', 'History API state support is available only ' + + 'in HTML5 mode and only in browsers supporting HTML5 History API'); + } + // The user might modify `stateObject` after invoking `$location.state(stateObject)` + // but we're changing the $$state reference to $browser.state() during the $digest + // so the modification window is narrow. + this.$$state = isUndefined(state) ? null : state; + + return this; + }; +}); + + +function locationGetter(property) { + return function() { + return this[property]; + }; +} + + +function locationGetterSetter(property, preprocess) { + return function(value) { + if (isUndefined(value)) + return this[property]; + + this[property] = preprocess(value); + this.$$compose(); + + return this; + }; +} + + +/** + * @ngdoc service + * @name $location + * + * @requires $rootElement + * + * @description + * The $location service parses the URL in the browser address bar (based on the + * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL + * available to your application. Changes to the URL in the address bar are reflected into + * $location service and changes to $location are reflected into the browser address bar. + * + * **The $location service:** + * + * - Exposes the current URL in the browser address bar, so you can + * - Watch and observe the URL. + * - Change the URL. + * - Synchronizes the URL with the browser when the user + * - Changes the address bar. + * - Clicks the back or forward button (or clicks a History link). + * - Clicks on a link. + * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). + * + * For more information see {@link guide/$location Developer Guide: Using $location} + */ + +/** + * @ngdoc provider + * @name $locationProvider + * @description + * Use the `$locationProvider` to configure how the application deep linking paths are stored. + */ +function $LocationProvider() { + var hashPrefix = '', + html5Mode = { + enabled: false, + requireBase: true, + rewriteLinks: true + }; + + /** + * @ngdoc method + * @name $locationProvider#hashPrefix + * @description + * @param {string=} prefix Prefix for hash part (containing path and search) + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.hashPrefix = function(prefix) { + if (isDefined(prefix)) { + hashPrefix = prefix; + return this; + } else { + return hashPrefix; + } + }; + + /** + * @ngdoc method + * @name $locationProvider#html5Mode + * @description + * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value. + * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported + * properties: + * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to + * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not + * support `pushState`. + * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies + * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are + * true, and a base tag is not present, an error will be thrown when `$location` is injected. + * See the {@link guide/$location $location guide for more information} + * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled, + * enables/disables url rewriting for relative links. + * + * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter + */ + this.html5Mode = function(mode) { + if (isBoolean(mode)) { + html5Mode.enabled = mode; + return this; + } else if (isObject(mode)) { + + if (isBoolean(mode.enabled)) { + html5Mode.enabled = mode.enabled; + } + + if (isBoolean(mode.requireBase)) { + html5Mode.requireBase = mode.requireBase; + } + + if (isBoolean(mode.rewriteLinks)) { + html5Mode.rewriteLinks = mode.rewriteLinks; + } + + return this; + } else { + return html5Mode; + } + }; + + /** + * @ngdoc event + * @name $location#$locationChangeStart + * @eventType broadcast on root scope + * @description + * Broadcasted before a URL will change. + * + * This change can be prevented by calling + * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more + * details about event object. Upon successful change + * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired. + * + * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when + * the browser supports the HTML5 History API. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + * @param {string=} newState New history state object + * @param {string=} oldState History state object that was before it was changed. + */ + + /** + * @ngdoc event + * @name $location#$locationChangeSuccess + * @eventType broadcast on root scope + * @description + * Broadcasted after a URL was changed. + * + * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when + * the browser supports the HTML5 History API. + * + * @param {Object} angularEvent Synthetic event object. + * @param {string} newUrl New URL + * @param {string=} oldUrl URL that was before it was changed. + * @param {string=} newState New history state object + * @param {string=} oldState History state object that was before it was changed. + */ + + this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window', + function($rootScope, $browser, $sniffer, $rootElement, $window) { + var $location, + LocationMode, + baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' + initialUrl = $browser.url(), + appBase; + + if (html5Mode.enabled) { + if (!baseHref && html5Mode.requireBase) { + throw $locationMinErr('nobase', + "$location in HTML5 mode requires a <base> tag to be present!"); + } + appBase = serverBase(initialUrl) + (baseHref || '/'); + LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; + } else { + appBase = stripHash(initialUrl); + LocationMode = LocationHashbangUrl; + } + $location = new LocationMode(appBase, '#' + hashPrefix); + $location.$$parseLinkUrl(initialUrl, initialUrl); + + $location.$$state = $browser.state(); + + var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; + + function setBrowserUrlWithFallback(url, replace, state) { + var oldUrl = $location.url(); + var oldState = $location.$$state; + try { + $browser.url(url, replace, state); + + // Make sure $location.state() returns referentially identical (not just deeply equal) + // state object; this makes possible quick checking if the state changed in the digest + // loop. Checking deep equality would be too expensive. + $location.$$state = $browser.state(); + } catch (e) { + // Restore old values if pushState fails + $location.url(oldUrl); + $location.$$state = oldState; + + throw e; + } + } + + $rootElement.on('click', function(event) { + // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) + // currently we open nice url link and redirect then + + if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return; + + var elm = jqLite(event.target); + + // traverse the DOM up to find first A tag + while (nodeName_(elm[0]) !== 'a') { + // ignore rewriting if no A tag (reached root element, or no parent - removed from document) + if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; + } + + var absHref = elm.prop('href'); + // get the actual href attribute - see + // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx + var relHref = elm.attr('href') || elm.attr('xlink:href'); + + if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { + // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during + // an animation. + absHref = urlResolve(absHref.animVal).href; + } + + // Ignore when url is started with javascript: or mailto: + if (IGNORE_URI_REGEXP.test(absHref)) return; + + if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { + if ($location.$$parseLinkUrl(absHref, relHref)) { + // We do a preventDefault for all urls that are part of the angular application, + // in html5mode and also without, so that we are able to abort navigation without + // getting double entries in the location history. + event.preventDefault(); + // update location manually + if ($location.absUrl() != $browser.url()) { + $rootScope.$apply(); + // hack to work around FF6 bug 684208 when scenario runner clicks on links + $window.angular['ff-684208-preventDefault'] = true; + } + } + } + }); + + + // rewrite hashbang url <> html5 url + if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) { + $browser.url($location.absUrl(), true); + } + + var initializing = true; + + // update $location when $browser url changes + $browser.onUrlChange(function(newUrl, newState) { + $rootScope.$evalAsync(function() { + var oldUrl = $location.absUrl(); + var oldState = $location.$$state; + var defaultPrevented; + + $location.$$parse(newUrl); + $location.$$state = newState; + + defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + newState, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + setBrowserUrlWithFallback(oldUrl, false, oldState); + } else { + initializing = false; + afterLocationChange(oldUrl, oldState); + } + }); + if (!$rootScope.$$phase) $rootScope.$digest(); + }); + + // update browser + $rootScope.$watch(function $locationWatch() { + var oldUrl = trimEmptyHash($browser.url()); + var newUrl = trimEmptyHash($location.absUrl()); + var oldState = $browser.state(); + var currentReplace = $location.$$replace; + var urlOrStateChanged = oldUrl !== newUrl || + ($location.$$html5 && $sniffer.history && oldState !== $location.$$state); + + if (initializing || urlOrStateChanged) { + initializing = false; + + $rootScope.$evalAsync(function() { + var newUrl = $location.absUrl(); + var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl, + $location.$$state, oldState).defaultPrevented; + + // if the location was changed by a `$locationChangeStart` handler then stop + // processing this location change + if ($location.absUrl() !== newUrl) return; + + if (defaultPrevented) { + $location.$$parse(oldUrl); + $location.$$state = oldState; + } else { + if (urlOrStateChanged) { + setBrowserUrlWithFallback(newUrl, currentReplace, + oldState === $location.$$state ? null : $location.$$state); + } + afterLocationChange(oldUrl, oldState); + } + }); + } + + $location.$$replace = false; + + // we don't need to return anything because $evalAsync will make the digest loop dirty when + // there is a change + }); + + return $location; + + function afterLocationChange(oldUrl, oldState) { + $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl, + $location.$$state, oldState); + } +}]; +} + +/** + * @ngdoc service + * @name $log + * @requires $window + * + * @description + * Simple service for logging. Default implementation safely writes the message + * into the browser's console (if present). + * + * The main purpose of this service is to simplify debugging and troubleshooting. + * + * The default is to log `debug` messages. You can use + * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. + * + * @example + <example module="logExample"> + <file name="script.js"> + angular.module('logExample', []) + .controller('LogController', ['$scope', '$log', function($scope, $log) { + $scope.$log = $log; + $scope.message = 'Hello World!'; + }]); + </file> + <file name="index.html"> + <div ng-controller="LogController"> + <p>Reload this page with open console, enter text and hit the log button...</p> + Message: + <input type="text" ng-model="message"/> + <button ng-click="$log.log(message)">log</button> + <button ng-click="$log.warn(message)">warn</button> + <button ng-click="$log.info(message)">info</button> + <button ng-click="$log.error(message)">error</button> + <button ng-click="$log.debug(message)">debug</button> + </div> + </file> + </example> + */ + +/** + * @ngdoc provider + * @name $logProvider + * @description + * Use the `$logProvider` to configure how the application logs messages + */ +function $LogProvider() { + var debug = true, + self = this; + + /** + * @ngdoc method + * @name $logProvider#debugEnabled + * @description + * @param {boolean=} flag enable or disable debug level messages + * @returns {*} current value if used as getter or itself (chaining) if used as setter + */ + this.debugEnabled = function(flag) { + if (isDefined(flag)) { + debug = flag; + return this; + } else { + return debug; + } + }; + + this.$get = ['$window', function($window) { + return { + /** + * @ngdoc method + * @name $log#log + * + * @description + * Write a log message + */ + log: consoleLog('log'), + + /** + * @ngdoc method + * @name $log#info + * + * @description + * Write an information message + */ + info: consoleLog('info'), + + /** + * @ngdoc method + * @name $log#warn + * + * @description + * Write a warning message + */ + warn: consoleLog('warn'), + + /** + * @ngdoc method + * @name $log#error + * + * @description + * Write an error message + */ + error: consoleLog('error'), + + /** + * @ngdoc method + * @name $log#debug + * + * @description + * Write a debug message + */ + debug: (function() { + var fn = consoleLog('debug'); + + return function() { + if (debug) { + fn.apply(self, arguments); + } + }; + }()) + }; + + function formatError(arg) { + if (arg instanceof Error) { + if (arg.stack) { + arg = (arg.message && arg.stack.indexOf(arg.message) === -1) + ? 'Error: ' + arg.message + '\n' + arg.stack + : arg.stack; + } else if (arg.sourceURL) { + arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; + } + } + return arg; + } + + function consoleLog(type) { + var console = $window.console || {}, + logFn = console[type] || console.log || noop, + hasApply = false; + + // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. + // The reason behind this is that console.log has type "object" in IE8... + try { + hasApply = !!logFn.apply; + } catch (e) {} + + if (hasApply) { + return function() { + var args = []; + forEach(arguments, function(arg) { + args.push(formatError(arg)); + }); + return logFn.apply(console, args); + }; + } + + // we are IE which either doesn't have window.console => this is noop and we do nothing, + // or we are IE where console.log doesn't have apply so we log at least first 2 args + return function(arg1, arg2) { + logFn(arg1, arg2 == null ? '' : arg2); + }; + } + }]; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 $parseMinErr = minErr('$parse'); + +// Sandboxing Angular Expressions +// ------------------------------ +// Angular expressions are generally considered safe because these expressions only have direct +// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by +// obtaining a reference to native JS functions such as the Function constructor. +// +// As an example, consider the following Angular expression: +// +// {}.toString.constructor('alert("evil JS code")') +// +// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits +// against the expression language, but not to prevent exploits that were enabled by exposing +// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good +// practice and therefore we are not even trying to protect against interaction with an object +// explicitly exposed in this way. +// +// In general, it is not possible to access a Window object from an angular expression unless a +// window or some DOM object that has a reference to window is published onto a Scope. +// Similarly we prevent invocations of function known to be dangerous, as well as assignments to +// native objects. +// +// See https://docs.angularjs.org/guide/security + + +function ensureSafeMemberName(name, fullExpression) { + if (name === "__defineGetter__" || name === "__defineSetter__" + || name === "__lookupGetter__" || name === "__lookupSetter__" + || name === "__proto__") { + throw $parseMinErr('isecfld', + 'Attempting to access a disallowed field in Angular expressions! ' + + 'Expression: {0}', fullExpression); + } + return name; +} + +function ensureSafeObject(obj, fullExpression) { + // nifty check if obj is Function that is fast and works across iframes and other contexts + if (obj) { + if (obj.constructor === obj) { + throw $parseMinErr('isecfn', + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// isWindow(obj) + obj.window === obj) { + throw $parseMinErr('isecwindow', + 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// isElement(obj) + obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { + throw $parseMinErr('isecdom', + 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (// block Object so that we can't get hold of dangerous Object.* methods + obj === Object) { + throw $parseMinErr('isecobj', + 'Referencing Object in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } + } + return obj; +} + +var CALL = Function.prototype.call; +var APPLY = Function.prototype.apply; +var BIND = Function.prototype.bind; + +function ensureSafeFunction(obj, fullExpression) { + if (obj) { + if (obj.constructor === obj) { + throw $parseMinErr('isecfn', + 'Referencing Function in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } else if (obj === CALL || obj === APPLY || obj === BIND) { + throw $parseMinErr('isecff', + 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', + fullExpression); + } + } +} + +//Keyword constants +var CONSTANTS = createMap(); +forEach({ + 'null': function() { return null; }, + 'true': function() { return true; }, + 'false': function() { return false; }, + 'undefined': function() {} +}, function(constantGetter, name) { + constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true; + CONSTANTS[name] = constantGetter; +}); + +//Not quite a constant, but can be lex/parsed the same +CONSTANTS['this'] = function(self) { return self; }; +CONSTANTS['this'].sharedGetter = true; + + +//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter +var OPERATORS = extend(createMap(), { + '+':function(self, locals, a, b) { + a=a(self, locals); b=b(self, locals); + if (isDefined(a)) { + if (isDefined(b)) { + return a + b; + } + return a; + } + return isDefined(b) ? b : undefined;}, + '-':function(self, locals, a, b) { + a=a(self, locals); b=b(self, locals); + return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0); + }, + '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);}, + '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);}, + '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);}, + '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);}, + '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);}, + '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);}, + '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);}, + '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);}, + '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);}, + '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);}, + '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);}, + '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);}, + '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);}, + '!':function(self, locals, a) {return !a(self, locals);}, + + //Tokenized as operators but parsed as assignment/filters + '=':true, + '|':true +}); +var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; + + +///////////////////////////////////////// + + +/** + * @constructor + */ +var Lexer = function(options) { + this.options = options; +}; + +Lexer.prototype = { + constructor: Lexer, + + lex: function(text) { + this.text = text; + this.index = 0; + this.tokens = []; + + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (ch === '"' || ch === "'") { + this.readString(ch); + } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { + this.readNumber(); + } else if (this.isIdent(ch)) { + this.readIdent(); + } else if (this.is(ch, '(){}[].,;:?')) { + this.tokens.push({index: this.index, text: ch}); + this.index++; + } else if (this.isWhitespace(ch)) { + this.index++; + } else { + var ch2 = ch + this.peek(); + var ch3 = ch2 + this.peek(2); + var op1 = OPERATORS[ch]; + var op2 = OPERATORS[ch2]; + var op3 = OPERATORS[ch3]; + if (op1 || op2 || op3) { + var token = op3 ? ch3 : (op2 ? ch2 : ch); + this.tokens.push({index: this.index, text: token, operator: true}); + this.index += token.length; + } else { + this.throwError('Unexpected next character ', this.index, this.index + 1); + } + } + } + return this.tokens; + }, + + is: function(ch, chars) { + return chars.indexOf(ch) !== -1; + }, + + peek: function(i) { + var num = i || 1; + return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; + }, + + isNumber: function(ch) { + return ('0' <= ch && ch <= '9') && typeof ch === "string"; + }, + + isWhitespace: function(ch) { + // IE treats non-breaking space as \u00A0 + return (ch === ' ' || ch === '\r' || ch === '\t' || + ch === '\n' || ch === '\v' || ch === '\u00A0'); + }, + + isIdent: function(ch) { + return ('a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' === ch || ch === '$'); + }, + + isExpOperator: function(ch) { + return (ch === '-' || ch === '+' || this.isNumber(ch)); + }, + + throwError: function(error, start, end) { + end = end || this.index; + var colStr = (isDefined(start) + ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' + : ' ' + end); + throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', + error, colStr, this.text); + }, + + readNumber: function() { + var number = ''; + var start = this.index; + while (this.index < this.text.length) { + var ch = lowercase(this.text.charAt(this.index)); + if (ch == '.' || this.isNumber(ch)) { + number += ch; + } else { + var peekCh = this.peek(); + if (ch == 'e' && this.isExpOperator(peekCh)) { + number += ch; + } else if (this.isExpOperator(ch) && + peekCh && this.isNumber(peekCh) && + number.charAt(number.length - 1) == 'e') { + number += ch; + } else if (this.isExpOperator(ch) && + (!peekCh || !this.isNumber(peekCh)) && + number.charAt(number.length - 1) == 'e') { + this.throwError('Invalid exponent'); + } else { + break; + } + } + this.index++; + } + this.tokens.push({ + index: start, + text: number, + constant: true, + value: Number(number) + }); + }, + + readIdent: function() { + var start = this.index; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + if (!(this.isIdent(ch) || this.isNumber(ch))) { + break; + } + this.index++; + } + this.tokens.push({ + index: start, + text: this.text.slice(start, this.index), + identifier: true + }); + }, + + readString: function(quote) { + var start = this.index; + this.index++; + var string = ''; + var rawString = quote; + var escape = false; + while (this.index < this.text.length) { + var ch = this.text.charAt(this.index); + rawString += ch; + if (escape) { + if (ch === 'u') { + var hex = this.text.substring(this.index + 1, this.index + 5); + if (!hex.match(/[\da-f]{4}/i)) + this.throwError('Invalid unicode escape [\\u' + hex + ']'); + this.index += 4; + string += String.fromCharCode(parseInt(hex, 16)); + } else { + var rep = ESCAPE[ch]; + string = string + (rep || ch); + } + escape = false; + } else if (ch === '\\') { + escape = true; + } else if (ch === quote) { + this.index++; + this.tokens.push({ + index: start, + text: rawString, + constant: true, + value: string + }); + return; + } else { + string += ch; + } + this.index++; + } + this.throwError('Unterminated quote', start); + } +}; + + +function isConstant(exp) { + return exp.constant; +} + +/** + * @constructor + */ +var Parser = function(lexer, $filter, options) { + this.lexer = lexer; + this.$filter = $filter; + this.options = options; +}; + +Parser.ZERO = extend(function() { + return 0; +}, { + sharedGetter: true, + constant: true +}); + +Parser.prototype = { + constructor: Parser, + + parse: function(text) { + this.text = text; + this.tokens = this.lexer.lex(text); + + var value = this.statements(); + + if (this.tokens.length !== 0) { + this.throwError('is an unexpected token', this.tokens[0]); + } + + value.literal = !!value.literal; + value.constant = !!value.constant; + + return value; + }, + + primary: function() { + var primary; + if (this.expect('(')) { + primary = this.filterChain(); + this.consume(')'); + } else if (this.expect('[')) { + primary = this.arrayDeclaration(); + } else if (this.expect('{')) { + primary = this.object(); + } else if (this.peek().identifier && this.peek().text in CONSTANTS) { + primary = CONSTANTS[this.consume().text]; + } else if (this.peek().identifier) { + primary = this.identifier(); + } else if (this.peek().constant) { + primary = this.constant(); + } else { + this.throwError('not a primary expression', this.peek()); + } + + var next, context; + while ((next = this.expect('(', '[', '.'))) { + if (next.text === '(') { + primary = this.functionCall(primary, context); + context = null; + } else if (next.text === '[') { + context = primary; + primary = this.objectIndex(primary); + } else if (next.text === '.') { + context = primary; + primary = this.fieldAccess(primary); + } else { + this.throwError('IMPOSSIBLE'); + } + } + return primary; + }, + + throwError: function(msg, token) { + throw $parseMinErr('syntax', + 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', + token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); + }, + + peekToken: function() { + if (this.tokens.length === 0) + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + return this.tokens[0]; + }, + + peek: function(e1, e2, e3, e4) { + return this.peekAhead(0, e1, e2, e3, e4); + }, + peekAhead: function(i, e1, e2, e3, e4) { + if (this.tokens.length > i) { + var token = this.tokens[i]; + var t = token.text; + if (t === e1 || t === e2 || t === e3 || t === e4 || + (!e1 && !e2 && !e3 && !e4)) { + return token; + } + } + return false; + }, + + expect: function(e1, e2, e3, e4) { + var token = this.peek(e1, e2, e3, e4); + if (token) { + this.tokens.shift(); + return token; + } + return false; + }, + + consume: function(e1) { + if (this.tokens.length === 0) { + throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); + } + + var token = this.expect(e1); + if (!token) { + this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); + } + return token; + }, + + unaryFn: function(op, right) { + var fn = OPERATORS[op]; + return extend(function $parseUnaryFn(self, locals) { + return fn(self, locals, right); + }, { + constant:right.constant, + inputs: [right] + }); + }, + + binaryFn: function(left, op, right, isBranching) { + var fn = OPERATORS[op]; + return extend(function $parseBinaryFn(self, locals) { + return fn(self, locals, left, right); + }, { + constant: left.constant && right.constant, + inputs: !isBranching && [left, right] + }); + }, + + identifier: function() { + var id = this.consume().text; + + //Continue reading each `.identifier` unless it is a method invocation + while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) { + id += this.consume().text + this.consume().text; + } + + return getterFn(id, this.options, this.text); + }, + + constant: function() { + var value = this.consume().value; + + return extend(function $parseConstant() { + return value; + }, { + constant: true, + literal: true + }); + }, + + statements: function() { + var statements = []; + while (true) { + if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) + statements.push(this.filterChain()); + if (!this.expect(';')) { + // optimize for the common case where there is only one statement. + // TODO(size): maybe we should not support multiple statements? + return (statements.length === 1) + ? statements[0] + : function $parseStatements(self, locals) { + var value; + for (var i = 0, ii = statements.length; i < ii; i++) { + value = statements[i](self, locals); + } + return value; + }; + } + } + }, + + filterChain: function() { + var left = this.expression(); + var token; + while ((token = this.expect('|'))) { + left = this.filter(left); + } + return left; + }, + + filter: function(inputFn) { + var fn = this.$filter(this.consume().text); + var argsFn; + var args; + + if (this.peek(':')) { + argsFn = []; + args = []; // we can safely reuse the array + while (this.expect(':')) { + argsFn.push(this.expression()); + } + } + + var inputs = [inputFn].concat(argsFn || []); + + return extend(function $parseFilter(self, locals) { + var input = inputFn(self, locals); + if (args) { + args[0] = input; + + var i = argsFn.length; + while (i--) { + args[i + 1] = argsFn[i](self, locals); + } + + return fn.apply(undefined, args); + } + + return fn(input); + }, { + constant: !fn.$stateful && inputs.every(isConstant), + inputs: !fn.$stateful && inputs + }); + }, + + expression: function() { + return this.assignment(); + }, + + assignment: function() { + var left = this.ternary(); + var right; + var token; + if ((token = this.expect('='))) { + if (!left.assign) { + this.throwError('implies assignment but [' + + this.text.substring(0, token.index) + '] can not be assigned to', token); + } + right = this.ternary(); + return extend(function $parseAssignment(scope, locals) { + return left.assign(scope, right(scope, locals), locals); + }, { + inputs: [left, right] + }); + } + return left; + }, + + ternary: function() { + var left = this.logicalOR(); + var middle; + var token; + if ((token = this.expect('?'))) { + middle = this.assignment(); + if (this.consume(':')) { + var right = this.assignment(); + + return extend(function $parseTernary(self, locals) { + return left(self, locals) ? middle(self, locals) : right(self, locals); + }, { + constant: left.constant && middle.constant && right.constant + }); + } + } + + return left; + }, + + logicalOR: function() { + var left = this.logicalAND(); + var token; + while ((token = this.expect('||'))) { + left = this.binaryFn(left, token.text, this.logicalAND(), true); + } + return left; + }, + + logicalAND: function() { + var left = this.equality(); + var token; + while ((token = this.expect('&&'))) { + left = this.binaryFn(left, token.text, this.equality(), true); + } + return left; + }, + + equality: function() { + var left = this.relational(); + var token; + while ((token = this.expect('==','!=','===','!=='))) { + left = this.binaryFn(left, token.text, this.relational()); + } + return left; + }, + + relational: function() { + var left = this.additive(); + var token; + while ((token = this.expect('<', '>', '<=', '>='))) { + left = this.binaryFn(left, token.text, this.additive()); + } + return left; + }, + + additive: function() { + var left = this.multiplicative(); + var token; + while ((token = this.expect('+','-'))) { + left = this.binaryFn(left, token.text, this.multiplicative()); + } + return left; + }, + + multiplicative: function() { + var left = this.unary(); + var token; + while ((token = this.expect('*','/','%'))) { + left = this.binaryFn(left, token.text, this.unary()); + } + return left; + }, + + unary: function() { + var token; + if (this.expect('+')) { + return this.primary(); + } else if ((token = this.expect('-'))) { + return this.binaryFn(Parser.ZERO, token.text, this.unary()); + } else if ((token = this.expect('!'))) { + return this.unaryFn(token.text, this.unary()); + } else { + return this.primary(); + } + }, + + fieldAccess: function(object) { + var getter = this.identifier(); + + return extend(function $parseFieldAccess(scope, locals, self) { + var o = self || object(scope, locals); + return (o == null) ? undefined : getter(o); + }, { + assign: function(scope, value, locals) { + var o = object(scope, locals); + if (!o) object.assign(scope, o = {}, locals); + return getter.assign(o, value); + } + }); + }, + + objectIndex: function(obj) { + var expression = this.text; + + var indexFn = this.expression(); + this.consume(']'); + + return extend(function $parseObjectIndex(self, locals) { + var o = obj(self, locals), + i = indexFn(self, locals), + v; + + ensureSafeMemberName(i, expression); + if (!o) return undefined; + v = ensureSafeObject(o[i], expression); + return v; + }, { + assign: function(self, value, locals) { + var key = ensureSafeMemberName(indexFn(self, locals), expression); + // prevent overwriting of Function.constructor which would break ensureSafeObject check + var o = ensureSafeObject(obj(self, locals), expression); + if (!o) obj.assign(self, o = {}, locals); + return o[key] = value; + } + }); + }, + + functionCall: function(fnGetter, contextGetter) { + var argsFn = []; + if (this.peekToken().text !== ')') { + do { + argsFn.push(this.expression()); + } while (this.expect(',')); + } + this.consume(')'); + + var expressionText = this.text; + // we can safely reuse the array across invocations + var args = argsFn.length ? [] : null; + + return function $parseFunctionCall(scope, locals) { + var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope; + var fn = fnGetter(scope, locals, context) || noop; + + if (args) { + var i = argsFn.length; + while (i--) { + args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText); + } + } + + ensureSafeObject(context, expressionText); + ensureSafeFunction(fn, expressionText); + + // IE doesn't have apply for some native functions + var v = fn.apply + ? fn.apply(context, args) + : fn(args[0], args[1], args[2], args[3], args[4]); + + if (args) { + // Free-up the memory (arguments of the last function call). + args.length = 0; + } + + return ensureSafeObject(v, expressionText); + }; + }, + + // This is used with json array declaration + arrayDeclaration: function() { + var elementFns = []; + if (this.peekToken().text !== ']') { + do { + if (this.peek(']')) { + // Support trailing commas per ES5.1. + break; + } + elementFns.push(this.expression()); + } while (this.expect(',')); + } + this.consume(']'); + + return extend(function $parseArrayLiteral(self, locals) { + var array = []; + for (var i = 0, ii = elementFns.length; i < ii; i++) { + array.push(elementFns[i](self, locals)); + } + return array; + }, { + literal: true, + constant: elementFns.every(isConstant), + inputs: elementFns + }); + }, + + object: function() { + var keys = [], valueFns = []; + if (this.peekToken().text !== '}') { + do { + if (this.peek('}')) { + // Support trailing commas per ES5.1. + break; + } + var token = this.consume(); + if (token.constant) { + keys.push(token.value); + } else if (token.identifier) { + keys.push(token.text); + } else { + this.throwError("invalid key", token); + } + this.consume(':'); + valueFns.push(this.expression()); + } while (this.expect(',')); + } + this.consume('}'); + + return extend(function $parseObjectLiteral(self, locals) { + var object = {}; + for (var i = 0, ii = valueFns.length; i < ii; i++) { + object[keys[i]] = valueFns[i](self, locals); + } + return object; + }, { + literal: true, + constant: valueFns.every(isConstant), + inputs: valueFns + }); + } +}; + + +////////////////////////////////////////////////// +// Parser helper functions +////////////////////////////////////////////////// + +function setter(obj, locals, path, setValue, fullExp) { + ensureSafeObject(obj, fullExp); + ensureSafeObject(locals, fullExp); + + var element = path.split('.'), key; + for (var i = 0; element.length > 1; i++) { + key = ensureSafeMemberName(element.shift(), fullExp); + var propertyObj = (i === 0 && locals && locals[key]) || obj[key]; + if (!propertyObj) { + propertyObj = {}; + obj[key] = propertyObj; + } + obj = ensureSafeObject(propertyObj, fullExp); + } + key = ensureSafeMemberName(element.shift(), fullExp); + ensureSafeObject(obj[key], fullExp); + obj[key] = setValue; + return setValue; +} + +var getterFnCacheDefault = createMap(); +var getterFnCacheExpensive = createMap(); + +function isPossiblyDangerousMemberName(name) { + return name == 'constructor'; +} + +/** + * Implementation of the "Black Hole" variant from: + * - http://jsperf.com/angularjs-parse-getter/4 + * - http://jsperf.com/path-evaluation-simplified/7 + */ +function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) { + ensureSafeMemberName(key0, fullExp); + ensureSafeMemberName(key1, fullExp); + ensureSafeMemberName(key2, fullExp); + ensureSafeMemberName(key3, fullExp); + ensureSafeMemberName(key4, fullExp); + var eso = function(o) { + return ensureSafeObject(o, fullExp); + }; + var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity; + var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity; + var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity; + var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity; + var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity; + + return function cspSafeGetter(scope, locals) { + var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; + + if (pathVal == null) return pathVal; + pathVal = eso0(pathVal[key0]); + + if (!key1) return pathVal; + if (pathVal == null) return undefined; + pathVal = eso1(pathVal[key1]); + + if (!key2) return pathVal; + if (pathVal == null) return undefined; + pathVal = eso2(pathVal[key2]); + + if (!key3) return pathVal; + if (pathVal == null) return undefined; + pathVal = eso3(pathVal[key3]); + + if (!key4) return pathVal; + if (pathVal == null) return undefined; + pathVal = eso4(pathVal[key4]); + + return pathVal; + }; +} + +function getterFnWithEnsureSafeObject(fn, fullExpression) { + return function(s, l) { + return fn(s, l, ensureSafeObject, fullExpression); + }; +} + +function getterFn(path, options, fullExp) { + var expensiveChecks = options.expensiveChecks; + var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault); + var fn = getterFnCache[path]; + if (fn) return fn; + + + var pathKeys = path.split('.'), + pathKeysLength = pathKeys.length; + + // http://jsperf.com/angularjs-parse-getter/6 + if (options.csp) { + if (pathKeysLength < 6) { + fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks); + } else { + fn = function cspSafeGetter(scope, locals) { + var i = 0, val; + do { + val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], + pathKeys[i++], fullExp, expensiveChecks)(scope, locals); + + locals = undefined; // clear after first iteration + scope = val; + } while (i < pathKeysLength); + return val; + }; + } + } else { + var code = ''; + if (expensiveChecks) { + code += 's = eso(s, fe);\nl = eso(l, fe);\n'; + } + var needsEnsureSafeObject = expensiveChecks; + forEach(pathKeys, function(key, index) { + ensureSafeMemberName(key, fullExp); + var lookupJs = (index + // we simply dereference 's' on any .dot notation + ? 's' + // but if we are first then we check locals first, and if so read it first + : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key; + if (expensiveChecks || isPossiblyDangerousMemberName(key)) { + lookupJs = 'eso(' + lookupJs + ', fe)'; + needsEnsureSafeObject = true; + } + code += 'if(s == null) return undefined;\n' + + 's=' + lookupJs + ';\n'; + }); + code += 'return s;'; + + /* jshint -W054 */ + var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject + /* jshint +W054 */ + evaledFnGetter.toString = valueFn(code); + if (needsEnsureSafeObject) { + evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp); + } + fn = evaledFnGetter; + } + + fn.sharedGetter = true; + fn.assign = function(self, value, locals) { + return setter(self, locals, path, value, path); + }; + getterFnCache[path] = fn; + return fn; +} + +var objectValueOf = Object.prototype.valueOf; + +function getValueOf(value) { + return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value); +} + +/////////////////////////////////// + +/** + * @ngdoc service + * @name $parse + * @kind function + * + * @description + * + * Converts Angular {@link guide/expression expression} into a function. + * + * ```js + * var getter = $parse('user.name'); + * var setter = getter.assign; + * var context = {user:{name:'angular'}}; + * var locals = {user:{name:'local'}}; + * + * expect(getter(context)).toEqual('angular'); + * setter(context, 'newValue'); + * expect(context.user.name).toEqual('newValue'); + * expect(getter(context, locals)).toEqual('local'); + * ``` + * + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + * + * The returned function also has the following properties: + * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript + * literal. + * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript + * constant literals. + * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be + * set to a function to change its value on the given context. + * + */ + + +/** + * @ngdoc provider + * @name $parseProvider + * + * @description + * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} + * service. + */ +function $ParseProvider() { + var cacheDefault = createMap(); + var cacheExpensive = createMap(); + + + + this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { + var $parseOptions = { + csp: $sniffer.csp, + expensiveChecks: false + }, + $parseOptionsExpensive = { + csp: $sniffer.csp, + expensiveChecks: true + }; + + function wrapSharedExpression(exp) { + var wrapped = exp; + + if (exp.sharedGetter) { + wrapped = function $parseWrapper(self, locals) { + return exp(self, locals); + }; + wrapped.literal = exp.literal; + wrapped.constant = exp.constant; + wrapped.assign = exp.assign; + } + + return wrapped; + } + + return function $parse(exp, interceptorFn, expensiveChecks) { + var parsedExpression, oneTime, cacheKey; + + switch (typeof exp) { + case 'string': + cacheKey = exp = exp.trim(); + + var cache = (expensiveChecks ? cacheExpensive : cacheDefault); + parsedExpression = cache[cacheKey]; + + if (!parsedExpression) { + if (exp.charAt(0) === ':' && exp.charAt(1) === ':') { + oneTime = true; + exp = exp.substring(2); + } + + var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions; + var lexer = new Lexer(parseOptions); + var parser = new Parser(lexer, $filter, parseOptions); + parsedExpression = parser.parse(exp); + + if (parsedExpression.constant) { + parsedExpression.$$watchDelegate = constantWatchDelegate; + } else if (oneTime) { + //oneTime is not part of the exp passed to the Parser so we may have to + //wrap the parsedExpression before adding a $$watchDelegate + parsedExpression = wrapSharedExpression(parsedExpression); + parsedExpression.$$watchDelegate = parsedExpression.literal ? + oneTimeLiteralWatchDelegate : oneTimeWatchDelegate; + } else if (parsedExpression.inputs) { + parsedExpression.$$watchDelegate = inputsWatchDelegate; + } + + cache[cacheKey] = parsedExpression; + } + return addInterceptor(parsedExpression, interceptorFn); + + case 'function': + return addInterceptor(exp, interceptorFn); + + default: + return addInterceptor(noop, interceptorFn); + } + }; + + function collectExpressionInputs(inputs, list) { + for (var i = 0, ii = inputs.length; i < ii; i++) { + var input = inputs[i]; + if (!input.constant) { + if (input.inputs) { + collectExpressionInputs(input.inputs, list); + } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better? + list.push(input); + } + } + } + + return list; + } + + function expressionInputDirtyCheck(newValue, oldValueOfValue) { + + if (newValue == null || oldValueOfValue == null) { // null/undefined + return newValue === oldValueOfValue; + } + + if (typeof newValue === 'object') { + + // attempt to convert the value to a primitive type + // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can + // be cheaply dirty-checked + newValue = getValueOf(newValue); + + if (typeof newValue === 'object') { + // objects/arrays are not supported - deep-watching them would be too expensive + return false; + } + + // fall-through to the primitive equality check + } + + //Primitive or NaN + return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue); + } + + function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var inputExpressions = parsedExpression.$$inputs || + (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, [])); + + var lastResult; + + if (inputExpressions.length === 1) { + var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails + inputExpressions = inputExpressions[0]; + return scope.$watch(function expressionInputWatch(scope) { + var newInputValue = inputExpressions(scope); + if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) { + lastResult = parsedExpression(scope); + oldInputValue = newInputValue && getValueOf(newInputValue); + } + return lastResult; + }, listener, objectEquality); + } + + var oldInputValueOfValues = []; + for (var i = 0, ii = inputExpressions.length; i < ii; i++) { + oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails + } + + return scope.$watch(function expressionInputsWatch(scope) { + var changed = false; + + for (var i = 0, ii = inputExpressions.length; i < ii; i++) { + var newInputValue = inputExpressions[i](scope); + if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) { + oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue); + } + } + + if (changed) { + lastResult = parsedExpression(scope); + } + + return lastResult; + }, listener, objectEquality); + } + + function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch, lastValue; + return unwatch = scope.$watch(function oneTimeWatch(scope) { + return parsedExpression(scope); + }, function oneTimeListener(value, old, scope) { + lastValue = value; + if (isFunction(listener)) { + listener.apply(this, arguments); + } + if (isDefined(value)) { + scope.$$postDigest(function() { + if (isDefined(lastValue)) { + unwatch(); + } + }); + } + }, objectEquality); + } + + function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch, lastValue; + return unwatch = scope.$watch(function oneTimeWatch(scope) { + return parsedExpression(scope); + }, function oneTimeListener(value, old, scope) { + lastValue = value; + if (isFunction(listener)) { + listener.call(this, value, old, scope); + } + if (isAllDefined(value)) { + scope.$$postDigest(function() { + if (isAllDefined(lastValue)) unwatch(); + }); + } + }, objectEquality); + + function isAllDefined(value) { + var allDefined = true; + forEach(value, function(val) { + if (!isDefined(val)) allDefined = false; + }); + return allDefined; + } + } + + function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) { + var unwatch; + return unwatch = scope.$watch(function constantWatch(scope) { + return parsedExpression(scope); + }, function constantListener(value, old, scope) { + if (isFunction(listener)) { + listener.apply(this, arguments); + } + unwatch(); + }, objectEquality); + } + + function addInterceptor(parsedExpression, interceptorFn) { + if (!interceptorFn) return parsedExpression; + var watchDelegate = parsedExpression.$$watchDelegate; + + var regularWatch = + watchDelegate !== oneTimeLiteralWatchDelegate && + watchDelegate !== oneTimeWatchDelegate; + + var fn = regularWatch ? function regularInterceptedExpression(scope, locals) { + var value = parsedExpression(scope, locals); + return interceptorFn(value, scope, locals); + } : function oneTimeInterceptedExpression(scope, locals) { + var value = parsedExpression(scope, locals); + var result = interceptorFn(value, scope, locals); + // we only return the interceptor's result if the + // initial value is defined (for bind-once) + return isDefined(value) ? result : value; + }; + + // Propagate $$watchDelegates other then inputsWatchDelegate + if (parsedExpression.$$watchDelegate && + parsedExpression.$$watchDelegate !== inputsWatchDelegate) { + fn.$$watchDelegate = parsedExpression.$$watchDelegate; + } else if (!interceptorFn.$stateful) { + // If there is an interceptor, but no watchDelegate then treat the interceptor like + // we treat filters - it is assumed to be a pure function unless flagged with $stateful + fn.$$watchDelegate = inputsWatchDelegate; + fn.inputs = [parsedExpression]; + } + + return fn; + } + }]; +} + +/** + * @ngdoc service + * @name $q + * @requires $rootScope + * + * @description + * A service that helps you run functions asynchronously, and use their return values (or exceptions) + * when they are done processing. + * + * This is an implementation of promises/deferred objects inspired by + * [Kris Kowal's Q](https://github.com/kriskowal/q). + * + * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred + * implementations, and the other which resembles ES6 promises to some degree. + * + * # $q constructor + * + * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver` + * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony, + * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + * + * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are + * available yet. + * + * It can be used like so: + * + * ```js + * // for the purpose of this example let's assume that variables `$q` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * // perform some asynchronous operation, resolve or reject the promise when appropriate. + * return $q(function(resolve, reject) { + * setTimeout(function() { + * if (okToGreet(name)) { + * resolve('Hello, ' + name + '!'); + * } else { + * reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * }); + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }); + * ``` + * + * Note: progress/notify callbacks are not currently supported via the ES6-style interface. + * + * However, the more traditional CommonJS-style usage is still available, and documented below. + * + * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an + * interface for interacting with an object that represents the result of an action that is + * performed asynchronously, and may or may not be finished at any given point in time. + * + * From the perspective of dealing with error handling, deferred and promise APIs are to + * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. + * + * ```js + * // for the purpose of this example let's assume that variables `$q` and `okToGreet` + * // are available in the current lexical scope (they could have been injected or passed in). + * + * function asyncGreet(name) { + * var deferred = $q.defer(); + * + * setTimeout(function() { + * deferred.notify('About to greet ' + name + '.'); + * + * if (okToGreet(name)) { + * deferred.resolve('Hello, ' + name + '!'); + * } else { + * deferred.reject('Greeting ' + name + ' is not allowed.'); + * } + * }, 1000); + * + * return deferred.promise; + * } + * + * var promise = asyncGreet('Robin Hood'); + * promise.then(function(greeting) { + * alert('Success: ' + greeting); + * }, function(reason) { + * alert('Failed: ' + reason); + * }, function(update) { + * alert('Got notification: ' + update); + * }); + * ``` + * + * At first it might not be obvious why this extra complexity is worth the trouble. The payoff + * comes in the way of guarantees that promise and deferred APIs make, see + * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. + * + * Additionally the promise api allows for composition that is very hard to do with the + * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. + * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the + * section on serial or parallel joining of promises. + * + * # The Deferred API + * + * A new instance of deferred is constructed by calling `$q.defer()`. + * + * The purpose of the deferred object is to expose the associated Promise instance as well as APIs + * that can be used for signaling the successful or unsuccessful completion, as well as the status + * of the task. + * + * **Methods** + * + * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection + * constructed via `$q.reject`, the promise will be rejected instead. + * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to + * resolving it with a rejection constructed via `$q.reject`. + * - `notify(value)` - provides updates on the status of the promise's execution. This may be called + * multiple times before the promise is either resolved or rejected. + * + * **Properties** + * + * - promise – `{Promise}` – promise object associated with this deferred. + * + * + * # The Promise API + * + * A new promise instance is created when a deferred instance is created and can be retrieved by + * calling `deferred.promise`. + * + * The purpose of the promise object is to allow for interested parties to get access to the result + * of the deferred task when it completes. + * + * **Methods** + * + * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or + * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously + * as soon as the result is available. The callbacks are called with a single argument: the result + * or rejection reason. Additionally, the notify callback may be called zero or more times to + * provide a progress indication, before the promise is resolved or rejected. + * + * This method *returns a new promise* which is resolved or rejected via the return value of the + * `successCallback`, `errorCallback`. It also notifies via the return value of the + * `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback + * method. + * + * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` + * + * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise, + * but to do so without modifying the final value. This is useful to release resources or do some + * clean-up that needs to be done whether the promise was rejected or resolved. See the [full + * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for + * more information. + * + * # Chaining promises + * + * Because calling the `then` method of a promise returns a new derived promise, it is easily + * possible to create a chain of promises: + * + * ```js + * promiseB = promiseA.then(function(result) { + * return result + 1; + * }); + * + * // promiseB will be resolved immediately after promiseA is resolved and its value + * // will be the result of promiseA incremented by 1 + * ``` + * + * It is possible to create chains of any length and since a promise can be resolved with another + * promise (which will defer its resolution further), it is possible to pause/defer resolution of + * the promises at any point in the chain. This makes it possible to implement powerful APIs like + * $http's response interceptors. + * + * + * # Differences between Kris Kowal's Q and $q + * + * There are two main differences: + * + * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation + * mechanism in angular, which means faster propagation of resolution or rejection into your + * models and avoiding unnecessary browser repaints, which would result in flickering UI. + * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains + * all the important functionality needed for common async tasks. + * + * # Testing + * + * ```js + * it('should simulate promise', inject(function($q, $rootScope) { + * var deferred = $q.defer(); + * var promise = deferred.promise; + * var resolvedValue; + * + * promise.then(function(value) { resolvedValue = value; }); + * expect(resolvedValue).toBeUndefined(); + * + * // Simulate resolving of promise + * deferred.resolve(123); + * // Note that the 'then' function does not get called synchronously. + * // This is because we want the promise API to always be async, whether or not + * // it got called synchronously or asynchronously. + * expect(resolvedValue).toBeUndefined(); + * + * // Propagate promise resolution to 'then' functions using $apply(). + * $rootScope.$apply(); + * expect(resolvedValue).toEqual(123); + * })); + * ``` + * + * @param {function(function, function)} resolver Function which is responsible for resolving or + * rejecting the newly created promise. The first parameter is a function which resolves the + * promise, the second parameter is a function which rejects the promise. + * + * @returns {Promise} The newly created promise. + */ +function $QProvider() { + + this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { + return qFactory(function(callback) { + $rootScope.$evalAsync(callback); + }, $exceptionHandler); + }]; +} + +function $$QProvider() { + this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) { + return qFactory(function(callback) { + $browser.defer(callback); + }, $exceptionHandler); + }]; +} + +/** + * Constructs a promise manager. + * + * @param {function(function)} nextTick Function for executing functions in the next turn. + * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for + * debugging purposes. + * @returns {object} Promise manager. + */ +function qFactory(nextTick, exceptionHandler) { + var $qMinErr = minErr('$q', TypeError); + function callOnce(self, resolveFn, rejectFn) { + var called = false; + function wrap(fn) { + return function(value) { + if (called) return; + called = true; + fn.call(self, value); + }; + } + + return [wrap(resolveFn), wrap(rejectFn)]; + } + + /** + * @ngdoc method + * @name ng.$q#defer + * @kind function + * + * @description + * Creates a `Deferred` object which represents a task which will finish in the future. + * + * @returns {Deferred} Returns a new instance of deferred. + */ + var defer = function() { + return new Deferred(); + }; + + function Promise() { + this.$$state = { status: 0 }; + } + + Promise.prototype = { + then: function(onFulfilled, onRejected, progressBack) { + var result = new Deferred(); + + this.$$state.pending = this.$$state.pending || []; + this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]); + if (this.$$state.status > 0) scheduleProcessQueue(this.$$state); + + return result.promise; + }, + + "catch": function(callback) { + return this.then(null, callback); + }, + + "finally": function(callback, progressBack) { + return this.then(function(value) { + return handleCallback(value, true, callback); + }, function(error) { + return handleCallback(error, false, callback); + }, progressBack); + } + }; + + //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native + function simpleBind(context, fn) { + return function(value) { + fn.call(context, value); + }; + } + + function processQueue(state) { + var fn, promise, pending; + + pending = state.pending; + state.processScheduled = false; + state.pending = undefined; + for (var i = 0, ii = pending.length; i < ii; ++i) { + promise = pending[i][0]; + fn = pending[i][state.status]; + try { + if (isFunction(fn)) { + promise.resolve(fn(state.value)); + } else if (state.status === 1) { + promise.resolve(state.value); + } else { + promise.reject(state.value); + } + } catch (e) { + promise.reject(e); + exceptionHandler(e); + } + } + } + + function scheduleProcessQueue(state) { + if (state.processScheduled || !state.pending) return; + state.processScheduled = true; + nextTick(function() { processQueue(state); }); + } + + function Deferred() { + this.promise = new Promise(); + //Necessary to support unbound execution :/ + this.resolve = simpleBind(this, this.resolve); + this.reject = simpleBind(this, this.reject); + this.notify = simpleBind(this, this.notify); + } + + Deferred.prototype = { + resolve: function(val) { + if (this.promise.$$state.status) return; + if (val === this.promise) { + this.$$reject($qMinErr( + 'qcycle', + "Expected promise to be resolved with value other than itself '{0}'", + val)); + } else { + this.$$resolve(val); + } + + }, + + $$resolve: function(val) { + var then, fns; + + fns = callOnce(this, this.$$resolve, this.$$reject); + try { + if ((isObject(val) || isFunction(val))) then = val && val.then; + if (isFunction(then)) { + this.promise.$$state.status = -1; + then.call(val, fns[0], fns[1], this.notify); + } else { + this.promise.$$state.value = val; + this.promise.$$state.status = 1; + scheduleProcessQueue(this.promise.$$state); + } + } catch (e) { + fns[1](e); + exceptionHandler(e); + } + }, + + reject: function(reason) { + if (this.promise.$$state.status) return; + this.$$reject(reason); + }, + + $$reject: function(reason) { + this.promise.$$state.value = reason; + this.promise.$$state.status = 2; + scheduleProcessQueue(this.promise.$$state); + }, + + notify: function(progress) { + var callbacks = this.promise.$$state.pending; + + if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) { + nextTick(function() { + var callback, result; + for (var i = 0, ii = callbacks.length; i < ii; i++) { + result = callbacks[i][0]; + callback = callbacks[i][3]; + try { + result.notify(isFunction(callback) ? callback(progress) : progress); + } catch (e) { + exceptionHandler(e); + } + } + }); + } + } + }; + + /** + * @ngdoc method + * @name $q#reject + * @kind function + * + * @description + * Creates a promise that is resolved as rejected with the specified `reason`. This api should be + * used to forward rejection in a chain of promises. If you are dealing with the last promise in + * a promise chain, you don't need to worry about it. + * + * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of + * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via + * a promise error callback and you want to forward the error to the promise derived from the + * current promise, you have to "rethrow" the error by returning a rejection constructed via + * `reject`. + * + * ```js + * promiseB = promiseA.then(function(result) { + * // success: do something and resolve promiseB + * // with the old or a new result + * return result; + * }, function(reason) { + * // error: handle the error if possible and + * // resolve promiseB with newPromiseOrValue, + * // otherwise forward the rejection to promiseB + * if (canHandle(reason)) { + * // handle the error and recover + * return newPromiseOrValue; + * } + * return $q.reject(reason); + * }); + * ``` + * + * @param {*} reason Constant, message, exception or an object representing the rejection reason. + * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. + */ + var reject = function(reason) { + var result = new Deferred(); + result.reject(reason); + return result.promise; + }; + + var makePromise = function makePromise(value, resolved) { + var result = new Deferred(); + if (resolved) { + result.resolve(value); + } else { + result.reject(value); + } + return result.promise; + }; + + var handleCallback = function handleCallback(value, isResolved, callback) { + var callbackOutput = null; + try { + if (isFunction(callback)) callbackOutput = callback(); + } catch (e) { + return makePromise(e, false); + } + if (isPromiseLike(callbackOutput)) { + return callbackOutput.then(function() { + return makePromise(value, isResolved); + }, function(error) { + return makePromise(error, false); + }); + } else { + return makePromise(value, isResolved); + } + }; + + /** + * @ngdoc method + * @name $q#when + * @kind function + * + * @description + * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. + * This is useful when you are dealing with an object that might or might not be a promise, or if + * the promise comes from a source that can't be trusted. + * + * @param {*} value Value or a promise + * @returns {Promise} Returns a promise of the passed value or promise + */ + + + var when = function(value, callback, errback, progressBack) { + var result = new Deferred(); + result.resolve(value); + return result.promise.then(callback, errback, progressBack); + }; + + /** + * @ngdoc method + * @name $q#all + * @kind function + * + * @description + * Combines multiple promises into a single promise that is resolved when all of the input + * promises are resolved. + * + * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises. + * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, + * each value corresponding to the promise at the same index/key in the `promises` array/hash. + * If any of the promises is resolved with a rejection, this resulting promise will be rejected + * with the same rejection value. + */ + + function all(promises) { + var deferred = new Deferred(), + counter = 0, + results = isArray(promises) ? [] : {}; + + forEach(promises, function(promise, key) { + counter++; + when(promise).then(function(value) { + if (results.hasOwnProperty(key)) return; + results[key] = value; + if (!(--counter)) deferred.resolve(results); + }, function(reason) { + if (results.hasOwnProperty(key)) return; + deferred.reject(reason); + }); + }); + + if (counter === 0) { + deferred.resolve(results); + } + + return deferred.promise; + } + + var $Q = function Q(resolver) { + if (!isFunction(resolver)) { + throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver); + } + + if (!(this instanceof Q)) { + // More useful when $Q is the Promise itself. + return new Q(resolver); + } + + var deferred = new Deferred(); + + function resolveFn(value) { + deferred.resolve(value); + } + + function rejectFn(reason) { + deferred.reject(reason); + } + + resolver(resolveFn, rejectFn); + + return deferred.promise; + }; + + $Q.defer = defer; + $Q.reject = reject; + $Q.when = when; + $Q.all = all; + + return $Q; +} + +function $$RAFProvider() { //rAF + this.$get = ['$window', '$timeout', function($window, $timeout) { + var requestAnimationFrame = $window.requestAnimationFrame || + $window.webkitRequestAnimationFrame; + + var cancelAnimationFrame = $window.cancelAnimationFrame || + $window.webkitCancelAnimationFrame || + $window.webkitCancelRequestAnimationFrame; + + var rafSupported = !!requestAnimationFrame; + var raf = rafSupported + ? function(fn) { + var id = requestAnimationFrame(fn); + return function() { + cancelAnimationFrame(id); + }; + } + : function(fn) { + var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 + return function() { + $timeout.cancel(timer); + }; + }; + + raf.supported = rafSupported; + + return raf; + }]; +} + +/** + * DESIGN NOTES + * + * The design decisions behind the scope are heavily favored for speed and memory consumption. + * + * The typical use of scope is to watch the expressions, which most of the time return the same + * value as last time so we optimize the operation. + * + * Closures construction is expensive in terms of speed as well as memory: + * - No closures, instead use prototypical inheritance for API + * - Internal state needs to be stored on scope directly, which means that private state is + * exposed as $$____ properties + * + * Loop operations are optimized by using while(count--) { ... } + * - this means that in order to keep the same order of execution as addition we have to add + * items to the array at the beginning (unshift) instead of at the end (push) + * + * Child scopes are created and removed often + * - Using an array would be slow since inserts in middle are expensive so we use linked list + * + * There are few watches then a lot of observers. This is why you don't want the observer to be + * implemented in the same way as watch. Watch requires return of initialization function which + * are expensive to construct. + */ + + +/** + * @ngdoc provider + * @name $rootScopeProvider + * @description + * + * Provider for the $rootScope service. + */ + +/** + * @ngdoc method + * @name $rootScopeProvider#digestTtl + * @description + * + * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and + * assuming that the model is unstable. + * + * The current default is 10 iterations. + * + * In complex applications it's possible that the dependencies between `$watch`s will result in + * several digest iterations. However if an application needs more than the default 10 digest + * iterations for its model to stabilize then you should investigate what is causing the model to + * continuously change during the digest. + * + * Increasing the TTL could have performance implications, so you should not change it without + * proper justification. + * + * @param {number} limit The number of digest iterations. + */ + + +/** + * @ngdoc service + * @name $rootScope + * @description + * + * Every application has a single root {@link ng.$rootScope.Scope scope}. + * All other scopes are descendant scopes of the root scope. Scopes provide separation + * between the model and the view, via a mechanism for watching the model for changes. + * They also provide an event emission/broadcast and subscription facility. See the + * {@link guide/scope developer guide on scopes}. + */ +function $RootScopeProvider() { + var TTL = 10; + var $rootScopeMinErr = minErr('$rootScope'); + var lastDirtyWatch = null; + var applyAsyncId = null; + + this.digestTtl = function(value) { + if (arguments.length) { + TTL = value; + } + return TTL; + }; + + function createChildScopeClass(parent) { + function ChildScope() { + this.$$watchers = this.$$nextSibling = + this.$$childHead = this.$$childTail = null; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$watchersCount = 0; + this.$id = nextUid(); + this.$$ChildScope = null; + } + ChildScope.prototype = parent; + return ChildScope; + } + + this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', + function($injector, $exceptionHandler, $parse, $browser) { + + function destroyChildScope($event) { + $event.currentScope.$$destroyed = true; + } + + /** + * @ngdoc type + * @name $rootScope.Scope + * + * @description + * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the + * {@link auto.$injector $injector}. Child scopes are created using the + * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when + * compiled HTML template is executed.) + * + * Here is a simple scope snippet to show how you can interact with the scope. + * ```html + * <file src="./test/ng/rootScopeSpec.js" tag="docs1" /> + * ``` + * + * # Inheritance + * A scope can inherit from a parent scope, as in this example: + * ```js + var parent = $rootScope; + var child = parent.$new(); + + parent.salutation = "Hello"; + expect(child.salutation).toEqual('Hello'); + + child.salutation = "Welcome"; + expect(child.salutation).toEqual('Welcome'); + expect(parent.salutation).toEqual('Hello'); + * ``` + * + * When interacting with `Scope` in tests, additional helper methods are available on the + * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional + * details. + * + * + * @param {Object.<string, function()>=} providers Map of service factory which need to be + * provided for the current scope. Defaults to {@link ng}. + * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should + * append/override services provided by `providers`. This is handy + * when unit-testing and having the need to override a default + * service. + * @returns {Object} Newly created scope. + * + */ + function Scope() { + this.$id = nextUid(); + this.$$phase = this.$parent = this.$$watchers = + this.$$nextSibling = this.$$prevSibling = + this.$$childHead = this.$$childTail = null; + this.$root = this; + this.$$destroyed = false; + this.$$listeners = {}; + this.$$listenerCount = {}; + this.$$isolateBindings = null; + } + + /** + * @ngdoc property + * @name $rootScope.Scope#$id + * + * @description + * Unique scope ID (monotonically increasing) useful for debugging. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$parent + * + * @description + * Reference to the parent scope. + */ + + /** + * @ngdoc property + * @name $rootScope.Scope#$root + * + * @description + * Reference to the root scope. + */ + + Scope.prototype = { + constructor: Scope, + /** + * @ngdoc method + * @name $rootScope.Scope#$new + * @kind function + * + * @description + * Creates a new child {@link ng.$rootScope.Scope scope}. + * + * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. + * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. + * + * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is + * desired for the scope and its child scopes to be permanently detached from the parent and + * thus stop participating in model change detection and listener notification by invoking. + * + * @param {boolean} isolate If true, then the scope does not prototypically inherit from the + * parent scope. The scope is isolated, as it can not see parent scope properties. + * When creating widgets, it is useful for the widget to not accidentally read parent + * state. + * + * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent` + * of the newly created scope. Defaults to `this` scope if not provided. + * This is used when creating a transclude scope to correctly place it + * in the scope hierarchy while maintaining the correct prototypical + * inheritance. + * + * @returns {Object} The newly created child scope. + * + */ + $new: function(isolate, parent) { + var child; + + parent = parent || this; + + if (isolate) { + child = new Scope(); + child.$root = this.$root; + } else { + // Only create a child scope class if somebody asks for one, + // but cache it to allow the VM to optimize lookups. + if (!this.$$ChildScope) { + this.$$ChildScope = createChildScopeClass(this); + } + child = new this.$$ChildScope(); + } + child.$parent = parent; + child.$$prevSibling = parent.$$childTail; + if (parent.$$childHead) { + parent.$$childTail.$$nextSibling = child; + parent.$$childTail = child; + } else { + parent.$$childHead = parent.$$childTail = child; + } + + // When the new scope is not isolated or we inherit from `this`, and + // the parent scope is destroyed, the property `$$destroyed` is inherited + // prototypically. In all other cases, this property needs to be set + // when the parent scope is destroyed. + // The listener needs to be added after the parent is set + if (isolate || parent != this) child.$on('$destroy', destroyChildScope); + + return child; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watch + * @kind function + * + * @description + * Registers a `listener` callback to be executed whenever the `watchExpression` changes. + * + * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest + * $digest()} and should return the value that will be watched. (Since + * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the + * `watchExpression` can execute multiple times per + * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) + * - The `listener` is called only when the value from the current `watchExpression` and the + * previous call to `watchExpression` are not equal (with the exception of the initial run, + * see below). Inequality is determined according to reference inequality, + * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) + * via the `!==` Javascript operator, unless `objectEquality == true` + * (see next point) + * - When `objectEquality == true`, inequality of the `watchExpression` is determined + * according to the {@link angular.equals} function. To save the value of the object for + * later comparison, the {@link angular.copy} function is used. This therefore means that + * watching complex objects will have adverse memory and performance implications. + * - The watch `listener` may change the model, which may trigger other `listener`s to fire. + * This is achieved by rerunning the watchers until no changes are detected. The rerun + * iteration limit is 10 to prevent an infinite loop deadlock. + * + * + * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, + * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` + * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a + * change is detected, be prepared for multiple calls to your listener.) + * + * After a watcher is registered with the scope, the `listener` fn is called asynchronously + * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the + * watcher. In rare cases, this is undesirable because the listener is called when the result + * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you + * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the + * listener was called due to initialization. + * + * + * + * # Example + * ```js + // let's assume that scope was dependency injected as the $rootScope + var scope = $rootScope; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + + + + // Using a function as a watchExpression + var food; + scope.foodCounter = 0; + expect(scope.foodCounter).toEqual(0); + scope.$watch( + // This function returns the value being watched. It is called for each turn of the $digest loop + function() { return food; }, + // This is the change listener, called when the value returned from the above function changes + function(newValue, oldValue) { + if ( newValue !== oldValue ) { + // Only increment the counter if the value changed + scope.foodCounter = scope.foodCounter + 1; + } + } + ); + // No digest has been run so the counter will be zero + expect(scope.foodCounter).toEqual(0); + + // Run the digest but since food has not changed count will still be zero + scope.$digest(); + expect(scope.foodCounter).toEqual(0); + + // Update food and run digest. Now the counter will increment + food = 'cheeseburger'; + scope.$digest(); + expect(scope.foodCounter).toEqual(1); + + * ``` + * + * + * + * @param {(function()|string)} watchExpression Expression that is evaluated on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers + * a call to the `listener`. + * + * - `string`: Evaluated as {@link guide/expression expression} + * - `function(scope)`: called with current `scope` as a parameter. + * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value + * of `watchExpression` changes. + * + * - `newVal` contains the current value of the `watchExpression` + * - `oldVal` contains the previous value of the `watchExpression` + * - `scope` refers to the current scope + * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of + * comparing for reference equality. + * @returns {function()} Returns a deregistration function for this listener. + */ + $watch: function(watchExp, listener, objectEquality) { + var get = $parse(watchExp); + + if (get.$$watchDelegate) { + return get.$$watchDelegate(this, listener, objectEquality, get); + } + var scope = this, + array = scope.$$watchers, + watcher = { + fn: listener, + last: initWatchVal, + get: get, + exp: watchExp, + eq: !!objectEquality + }; + + lastDirtyWatch = null; + + if (!isFunction(listener)) { + watcher.fn = noop; + } + + if (!array) { + array = scope.$$watchers = []; + } + // we use unshift since we use a while loop in $digest for speed. + // the while loop reads in reverse order. + array.unshift(watcher); + + return function deregisterWatch() { + arrayRemove(array, watcher); + lastDirtyWatch = null; + }; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchGroup + * @kind function + * + * @description + * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`. + * If any one expression in the collection changes the `listener` is executed. + * + * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every + * call to $digest() to see if any items changes. + * - The `listener` is called whenever any expression in the `watchExpressions` array changes. + * + * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually + * watched using {@link ng.$rootScope.Scope#$watch $watch()} + * + * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any + * expression in `watchExpressions` changes + * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching + * those of `watchExpression` + * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching + * those of `watchExpression` + * The `scope` refers to the current scope. + * @returns {function()} Returns a de-registration function for all listeners. + */ + $watchGroup: function(watchExpressions, listener) { + var oldValues = new Array(watchExpressions.length); + var newValues = new Array(watchExpressions.length); + var deregisterFns = []; + var self = this; + var changeReactionScheduled = false; + var firstRun = true; + + if (!watchExpressions.length) { + // No expressions means we call the listener ASAP + var shouldCall = true; + self.$evalAsync(function() { + if (shouldCall) listener(newValues, newValues, self); + }); + return function deregisterWatchGroup() { + shouldCall = false; + }; + } + + if (watchExpressions.length === 1) { + // Special case size of one + return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) { + newValues[0] = value; + oldValues[0] = oldValue; + listener(newValues, (value === oldValue) ? newValues : oldValues, scope); + }); + } + + forEach(watchExpressions, function(expr, i) { + var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) { + newValues[i] = value; + oldValues[i] = oldValue; + if (!changeReactionScheduled) { + changeReactionScheduled = true; + self.$evalAsync(watchGroupAction); + } + }); + deregisterFns.push(unwatchFn); + }); + + function watchGroupAction() { + changeReactionScheduled = false; + + if (firstRun) { + firstRun = false; + listener(newValues, newValues, self); + } else { + listener(newValues, oldValues, self); + } + } + + return function deregisterWatchGroup() { + while (deregisterFns.length) { + deregisterFns.shift()(); + } + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$watchCollection + * @kind function + * + * @description + * Shallow watches the properties of an object and fires whenever any of the properties change + * (for arrays, this implies watching the array items; for object maps, this implies watching + * the properties). If a change is detected, the `listener` callback is fired. + * + * - The `obj` collection is observed via standard $watch operation and is examined on every + * call to $digest() to see if any items have been added, removed, or moved. + * - The `listener` is called whenever anything within the `obj` has changed. Examples include + * adding, removing, and moving items belonging to an object or array. + * + * + * # Example + * ```js + $scope.names = ['igor', 'matias', 'misko', 'james']; + $scope.dataCount = 4; + + $scope.$watchCollection('names', function(newNames, oldNames) { + $scope.dataCount = newNames.length; + }); + + expect($scope.dataCount).toEqual(4); + $scope.$digest(); + + //still at 4 ... no changes + expect($scope.dataCount).toEqual(4); + + $scope.names.pop(); + $scope.$digest(); + + //now there's been a change + expect($scope.dataCount).toEqual(3); + * ``` + * + * + * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The + * expression value should evaluate to an object or an array which is observed on each + * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the + * collection will trigger a call to the `listener`. + * + * @param {function(newCollection, oldCollection, scope)} listener a callback function called + * when a change is detected. + * - The `newCollection` object is the newly modified data obtained from the `obj` expression + * - The `oldCollection` object is a copy of the former collection data. + * Due to performance considerations, the`oldCollection` value is computed only if the + * `listener` function declares two or more arguments. + * - The `scope` argument refers to the current scope. + * + * @returns {function()} Returns a de-registration function for this listener. When the + * de-registration function is executed, the internal watch operation is terminated. + */ + $watchCollection: function(obj, listener) { + $watchCollectionInterceptor.$stateful = true; + + var self = this; + // the current value, updated on each dirty-check run + var newValue; + // a shallow copy of the newValue from the last dirty-check run, + // updated to match newValue during dirty-check run + var oldValue; + // a shallow copy of the newValue from when the last change happened + var veryOldValue; + // only track veryOldValue if the listener is asking for it + var trackVeryOldValue = (listener.length > 1); + var changeDetected = 0; + var changeDetector = $parse(obj, $watchCollectionInterceptor); + var internalArray = []; + var internalObject = {}; + var initRun = true; + var oldLength = 0; + + function $watchCollectionInterceptor(_value) { + newValue = _value; + var newLength, key, bothNaN, newItem, oldItem; + + // If the new value is undefined, then return undefined as the watch may be a one-time watch + if (isUndefined(newValue)) return; + + if (!isObject(newValue)) { // if primitive + if (oldValue !== newValue) { + oldValue = newValue; + changeDetected++; + } + } else if (isArrayLike(newValue)) { + if (oldValue !== internalArray) { + // we are transitioning from something which was not an array into array. + oldValue = internalArray; + oldLength = oldValue.length = 0; + changeDetected++; + } + + newLength = newValue.length; + + if (oldLength !== newLength) { + // if lengths do not match we need to trigger change notification + changeDetected++; + oldValue.length = oldLength = newLength; + } + // copy the items to oldValue and look for changes. + for (var i = 0; i < newLength; i++) { + oldItem = oldValue[i]; + newItem = newValue[i]; + + bothNaN = (oldItem !== oldItem) && (newItem !== newItem); + if (!bothNaN && (oldItem !== newItem)) { + changeDetected++; + oldValue[i] = newItem; + } + } + } else { + if (oldValue !== internalObject) { + // we are transitioning from something which was not an object into object. + oldValue = internalObject = {}; + oldLength = 0; + changeDetected++; + } + // copy the items to oldValue and look for changes. + newLength = 0; + for (key in newValue) { + if (newValue.hasOwnProperty(key)) { + newLength++; + newItem = newValue[key]; + oldItem = oldValue[key]; + + if (key in oldValue) { + bothNaN = (oldItem !== oldItem) && (newItem !== newItem); + if (!bothNaN && (oldItem !== newItem)) { + changeDetected++; + oldValue[key] = newItem; + } + } else { + oldLength++; + oldValue[key] = newItem; + changeDetected++; + } + } + } + if (oldLength > newLength) { + // we used to have more keys, need to find them and destroy them. + changeDetected++; + for (key in oldValue) { + if (!newValue.hasOwnProperty(key)) { + oldLength--; + delete oldValue[key]; + } + } + } + } + return changeDetected; + } + + function $watchCollectionAction() { + if (initRun) { + initRun = false; + listener(newValue, newValue, self); + } else { + listener(newValue, veryOldValue, self); + } + + // make a copy for the next time a collection is changed + if (trackVeryOldValue) { + if (!isObject(newValue)) { + //primitive + veryOldValue = newValue; + } else if (isArrayLike(newValue)) { + veryOldValue = new Array(newValue.length); + for (var i = 0; i < newValue.length; i++) { + veryOldValue[i] = newValue[i]; + } + } else { // if object + veryOldValue = {}; + for (var key in newValue) { + if (hasOwnProperty.call(newValue, key)) { + veryOldValue[key] = newValue[key]; + } + } + } + } + } + + return this.$watch(changeDetector, $watchCollectionAction); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$digest + * @kind function + * + * @description + * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and + * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change + * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} + * until no more listeners are firing. This means that it is possible to get into an infinite + * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of + * iterations exceeds 10. + * + * Usually, you don't call `$digest()` directly in + * {@link ng.directive:ngController controllers} or in + * {@link ng.$compileProvider#directive directives}. + * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within + * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`. + * + * If you want to be notified whenever `$digest()` is called, + * you can register a `watchExpression` function with + * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. + * + * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. + * + * # Example + * ```js + var scope = ...; + scope.name = 'misko'; + scope.counter = 0; + + expect(scope.counter).toEqual(0); + scope.$watch('name', function(newValue, oldValue) { + scope.counter = scope.counter + 1; + }); + expect(scope.counter).toEqual(0); + + scope.$digest(); + // the listener is always called during the first $digest loop after it was registered + expect(scope.counter).toEqual(1); + + scope.$digest(); + // but now it will not be called unless the value changes + expect(scope.counter).toEqual(1); + + scope.name = 'adam'; + scope.$digest(); + expect(scope.counter).toEqual(2); + * ``` + * + */ + $digest: function() { + var watch, value, last, + watchers, + length, + dirty, ttl = TTL, + next, current, target = this, + watchLog = [], + logIdx, logMsg, asyncTask; + + beginPhase('$digest'); + // Check for changes to browser url that happened in sync before the call to $digest + $browser.$$checkUrlChange(); + + if (this === $rootScope && applyAsyncId !== null) { + // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then + // cancel the scheduled $apply and flush the queue of expressions to be evaluated. + $browser.defer.cancel(applyAsyncId); + flushApplyAsync(); + } + + lastDirtyWatch = null; + + do { // "while dirty" loop + dirty = false; + current = target; + + while (asyncQueue.length) { + try { + asyncTask = asyncQueue.shift(); + asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals); + } catch (e) { + $exceptionHandler(e); + } + lastDirtyWatch = null; + } + + traverseScopesLoop: + do { // "traverse the scopes" loop + if ((watchers = current.$$watchers)) { + // process our watches + length = watchers.length; + while (length--) { + try { + watch = watchers[length]; + // Most common watches are on primitives, in which case we can short + // circuit it with === operator, only when === fails do we use .equals + if (watch) { + if ((value = watch.get(current)) !== (last = watch.last) && + !(watch.eq + ? equals(value, last) + : (typeof value === 'number' && typeof last === 'number' + && isNaN(value) && isNaN(last)))) { + dirty = true; + lastDirtyWatch = watch; + watch.last = watch.eq ? copy(value, null) : value; + watch.fn(value, ((last === initWatchVal) ? value : last), current); + if (ttl < 5) { + logIdx = 4 - ttl; + if (!watchLog[logIdx]) watchLog[logIdx] = []; + watchLog[logIdx].push({ + msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp, + newVal: value, + oldVal: last + }); + } + } else if (watch === lastDirtyWatch) { + // If the most recently dirty watcher is now clean, short circuit since the remaining watchers + // have already been tested. + dirty = false; + break traverseScopesLoop; + } + } + } catch (e) { + $exceptionHandler(e); + } + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $broadcast + if (!(next = (current.$$childHead || + (current !== target && current.$$nextSibling)))) { + while (current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } while ((current = next)); + + // `break traverseScopesLoop;` takes us to here + + if ((dirty || asyncQueue.length) && !(ttl--)) { + clearPhase(); + throw $rootScopeMinErr('infdig', + '{0} $digest() iterations reached. Aborting!\n' + + 'Watchers fired in the last 5 iterations: {1}', + TTL, watchLog); + } + + } while (dirty || asyncQueue.length); + + clearPhase(); + + while (postDigestQueue.length) { + try { + postDigestQueue.shift()(); + } catch (e) { + $exceptionHandler(e); + } + } + }, + + + /** + * @ngdoc event + * @name $rootScope.Scope#$destroy + * @eventType broadcast on scope being destroyed + * + * @description + * Broadcasted when a scope and its children are being destroyed. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + + /** + * @ngdoc method + * @name $rootScope.Scope#$destroy + * @kind function + * + * @description + * Removes the current scope (and all of its children) from the parent scope. Removal implies + * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer + * propagate to the current scope and its children. Removal also implies that the current + * scope is eligible for garbage collection. + * + * The `$destroy()` is usually used by directives such as + * {@link ng.directive:ngRepeat ngRepeat} for managing the + * unrolling of the loop. + * + * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. + * Application code can register a `$destroy` event handler that will give it a chance to + * perform any necessary cleanup. + * + * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to + * clean up DOM bindings before an element is removed from the DOM. + */ + $destroy: function() { + // we can't destroy the root scope or a scope that has been already destroyed + if (this.$$destroyed) return; + var parent = this.$parent; + + this.$broadcast('$destroy'); + this.$$destroyed = true; + if (this === $rootScope) return; + + for (var eventName in this.$$listenerCount) { + decrementListenerCount(this, this.$$listenerCount[eventName], eventName); + } + + // sever all the references to parent scopes (after this cleanup, the current scope should + // not be retained by any of our references and should be eligible for garbage collection) + if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; + if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; + if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; + if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; + + // Disable listeners, watchers and apply/digest methods + this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop; + this.$on = this.$watch = this.$watchGroup = function() { return noop; }; + this.$$listeners = {}; + + // All of the code below is bogus code that works around V8's memory leak via optimized code + // and inline caches. + // + // see: + // - https://code.google.com/p/v8/issues/detail?id=2073#c26 + // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 + // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 + + this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = + this.$$childTail = this.$root = this.$$watchers = null; + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$eval + * @kind function + * + * @description + * Executes the `expression` on the current scope and returns the result. Any exceptions in + * the expression are propagated (uncaught). This is useful when evaluating Angular + * expressions. + * + * # Example + * ```js + var scope = ng.$rootScope.Scope(); + scope.a = 1; + scope.b = 2; + + expect(scope.$eval('a+b')).toEqual(3); + expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); + * ``` + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + * @returns {*} The result of evaluating the expression. + */ + $eval: function(expr, locals) { + return $parse(expr)(this, locals); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$evalAsync + * @kind function + * + * @description + * Executes the expression on the current scope at a later point in time. + * + * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only + * that: + * + * - it will execute after the function that scheduled the evaluation (preferably before DOM + * rendering). + * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after + * `expression` execution. + * + * Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle + * will be scheduled. However, it is encouraged to always call code that changes the model + * from within an `$apply` call. That includes code evaluated via `$evalAsync`. + * + * @param {(string|function())=} expression An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with the current `scope` parameter. + * + * @param {(object)=} locals Local variables object, useful for overriding values in scope. + */ + $evalAsync: function(expr, locals) { + // if we are outside of an $digest loop and this is the first time we are scheduling async + // task also schedule async auto-flush + if (!$rootScope.$$phase && !asyncQueue.length) { + $browser.defer(function() { + if (asyncQueue.length) { + $rootScope.$digest(); + } + }); + } + + asyncQueue.push({scope: this, expression: expr, locals: locals}); + }, + + $$postDigest: function(fn) { + postDigestQueue.push(fn); + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$apply + * @kind function + * + * @description + * `$apply()` is used to execute an expression in angular from outside of the angular + * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). + * Because we are calling into the angular framework we need to perform proper scope life + * cycle of {@link ng.$exceptionHandler exception handling}, + * {@link ng.$rootScope.Scope#$digest executing watches}. + * + * ## Life cycle + * + * # Pseudo-Code of `$apply()` + * ```js + function $apply(expr) { + try { + return $eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + $root.$digest(); + } + } + * ``` + * + * + * Scope's `$apply()` method transitions through the following stages: + * + * 1. The {@link guide/expression expression} is executed using the + * {@link ng.$rootScope.Scope#$eval $eval()} method. + * 2. Any exceptions from the execution of the expression are forwarded to the + * {@link ng.$exceptionHandler $exceptionHandler} service. + * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the + * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. + * + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + * + * @returns {*} The result of evaluating the expression. + */ + $apply: function(expr) { + try { + beginPhase('$apply'); + return this.$eval(expr); + } catch (e) { + $exceptionHandler(e); + } finally { + clearPhase(); + try { + $rootScope.$digest(); + } catch (e) { + $exceptionHandler(e); + throw e; + } + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$applyAsync + * @kind function + * + * @description + * Schedule the invocation of $apply to occur at a later time. The actual time difference + * varies across browsers, but is typically around ~10 milliseconds. + * + * This can be used to queue up multiple expressions which need to be evaluated in the same + * digest. + * + * @param {(string|function())=} exp An angular expression to be executed. + * + * - `string`: execute using the rules as defined in {@link guide/expression expression}. + * - `function(scope)`: execute the function with current `scope` parameter. + */ + $applyAsync: function(expr) { + var scope = this; + expr && applyAsyncQueue.push($applyAsyncExpression); + scheduleApplyAsync(); + + function $applyAsyncExpression() { + scope.$eval(expr); + } + }, + + /** + * @ngdoc method + * @name $rootScope.Scope#$on + * @kind function + * + * @description + * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for + * discussion of event life cycle. + * + * The event listener function format is: `function(event, args...)`. The `event` object + * passed into the listener has the following attributes: + * + * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or + * `$broadcast`-ed. + * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the + * event propagates through the scope hierarchy, this property is set to null. + * - `name` - `{string}`: name of the event. + * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel + * further event propagation (available only for events that were `$emit`-ed). + * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag + * to true. + * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. + * + * @param {string} name Event name to listen on. + * @param {function(event, ...args)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. + */ + $on: function(name, listener) { + var namedListeners = this.$$listeners[name]; + if (!namedListeners) { + this.$$listeners[name] = namedListeners = []; + } + namedListeners.push(listener); + + var current = this; + do { + if (!current.$$listenerCount[name]) { + current.$$listenerCount[name] = 0; + } + current.$$listenerCount[name]++; + } while ((current = current.$parent)); + + var self = this; + return function() { + var indexOfListener = namedListeners.indexOf(listener); + if (indexOfListener !== -1) { + namedListeners[indexOfListener] = null; + decrementListenerCount(self, 1, name); + } + }; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$emit + * @kind function + * + * @description + * Dispatches an event `name` upwards through the scope hierarchy notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$emit` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event traverses upwards toward the root scope and calls all + * registered listeners along the way. The event will stop propagating if one of the listeners + * cancels it. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to emit. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). + */ + $emit: function(name, args) { + var empty = [], + namedListeners, + scope = this, + stopPropagation = false, + event = { + name: name, + targetScope: scope, + stopPropagation: function() {stopPropagation = true;}, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }, + listenerArgs = concat([event], arguments, 1), + i, length; + + do { + namedListeners = scope.$$listeners[name] || empty; + event.currentScope = scope; + for (i = 0, length = namedListeners.length; i < length; i++) { + + // if listeners were deregistered, defragment the array + if (!namedListeners[i]) { + namedListeners.splice(i, 1); + i--; + length--; + continue; + } + try { + //allow all listeners attached to the current scope to run + namedListeners[i].apply(null, listenerArgs); + } catch (e) { + $exceptionHandler(e); + } + } + //if any listener on the current scope stops propagation, prevent bubbling + if (stopPropagation) { + event.currentScope = null; + return event; + } + //traverse upwards + scope = scope.$parent; + } while (scope); + + event.currentScope = null; + + return event; + }, + + + /** + * @ngdoc method + * @name $rootScope.Scope#$broadcast + * @kind function + * + * @description + * Dispatches an event `name` downwards to all child scopes (and their children) notifying the + * registered {@link ng.$rootScope.Scope#$on} listeners. + * + * The event life cycle starts at the scope on which `$broadcast` was called. All + * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get + * notified. Afterwards, the event propagates to all direct and indirect scopes of the current + * scope and calls all registered listeners along the way. The event cannot be canceled. + * + * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed + * onto the {@link ng.$exceptionHandler $exceptionHandler} service. + * + * @param {string} name Event name to broadcast. + * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. + * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} + */ + $broadcast: function(name, args) { + var target = this, + current = target, + next = target, + event = { + name: name, + targetScope: target, + preventDefault: function() { + event.defaultPrevented = true; + }, + defaultPrevented: false + }; + + if (!target.$$listenerCount[name]) return event; + + var listenerArgs = concat([event], arguments, 1), + listeners, i, length; + + //down while you can, then up and next sibling or up and next sibling until back at root + while ((current = next)) { + event.currentScope = current; + listeners = current.$$listeners[name] || []; + for (i = 0, length = listeners.length; i < length; i++) { + // if listeners were deregistered, defragment the array + if (!listeners[i]) { + listeners.splice(i, 1); + i--; + length--; + continue; + } + + try { + listeners[i].apply(null, listenerArgs); + } catch (e) { + $exceptionHandler(e); + } + } + + // Insanity Warning: scope depth-first traversal + // yes, this code is a bit crazy, but it works and we have tests to prove it! + // this piece should be kept in sync with the traversal in $digest + // (though it differs due to having the extra check for $$listenerCount) + if (!(next = ((current.$$listenerCount[name] && current.$$childHead) || + (current !== target && current.$$nextSibling)))) { + while (current !== target && !(next = current.$$nextSibling)) { + current = current.$parent; + } + } + } + + event.currentScope = null; + return event; + } + }; + + var $rootScope = new Scope(); + + //The internal queues. Expose them on the $rootScope for debugging/testing purposes. + var asyncQueue = $rootScope.$$asyncQueue = []; + var postDigestQueue = $rootScope.$$postDigestQueue = []; + var applyAsyncQueue = $rootScope.$$applyAsyncQueue = []; + + return $rootScope; + + + function beginPhase(phase) { + if ($rootScope.$$phase) { + throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase); + } + + $rootScope.$$phase = phase; + } + + function clearPhase() { + $rootScope.$$phase = null; + } + + + function decrementListenerCount(current, count, name) { + do { + current.$$listenerCount[name] -= count; + + if (current.$$listenerCount[name] === 0) { + delete current.$$listenerCount[name]; + } + } while ((current = current.$parent)); + } + + /** + * function used as an initial value for watchers. + * because it's unique we can easily tell it apart from other values + */ + function initWatchVal() {} + + function flushApplyAsync() { + while (applyAsyncQueue.length) { + try { + applyAsyncQueue.shift()(); + } catch (e) { + $exceptionHandler(e); + } + } + applyAsyncId = null; + } + + function scheduleApplyAsync() { + if (applyAsyncId === null) { + applyAsyncId = $browser.defer(function() { + $rootScope.$apply(flushApplyAsync); + }); + } + } + }]; +} + +/** + * @description + * Private service to sanitize uris for links and images. Used by $compile and $sanitize. + */ +function $$SanitizeUriProvider() { + var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/, + imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; + + /** + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during a[href] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to a[href] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.aHrefSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + aHrefSanitizationWhitelist = regexp; + return this; + } + return aHrefSanitizationWhitelist; + }; + + + /** + * @description + * Retrieves or overrides the default regular expression that is used for whitelisting of safe + * urls during img[src] sanitization. + * + * The sanitization is a security measure aimed at prevent XSS attacks via html links. + * + * Any url about to be assigned to img[src] via data-binding is first normalized and turned into + * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` + * regular expression. If a match is found, the original url is written into the dom. Otherwise, + * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. + * + * @param {RegExp=} regexp New regexp to whitelist urls with. + * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for + * chaining otherwise. + */ + this.imgSrcSanitizationWhitelist = function(regexp) { + if (isDefined(regexp)) { + imgSrcSanitizationWhitelist = regexp; + return this; + } + return imgSrcSanitizationWhitelist; + }; + + this.$get = function() { + return function sanitizeUri(uri, isImage) { + var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; + var normalizedVal; + normalizedVal = urlResolve(uri).href; + if (normalizedVal !== '' && !normalizedVal.match(regex)) { + return 'unsafe:' + normalizedVal; + } + return uri; + }; + }; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * 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 $sceMinErr = minErr('$sce'); + +var SCE_CONTEXTS = { + HTML: 'html', + CSS: 'css', + URL: 'url', + // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a + // url. (e.g. ng-include, script src, templateUrl) + RESOURCE_URL: 'resourceUrl', + JS: 'js' +}; + +// Helper functions follow. + +function adjustMatcher(matcher) { + if (matcher === 'self') { + return matcher; + } else if (isString(matcher)) { + // Strings match exactly except for 2 wildcards - '*' and '**'. + // '*' matches any character except those from the set ':/.?&'. + // '**' matches any character (like .* in a RegExp). + // More than 2 *'s raises an error as it's ill defined. + if (matcher.indexOf('***') > -1) { + throw $sceMinErr('iwcard', + 'Illegal sequence *** in string matcher. String: {0}', matcher); + } + matcher = escapeForRegexp(matcher). + replace('\\*\\*', '.*'). + replace('\\*', '[^:/.?&;]*'); + return new RegExp('^' + matcher + '$'); + } else if (isRegExp(matcher)) { + // The only other type of matcher allowed is a Regexp. + // Match entire URL / disallow partial matches. + // Flags are reset (i.e. no global, ignoreCase or multiline) + return new RegExp('^' + matcher.source + '$'); + } else { + throw $sceMinErr('imatcher', + 'Matchers may only be "self", string patterns or RegExp objects'); + } +} + + +function adjustMatchers(matchers) { + var adjustedMatchers = []; + if (isDefined(matchers)) { + forEach(matchers, function(matcher) { + adjustedMatchers.push(adjustMatcher(matcher)); + }); + } + return adjustedMatchers; +} + + +/** + * @ngdoc service + * @name $sceDelegate + * @kind function + * + * @description + * + * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict + * Contextual Escaping (SCE)} services to AngularJS. + * + * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of + * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is + * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to + * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things + * work because `$sce` delegates to `$sceDelegate` for these operations. + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. + * + * The default instance of `$sceDelegate` should work out of the box with little pain. While you + * can override it completely to change the behavior of `$sce`, the common case would + * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting + * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as + * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist + * $sceDelegateProvider.resourceUrlWhitelist} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + */ + +/** + * @ngdoc provider + * @name $sceDelegateProvider + * @description + * + * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate + * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure + * that the URLs used for sourcing Angular templates are safe. Refer {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and + * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} + * + * For the general details about this service in Angular, read the main page for {@link ng.$sce + * Strict Contextual Escaping (SCE)}. + * + * **Example**: Consider the following case. <a name="example"></a> + * + * - your app is hosted at url `http://myapp.example.com/` + * - but some of your templates are hosted on other domains you control such as + * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc. + * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. + * + * Here is what a secure configuration for this scenario might look like: + * + * ``` + * angular.module('myApp', []).config(function($sceDelegateProvider) { + * $sceDelegateProvider.resourceUrlWhitelist([ + * // Allow same origin resource loads. + * 'self', + * // Allow loading from our assets domain. Notice the difference between * and **. + * 'http://srv*.assets.example.com/**' + * ]); + * + * // The blacklist overrides the whitelist so the open redirect here is blocked. + * $sceDelegateProvider.resourceUrlBlacklist([ + * 'http://myapp.example.com/clickThru**' + * ]); + * }); + * ``` + */ + +function $SceDelegateProvider() { + this.SCE_CONTEXTS = SCE_CONTEXTS; + + // Resource URLs can also be trusted by policy. + var resourceUrlWhitelist = ['self'], + resourceUrlBlacklist = []; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlWhitelist + * @kind function + * + * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + * Note: **an empty whitelist array will block all URLs**! + * + * @return {Array} the currently set whitelist array. + * + * The **default value** when no whitelist has been explicitly set is `['self']` allowing only + * same origin resource requests. + * + * @description + * Sets/Gets the whitelist of trusted resource URLs. + */ + this.resourceUrlWhitelist = function(value) { + if (arguments.length) { + resourceUrlWhitelist = adjustMatchers(value); + } + return resourceUrlWhitelist; + }; + + /** + * @ngdoc method + * @name $sceDelegateProvider#resourceUrlBlacklist + * @kind function + * + * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value + * provided. This must be an array or null. A snapshot of this array is used so further + * changes to the array are ignored. + * + * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items + * allowed in this array. + * + * The typical usage for the blacklist is to **block + * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as + * these would otherwise be trusted but actually return content from the redirected domain. + * + * Finally, **the blacklist overrides the whitelist** and has the final say. + * + * @return {Array} the currently set blacklist array. + * + * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there + * is no blacklist.) + * + * @description + * Sets/Gets the blacklist of trusted resource URLs. + */ + + this.resourceUrlBlacklist = function(value) { + if (arguments.length) { + resourceUrlBlacklist = adjustMatchers(value); + } + return resourceUrlBlacklist; + }; + + this.$get = ['$injector', function($injector) { + + var htmlSanitizer = function htmlSanitizer(html) { + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + }; + + if ($injector.has('$sanitize')) { + htmlSanitizer = $injector.get('$sanitize'); + } + + + function matchUrl(matcher, parsedUrl) { + if (matcher === 'self') { + return urlIsSameOrigin(parsedUrl); + } else { + // definitely a regex. See adjustMatchers() + return !!matcher.exec(parsedUrl.href); + } + } + + function isResourceUrlAllowedByPolicy(url) { + var parsedUrl = urlResolve(url.toString()); + var i, n, allowed = false; + // Ensure that at least one item from the whitelist allows this url. + for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { + if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { + allowed = true; + break; + } + } + if (allowed) { + // Ensure that no item from the blacklist blocked this url. + for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { + if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { + allowed = false; + break; + } + } + } + return allowed; + } + + function generateHolderType(Base) { + var holderType = function TrustedValueHolderType(trustedValue) { + this.$$unwrapTrustedValue = function() { + return trustedValue; + }; + }; + if (Base) { + holderType.prototype = new Base(); + } + holderType.prototype.valueOf = function sceValueOf() { + return this.$$unwrapTrustedValue(); + }; + holderType.prototype.toString = function sceToString() { + return this.$$unwrapTrustedValue().toString(); + }; + return holderType; + } + + var trustedValueHolderBase = generateHolderType(), + byType = {}; + + byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); + byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); + + /** + * @ngdoc method + * @name $sceDelegate#trustAs + * + * @description + * Returns an object that is trusted by angular for use in specified strict + * contextual escaping contexts (such as ng-bind-html, ng-include, any src + * attribute interpolation, any dom event binding attribute interpolation + * such as for onclick, etc.) that uses the provided value. + * See {@link ng.$sce $sce} for enabling strict contextual escaping. + * + * @param {string} type The kind of context in which this value is safe for use. e.g. url, + * resourceUrl, html, js and css. + * @param {*} value The value that that should be considered trusted/safe. + * @returns {*} A value that can be used to stand in for the provided `value` in places + * where Angular expects a $sce.trustAs() return value. + */ + function trustAs(type, trustedValue) { + var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (!Constructor) { + throw $sceMinErr('icontext', + 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', + type, trustedValue); + } + if (trustedValue === null || trustedValue === undefined || trustedValue === '') { + return trustedValue; + } + // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting + // mutable objects, we ensure here that the value passed in is actually a string. + if (typeof trustedValue !== 'string') { + throw $sceMinErr('itype', + 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', + type); + } + return new Constructor(trustedValue); + } + + /** + * @ngdoc method + * @name $sceDelegate#valueOf + * + * @description + * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. + * + * If the passed parameter is not a value that had been returned by {@link + * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. + * + * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} + * call or anything else. + * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns + * `value` unchanged. + */ + function valueOf(maybeTrusted) { + if (maybeTrusted instanceof trustedValueHolderBase) { + return maybeTrusted.$$unwrapTrustedValue(); + } else { + return maybeTrusted; + } + } + + /** + * @ngdoc method + * @name $sceDelegate#getTrusted + * + * @description + * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and + * returns the originally supplied value if the queried context type is a supertype of the + * created type. If this condition isn't satisfied, throws an exception. + * + * @param {string} type The kind of context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} call. + * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs + * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. + */ + function getTrusted(type, maybeTrusted) { + if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') { + return maybeTrusted; + } + var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); + if (constructor && maybeTrusted instanceof constructor) { + return maybeTrusted.$$unwrapTrustedValue(); + } + // If we get here, then we may only take one of two actions. + // 1. sanitize the value for the requested type, or + // 2. throw an exception. + if (type === SCE_CONTEXTS.RESOURCE_URL) { + if (isResourceUrlAllowedByPolicy(maybeTrusted)) { + return maybeTrusted; + } else { + throw $sceMinErr('insecurl', + 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', + maybeTrusted.toString()); + } + } else if (type === SCE_CONTEXTS.HTML) { + return htmlSanitizer(maybeTrusted); + } + throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); + } + + return { trustAs: trustAs, + getTrusted: getTrusted, + valueOf: valueOf }; + }]; +} + + +/** + * @ngdoc provider + * @name $sceProvider + * @description + * + * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. + * - enable/disable Strict Contextual Escaping (SCE) in a module + * - override the default implementation with a custom delegate + * + * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. + */ + +/* jshint maxlen: false*/ + +/** + * @ngdoc service + * @name $sce + * @kind function + * + * @description + * + * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. + * + * # Strict Contextual Escaping + * + * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain + * contexts to result in a value that is marked as safe to use for that context. One example of + * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer + * to these contexts as privileged or SCE contexts. + * + * As of version 1.2, Angular ships with SCE enabled by default. + * + * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow + * one to execute arbitrary javascript by the use of the expression() syntax. Refer + * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them. + * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>` + * to the top of your HTML document. + * + * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for + * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. + * + * Here's an example of a binding in a privileged context: + * + * ``` + * <input ng-model="userHtml"> + * <div ng-bind-html="userHtml"></div> + * ``` + * + * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE + * disabled, this application allows the user to render arbitrary HTML into the DIV. + * In a more realistic example, one may be rendering user comments, blog articles, etc. via + * bindings. (HTML is just one example of a context where rendering user controlled input creates + * security vulnerabilities.) + * + * For the case of HTML, you might use a library, either on the client side, or on the server side, + * to sanitize unsafe HTML before binding to the value and rendering it in the document. + * + * How would you ensure that every place that used these types of bindings was bound to a value that + * was sanitized by your library (or returned as safe for rendering by your server?) How can you + * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some + * properties/fields and forgot to update the binding to the sanitized value? + * + * To be secure by default, you want to ensure that any such bindings are disallowed unless you can + * determine that something explicitly says it's safe to use a value for binding in that + * context. You can then audit your code (a simple grep would do) to ensure that this is only done + * for those values that you can easily tell are safe - because they were received from your server, + * sanitized by your library, etc. You can organize your codebase to help with this - perhaps + * allowing only the files in a specific directory to do this. Ensuring that the internal API + * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. + * + * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} + * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to + * obtain values that will be accepted by SCE / privileged contexts. + * + * + * ## How does it work? + * + * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted + * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link + * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the + * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. + * + * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link + * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly + * simplified): + * + * ``` + * var ngBindHtmlDirective = ['$sce', function($sce) { + * return function(scope, element, attr) { + * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { + * element.html(value || ''); + * }); + * }; + * }]; + * ``` + * + * ## Impact on loading templates + * + * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as + * `templateUrl`'s specified by {@link guide/directive directives}. + * + * By default, Angular only loads templates from the same domain and protocol as the application + * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or + * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist + * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. + * + * *Please note*: + * The browser's + * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) + * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) + * policy apply in addition to this and may further restrict whether the template is successfully + * loaded. This means that without the right CORS policy, loading templates from a different domain + * won't work on all browsers. Also, loading templates from `file://` URL does not work on some + * browsers. + * + * ## This feels like too much overhead + * + * It's important to remember that SCE only applies to interpolation expressions. + * + * If your expressions are constant literals, they're automatically trusted and you don't need to + * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. + * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. + * + * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them + * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. + * + * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load + * templates in `ng-include` from your application's domain without having to even know about SCE. + * It blocks loading templates from other domains or loading templates over http from an https + * served document. You can change these by setting your own custom {@link + * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link + * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. + * + * This significantly reduces the overhead. It is far easier to pay the small overhead and have an + * application that's secure and can be audited to verify that with much more ease than bolting + * security onto an application later. + * + * <a name="contexts"></a> + * ## What trusted context types are supported? + * + * | Context | Notes | + * |---------------------|----------------| + * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | + * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | + * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. | + * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | + * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | + * + * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a> + * + * Each element in these arrays must be one of the following: + * + * - **'self'** + * - The special **string**, `'self'`, can be used to match against all URLs of the **same + * domain** as the application document using the **same protocol**. + * - **String** (except the special value `'self'`) + * - The string is matched against the full *normalized / absolute URL* of the resource + * being tested (substring matches are not good enough.) + * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters + * match themselves. + * - `*`: matches zero or more occurrences of any character other than one of the following 6 + * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use + * in a whitelist. + * - `**`: matches zero or more occurrences of *any* character. As such, it's not + * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g. + * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might + * not have been the intention.) Its usage at the very end of the path is ok. (e.g. + * http://foo.example.com/templates/**). + * - **RegExp** (*see caveat below*) + * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax + * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to + * accidentally introduce a bug when one updates a complex expression (imho, all regexes should + * have good test coverage.). For instance, the use of `.` in the regex is correct only in a + * small number of cases. A `.` character in the regex used when matching the scheme or a + * subdomain could be matched against a `:` or literal `.` that was likely not intended. It + * is highly recommended to use the string patterns and only fall back to regular expressions + * if they as a last resort. + * - The regular expression must be an instance of RegExp (i.e. not a string.) It is + * matched against the **entire** *normalized / absolute URL* of the resource being tested + * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags + * present on the RegExp (such as multiline, global, ignoreCase) are ignored. + * - If you are generating your JavaScript from some other templating engine (not + * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), + * remember to escape your regular expression (and be aware that you might need more than + * one level of escaping depending on your templating engine and the way you interpolated + * the value.) Do make use of your platform's escaping mechanism as it might be good + * enough before coding your own. e.g. Ruby has + * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) + * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). + * Javascript lacks a similar built in function for escaping. Take a look at Google + * Closure library's [goog.string.regExpEscape(s)]( + * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). + * + * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. + * + * ## Show me an example using SCE. + * + * <example module="mySceApp" deps="angular-sanitize.js"> + * <file name="index.html"> + * <div ng-controller="AppController as myCtrl"> + * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br> + * <b>User comments</b><br> + * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when + * $sanitize is available. If $sanitize isn't available, this results in an error instead of an + * exploit. + * <div class="well"> + * <div ng-repeat="userComment in myCtrl.userComments"> + * <b>{{userComment.name}}</b>: + * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span> + * <br> + * </div> + * </div> + * </div> + * </file> + * + * <file name="script.js"> + * angular.module('mySceApp', ['ngSanitize']) + * .controller('AppController', ['$http', '$templateCache', '$sce', + * function($http, $templateCache, $sce) { + * var self = this; + * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { + * self.userComments = userComments; + * }); + * self.explicitlyTrustedHtml = $sce.trustAsHtml( + * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + + * 'sanitization."">Hover over this text.</span>'); + * }]); + * </file> + * + * <file name="test_data.json"> + * [ + * { "name": "Alice", + * "htmlComment": + * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>" + * }, + * { "name": "Bob", + * "htmlComment": "<i>Yes!</i> Am I the only other one?" + * } + * ] + * </file> + * + * <file name="protractor.js" type="protractor"> + * describe('SCE doc demo', function() { + * it('should sanitize untrusted values', function() { + * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) + * .toBe('<span>Is <i>anyone</i> reading this?</span>'); + * }); + * + * it('should NOT sanitize explicitly trusted values', function() { + * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( + * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' + + * 'sanitization."">Hover over this text.</span>'); + * }); + * }); + * </file> + * </example> + * + * + * + * ## Can I disable SCE completely? + * + * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits + * for little coding overhead. It will be much harder to take an SCE disabled application and + * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE + * for cases where you have a lot of existing code that was written before SCE was introduced and + * you're migrating them a module at a time. + * + * That said, here's how you can completely disable SCE: + * + * ``` + * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { + * // Completely disable SCE. For demonstration purposes only! + * // Do not use in new projects. + * $sceProvider.enabled(false); + * }); + * ``` + * + */ +/* jshint maxlen: 100 */ + +function $SceProvider() { + var enabled = true; + + /** + * @ngdoc method + * @name $sceProvider#enabled + * @kind function + * + * @param {boolean=} value If provided, then enables/disables SCE. + * @return {boolean} true if SCE is enabled, false otherwise. + * + * @description + * Enables/disables SCE and returns the current value. + */ + this.enabled = function(value) { + if (arguments.length) { + enabled = !!value; + } + return enabled; + }; + + + /* Design notes on the default implementation for SCE. + * + * The API contract for the SCE delegate + * ------------------------------------- + * The SCE delegate object must provide the following 3 methods: + * + * - trustAs(contextEnum, value) + * This method is used to tell the SCE service that the provided value is OK to use in the + * contexts specified by contextEnum. It must return an object that will be accepted by + * getTrusted() for a compatible contextEnum and return this value. + * + * - valueOf(value) + * For values that were not produced by trustAs(), return them as is. For values that were + * produced by trustAs(), return the corresponding input value to trustAs. Basically, if + * trustAs is wrapping the given values into some type, this operation unwraps it when given + * such a value. + * + * - getTrusted(contextEnum, value) + * This function should return the a value that is safe to use in the context specified by + * contextEnum or throw and exception otherwise. + * + * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be + * opaque or wrapped in some holder object. That happens to be an implementation detail. For + * instance, an implementation could maintain a registry of all trusted objects by context. In + * such a case, trustAs() would return the same object that was passed in. getTrusted() would + * return the same object passed in if it was found in the registry under a compatible context or + * throw an exception otherwise. An implementation might only wrap values some of the time based + * on some criteria. getTrusted() might return a value and not throw an exception for special + * constants or objects even if not wrapped. All such implementations fulfill this contract. + * + * + * A note on the inheritance model for SCE contexts + * ------------------------------------------------ + * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This + * is purely an implementation details. + * + * The contract is simply this: + * + * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) + * will also succeed. + * + * Inheritance happens to capture this in a natural way. In some future, we + * may not use inheritance anymore. That is OK because no code outside of + * sce.js and sceSpecs.js would need to be aware of this detail. + */ + + this.$get = ['$parse', '$sceDelegate', function( + $parse, $sceDelegate) { + // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow + // the "expression(javascript expression)" syntax which is insecure. + if (enabled && msie < 8) { + throw $sceMinErr('iequirks', + 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' + + 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' + + 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); + } + + var sce = shallowCopy(SCE_CONTEXTS); + + /** + * @ngdoc method + * @name $sce#isEnabled + * @kind function + * + * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you + * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. + * + * @description + * Returns a boolean indicating if SCE is enabled. + */ + sce.isEnabled = function() { + return enabled; + }; + sce.trustAs = $sceDelegate.trustAs; + sce.getTrusted = $sceDelegate.getTrusted; + sce.valueOf = $sceDelegate.valueOf; + + if (!enabled) { + sce.trustAs = sce.getTrusted = function(type, value) { return value; }; + sce.valueOf = identity; + } + + /** + * @ngdoc method + * @name $sce#parseAs + * + * @description + * Converts Angular {@link guide/expression expression} into a function. This is like {@link + * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it + * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, + * *result*)} + * + * @param {string} type The kind of SCE context in which this result will be used. + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + sce.parseAs = function sceParseAs(type, expr) { + var parsed = $parse(expr); + if (parsed.literal && parsed.constant) { + return parsed; + } else { + return $parse(expr, function(value) { + return sce.getTrusted(type, value); + }); + } + }; + + /** + * @ngdoc method + * @name $sce#trustAs + * + * @description + * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, + * returns an object that is trusted by angular for use in specified strict contextual + * escaping contexts (such as ng-bind-html, ng-include, any src attribute + * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) + * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual + * escaping. + * + * @param {string} type The kind of context in which this value is safe for use. e.g. url, + * resource_url, html, js and css. + * @param {*} value The value that that should be considered trusted/safe. + * @returns {*} A value that can be used to stand in for the provided `value` in places + * where Angular expects a $sce.trustAs() return value. + */ + + /** + * @ngdoc method + * @name $sce#trustAsHtml + * + * @description + * Shorthand method. `$sce.trustAsHtml(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml + * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsUrl + * + * @description + * Shorthand method. `$sce.trustAsUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl + * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsResourceUrl + * + * @description + * Shorthand method. `$sce.trustAsResourceUrl(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the return + * value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#trustAsJs + * + * @description + * Shorthand method. `$sce.trustAsJs(value)` → + * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} + * + * @param {*} value The value to trustAs. + * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs + * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives + * only accept expressions that are either literal constants or are the + * return value of {@link ng.$sce#trustAs $sce.trustAs}.) + */ + + /** + * @ngdoc method + * @name $sce#getTrusted + * + * @description + * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, + * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the + * originally supplied value if the queried context type is a supertype of the created type. + * If this condition isn't satisfied, throws an exception. + * + * @param {string} type The kind of context in which this value is to be used. + * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} + * call. + * @returns {*} The value the was originally provided to + * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. + * Otherwise, throws an exception. + */ + + /** + * @ngdoc method + * @name $sce#getTrustedHtml + * + * @description + * Shorthand method. `$sce.getTrustedHtml(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedCss + * + * @description + * Shorthand method. `$sce.getTrustedCss(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedUrl + * + * @description + * Shorthand method. `$sce.getTrustedUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedResourceUrl + * + * @description + * Shorthand method. `$sce.getTrustedResourceUrl(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} + * + * @param {*} value The value to pass to `$sceDelegate.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` + */ + + /** + * @ngdoc method + * @name $sce#getTrustedJs + * + * @description + * Shorthand method. `$sce.getTrustedJs(value)` → + * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} + * + * @param {*} value The value to pass to `$sce.getTrusted`. + * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` + */ + + /** + * @ngdoc method + * @name $sce#parseAsHtml + * + * @description + * Shorthand method. `$sce.parseAsHtml(expression string)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsCss + * + * @description + * Shorthand method. `$sce.parseAsCss(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsUrl + * + * @description + * Shorthand method. `$sce.parseAsUrl(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsResourceUrl + * + * @description + * Shorthand method. `$sce.parseAsResourceUrl(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + /** + * @ngdoc method + * @name $sce#parseAsJs + * + * @description + * Shorthand method. `$sce.parseAsJs(value)` → + * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`} + * + * @param {string} expression String expression to compile. + * @returns {function(context, locals)} a function which represents the compiled expression: + * + * * `context` – `{object}` – an object against which any expressions embedded in the strings + * are evaluated against (typically a scope object). + * * `locals` – `{object=}` – local variables context object, useful for overriding values in + * `context`. + */ + + // Shorthand delegations. + var parse = sce.parseAs, + getTrusted = sce.getTrusted, + trustAs = sce.trustAs; + + forEach(SCE_CONTEXTS, function(enumValue, name) { + var lName = lowercase(name); + sce[camelCase("parse_as_" + lName)] = function(expr) { + return parse(enumValue, expr); + }; + sce[camelCase("get_trusted_" + lName)] = function(value) { + return getTrusted(enumValue, value); + }; + sce[camelCase("trust_as_" + lName)] = function(value) { + return trustAs(enumValue, value); + }; + }); + + return sce; + }]; +} + +/** + * !!! This is an undocumented "private" service !!! + * + * @name $sniffer + * @requires $window + * @requires $document + * + * @property {boolean} history Does the browser support html5 history api ? + * @property {boolean} transitions Does the browser support CSS transition events ? + * @property {boolean} animations Does the browser support CSS animation events ? + * + * @description + * This is very simple implementation of testing browser's features. + */ +function $SnifferProvider() { + this.$get = ['$window', '$document', function($window, $document) { + var eventSupport = {}, + android = + int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), + boxee = /Boxee/i.test(($window.navigator || {}).userAgent), + document = $document[0] || {}, + vendorPrefix, + vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/, + bodyStyle = document.body && document.body.style, + transitions = false, + animations = false, + match; + + if (bodyStyle) { + for (var prop in bodyStyle) { + if (match = vendorRegex.exec(prop)) { + vendorPrefix = match[0]; + vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); + break; + } + } + + if (!vendorPrefix) { + vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; + } + + transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); + animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); + + if (android && (!transitions || !animations)) { + transitions = isString(document.body.style.webkitTransition); + animations = isString(document.body.style.webkitAnimation); + } + } + + + return { + // Android has history.pushState, but it does not update location correctly + // so let's not use the history API at all. + // http://code.google.com/p/android/issues/detail?id=17471 + // https://github.com/angular/angular.js/issues/904 + + // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has + // so let's not use the history API also + // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined + // jshint -W018 + history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), + // jshint +W018 + hasEvent: function(event) { + // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have + // it. In particular the event is not fired when backspace or delete key are pressed or + // when cut operation is performed. + // IE10+ implements 'input' event but it erroneously fires under various situations, + // e.g. when placeholder changes, or a form is focused. + if (event === 'input' && msie <= 11) return false; + + if (isUndefined(eventSupport[event])) { + var divElm = document.createElement('div'); + eventSupport[event] = 'on' + event in divElm; + } + + return eventSupport[event]; + }, + csp: csp(), + vendorPrefix: vendorPrefix, + transitions: transitions, + animations: animations, + android: android + }; + }]; +} + +var $compileMinErr = minErr('$compile'); + +/** + * @ngdoc service + * @name $templateRequest + * + * @description + * The `$templateRequest` service downloads the provided template using `$http` and, upon success, + * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data + * of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted + * by setting the 2nd parameter of the function to true). + * + * @param {string} tpl The HTTP request template URL + * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty + * + * @return {Promise} the HTTP Promise for the given. + * + * @property {number} totalPendingRequests total amount of pending template requests being downloaded. + */ +function $TemplateRequestProvider() { + this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) { + function handleRequestFn(tpl, ignoreRequestError) { + handleRequestFn.totalPendingRequests++; + + var transformResponse = $http.defaults && $http.defaults.transformResponse; + + if (isArray(transformResponse)) { + transformResponse = transformResponse.filter(function(transformer) { + return transformer !== defaultHttpResponseTransform; + }); + } else if (transformResponse === defaultHttpResponseTransform) { + transformResponse = null; + } + + var httpOptions = { + cache: $templateCache, + transformResponse: transformResponse + }; + + return $http.get(tpl, httpOptions) + ['finally'](function() { + handleRequestFn.totalPendingRequests--; + }) + .then(function(response) { + return response.data; + }, handleError); + + function handleError(resp) { + if (!ignoreRequestError) { + throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl); + } + return $q.reject(resp); + } + } + + handleRequestFn.totalPendingRequests = 0; + + return handleRequestFn; + }]; +} + +function $$TestabilityProvider() { + this.$get = ['$rootScope', '$browser', '$location', + function($rootScope, $browser, $location) { + + /** + * @name $testability + * + * @description + * The private $$testability service provides a collection of methods for use when debugging + * or by automated test and debugging tools. + */ + var testability = {}; + + /** + * @name $$testability#findBindings + * + * @description + * Returns an array of elements that are bound (via ng-bind or {{}}) + * to expressions matching the input. + * + * @param {Element} element The element root to search from. + * @param {string} expression The binding expression to match. + * @param {boolean} opt_exactMatch If true, only returns exact matches + * for the expression. Filters and whitespace are ignored. + */ + testability.findBindings = function(element, expression, opt_exactMatch) { + var bindings = element.getElementsByClassName('ng-binding'); + var matches = []; + forEach(bindings, function(binding) { + var dataBinding = angular.element(binding).data('$binding'); + if (dataBinding) { + forEach(dataBinding, function(bindingName) { + if (opt_exactMatch) { + var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)'); + if (matcher.test(bindingName)) { + matches.push(binding); + } + } else { + if (bindingName.indexOf(expression) != -1) { + matches.push(binding); + } + } + }); + } + }); + return matches; + }; + + /** + * @name $$testability#findModels + * + * @description + * Returns an array of elements that are two-way found via ng-model to + * expressions matching the input. + * + * @param {Element} element The element root to search from. + * @param {string} expression The model expression to match. + * @param {boolean} opt_exactMatch If true, only returns exact matches + * for the expression. + */ + testability.findModels = function(element, expression, opt_exactMatch) { + var prefixes = ['ng-', 'data-ng-', 'ng\\:']; + for (var p = 0; p < prefixes.length; ++p) { + var attributeEquals = opt_exactMatch ? '=' : '*='; + var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]'; + var elements = element.querySelectorAll(selector); + if (elements.length) { + return elements; + } + } + }; + + /** + * @name $$testability#getLocation + * + * @description + * Shortcut for getting the location in a browser agnostic way. Returns + * the path, search, and hash. (e.g. /path?a=b#hash) + */ + testability.getLocation = function() { + return $location.url(); + }; + + /** + * @name $$testability#setLocation + * + * @description + * Shortcut for navigating to a location without doing a full page reload. + * + * @param {string} url The location url (path, search and hash, + * e.g. /path?a=b#hash) to go to. + */ + testability.setLocation = function(url) { + if (url !== $location.url()) { + $location.url(url); + $rootScope.$digest(); + } + }; + + /** + * @name $$testability#whenStable + * + * @description + * Calls the callback when $timeout and $http requests are completed. + * + * @param {function} callback + */ + testability.whenStable = function(callback) { + $browser.notifyWhenNoOutstandingRequests(callback); + }; + + return testability; + }]; +} + +function $TimeoutProvider() { + this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler', + function($rootScope, $browser, $q, $$q, $exceptionHandler) { + var deferreds = {}; + + + /** + * @ngdoc service + * @name $timeout + * + * @description + * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch + * block and delegates any exceptions to + * {@link ng.$exceptionHandler $exceptionHandler} service. + * + * The return value of registering a timeout function is a promise, which will be resolved when + * the timeout is reached and the timeout function is executed. + * + * To cancel a timeout request, call `$timeout.cancel(promise)`. + * + * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to + * synchronously flush the queue of deferred functions. + * + * @param {function()} fn A function, whose execution should be delayed. + * @param {number=} [delay=0] Delay in milliseconds. + * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise + * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. + * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this + * promise will be resolved with is the return value of the `fn` function. + * + */ + function timeout(fn, delay, invokeApply) { + var skipApply = (isDefined(invokeApply) && !invokeApply), + deferred = (skipApply ? $$q : $q).defer(), + promise = deferred.promise, + timeoutId; + + timeoutId = $browser.defer(function() { + try { + deferred.resolve(fn()); + } catch (e) { + deferred.reject(e); + $exceptionHandler(e); + } + finally { + delete deferreds[promise.$$timeoutId]; + } + + if (!skipApply) $rootScope.$apply(); + }, delay); + + promise.$$timeoutId = timeoutId; + deferreds[timeoutId] = deferred; + + return promise; + } + + + /** + * @ngdoc method + * @name $timeout#cancel + * + * @description + * Cancels a task associated with the `promise`. As a result of this, the promise will be + * resolved with a rejection. + * + * @param {Promise=} promise Promise returned by the `$timeout` function. + * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully + * canceled. + */ + timeout.cancel = function(promise) { + if (promise && promise.$$timeoutId in deferreds) { + deferreds[promise.$$timeoutId].reject('canceled'); + delete deferreds[promise.$$timeoutId]; + return $browser.defer.cancel(promise.$$timeoutId); + } + return false; + }; + + return timeout; + }]; +} + +// NOTE: The usage of window and document instead of $window and $document here is +// deliberate. This service depends on the specific behavior of anchor nodes created by the +// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and +// cause us to break tests. In addition, when the browser resolves a URL for XHR, it +// doesn't know about mocked locations and resolves URLs to the real document - which is +// exactly the behavior needed here. There is little value is mocking these out for this +// service. +var urlParsingNode = document.createElement("a"); +var originUrl = urlResolve(window.location.href); + + +/** + * + * Implementation Notes for non-IE browsers + * ---------------------------------------- + * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, + * results both in the normalizing and parsing of the URL. Normalizing means that a relative + * URL will be resolved into an absolute URL in the context of the application document. + * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related + * properties are all populated to reflect the normalized URL. This approach has wide + * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * + * Implementation Notes for IE + * --------------------------- + * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other + * browsers. However, the parsed components will not be set if the URL assigned did not specify + * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We + * work around that by performing the parsing in a 2nd step by taking a previously normalized + * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the + * properties such as protocol, hostname, port, etc. + * + * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one + * uses the inner HTML approach to assign the URL as part of an HTML snippet - + * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL. + * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception. + * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that + * method and IE < 8 is unsupported. + * + * References: + * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement + * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html + * http://url.spec.whatwg.org/#urlutils + * https://github.com/angular/angular.js/pull/2902 + * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ + * + * @kind function + * @param {string} url The URL to be parsed. + * @description Normalizes and parses a URL. + * @returns {object} Returns the normalized URL as a dictionary. + * + * | member name | Description | + * |---------------|----------------| + * | href | A normalized version of the provided URL if it was not an absolute URL | + * | protocol | The protocol including the trailing colon | + * | host | The host and port (if the port is non-default) of the normalizedUrl | + * | search | The search params, minus the question mark | + * | hash | The hash string, minus the hash symbol + * | hostname | The hostname + * | port | The port, without ":" + * | pathname | The pathname, beginning with "/" + * + */ +function urlResolve(url) { + var href = url; + + if (msie) { + // Normalize before parse. Refer Implementation Notes on why this is + // done in two steps on IE. + urlParsingNode.setAttribute("href", href); + href = urlParsingNode.href; + } + + urlParsingNode.setAttribute('href', href); + + // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils + return { + href: urlParsingNode.href, + protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', + host: urlParsingNode.host, + search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', + hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', + hostname: urlParsingNode.hostname, + port: urlParsingNode.port, + pathname: (urlParsingNode.pathname.charAt(0) === '/') + ? urlParsingNode.pathname + : '/' + urlParsingNode.pathname + }; +} + +/** + * Parse a request URL and determine whether this is a same-origin request as the application document. + * + * @param {string|object} requestUrl The url of the request as a string that will be resolved + * or a parsed URL object. + * @returns {boolean} Whether the request is for the same origin as the application document. + */ +function urlIsSameOrigin(requestUrl) { + var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; + return (parsed.protocol === originUrl.protocol && + parsed.host === originUrl.host); +} + +/** + * @ngdoc service + * @name $window + * + * @description + * A reference to the browser's `window` object. While `window` + * is globally available in JavaScript, it causes testability problems, because + * it is a global variable. In angular we always refer to it through the + * `$window` service, so it may be overridden, removed or mocked for testing. + * + * Expressions, like the one defined for the `ngClick` directive in the example + * below, are evaluated with respect to the current scope. Therefore, there is + * no risk of inadvertently coding in a dependency on a global value in such an + * expression. + * + * @example + <example module="windowExample"> + <file name="index.html"> + <script> + angular.module('windowExample', []) + .controller('ExampleController', ['$scope', '$window', function($scope, $window) { + $scope.greeting = 'Hello, World!'; + $scope.doGreeting = function(greeting) { + $window.alert(greeting); + }; + }]); + </script> + <div ng-controller="ExampleController"> + <input type="text" ng-model="greeting" /> + <button ng-click="doGreeting(greeting)">ALERT</button> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should display the greeting in the input box', function() { + element(by.model('greeting')).sendKeys('Hello, E2E Tests'); + // If we click the button it will block the test runner + // element(':button').click(); + }); + </file> + </example> + */ +function $WindowProvider() { + this.$get = valueFn(window); +} + +/* global currencyFilter: true, + dateFilter: true, + filterFilter: true, + jsonFilter: true, + limitToFilter: true, + lowercaseFilter: true, + numberFilter: true, + orderByFilter: true, + uppercaseFilter: true, + */ + +/** + * @ngdoc provider + * @name $filterProvider + * @description + * + * Filters are just functions which transform input to an output. However filters need to be + * Dependency Injected. To achieve this a filter definition consists of a factory function which is + * annotated with dependencies and is responsible for creating a filter function. + * + * ```js + * // Filter registration + * function MyModule($provide, $filterProvider) { + * // create a service to demonstrate injection (not always needed) + * $provide.value('greet', function(name){ + * return 'Hello ' + name + '!'; + * }); + * + * // register a filter factory which uses the + * // greet service to demonstrate DI. + * $filterProvider.register('greet', function(greet){ + * // return the filter function which uses the greet service + * // to generate salutation + * return function(text) { + * // filters need to be forgiving so check input validity + * return text && greet(text) || text; + * }; + * }); + * } + * ``` + * + * The filter function is registered with the `$injector` under the filter name suffix with + * `Filter`. + * + * ```js + * it('should be the same instance', inject( + * function($filterProvider) { + * $filterProvider.register('reverse', function(){ + * return ...; + * }); + * }, + * function($filter, reverseFilter) { + * expect($filter('reverse')).toBe(reverseFilter); + * }); + * ``` + * + * + * For more information about how angular filters work, and how to create your own filters, see + * {@link guide/filter Filters} in the Angular Developer Guide. + */ + +/** + * @ngdoc service + * @name $filter + * @kind function + * @description + * Filters are used for formatting data displayed to the user. + * + * The general syntax in templates is as follows: + * + * {{ expression [| filter_name[:parameter_value] ... ] }} + * + * @param {String} name Name of the filter function to retrieve + * @return {Function} the filter function + * @example + <example name="$filter" module="filterExample"> + <file name="index.html"> + <div ng-controller="MainCtrl"> + <h3>{{ originalText }}</h3> + <h3>{{ filteredText }}</h3> + </div> + </file> + + <file name="script.js"> + angular.module('filterExample', []) + .controller('MainCtrl', function($scope, $filter) { + $scope.originalText = 'hello'; + $scope.filteredText = $filter('uppercase')($scope.originalText); + }); + </file> + </example> + */ +$FilterProvider.$inject = ['$provide']; +function $FilterProvider($provide) { + var suffix = 'Filter'; + + /** + * @ngdoc method + * @name $filterProvider#register + * @param {string|Object} name Name of the filter function, or an object map of filters where + * the keys are the filter names and the values are the filter factories. + * @returns {Object} Registered filter instance, or if a map of filters was provided then a map + * of the registered filter instances. + */ + function register(name, factory) { + if (isObject(name)) { + var filters = {}; + forEach(name, function(filter, key) { + filters[key] = register(key, filter); + }); + return filters; + } else { + return $provide.factory(name + suffix, factory); + } + } + this.register = register; + + this.$get = ['$injector', function($injector) { + return function(name) { + return $injector.get(name + suffix); + }; + }]; + + //////////////////////////////////////// + + /* global + currencyFilter: false, + dateFilter: false, + filterFilter: false, + jsonFilter: false, + limitToFilter: false, + lowercaseFilter: false, + numberFilter: false, + orderByFilter: false, + uppercaseFilter: false, + */ + + register('currency', currencyFilter); + register('date', dateFilter); + register('filter', filterFilter); + register('json', jsonFilter); + register('limitTo', limitToFilter); + register('lowercase', lowercaseFilter); + register('number', numberFilter); + register('orderBy', orderByFilter); + register('uppercase', uppercaseFilter); +} + +/** + * @ngdoc filter + * @name filter + * @kind function + * + * @description + * Selects a subset of items from `array` and returns it as a new array. + * + * @param {Array} array The source array. + * @param {string|Object|function()} expression The predicate to be used for selecting items from + * `array`. + * + * Can be one of: + * + * - `string`: The string is used for matching against the contents of the `array`. All strings or + * objects with string properties in `array` that match this string will be returned. This also + * applies to nested object properties. + * The predicate can be negated by prefixing the string with `!`. + * + * - `Object`: A pattern object can be used to filter specific properties on objects contained + * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items + * which have property `name` containing "M" and property `phone` containing "1". A special + * property name `$` can be used (as in `{$:"text"}`) to accept a match against any + * property of the object or its nested object properties. That's equivalent to the simple + * substring match with a `string` as described above. The predicate can be negated by prefixing + * the string with `!`. + * For example `{name: "!M"}` predicate will return an array of items which have property `name` + * not containing "M". + * + * Note that a named property will match properties on the same level only, while the special + * `$` property will match properties on the same level or deeper. E.g. an array item like + * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but + * **will** be matched by `{$: 'John'}`. + * + * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The + * function is called for each element of `array`. The final result is an array of those + * elements that the predicate returned true for. + * + * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in + * determining if the expected value (from the filter expression) and actual value (from + * the object in the array) should be considered a match. + * + * Can be one of: + * + * - `function(actual, expected)`: + * The function will be given the object value and the predicate value to compare and + * should return true if both values should be considered equal. + * + * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`. + * This is essentially strict comparison of expected and actual. + * + * - `false|undefined`: A short hand for a function which will look for a substring match in case + * insensitive way. + * + * @example + <example> + <file name="index.html"> + <div ng-init="friends = [{name:'John', phone:'555-1276'}, + {name:'Mary', phone:'800-BIG-MARY'}, + {name:'Mike', phone:'555-4321'}, + {name:'Adam', phone:'555-5678'}, + {name:'Julie', phone:'555-8765'}, + {name:'Juliette', phone:'555-5678'}]"></div> + + Search: <input ng-model="searchText"> + <table id="searchTextResults"> + <tr><th>Name</th><th>Phone</th></tr> + <tr ng-repeat="friend in friends | filter:searchText"> + <td>{{friend.name}}</td> + <td>{{friend.phone}}</td> + </tr> + </table> + <hr> + Any: <input ng-model="search.$"> <br> + Name only <input ng-model="search.name"><br> + Phone only <input ng-model="search.phone"><br> + Equality <input type="checkbox" ng-model="strict"><br> + <table id="searchObjResults"> + <tr><th>Name</th><th>Phone</th></tr> + <tr ng-repeat="friendObj in friends | filter:search:strict"> + <td>{{friendObj.name}}</td> + <td>{{friendObj.phone}}</td> + </tr> + </table> + </file> + <file name="protractor.js" type="protractor"> + var expectFriendNames = function(expectedNames, key) { + element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { + arr.forEach(function(wd, i) { + expect(wd.getText()).toMatch(expectedNames[i]); + }); + }); + }; + + it('should search across all fields when filtering with a string', function() { + var searchText = element(by.model('searchText')); + searchText.clear(); + searchText.sendKeys('m'); + expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); + + searchText.clear(); + searchText.sendKeys('76'); + expectFriendNames(['John', 'Julie'], 'friend'); + }); + + it('should search in specific fields when filtering with a predicate object', function() { + var searchAny = element(by.model('search.$')); + searchAny.clear(); + searchAny.sendKeys('i'); + expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); + }); + it('should use a equal comparison when comparator is true', function() { + var searchName = element(by.model('search.name')); + var strict = element(by.model('strict')); + searchName.clear(); + searchName.sendKeys('Julie'); + strict.click(); + expectFriendNames(['Julie'], 'friendObj'); + }); + </file> + </example> + */ +function filterFilter() { + return function(array, expression, comparator) { + if (!isArray(array)) return array; + + var predicateFn; + var matchAgainstAnyProp; + + switch (typeof expression) { + case 'function': + predicateFn = expression; + break; + case 'boolean': + case 'number': + case 'string': + matchAgainstAnyProp = true; + //jshint -W086 + case 'object': + //jshint +W086 + predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp); + break; + default: + return array; + } + + return array.filter(predicateFn); + }; +} + +// Helper functions for `filterFilter` +function createPredicateFn(expression, comparator, matchAgainstAnyProp) { + var shouldMatchPrimitives = isObject(expression) && ('$' in expression); + var predicateFn; + + if (comparator === true) { + comparator = equals; + } else if (!isFunction(comparator)) { + comparator = function(actual, expected) { + if (isObject(actual) || isObject(expected)) { + // Prevent an object to be considered equal to a string like `'[object'` + return false; + } + + actual = lowercase('' + actual); + expected = lowercase('' + expected); + return actual.indexOf(expected) !== -1; + }; + } + + predicateFn = function(item) { + if (shouldMatchPrimitives && !isObject(item)) { + return deepCompare(item, expression.$, comparator, false); + } + return deepCompare(item, expression, comparator, matchAgainstAnyProp); + }; + + return predicateFn; +} + +function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) { + var actualType = (actual !== null) ? typeof actual : 'null'; + var expectedType = (expected !== null) ? typeof expected : 'null'; + + if ((expectedType === 'string') && (expected.charAt(0) === '!')) { + return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp); + } else if (isArray(actual)) { + // In case `actual` is an array, consider it a match + // if ANY of it's items matches `expected` + return actual.some(function(item) { + return deepCompare(item, expected, comparator, matchAgainstAnyProp); + }); + } + + switch (actualType) { + case 'object': + var key; + if (matchAgainstAnyProp) { + for (key in actual) { + if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) { + return true; + } + } + return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false); + } else if (expectedType === 'object') { + for (key in expected) { + var expectedVal = expected[key]; + if (isFunction(expectedVal) || isUndefined(expectedVal)) { + continue; + } + + var matchAnyProperty = key === '$'; + var actualVal = matchAnyProperty ? actual : actual[key]; + if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) { + return false; + } + } + return true; + } else { + return comparator(actual, expected); + } + break; + case 'function': + return false; + default: + return comparator(actual, expected); + } +} + +/** + * @ngdoc filter + * @name currency + * @kind function + * + * @description + * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default + * symbol for current locale is used. + * + * @param {number} amount Input to filter. + * @param {string=} symbol Currency symbol or identifier to be displayed. + * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale + * @returns {string} Formatted number. + * + * + * @example + <example module="currencyExample"> + <file name="index.html"> + <script> + angular.module('currencyExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.amount = 1234.56; + }]); + </script> + <div ng-controller="ExampleController"> + <input type="number" ng-model="amount"> <br> + default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br> + custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span> + no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should init with 1234.56', function() { + expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); + expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56'); + expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235'); + }); + it('should update', function() { + if (browser.params.browser == 'safari') { + // Safari does not understand the minus key. See + // https://github.com/angular/protractor/issues/481 + return; + } + element(by.model('amount')).clear(); + element(by.model('amount')).sendKeys('-1234'); + expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)'); + expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)'); + expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)'); + }); + </file> + </example> + */ +currencyFilter.$inject = ['$locale']; +function currencyFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(amount, currencySymbol, fractionSize) { + if (isUndefined(currencySymbol)) { + currencySymbol = formats.CURRENCY_SYM; + } + + if (isUndefined(fractionSize)) { + fractionSize = formats.PATTERNS[1].maxFrac; + } + + // if null or undefined pass it through + return (amount == null) + ? amount + : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize). + replace(/\u00A4/g, currencySymbol); + }; +} + +/** + * @ngdoc filter + * @name number + * @kind function + * + * @description + * Formats a number as text. + * + * If the input is not a number an empty string is returned. + * + * @param {number|string} number Number to format. + * @param {(number|string)=} fractionSize Number of decimal places to round the number to. + * If this is not provided then the fraction size is computed from the current locale's number + * formatting pattern. In the case of the default locale, it will be 3. + * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. + * + * @example + <example module="numberFilterExample"> + <file name="index.html"> + <script> + angular.module('numberFilterExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.val = 1234.56789; + }]); + </script> + <div ng-controller="ExampleController"> + Enter number: <input ng-model='val'><br> + Default formatting: <span id='number-default'>{{val | number}}</span><br> + No fractions: <span>{{val | number:0}}</span><br> + Negative number: <span>{{-val | number:4}}</span> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should format numbers', function() { + expect(element(by.id('number-default')).getText()).toBe('1,234.568'); + expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); + }); + + it('should update', function() { + element(by.model('val')).clear(); + element(by.model('val')).sendKeys('3374.333'); + expect(element(by.id('number-default')).getText()).toBe('3,374.333'); + expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); + expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); + }); + </file> + </example> + */ + + +numberFilter.$inject = ['$locale']; +function numberFilter($locale) { + var formats = $locale.NUMBER_FORMATS; + return function(number, fractionSize) { + + // if null or undefined pass it through + return (number == null) + ? number + : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, + fractionSize); + }; +} + +var DECIMAL_SEP = '.'; +function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { + if (!isFinite(number) || isObject(number)) return ''; + + var isNegative = number < 0; + number = Math.abs(number); + var numStr = number + '', + formatedText = '', + parts = []; + + var hasExponent = false; + if (numStr.indexOf('e') !== -1) { + var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); + if (match && match[2] == '-' && match[3] > fractionSize + 1) { + number = 0; + } else { + formatedText = numStr; + hasExponent = true; + } + } + + if (!hasExponent) { + var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; + + // determine fractionSize if it is not specified + if (isUndefined(fractionSize)) { + fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); + } + + // safely round numbers in JS without hitting imprecisions of floating-point arithmetics + // inspired by: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round + number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); + + var fraction = ('' + number).split(DECIMAL_SEP); + var whole = fraction[0]; + fraction = fraction[1] || ''; + + var i, pos = 0, + lgroup = pattern.lgSize, + group = pattern.gSize; + + if (whole.length >= (lgroup + group)) { + pos = whole.length - lgroup; + for (i = 0; i < pos; i++) { + if ((pos - i) % group === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + } + + for (i = pos; i < whole.length; i++) { + if ((whole.length - i) % lgroup === 0 && i !== 0) { + formatedText += groupSep; + } + formatedText += whole.charAt(i); + } + + // format fraction part. + while (fraction.length < fractionSize) { + fraction += '0'; + } + + if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); + } else { + if (fractionSize > 0 && number < 1) { + formatedText = number.toFixed(fractionSize); + number = parseFloat(formatedText); + } + } + + if (number === 0) { + isNegative = false; + } + + parts.push(isNegative ? pattern.negPre : pattern.posPre, + formatedText, + isNegative ? pattern.negSuf : pattern.posSuf); + return parts.join(''); +} + +function padNumber(num, digits, trim) { + var neg = ''; + if (num < 0) { + neg = '-'; + num = -num; + } + num = '' + num; + while (num.length < digits) num = '0' + num; + if (trim) + num = num.substr(num.length - digits); + return neg + num; +} + + +function dateGetter(name, size, offset, trim) { + offset = offset || 0; + return function(date) { + var value = date['get' + name](); + if (offset > 0 || value > -offset) + value += offset; + if (value === 0 && offset == -12) value = 12; + return padNumber(value, size, trim); + }; +} + +function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date['get' + name](); + var get = uppercase(shortForm ? ('SHORT' + name) : name); + + return formats[get][value]; + }; +} + +function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset(); + var paddedZone = (zone >= 0) ? "+" : ""; + + paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + + padNumber(Math.abs(zone % 60), 2); + + return paddedZone; +} + +function getFirstThursdayOfYear(year) { + // 0 = index of January + var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay(); + // 4 = index of Thursday (+1 to account for 1st = 5) + // 11 = index of *next* Thursday (+1 account for 1st = 12) + return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst); +} + +function getThursdayThisWeek(datetime) { + return new Date(datetime.getFullYear(), datetime.getMonth(), + // 4 = index of Thursday + datetime.getDate() + (4 - datetime.getDay())); +} + +function weekGetter(size) { + return function(date) { + var firstThurs = getFirstThursdayOfYear(date.getFullYear()), + thisThurs = getThursdayThisWeek(date); + + var diff = +thisThurs - +firstThurs, + result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week + + return padNumber(result, size); + }; +} + +function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; +} + +function eraGetter(date, formats) { + return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1]; +} + +function longEraGetter(date, formats) { + return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1]; +} + +var DATE_FORMATS = { + yyyy: dateGetter('FullYear', 4), + yy: dateGetter('FullYear', 2, 0, true), + y: dateGetter('FullYear', 1), + MMMM: dateStrGetter('Month'), + MMM: dateStrGetter('Month', true), + MM: dateGetter('Month', 2, 1), + M: dateGetter('Month', 1, 1), + dd: dateGetter('Date', 2), + d: dateGetter('Date', 1), + HH: dateGetter('Hours', 2), + H: dateGetter('Hours', 1), + hh: dateGetter('Hours', 2, -12), + h: dateGetter('Hours', 1, -12), + mm: dateGetter('Minutes', 2), + m: dateGetter('Minutes', 1), + ss: dateGetter('Seconds', 2), + s: dateGetter('Seconds', 1), + // while ISO 8601 requires fractions to be prefixed with `.` or `,` + // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions + sss: dateGetter('Milliseconds', 3), + EEEE: dateStrGetter('Day'), + EEE: dateStrGetter('Day', true), + a: ampmGetter, + Z: timeZoneGetter, + ww: weekGetter(2), + w: weekGetter(1), + G: eraGetter, + GG: eraGetter, + GGG: eraGetter, + GGGG: longEraGetter +}; + +var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, + NUMBER_STRING = /^\-?\d+$/; + +/** + * @ngdoc filter + * @name date + * @kind function + * + * @description + * Formats `date` to a string based on the requested `format`. + * + * `format` string can be composed of the following elements: + * + * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + * * `'MMMM'`: Month in year (January-December) + * * `'MMM'`: Month in year (Jan-Dec) + * * `'MM'`: Month in year, padded (01-12) + * * `'M'`: Month in year (1-12) + * * `'dd'`: Day in month, padded (01-31) + * * `'d'`: Day in month (1-31) + * * `'EEEE'`: Day in Week,(Sunday-Saturday) + * * `'EEE'`: Day in Week, (Sun-Sat) + * * `'HH'`: Hour in day, padded (00-23) + * * `'H'`: Hour in day (0-23) + * * `'hh'`: Hour in AM/PM, padded (01-12) + * * `'h'`: Hour in AM/PM, (1-12) + * * `'mm'`: Minute in hour, padded (00-59) + * * `'m'`: Minute in hour (0-59) + * * `'ss'`: Second in minute, padded (00-59) + * * `'s'`: Second in minute (0-59) + * * `'sss'`: Millisecond in second, padded (000-999) + * * `'a'`: AM/PM marker + * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) + * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year + * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year + * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD') + * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini') + * + * `format` string can also be one of the following predefined + * {@link guide/i18n localizable formats}: + * + * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale + * (e.g. Sep 3, 2010 12:05:08 PM) + * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM) + * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale + * (e.g. Friday, September 3, 2010) + * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) + * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) + * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) + * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM) + * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM) + * + * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. + * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence + * (e.g. `"h 'o''clock'"`). + * + * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or + * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its + * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is + * specified in the string input, the time is considered to be in the local timezone. + * @param {string=} format Formatting rules (see Description). If not specified, + * `mediumDate` is used. + * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported. + * If not specified, the timezone of the browser will be used. + * @returns {string} Formatted string or the input if input is not recognized as date/millis. + * + * @example + <example> + <file name="index.html"> + <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>: + <span>{{1288323623006 | date:'medium'}}</span><br> + <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>: + <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br> + <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>: + <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br> + <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>: + <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br> + </file> + <file name="protractor.js" type="protractor"> + it('should format date', function() { + expect(element(by.binding("1288323623006 | date:'medium'")).getText()). + toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); + expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). + toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); + expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). + toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); + expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). + toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); + }); + </file> + </example> + */ +dateFilter.$inject = ['$locale']; +function dateFilter($locale) { + + + var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; + // 1 2 3 4 5 6 7 8 9 10 11 + function jsonStringToDate(string) { + var match; + if (match = string.match(R_ISO8601_STR)) { + var date = new Date(0), + tzHour = 0, + tzMin = 0, + dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, + timeSetter = match[8] ? date.setUTCHours : date.setHours; + + if (match[9]) { + tzHour = int(match[9] + match[10]); + tzMin = int(match[9] + match[11]); + } + dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); + var h = int(match[4] || 0) - tzHour; + var m = int(match[5] || 0) - tzMin; + var s = int(match[6] || 0); + var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); + timeSetter.call(date, h, m, s, ms); + return date; + } + return string; + } + + + return function(date, format, timezone) { + var text = '', + parts = [], + fn, match; + + format = format || 'mediumDate'; + format = $locale.DATETIME_FORMATS[format] || format; + if (isString(date)) { + date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date); + } + + if (isNumber(date)) { + date = new Date(date); + } + + if (!isDate(date)) { + return date; + } + + while (format) { + match = DATE_FORMATS_SPLIT.exec(format); + if (match) { + parts = concat(parts, match, 1); + format = parts.pop(); + } else { + parts.push(format); + format = null; + } + } + + if (timezone && timezone === 'UTC') { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); + } + forEach(parts, function(value) { + fn = DATE_FORMATS[value]; + text += fn ? fn(date, $locale.DATETIME_FORMATS) + : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); + }); + + return text; + }; +} + + +/** + * @ngdoc filter + * @name json + * @kind function + * + * @description + * Allows you to convert a JavaScript object into JSON string. + * + * This filter is mostly useful for debugging. When using the double curly {{value}} notation + * the binding is automatically converted to JSON. + * + * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. + * @param {number=} spacing The number of spaces to use per indentation, defaults to 2. + * @returns {string} JSON string. + * + * + * @example + <example> + <file name="index.html"> + <pre id="default-spacing">{{ {'name':'value'} | json }}</pre> + <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre> + </file> + <file name="protractor.js" type="protractor"> + it('should jsonify filtered objects', function() { + expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); + expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/); + }); + </file> + </example> + * + */ +function jsonFilter() { + return function(object, spacing) { + if (isUndefined(spacing)) { + spacing = 2; + } + return toJson(object, spacing); + }; +} + + +/** + * @ngdoc filter + * @name lowercase + * @kind function + * @description + * Converts string to lowercase. + * @see angular.lowercase + */ +var lowercaseFilter = valueFn(lowercase); + + +/** + * @ngdoc filter + * @name uppercase + * @kind function + * @description + * Converts string to uppercase. + * @see angular.uppercase + */ +var uppercaseFilter = valueFn(uppercase); + +/** + * @ngdoc filter + * @name limitTo + * @kind function + * + * @description + * Creates a new array or string containing only a specified number of elements. The elements + * are taken from either the beginning or the end of the source array, string or number, as specified by + * the value and sign (positive or negative) of `limit`. If a number is used as input, it is + * converted to a string. + * + * @param {Array|string|number} input Source array, string or number to be limited. + * @param {string|number} limit The length of the returned array or string. If the `limit` number + * is positive, `limit` number of items from the beginning of the source array/string are copied. + * If the number is negative, `limit` number of items from the end of the source array/string + * are copied. The `limit` will be trimmed if it exceeds `array.length` + * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array + * had less than `limit` elements. + * + * @example + <example module="limitToExample"> + <file name="index.html"> + <script> + angular.module('limitToExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.numbers = [1,2,3,4,5,6,7,8,9]; + $scope.letters = "abcdefghi"; + $scope.longNumber = 2345432342; + $scope.numLimit = 3; + $scope.letterLimit = 3; + $scope.longNumberLimit = 3; + }]); + </script> + <div ng-controller="ExampleController"> + Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit"> + <p>Output numbers: {{ numbers | limitTo:numLimit }}</p> + Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit"> + <p>Output letters: {{ letters | limitTo:letterLimit }}</p> + Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit"> + <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p> + </div> + </file> + <file name="protractor.js" type="protractor"> + var numLimitInput = element(by.model('numLimit')); + var letterLimitInput = element(by.model('letterLimit')); + var longNumberLimitInput = element(by.model('longNumberLimit')); + var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); + var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); + var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit')); + + it('should limit the number array to first three items', function() { + expect(numLimitInput.getAttribute('value')).toBe('3'); + expect(letterLimitInput.getAttribute('value')).toBe('3'); + expect(longNumberLimitInput.getAttribute('value')).toBe('3'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); + expect(limitedLetters.getText()).toEqual('Output letters: abc'); + expect(limitedLongNumber.getText()).toEqual('Output long number: 234'); + }); + + // There is a bug in safari and protractor that doesn't like the minus key + // it('should update the output when -3 is entered', function() { + // numLimitInput.clear(); + // numLimitInput.sendKeys('-3'); + // letterLimitInput.clear(); + // letterLimitInput.sendKeys('-3'); + // longNumberLimitInput.clear(); + // longNumberLimitInput.sendKeys('-3'); + // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); + // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); + // expect(limitedLongNumber.getText()).toEqual('Output long number: 342'); + // }); + + it('should not exceed the maximum size of input array', function() { + numLimitInput.clear(); + numLimitInput.sendKeys('100'); + letterLimitInput.clear(); + letterLimitInput.sendKeys('100'); + longNumberLimitInput.clear(); + longNumberLimitInput.sendKeys('100'); + expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); + expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); + expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342'); + }); + </file> + </example> +*/ +function limitToFilter() { + return function(input, limit) { + if (isNumber(input)) input = input.toString(); + if (!isArray(input) && !isString(input)) return input; + + if (Math.abs(Number(limit)) === Infinity) { + limit = Number(limit); + } else { + limit = int(limit); + } + + //NaN check on limit + if (limit) { + return limit > 0 ? input.slice(0, limit) : input.slice(limit); + } else { + return isString(input) ? "" : []; + } + }; +} + +/** + * @ngdoc filter + * @name orderBy + * @kind function + * + * @description + * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically + * for strings and numerically for numbers. Note: if you notice numbers are not being sorted + * correctly, make sure they are actually being saved as numbers and not strings. + * + * @param {Array} array The array to sort. + * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be + * used by the comparator to determine the order of elements. + * + * Can be one of: + * + * - `function`: Getter function. The result of this function will be sorted using the + * `<`, `=`, `>` operator. + * - `string`: An Angular expression. The result of this expression is used to compare elements + * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by + * 3 first characters of a property called `name`). The result of a constant expression + * is interpreted as a property name to be used in comparisons (for example `"special name"` + * to sort object by the value of their `special name` property). An expression can be + * optionally prefixed with `+` or `-` to control ascending or descending sort order + * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array + * element itself is used to compare where sorting. + * - `Array`: An array of function or string predicates. The first predicate in the array + * is used for sorting, but when two items are equivalent, the next predicate is used. + * + * If the predicate is missing or empty then it defaults to `'+'`. + * + * @param {boolean=} reverse Reverse the order of the array. + * @returns {Array} Sorted copy of the source array. + * + * + * @example + * The example below demonstrates a simple ngRepeat, where the data is sorted + * by age in descending order (predicate is set to `'-age'`). + * `reverse` is not set, which means it defaults to `false`. + <example module="orderByExample"> + <file name="index.html"> + <script> + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.friends = + [{name:'John', phone:'555-1212', age:10}, + {name:'Mary', phone:'555-9876', age:19}, + {name:'Mike', phone:'555-4321', age:21}, + {name:'Adam', phone:'555-5678', age:35}, + {name:'Julie', phone:'555-8765', age:29}]; + }]); + </script> + <div ng-controller="ExampleController"> + <table class="friend"> + <tr> + <th>Name</th> + <th>Phone Number</th> + <th>Age</th> + </tr> + <tr ng-repeat="friend in friends | orderBy:'-age'"> + <td>{{friend.name}}</td> + <td>{{friend.phone}}</td> + <td>{{friend.age}}</td> + </tr> + </table> + </div> + </file> + </example> + * + * The predicate and reverse parameters can be controlled dynamically through scope properties, + * as shown in the next example. + * @example + <example module="orderByExample"> + <file name="index.html"> + <script> + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.friends = + [{name:'John', phone:'555-1212', age:10}, + {name:'Mary', phone:'555-9876', age:19}, + {name:'Mike', phone:'555-4321', age:21}, + {name:'Adam', phone:'555-5678', age:35}, + {name:'Julie', phone:'555-8765', age:29}]; + $scope.predicate = '-age'; + }]); + </script> + <div ng-controller="ExampleController"> + <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre> + <hr/> + [ <a href="" ng-click="predicate=''">unsorted</a> ] + <table class="friend"> + <tr> + <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a> + (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th> + <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th> + <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th> + </tr> + <tr ng-repeat="friend in friends | orderBy:predicate:reverse"> + <td>{{friend.name}}</td> + <td>{{friend.phone}}</td> + <td>{{friend.age}}</td> + </tr> + </table> + </div> + </file> + </example> + * + * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the + * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the + * desired parameters. + * + * Example: + * + * @example + <example module="orderByExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <table class="friend"> + <tr> + <th><a href="" ng-click="reverse=false;order('name', false)">Name</a> + (<a href="" ng-click="order('-name',false)">^</a>)</th> + <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th> + <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th> + </tr> + <tr ng-repeat="friend in friends"> + <td>{{friend.name}}</td> + <td>{{friend.phone}}</td> + <td>{{friend.age}}</td> + </tr> + </table> + </div> + </file> + + <file name="script.js"> + angular.module('orderByExample', []) + .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { + var orderBy = $filter('orderBy'); + $scope.friends = [ + { name: 'John', phone: '555-1212', age: 10 }, + { name: 'Mary', phone: '555-9876', age: 19 }, + { name: 'Mike', phone: '555-4321', age: 21 }, + { name: 'Adam', phone: '555-5678', age: 35 }, + { name: 'Julie', phone: '555-8765', age: 29 } + ]; + $scope.order = function(predicate, reverse) { + $scope.friends = orderBy($scope.friends, predicate, reverse); + }; + $scope.order('-age',false); + }]); + </file> +</example> + */ +orderByFilter.$inject = ['$parse']; +function orderByFilter($parse) { + return function(array, sortPredicate, reverseOrder) { + if (!(isArrayLike(array))) return array; + sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate]; + if (sortPredicate.length === 0) { sortPredicate = ['+']; } + sortPredicate = sortPredicate.map(function(predicate) { + var descending = false, get = predicate || identity; + if (isString(predicate)) { + if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { + descending = predicate.charAt(0) == '-'; + predicate = predicate.substring(1); + } + if (predicate === '') { + // Effectively no predicate was passed so we compare identity + return reverseComparator(compare, descending); + } + get = $parse(predicate); + if (get.constant) { + var key = get(); + return reverseComparator(function(a, b) { + return compare(a[key], b[key]); + }, descending); + } + } + return reverseComparator(function(a, b) { + return compare(get(a),get(b)); + }, descending); + }); + return slice.call(array).sort(reverseComparator(comparator, reverseOrder)); + + function comparator(o1, o2) { + for (var i = 0; i < sortPredicate.length; i++) { + var comp = sortPredicate[i](o1, o2); + if (comp !== 0) return comp; + } + return 0; + } + function reverseComparator(comp, descending) { + return descending + ? function(a, b) {return comp(b,a);} + : comp; + } + + function isPrimitive(value) { + switch (typeof value) { + case 'number': /* falls through */ + case 'boolean': /* falls through */ + case 'string': + return true; + default: + return false; + } + } + + function objectToString(value) { + if (value === null) return 'null'; + if (typeof value.valueOf === 'function') { + value = value.valueOf(); + if (isPrimitive(value)) return value; + } + if (typeof value.toString === 'function') { + value = value.toString(); + if (isPrimitive(value)) return value; + } + return ''; + } + + function compare(v1, v2) { + var t1 = typeof v1; + var t2 = typeof v2; + if (t1 === t2 && t1 === "object") { + v1 = objectToString(v1); + v2 = objectToString(v2); + } + if (t1 === t2) { + if (t1 === "string") { + v1 = v1.toLowerCase(); + v2 = v2.toLowerCase(); + } + if (v1 === v2) return 0; + return v1 < v2 ? -1 : 1; + } else { + return t1 < t2 ? -1 : 1; + } + } + }; +} + +function ngDirective(directive) { + if (isFunction(directive)) { + directive = { + link: directive + }; + } + directive.restrict = directive.restrict || 'AC'; + return valueFn(directive); +} + +/** + * @ngdoc directive + * @name a + * @restrict E + * + * @description + * Modifies the default behavior of the html A tag so that the default action is prevented when + * the href attribute is empty. + * + * This change permits the easy creation of action links with the `ngClick` directive + * without changing the location or causing page reloads, e.g.: + * `<a href="" ng-click="list.addItem()">Add Item</a>` + */ +var htmlAnchorDirective = valueFn({ + restrict: 'E', + compile: function(element, attr) { + if (!attr.href && !attr.xlinkHref && !attr.name) { + return function(scope, element) { + // If the linked element is not an anchor tag anymore, do nothing + if (element[0].nodeName.toLowerCase() !== 'a') return; + + // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. + var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? + 'xlink:href' : 'href'; + element.on('click', function(event) { + // if we have no href url, then don't navigate anywhere. + if (!element.attr(href)) { + event.preventDefault(); + } + }); + }; + } + } +}); + +/** + * @ngdoc directive + * @name ngHref + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in an href attribute will + * make the link go to the wrong URL if the user clicks it before + * Angular has a chance to replace the `{{hash}}` markup with its + * value. Until Angular replaces the markup the link will be broken + * and will most likely return a 404 error. The `ngHref` directive + * solves this problem. + * + * The wrong way to write it: + * ```html + * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a> + * ``` + * + * The correct way to write it: + * ```html + * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a> + * ``` + * + * @element A + * @param {template} ngHref any string which can contain `{{}}` markup. + * + * @example + * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes + * in links and their different behaviors: + <example> + <file name="index.html"> + <input ng-model="value" /><br /> + <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> + <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> + <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br /> + <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> + <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> + <a id="link-6" ng-href="{{value}}">link</a> (link, change location) + </file> + <file name="protractor.js" type="protractor"> + it('should execute ng-click but not reload when href without value', function() { + element(by.id('link-1')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('1'); + expect(element(by.id('link-1')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when href empty string', function() { + element(by.id('link-2')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('2'); + expect(element(by.id('link-2')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click and change url when ng-href specified', function() { + expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); + + element(by.id('link-3')).click(); + + // At this point, we navigate away from an Angular page, so we need + // to use browser.driver to get the base webdriver. + + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/123$/); + }); + }, 5000, 'page should navigate to /123'); + }); + + xit('should execute ng-click but not reload when href empty string and name specified', function() { + element(by.id('link-4')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('4'); + expect(element(by.id('link-4')).getAttribute('href')).toBe(''); + }); + + it('should execute ng-click but not reload when no href but name specified', function() { + element(by.id('link-5')).click(); + expect(element(by.model('value')).getAttribute('value')).toEqual('5'); + expect(element(by.id('link-5')).getAttribute('href')).toBe(null); + }); + + it('should only change url when only ng-href', function() { + element(by.model('value')).clear(); + element(by.model('value')).sendKeys('6'); + expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); + + element(by.id('link-6')).click(); + + // At this point, we navigate away from an Angular page, so we need + // to use browser.driver to get the base webdriver. + browser.wait(function() { + return browser.driver.getCurrentUrl().then(function(url) { + return url.match(/\/6$/); + }); + }, 5000, 'page should navigate to /6'); + }); + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngSrc + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in a `src` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrc` directive solves this problem. + * + * The buggy way to write it: + * ```html + * <img src="http://www.gravatar.com/avatar/{{hash}}"/> + * ``` + * + * The correct way to write it: + * ```html + * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/> + * ``` + * + * @element IMG + * @param {template} ngSrc any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngSrcset + * @restrict A + * @priority 99 + * + * @description + * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't + * work right: The browser will fetch from the URL with the literal + * text `{{hash}}` until Angular replaces the expression inside + * `{{hash}}`. The `ngSrcset` directive solves this problem. + * + * The buggy way to write it: + * ```html + * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/> + * ``` + * + * The correct way to write it: + * ```html + * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/> + * ``` + * + * @element IMG + * @param {template} ngSrcset any string which can contain `{{}}` markup. + */ + +/** + * @ngdoc directive + * @name ngDisabled + * @restrict A + * @priority 100 + * + * @description + * + * This directive sets the `disabled` attribute on the element if the + * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy. + * + * A special directive is necessary because we cannot use interpolation inside the `disabled` + * attribute. The following example would make the button enabled on Chrome/Firefox + * but not on older IEs: + * + * ```html + * <!-- See below for an example of ng-disabled being used correctly --> + * <div ng-init="isDisabled = false"> + * <button disabled="{{isDisabled}}">Disabled</button> + * </div> + * ``` + * + * This is because the HTML specification does not require browsers to preserve the values of + * boolean attributes such as `disabled` (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * + * @example + <example> + <file name="index.html"> + Click me to toggle: <input type="checkbox" ng-model="checked"><br/> + <button ng-model="button" ng-disabled="checked">Button</button> + </file> + <file name="protractor.js" type="protractor"> + it('should toggle button', function() { + expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); + }); + </file> + </example> + * + * @element INPUT + * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, + * then the `disabled` attribute will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngChecked + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as checked. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngChecked` directive solves this problem for the `checked` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * @example + <example> + <file name="index.html"> + Check me to check both: <input type="checkbox" ng-model="master"><br/> + <input id="checkSlave" type="checkbox" ng-checked="master"> + </file> + <file name="protractor.js" type="protractor"> + it('should check both checkBoxes', function() { + expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); + element(by.model('master')).click(); + expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); + }); + </file> + </example> + * + * @element INPUT + * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, + * then special attribute "checked" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngReadonly + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as readonly. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngReadonly` directive solves this problem for the `readonly` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * @example + <example> + <file name="index.html"> + Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/> + <input type="text" ng-readonly="checked" value="I'm Angular"/> + </file> + <file name="protractor.js" type="protractor"> + it('should toggle readonly attr', function() { + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); + element(by.model('checked')).click(); + expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); + }); + </file> + </example> + * + * @element INPUT + * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, + * then special attribute "readonly" will be set on the element + */ + + +/** + * @ngdoc directive + * @name ngSelected + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as selected. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngSelected` directive solves this problem for the `selected` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * + * @example + <example> + <file name="index.html"> + Check me to select: <input type="checkbox" ng-model="selected"><br/> + <select> + <option>Hello!</option> + <option id="greet" ng-selected="selected">Greetings!</option> + </select> + </file> + <file name="protractor.js" type="protractor"> + it('should select Greetings!', function() { + expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); + element(by.model('selected')).click(); + expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); + }); + </file> + </example> + * + * @element OPTION + * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, + * then special attribute "selected" will be set on the element + */ + +/** + * @ngdoc directive + * @name ngOpen + * @restrict A + * @priority 100 + * + * @description + * The HTML specification does not require browsers to preserve the values of boolean attributes + * such as open. (Their presence means true and their absence means false.) + * If we put an Angular interpolation expression into such an attribute then the + * binding information would be lost when the browser removes the attribute. + * The `ngOpen` directive solves this problem for the `open` attribute. + * This complementary directive is not removed by the browser and so provides + * a permanent reliable place to store the binding information. + * @example + <example> + <file name="index.html"> + Check me check multiple: <input type="checkbox" ng-model="open"><br/> + <details id="details" ng-open="open"> + <summary>Show/Hide me</summary> + </details> + </file> + <file name="protractor.js" type="protractor"> + it('should toggle open', function() { + expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); + element(by.model('open')).click(); + expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); + }); + </file> + </example> + * + * @element DETAILS + * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, + * then special attribute "open" will be set on the element + */ + +var ngAttributeAliasDirectives = {}; + + +// boolean attrs are evaluated +forEach(BOOLEAN_ATTR, function(propName, attrName) { + // binding to multiple is not supported + if (propName == "multiple") return; + + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + restrict: 'A', + priority: 100, + link: function(scope, element, attr) { + scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { + attr.$set(attrName, !!value); + }); + } + }; + }; +}); + +// aliased input attrs are evaluated +forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) { + ngAttributeAliasDirectives[ngAttr] = function() { + return { + priority: 100, + link: function(scope, element, attr) { + //special case ngPattern when a literal regular expression value + //is used as the expression (this way we don't have to watch anything). + if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") { + var match = attr.ngPattern.match(REGEX_STRING_REGEXP); + if (match) { + attr.$set("ngPattern", new RegExp(match[1], match[2])); + return; + } + } + + scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) { + attr.$set(ngAttr, value); + }); + } + }; + }; +}); + +// ng-src, ng-srcset, ng-href are interpolated +forEach(['src', 'srcset', 'href'], function(attrName) { + var normalized = directiveNormalize('ng-' + attrName); + ngAttributeAliasDirectives[normalized] = function() { + return { + priority: 99, // it needs to run after the attributes are interpolated + link: function(scope, element, attr) { + var propName = attrName, + name = attrName; + + if (attrName === 'href' && + toString.call(element.prop('href')) === '[object SVGAnimatedString]') { + name = 'xlinkHref'; + attr.$attr[name] = 'xlink:href'; + propName = null; + } + + attr.$observe(normalized, function(value) { + if (!value) { + if (attrName === 'href') { + attr.$set(name, null); + } + return; + } + + attr.$set(name, value); + + // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist + // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need + // to set the property as well to achieve the desired effect. + // we use attr[attrName] value since $set can sanitize the url. + if (msie && propName) element.prop(propName, attr[name]); + }); + } + }; + }; +}); + +/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true + */ +var nullFormCtrl = { + $addControl: noop, + $$renameControl: nullFormRenameControl, + $removeControl: noop, + $setValidity: noop, + $setDirty: noop, + $setPristine: noop, + $setSubmitted: noop +}, +SUBMITTED_CLASS = 'ng-submitted'; + +function nullFormRenameControl(control, name) { + control.$name = name; +} + +/** + * @ngdoc type + * @name form.FormController + * + * @property {boolean} $pristine True if user has not interacted with the form yet. + * @property {boolean} $dirty True if user has already interacted with the form. + * @property {boolean} $valid True if all of the containing forms and controls are valid. + * @property {boolean} $invalid True if at least one containing control or form is invalid. + * @property {boolean} $submitted True if user has submitted the form even if its invalid. + * + * @property {Object} $error Is an object hash, containing references to controls or + * forms with failing validators, where: + * + * - keys are validation tokens (error names), + * - values are arrays of controls or forms that have a failing validator for given error name. + * + * Built-in validation tokens: + * + * - `email` + * - `max` + * - `maxlength` + * - `min` + * - `minlength` + * - `number` + * - `pattern` + * - `required` + * - `url` + * - `date` + * - `datetimelocal` + * - `time` + * - `week` + * - `month` + * + * @description + * `FormController` keeps track of all its controls and nested forms as well as the state of them, + * such as being valid/invalid or dirty/pristine. + * + * Each {@link ng.directive:form form} directive creates an instance + * of `FormController`. + * + */ +//asks for $scope to fool the BC controller module +FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate']; +function FormController(element, attrs, $scope, $animate, $interpolate) { + var form = this, + controls = []; + + var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl; + + // init state + form.$error = {}; + form.$$success = {}; + form.$pending = undefined; + form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope); + form.$dirty = false; + form.$pristine = true; + form.$valid = true; + form.$invalid = false; + form.$submitted = false; + + parentForm.$addControl(form); + + /** + * @ngdoc method + * @name form.FormController#$rollbackViewValue + * + * @description + * Rollback all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is typically needed by the reset button of + * a form that uses `ng-model-options` to pend updates. + */ + form.$rollbackViewValue = function() { + forEach(controls, function(control) { + control.$rollbackViewValue(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$commitViewValue + * + * @description + * Commit all form controls pending updates to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. This method is rarely needed as `NgModelController` + * usually handles calling this in response to input events. + */ + form.$commitViewValue = function() { + forEach(controls, function(control) { + control.$commitViewValue(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$addControl + * + * @description + * Register a control with the form. + * + * Input elements using ngModelController do this automatically when they are linked. + */ + form.$addControl = function(control) { + // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored + // and not added to the scope. Now we throw an error. + assertNotHasOwnProperty(control.$name, 'input'); + controls.push(control); + + if (control.$name) { + form[control.$name] = control; + } + }; + + // Private API: rename a form control + form.$$renameControl = function(control, newName) { + var oldName = control.$name; + + if (form[oldName] === control) { + delete form[oldName]; + } + form[newName] = control; + control.$name = newName; + }; + + /** + * @ngdoc method + * @name form.FormController#$removeControl + * + * @description + * Deregister a control from the form. + * + * Input elements using ngModelController do this automatically when they are destroyed. + */ + form.$removeControl = function(control) { + if (control.$name && form[control.$name] === control) { + delete form[control.$name]; + } + forEach(form.$pending, function(value, name) { + form.$setValidity(name, null, control); + }); + forEach(form.$error, function(value, name) { + form.$setValidity(name, null, control); + }); + forEach(form.$$success, function(value, name) { + form.$setValidity(name, null, control); + }); + + arrayRemove(controls, control); + }; + + + /** + * @ngdoc method + * @name form.FormController#$setValidity + * + * @description + * Sets the validity of a form control. + * + * This method will also propagate to parent forms. + */ + addSetValidityMethod({ + ctrl: this, + $element: element, + set: function(object, property, controller) { + var list = object[property]; + if (!list) { + object[property] = [controller]; + } else { + var index = list.indexOf(controller); + if (index === -1) { + list.push(controller); + } + } + }, + unset: function(object, property, controller) { + var list = object[property]; + if (!list) { + return; + } + arrayRemove(list, controller); + if (list.length === 0) { + delete object[property]; + } + }, + parentForm: parentForm, + $animate: $animate + }); + + /** + * @ngdoc method + * @name form.FormController#$setDirty + * + * @description + * Sets the form to a dirty state. + * + * This method can be called to add the 'ng-dirty' class and set the form to a dirty + * state (ng-dirty class). This method will also propagate to parent forms. + */ + form.$setDirty = function() { + $animate.removeClass(element, PRISTINE_CLASS); + $animate.addClass(element, DIRTY_CLASS); + form.$dirty = true; + form.$pristine = false; + parentForm.$setDirty(); + }; + + /** + * @ngdoc method + * @name form.FormController#$setPristine + * + * @description + * Sets the form to its pristine state. + * + * This method can be called to remove the 'ng-dirty' class and set the form to its pristine + * state (ng-pristine class). This method will also propagate to all the controls contained + * in this form. + * + * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after + * saving or resetting it. + */ + form.$setPristine = function() { + $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS); + form.$dirty = false; + form.$pristine = true; + form.$submitted = false; + forEach(controls, function(control) { + control.$setPristine(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$setUntouched + * + * @description + * Sets the form to its untouched state. + * + * This method can be called to remove the 'ng-touched' class and set the form controls to their + * untouched state (ng-untouched class). + * + * Setting a form controls back to their untouched state is often useful when setting the form + * back to its pristine state. + */ + form.$setUntouched = function() { + forEach(controls, function(control) { + control.$setUntouched(); + }); + }; + + /** + * @ngdoc method + * @name form.FormController#$setSubmitted + * + * @description + * Sets the form to its submitted state. + */ + form.$setSubmitted = function() { + $animate.addClass(element, SUBMITTED_CLASS); + form.$submitted = true; + parentForm.$setSubmitted(); + }; +} + +/** + * @ngdoc directive + * @name ngForm + * @restrict EAC + * + * @description + * Nestable alias of {@link ng.directive:form `form`} directive. HTML + * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a + * sub-group of controls needs to be determined. + * + * Note: the purpose of `ngForm` is to group controls, + * but not to be a replacement for the `<form>` tag with all of its capabilities + * (e.g. posting to the server, ...). + * + * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + * + */ + + /** + * @ngdoc directive + * @name form + * @restrict E + * + * @description + * Directive that instantiates + * {@link form.FormController FormController}. + * + * If the `name` attribute is specified, the form controller is published onto the current scope under + * this name. + * + * # Alias: {@link ng.directive:ngForm `ngForm`} + * + * In Angular, forms can be nested. This means that the outer form is valid when all of the child + * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so + * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to + * `<form>` but can be nested. This allows you to have nested forms, which is very useful when + * using Angular validation directives in forms that are dynamically generated using the + * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name` + * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an + * `ngForm` directive and nest these in an outer `form` element. + * + * + * # CSS classes + * - `ng-valid` is set if the form is valid. + * - `ng-invalid` is set if the form is invalid. + * - `ng-pristine` is set if the form is pristine. + * - `ng-dirty` is set if the form is dirty. + * - `ng-submitted` is set if the form was submitted. + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * + * # Submitting a form and preventing the default action + * + * Since the role of forms in client-side Angular applications is different than in classical + * roundtrip apps, it is desirable for the browser not to translate the form submission into a full + * page reload that sends the data to the server. Instead some javascript logic should be triggered + * to handle the form submission in an application-specific way. + * + * For this reason, Angular prevents the default action (form submission to the server) unless the + * `<form>` element has an `action` attribute specified. + * + * You can use one of the following two ways to specify what javascript method should be called when + * a form is submitted: + * + * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element + * - {@link ng.directive:ngClick ngClick} directive on the first + * button or input field of type submit (input[type=submit]) + * + * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} + * or {@link ng.directive:ngClick ngClick} directives. + * This is because of the following form submission rules in the HTML specification: + * + * - If a form has only one input field then hitting enter in this field triggers form submit + * (`ngSubmit`) + * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter + * doesn't trigger submit + * - if a form has one or more input fields and one or more buttons or input[type=submit] then + * hitting enter in any of the input fields will trigger the click handler on the *first* button or + * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) + * + * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is + * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` + * to have access to the updated model. + * + * ## Animation Hooks + * + * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. + * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any + * other validations that are performed within the form. Animations in ngForm are similar to how + * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well + * as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style a form element + * that has been rendered as invalid after it has been validated: + * + * <pre> + * //be sure to include ngAnimate as a module to hook into more + * //advanced animations + * .my-form { + * transition:0.5s linear all; + * background: white; + * } + * .my-form.ng-invalid { + * background: red; + * color:white; + * } + * </pre> + * + * @example + <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample"> + <file name="index.html"> + <script> + angular.module('formExample', []) + .controller('FormController', ['$scope', function($scope) { + $scope.userType = 'guest'; + }]); + </script> + <style> + .my-form { + -webkit-transition:all linear 0.5s; + transition:all linear 0.5s; + background: transparent; + } + .my-form.ng-invalid { + background: red; + } + </style> + <form name="myForm" ng-controller="FormController" class="my-form"> + userType: <input name="input" ng-model="userType" required> + <span class="error" ng-show="myForm.input.$error.required">Required!</span><br> + <tt>userType = {{userType}}</tt><br> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> + </form> + </file> + <file name="protractor.js" type="protractor"> + it('should initialize to model', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + + expect(userType.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + var userType = element(by.binding('userType')); + var valid = element(by.binding('myForm.input.$valid')); + var userInput = element(by.model('userType')); + + userInput.clear(); + userInput.sendKeys(''); + + expect(userType.getText()).toEqual('userType ='); + expect(valid.getText()).toContain('false'); + }); + </file> + </example> + * + * @param {string=} name Name of the form. If specified, the form controller will be published into + * related scope, under this name. + */ +var formDirectiveFactory = function(isNgForm) { + return ['$timeout', function($timeout) { + var formDirective = { + name: 'form', + restrict: isNgForm ? 'EAC' : 'E', + controller: FormController, + compile: function ngFormCompile(formElement, attr) { + // Setup initial state of the control + formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS); + + var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false); + + return { + pre: function ngFormPreLink(scope, formElement, attr, controller) { + // if `action` attr is not present on the form, prevent the default action (submission) + if (!('action' in attr)) { + // we can't use jq events because if a form is destroyed during submission the default + // action is not prevented. see #1238 + // + // IE 9 is not affected because it doesn't fire a submit event and try to do a full + // page reload if the form was destroyed by submission of the form via a click handler + // on a button in the form. Looks like an IE9 specific bug. + var handleFormSubmission = function(event) { + scope.$apply(function() { + controller.$commitViewValue(); + controller.$setSubmitted(); + }); + + event.preventDefault(); + }; + + addEventListenerFn(formElement[0], 'submit', handleFormSubmission); + + // unregister the preventDefault listener so that we don't not leak memory but in a + // way that will achieve the prevention of the default action. + formElement.on('$destroy', function() { + $timeout(function() { + removeEventListenerFn(formElement[0], 'submit', handleFormSubmission); + }, 0, false); + }); + } + + var parentFormCtrl = controller.$$parentForm; + + if (nameAttr) { + setter(scope, null, controller.$name, controller, controller.$name); + attr.$observe(nameAttr, function(newValue) { + if (controller.$name === newValue) return; + setter(scope, null, controller.$name, undefined, controller.$name); + parentFormCtrl.$$renameControl(controller, newValue); + setter(scope, null, controller.$name, controller, controller.$name); + }); + } + formElement.on('$destroy', function() { + parentFormCtrl.$removeControl(controller); + if (nameAttr) { + setter(scope, null, attr[nameAttr], undefined, controller.$name); + } + extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards + }); + } + }; + } + }; + + return formDirective; + }]; +}; + +var formDirective = formDirectiveFactory(); +var ngFormDirective = formDirectiveFactory(true); + +/* global VALID_CLASS: false, + INVALID_CLASS: false, + PRISTINE_CLASS: false, + DIRTY_CLASS: false, + UNTOUCHED_CLASS: false, + TOUCHED_CLASS: false, + $ngModelMinErr: false, +*/ + +// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231 +var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; +var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; +var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; +var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; +var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/; +var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; +var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/; +var MONTH_REGEXP = /^(\d{4})-(\d\d)$/; +var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/; + +var inputType = { + + /** + * @ngdoc input + * @name input[text] + * + * @description + * Standard HTML text input with angular data binding, inherited by most of the `input` elements. + * + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Adds `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match + * a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object then this is used directly. + * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` + * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + <example name="text-input-directive" module="textInputExample"> + <file name="index.html"> + <script> + angular.module('textInputExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.example = { + text: 'guest', + word: /^\s*\w*\s*$/ + }; + }]); + </script> + <form name="myForm" ng-controller="ExampleController"> + Single word: <input type="text" name="input" ng-model="example.text" + ng-pattern="example.word" required ng-trim="false"> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.pattern"> + Single word only!</span> + + <tt>text = {{example.text}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var text = element(by.binding('example.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('guest'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if multi word', function() { + input.clear(); + input.sendKeys('hello world'); + + expect(valid.getText()).toContain('false'); + }); + </file> + </example> + */ + 'text': textInputType, + + /** + * @ngdoc input + * @name input[date] + * + * @description + * Input with date validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601 + * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many + * modern browsers do not yet support this input type, it is important to provide cues to users on the + * expected input format via a placeholder or label. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a + * valid ISO date string (yyyy-MM-dd). + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be + * a valid ISO date string (yyyy-MM-dd). + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="date-input-directive" module="dateInputExample"> + <file name="index.html"> + <script> + angular.module('dateInputExample', []) + .controller('DateController', ['$scope', function($scope) { + $scope.example = { + value: new Date(2013, 9, 22) + }; + }]); + </script> + <form name="myForm" ng-controller="DateController as dateCtrl"> + Pick a date in 2013: + <input type="date" id="exampleInput" name="input" ng-model="example.value" + placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required /> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.date"> + Not a valid date!</span> + <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var value = element(by.binding('example.value | date: "yyyy-MM-dd"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (see https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-10-22'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01-01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + </file> + </example> + */ + 'date': createDateInputType('date', DATE_REGEXP, + createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']), + 'yyyy-MM-dd'), + + /** + * @ngdoc input + * @name input[datetime-local] + * + * @description + * Input with datetime validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a + * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be + * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="datetimelocal-input-directive" module="dateExample"> + <file name="index.html"> + <script> + angular.module('dateExample', []) + .controller('DateController', ['$scope', function($scope) { + $scope.example = { + value: new Date(2010, 11, 28, 14, 57) + }; + }]); + </script> + <form name="myForm" ng-controller="DateController as dateCtrl"> + Pick a date between in 2013: + <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value" + placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required /> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.datetimelocal"> + Not a valid date!</span> + <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2010-12-28T14:57:00'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01-01T23:59:00'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + </file> + </example> + */ + 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP, + createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']), + 'yyyy-MM-ddTHH:mm:ss.sss'), + + /** + * @ngdoc input + * @name input[time] + * + * @description + * Input with time validation and transformation. In browsers that do not yet support + * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a + * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a + * valid ISO time format (HH:mm:ss). + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a + * valid ISO time format (HH:mm:ss). + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="time-input-directive" module="timeExample"> + <file name="index.html"> + <script> + angular.module('timeExample', []) + .controller('DateController', ['$scope', function($scope) { + $scope.example = { + value: new Date(1970, 0, 1, 14, 57, 0) + }; + }]); + </script> + <form name="myForm" ng-controller="DateController as dateCtrl"> + Pick a between 8am and 5pm: + <input type="time" id="exampleInput" name="input" ng-model="example.value" + placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required /> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.time"> + Not a valid date!</span> + <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var value = element(by.binding('example.value | date: "HH:mm:ss"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('14:57:00'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('23:59:00'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + </file> + </example> + */ + 'time': createDateInputType('time', TIME_REGEXP, + createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']), + 'HH:mm:ss.sss'), + + /** + * @ngdoc input + * @name input[week] + * + * @description + * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support + * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * week format (yyyy-W##), for example: `2013-W02`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a + * valid ISO week format (yyyy-W##). + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be + * a valid ISO week format (yyyy-W##). + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="week-input-directive" module="weekExample"> + <file name="index.html"> + <script> + angular.module('weekExample', []) + .controller('DateController', ['$scope', function($scope) { + $scope.example = { + value: new Date(2013, 0, 3) + }; + }]); + </script> + <form name="myForm" ng-controller="DateController as dateCtrl"> + Pick a date between in 2013: + <input id="exampleInput" type="week" name="input" ng-model="example.value" + placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required /> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.week"> + Not a valid date!</span> + <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var value = element(by.binding('example.value | date: "yyyy-Www"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-W01'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-W01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + </file> + </example> + */ + 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'), + + /** + * @ngdoc input + * @name input[month] + * + * @description + * Input with month validation and transformation. In browsers that do not yet support + * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601 + * month format (yyyy-MM), for example: `2009-01`. + * + * The model must always be a Date object, otherwise Angular will throw an error. + * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string. + * If the model is not set to the first of the month, the next view to model update will set it + * to the first of the month. + * + * The timezone to be used to read/write the `Date` instance in the model can be defined using + * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be + * a valid ISO month format (yyyy-MM). + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must + * be a valid ISO month format (yyyy-MM). + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="month-input-directive" module="monthExample"> + <file name="index.html"> + <script> + angular.module('monthExample', []) + .controller('DateController', ['$scope', function($scope) { + $scope.example = { + value: new Date(2013, 9, 1) + }; + }]); + </script> + <form name="myForm" ng-controller="DateController as dateCtrl"> + Pick a month in 2013: + <input id="exampleInput" type="month" name="input" ng-model="example.value" + placeholder="yyyy-MM" min="2013-01" max="2013-12" required /> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.month"> + Not a valid month!</span> + <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var value = element(by.binding('example.value | date: "yyyy-MM"')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + // currently protractor/webdriver does not support + // sending keys to all known HTML5 input controls + // for various browsers (https://github.com/angular/protractor/issues/562). + function setInput(val) { + // set the value of the element and force validation. + var scr = "var ipt = document.getElementById('exampleInput'); " + + "ipt.value = '" + val + "';" + + "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });"; + browser.executeScript(scr); + } + + it('should initialize to model', function() { + expect(value.getText()).toContain('2013-10'); + expect(valid.getText()).toContain('myForm.input.$valid = true'); + }); + + it('should be invalid if empty', function() { + setInput(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + + it('should be invalid if over max', function() { + setInput('2015-01'); + expect(value.getText()).toContain(''); + expect(valid.getText()).toContain('myForm.input.$valid = false'); + }); + </file> + </example> + */ + 'month': createDateInputType('month', MONTH_REGEXP, + createDateParser(MONTH_REGEXP, ['yyyy', 'MM']), + 'yyyy-MM'), + + /** + * @ngdoc input + * @name input[number] + * + * @description + * Text input with number validation and transformation. Sets the `number` validation + * error if not a valid number. + * + * The model must always be a number, otherwise Angular will throw an error. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. + * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match + * a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object then this is used directly. + * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` + * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="number-input-directive" module="numberExample"> + <file name="index.html"> + <script> + angular.module('numberExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.example = { + value: 12 + }; + }]); + </script> + <form name="myForm" ng-controller="ExampleController"> + Number: <input type="number" name="input" ng-model="example.value" + min="0" max="99" required> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.number"> + Not valid number!</span> + <tt>value = {{example.value}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var value = element(by.binding('example.value')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('example.value')); + + it('should initialize to model', function() { + expect(value.getText()).toContain('12'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if over max', function() { + input.clear(); + input.sendKeys('123'); + expect(value.getText()).toEqual('value ='); + expect(valid.getText()).toContain('false'); + }); + </file> + </example> + */ + 'number': numberInputType, + + + /** + * @ngdoc input + * @name input[url] + * + * @description + * Text input with URL validation. Sets the `url` validation error key if the content is not a + * valid URL. + * + * <div class="alert alert-warning"> + * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex + * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify + * the built-in validators (see the {@link guide/forms Forms guide}) + * </div> + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match + * a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object then this is used directly. + * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` + * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="url-input-directive" module="urlExample"> + <file name="index.html"> + <script> + angular.module('urlExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.url = { + text: 'http://google.com' + }; + }]); + </script> + <form name="myForm" ng-controller="ExampleController"> + URL: <input type="url" name="input" ng-model="url.text" required> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.url"> + Not valid url!</span> + <tt>text = {{url.text}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var text = element(by.binding('url.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('url.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('http://google.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not url', function() { + input.clear(); + input.sendKeys('box'); + + expect(valid.getText()).toContain('false'); + }); + </file> + </example> + */ + 'url': urlInputType, + + + /** + * @ngdoc input + * @name input[email] + * + * @description + * Text input with email validation. Sets the `email` validation error key if not a valid email + * address. + * + * <div class="alert alert-warning"> + * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex + * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can + * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide}) + * </div> + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of + * any length. + * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string + * that contains the regular expression body that will be converted to a regular expression + * as in the ngPattern directive. + * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match + * a RegExp found by evaluating the Angular expression given in the attribute value. + * If the expression evaluates to a RegExp object then this is used directly. + * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$` + * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="email-input-directive" module="emailExample"> + <file name="index.html"> + <script> + angular.module('emailExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.email = { + text: 'me@example.com' + }; + }]); + </script> + <form name="myForm" ng-controller="ExampleController"> + Email: <input type="email" name="input" ng-model="email.text" required> + <span class="error" ng-show="myForm.input.$error.required"> + Required!</span> + <span class="error" ng-show="myForm.input.$error.email"> + Not valid email!</span> + <tt>text = {{email.text}}</tt><br/> + <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> + <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + var text = element(by.binding('email.text')); + var valid = element(by.binding('myForm.input.$valid')); + var input = element(by.model('email.text')); + + it('should initialize to model', function() { + expect(text.getText()).toContain('me@example.com'); + expect(valid.getText()).toContain('true'); + }); + + it('should be invalid if empty', function() { + input.clear(); + input.sendKeys(''); + expect(text.getText()).toEqual('text ='); + expect(valid.getText()).toContain('false'); + }); + + it('should be invalid if not email', function() { + input.clear(); + input.sendKeys('xxx'); + + expect(valid.getText()).toContain('false'); + }); + </file> + </example> + */ + 'email': emailInputType, + + + /** + * @ngdoc input + * @name input[radio] + * + * @description + * HTML radio button. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string} value The value to which the expression should be set when selected. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {string} ngValue Angular expression which sets the value to which the expression should + * be set when selected. + * + * @example + <example name="radio-input-directive" module="radioExample"> + <file name="index.html"> + <script> + angular.module('radioExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.color = { + name: 'blue' + }; + $scope.specialValue = { + "id": "12345", + "value": "green" + }; + }]); + </script> + <form name="myForm" ng-controller="ExampleController"> + <input type="radio" ng-model="color.name" value="red"> Red <br/> + <input type="radio" ng-model="color.name" ng-value="specialValue"> Green <br/> + <input type="radio" ng-model="color.name" value="blue"> Blue <br/> + <tt>color = {{color.name | json}}</tt><br/> + </form> + Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`. + </file> + <file name="protractor.js" type="protractor"> + it('should change state', function() { + var color = element(by.binding('color.name')); + + expect(color.getText()).toContain('blue'); + + element.all(by.model('color.name')).get(0).click(); + + expect(color.getText()).toContain('red'); + }); + </file> + </example> + */ + 'radio': radioInputType, + + + /** + * @ngdoc input + * @name input[checkbox] + * + * @description + * HTML checkbox. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {expression=} ngTrueValue The value to which the expression should be set when selected. + * @param {expression=} ngFalseValue The value to which the expression should be set when not selected. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * + * @example + <example name="checkbox-input-directive" module="checkboxExample"> + <file name="index.html"> + <script> + angular.module('checkboxExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.checkboxModel = { + value1 : true, + value2 : 'YES' + }; + }]); + </script> + <form name="myForm" ng-controller="ExampleController"> + Value1: <input type="checkbox" ng-model="checkboxModel.value1"> <br/> + Value2: <input type="checkbox" ng-model="checkboxModel.value2" + ng-true-value="'YES'" ng-false-value="'NO'"> <br/> + <tt>value1 = {{checkboxModel.value1}}</tt><br/> + <tt>value2 = {{checkboxModel.value2}}</tt><br/> + </form> + </file> + <file name="protractor.js" type="protractor"> + it('should change state', function() { + var value1 = element(by.binding('checkboxModel.value1')); + var value2 = element(by.binding('checkboxModel.value2')); + + expect(value1.getText()).toContain('true'); + expect(value2.getText()).toContain('YES'); + + element(by.model('checkboxModel.value1')).click(); + element(by.model('checkboxModel.value2')).click(); + + expect(value1.getText()).toContain('false'); + expect(value2.getText()).toContain('NO'); + }); + </file> + </example> + */ + 'checkbox': checkboxInputType, + + 'hidden': noop, + 'button': noop, + 'submit': noop, + 'reset': noop, + 'file': noop +}; + +function stringBasedInputType(ctrl) { + ctrl.$formatters.push(function(value) { + return ctrl.$isEmpty(value) ? value : value.toString(); + }); +} + +function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); +} + +function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) { + var type = lowercase(element[0].type); + + // In composition mode, users are still inputing intermediate text buffer, + // hold the listener until composition is done. + // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent + if (!$sniffer.android) { + var composing = false; + + element.on('compositionstart', function(data) { + composing = true; + }); + + element.on('compositionend', function() { + composing = false; + listener(); + }); + } + + var listener = function(ev) { + if (timeout) { + $browser.defer.cancel(timeout); + timeout = null; + } + if (composing) return; + var value = element.val(), + event = ev && ev.type; + + // By default we will trim the value + // If the attribute ng-trim exists we will avoid trimming + // If input type is 'password', the value is never trimmed + if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) { + value = trim(value); + } + + // If a control is suffering from bad input (due to native validators), browsers discard its + // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the + // control's value is the same empty value twice in a row. + if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) { + ctrl.$setViewValue(value, event); + } + }; + + // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the + // input event on backspace, delete or cut + if ($sniffer.hasEvent('input')) { + element.on('input', listener); + } else { + var timeout; + + var deferListener = function(ev, input, origValue) { + if (!timeout) { + timeout = $browser.defer(function() { + timeout = null; + if (!input || input.value !== origValue) { + listener(ev); + } + }); + } + }; + + element.on('keydown', function(event) { + var key = event.keyCode; + + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; + + deferListener(event, this, this.value); + }); + + // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it + if ($sniffer.hasEvent('paste')) { + element.on('paste cut', deferListener); + } + } + + // if user paste into input using mouse on older browser + // or form autocomplete on newer browser, we need "change" event to catch it + element.on('change', listener); + + ctrl.$render = function() { + element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); + }; +} + +function weekParser(isoWeek, existingDate) { + if (isDate(isoWeek)) { + return isoWeek; + } + + if (isString(isoWeek)) { + WEEK_REGEXP.lastIndex = 0; + var parts = WEEK_REGEXP.exec(isoWeek); + if (parts) { + var year = +parts[1], + week = +parts[2], + hours = 0, + minutes = 0, + seconds = 0, + milliseconds = 0, + firstThurs = getFirstThursdayOfYear(year), + addDays = (week - 1) * 7; + + if (existingDate) { + hours = existingDate.getHours(); + minutes = existingDate.getMinutes(); + seconds = existingDate.getSeconds(); + milliseconds = existingDate.getMilliseconds(); + } + + return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds); + } + } + + return NaN; +} + +function createDateParser(regexp, mapping) { + return function(iso, date) { + var parts, map; + + if (isDate(iso)) { + return iso; + } + + if (isString(iso)) { + // When a date is JSON'ified to wraps itself inside of an extra + // set of double quotes. This makes the date parsing code unable + // to match the date string and parse it as a date. + if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') { + iso = iso.substring(1, iso.length - 1); + } + if (ISO_DATE_REGEXP.test(iso)) { + return new Date(iso); + } + regexp.lastIndex = 0; + parts = regexp.exec(iso); + + if (parts) { + parts.shift(); + if (date) { + map = { + yyyy: date.getFullYear(), + MM: date.getMonth() + 1, + dd: date.getDate(), + HH: date.getHours(), + mm: date.getMinutes(), + ss: date.getSeconds(), + sss: date.getMilliseconds() / 1000 + }; + } else { + map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 }; + } + + forEach(parts, function(part, index) { + if (index < mapping.length) { + map[mapping[index]] = +part; + } + }); + return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0); + } + } + + return NaN; + }; +} + +function createDateInputType(type, regexp, parseDate, format) { + return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) { + badInputChecker(scope, element, attr, ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + var timezone = ctrl && ctrl.$options && ctrl.$options.timezone; + var previousDate; + + ctrl.$$parserName = type; + ctrl.$parsers.push(function(value) { + if (ctrl.$isEmpty(value)) return null; + if (regexp.test(value)) { + // Note: We cannot read ctrl.$modelValue, as there might be a different + // parser/formatter in the processing chain so that the model + // contains some different data format! + var parsedDate = parseDate(value, previousDate); + if (timezone === 'UTC') { + parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset()); + } + return parsedDate; + } + return undefined; + }); + + ctrl.$formatters.push(function(value) { + if (value && !isDate(value)) { + throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value); + } + if (isValidDate(value)) { + previousDate = value; + if (previousDate && timezone === 'UTC') { + var timezoneOffset = 60000 * previousDate.getTimezoneOffset(); + previousDate = new Date(previousDate.getTime() + timezoneOffset); + } + return $filter('date')(value, format, timezone); + } else { + previousDate = null; + return ''; + } + }); + + if (isDefined(attr.min) || attr.ngMin) { + var minVal; + ctrl.$validators.min = function(value) { + return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal; + }; + attr.$observe('min', function(val) { + minVal = parseObservedDateValue(val); + ctrl.$validate(); + }); + } + + if (isDefined(attr.max) || attr.ngMax) { + var maxVal; + ctrl.$validators.max = function(value) { + return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal; + }; + attr.$observe('max', function(val) { + maxVal = parseObservedDateValue(val); + ctrl.$validate(); + }); + } + + function isValidDate(value) { + // Invalid Date: getTime() returns NaN + return value && !(value.getTime && value.getTime() !== value.getTime()); + } + + function parseObservedDateValue(val) { + return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined; + } + }; +} + +function badInputChecker(scope, element, attr, ctrl) { + var node = element[0]; + var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity); + if (nativeValidation) { + ctrl.$parsers.push(function(value) { + var validity = element.prop(VALIDITY_STATE_PROPERTY) || {}; + // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430): + // - also sets validity.badInput (should only be validity.typeMismatch). + // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email) + // - can ignore this case as we can still read out the erroneous email... + return validity.badInput && !validity.typeMismatch ? undefined : value; + }); + } +} + +function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { + badInputChecker(scope, element, attr, ctrl); + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + + ctrl.$$parserName = 'number'; + ctrl.$parsers.push(function(value) { + if (ctrl.$isEmpty(value)) return null; + if (NUMBER_REGEXP.test(value)) return parseFloat(value); + return undefined; + }); + + ctrl.$formatters.push(function(value) { + if (!ctrl.$isEmpty(value)) { + if (!isNumber(value)) { + throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value); + } + value = value.toString(); + } + return value; + }); + + if (isDefined(attr.min) || attr.ngMin) { + var minVal; + ctrl.$validators.min = function(value) { + return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal; + }; + + attr.$observe('min', function(val) { + if (isDefined(val) && !isNumber(val)) { + val = parseFloat(val, 10); + } + minVal = isNumber(val) && !isNaN(val) ? val : undefined; + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } + + if (isDefined(attr.max) || attr.ngMax) { + var maxVal; + ctrl.$validators.max = function(value) { + return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal; + }; + + attr.$observe('max', function(val) { + if (isDefined(val) && !isNumber(val)) { + val = parseFloat(val, 10); + } + maxVal = isNumber(val) && !isNaN(val) ? val : undefined; + // TODO(matsko): implement validateLater to reduce number of validations + ctrl.$validate(); + }); + } +} + +function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { + // Note: no badInputChecker here by purpose as `url` is only a validation + // in browsers, i.e. we can always read out input.value even if it is not valid! + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); + + ctrl.$$parserName = 'url'; + ctrl.$validators.url = function(modelValue, viewValue) { + var value = modelValue || viewValue; + return ctrl.$isEmpty(value) || URL_REGEXP.test(value); + }; +} + +function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { + // Note: no badInputChecker here by purpose as `url` is only a validation + // in browsers, i.e. we can always read out input.value even if it is not valid! + baseInputType(scope, element, attr, ctrl, $sniffer, $browser); + stringBasedInputType(ctrl); + + ctrl.$$parserName = 'email'; + ctrl.$validators.email = function(modelValue, viewValue) { + var value = modelValue || viewValue; + return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value); + }; +} + +function radioInputType(scope, element, attr, ctrl) { + // make the name unique, if not defined + if (isUndefined(attr.name)) { + element.attr('name', nextUid()); + } + + var listener = function(ev) { + if (element[0].checked) { + ctrl.$setViewValue(attr.value, ev && ev.type); + } + }; + + element.on('click', listener); + + ctrl.$render = function() { + var value = attr.value; + element[0].checked = (value == ctrl.$viewValue); + }; + + attr.$observe('value', ctrl.$render); +} + +function parseConstantExpr($parse, context, name, expression, fallback) { + var parseFn; + if (isDefined(expression)) { + parseFn = $parse(expression); + if (!parseFn.constant) { + throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' + + '`{1}`.', name, expression); + } + return parseFn(context); + } + return fallback; +} + +function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) { + var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true); + var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false); + + var listener = function(ev) { + ctrl.$setViewValue(element[0].checked, ev && ev.type); + }; + + element.on('click', listener); + + ctrl.$render = function() { + element[0].checked = ctrl.$viewValue; + }; + + // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false` + // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert + // it to a boolean. + ctrl.$isEmpty = function(value) { + return value === false; + }; + + ctrl.$formatters.push(function(value) { + return equals(value, trueValue); + }); + + ctrl.$parsers.push(function(value) { + return value ? trueValue : falseValue; + }); +} + + +/** + * @ngdoc directive + * @name textarea + * @restrict E + * + * @description + * HTML textarea element control with angular data-binding. The data-binding and validation + * properties of this element are exactly the same as those of the + * {@link ng.directive:input input element}. + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any + * length. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + */ + + +/** + * @ngdoc directive + * @name input + * @restrict E + * + * @description + * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding, + * input state control, and validation. + * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers. + * + * <div class="alert alert-warning"> + * **Note:** Not every feature offered is available for all input types. + * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`. + * </div> + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required Sets `required` validation error key if the value is not entered. + * @param {boolean=} ngRequired Sets `required` attribute if set to true + * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than + * minlength. + * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than + * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any + * length. + * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the + * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for + * patterns defined as scope expressions. + * @param {string=} ngChange Angular expression to be executed when input changes due to user + * interaction with the input element. + * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. + * This parameter is ignored for input[type=password] controls, which will never trim the + * input. + * + * @example + <example name="input-directive" module="inputExample"> + <file name="index.html"> + <script> + angular.module('inputExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.user = {name: 'guest', last: 'visitor'}; + }]); + </script> + <div ng-controller="ExampleController"> + <form name="myForm"> + User name: <input type="text" name="userName" ng-model="user.name" required> + <span class="error" ng-show="myForm.userName.$error.required"> + Required!</span><br> + Last name: <input type="text" name="lastName" ng-model="user.last" + ng-minlength="3" ng-maxlength="10"> + <span class="error" ng-show="myForm.lastName.$error.minlength"> + Too short!</span> + <span class="error" ng-show="myForm.lastName.$error.maxlength"> + Too long!</span><br> + </form> + <hr> + <tt>user = {{user}}</tt><br/> + <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br> + <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br> + <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br> + <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br> + <tt>myForm.$valid = {{myForm.$valid}}</tt><br> + <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> + <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br> + <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br> + </div> + </file> + <file name="protractor.js" type="protractor"> + var user = element(by.exactBinding('user')); + var userNameValid = element(by.binding('myForm.userName.$valid')); + var lastNameValid = element(by.binding('myForm.lastName.$valid')); + var lastNameError = element(by.binding('myForm.lastName.$error')); + var formValid = element(by.binding('myForm.$valid')); + var userNameInput = element(by.model('user.name')); + var userLastInput = element(by.model('user.last')); + + it('should initialize to model', function() { + expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); + expect(userNameValid.getText()).toContain('true'); + expect(formValid.getText()).toContain('true'); + }); + + it('should be invalid if empty when required', function() { + userNameInput.clear(); + userNameInput.sendKeys(''); + + expect(user.getText()).toContain('{"last":"visitor"}'); + expect(userNameValid.getText()).toContain('false'); + expect(formValid.getText()).toContain('false'); + }); + + it('should be valid if empty when min length is set', function() { + userLastInput.clear(); + userLastInput.sendKeys(''); + + expect(user.getText()).toContain('{"name":"guest","last":""}'); + expect(lastNameValid.getText()).toContain('true'); + expect(formValid.getText()).toContain('true'); + }); + + it('should be invalid if less than required min length', function() { + userLastInput.clear(); + userLastInput.sendKeys('xx'); + + expect(user.getText()).toContain('{"name":"guest"}'); + expect(lastNameValid.getText()).toContain('false'); + expect(lastNameError.getText()).toContain('minlength'); + expect(formValid.getText()).toContain('false'); + }); + + it('should be invalid if longer than max length', function() { + userLastInput.clear(); + userLastInput.sendKeys('some ridiculously long name'); + + expect(user.getText()).toContain('{"name":"guest"}'); + expect(lastNameValid.getText()).toContain('false'); + expect(lastNameError.getText()).toContain('maxlength'); + expect(formValid.getText()).toContain('false'); + }); + </file> + </example> + */ +var inputDirective = ['$browser', '$sniffer', '$filter', '$parse', + function($browser, $sniffer, $filter, $parse) { + return { + restrict: 'E', + require: ['?ngModel'], + link: { + pre: function(scope, element, attr, ctrls) { + if (ctrls[0]) { + (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer, + $browser, $filter, $parse); + } + } + } + }; +}]; + + + +var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; +/** + * @ngdoc directive + * @name ngValue + * + * @description + * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`}, + * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to + * the bound value. + * + * `ngValue` is useful when dynamically generating lists of radio buttons using + * {@link ngRepeat `ngRepeat`}, as shown below. + * + * Likewise, `ngValue` can be used to generate `<option>` elements for + * the {@link select `select`} element. In that case however, only strings are supported + * for the `value `attribute, so the resulting `ngModel` will always be a string. + * Support for `select` models with non-string values is available via `ngOptions`. + * + * @element input + * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute + * of the `input` element + * + * @example + <example name="ngValue-directive" module="valueExample"> + <file name="index.html"> + <script> + angular.module('valueExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.names = ['pizza', 'unicorns', 'robots']; + $scope.my = { favorite: 'unicorns' }; + }]); + </script> + <form ng-controller="ExampleController"> + <h2>Which is your favorite?</h2> + <label ng-repeat="name in names" for="{{name}}"> + {{name}} + <input type="radio" + ng-model="my.favorite" + ng-value="name" + id="{{name}}" + name="favorite"> + </label> + <div>You chose {{my.favorite}}</div> + </form> + </file> + <file name="protractor.js" type="protractor"> + var favorite = element(by.binding('my.favorite')); + + it('should initialize to model', function() { + expect(favorite.getText()).toContain('unicorns'); + }); + it('should bind the values to the inputs', function() { + element.all(by.model('my.favorite')).get(0).click(); + expect(favorite.getText()).toContain('pizza'); + }); + </file> + </example> + */ +var ngValueDirective = function() { + return { + restrict: 'A', + priority: 100, + compile: function(tpl, tplAttr) { + if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { + return function ngValueConstantLink(scope, elm, attr) { + attr.$set('value', scope.$eval(attr.ngValue)); + }; + } else { + return function ngValueLink(scope, elm, attr) { + scope.$watch(attr.ngValue, function valueWatchAction(value) { + attr.$set('value', value); + }); + }; + } + } + }; +}; + +/** + * @ngdoc directive + * @name ngBind + * @restrict AC + * + * @description + * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element + * with the value of a given expression, and to update the text content when the value of that + * expression changes. + * + * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like + * `{{ expression }}` which is similar but less verbose. + * + * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily + * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an + * element attribute, it makes the bindings invisible to the user while the page is loading. + * + * An alternative solution to this problem would be using the + * {@link ng.directive:ngCloak ngCloak} directive. + * + * + * @element ANY + * @param {expression} ngBind {@link guide/expression Expression} to evaluate. + * + * @example + * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. + <example module="bindExample"> + <file name="index.html"> + <script> + angular.module('bindExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.name = 'Whirled'; + }]); + </script> + <div ng-controller="ExampleController"> + Enter name: <input type="text" ng-model="name"><br> + Hello <span ng-bind="name"></span>! + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-bind', function() { + var nameInput = element(by.model('name')); + + expect(element(by.binding('name')).getText()).toBe('Whirled'); + nameInput.clear(); + nameInput.sendKeys('world'); + expect(element(by.binding('name')).getText()).toBe('world'); + }); + </file> + </example> + */ +var ngBindDirective = ['$compile', function($compile) { + return { + restrict: 'AC', + compile: function ngBindCompile(templateElement) { + $compile.$$addBindingClass(templateElement); + return function ngBindLink(scope, element, attr) { + $compile.$$addBindingInfo(element, attr.ngBind); + element = element[0]; + scope.$watch(attr.ngBind, function ngBindWatchAction(value) { + element.textContent = value === undefined ? '' : value; + }); + }; + } + }; +}]; + + +/** + * @ngdoc directive + * @name ngBindTemplate + * + * @description + * The `ngBindTemplate` directive specifies that the element + * text content should be replaced with the interpolation of the template + * in the `ngBindTemplate` attribute. + * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` + * expressions. This directive is needed since some HTML elements + * (such as TITLE and OPTION) cannot contain SPAN elements. + * + * @element ANY + * @param {string} ngBindTemplate template of form + * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. + * + * @example + * Try it here: enter text in text box and watch the greeting change. + <example module="bindExample"> + <file name="index.html"> + <script> + angular.module('bindExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.salutation = 'Hello'; + $scope.name = 'World'; + }]); + </script> + <div ng-controller="ExampleController"> + Salutation: <input type="text" ng-model="salutation"><br> + Name: <input type="text" ng-model="name"><br> + <pre ng-bind-template="{{salutation}} {{name}}!"></pre> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-bind', function() { + var salutationElem = element(by.binding('salutation')); + var salutationInput = element(by.model('salutation')); + var nameInput = element(by.model('name')); + + expect(salutationElem.getText()).toBe('Hello World!'); + + salutationInput.clear(); + salutationInput.sendKeys('Greetings'); + nameInput.clear(); + nameInput.sendKeys('user'); + + expect(salutationElem.getText()).toBe('Greetings user!'); + }); + </file> + </example> + */ +var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) { + return { + compile: function ngBindTemplateCompile(templateElement) { + $compile.$$addBindingClass(templateElement); + return function ngBindTemplateLink(scope, element, attr) { + var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); + $compile.$$addBindingInfo(element, interpolateFn.expressions); + element = element[0]; + attr.$observe('ngBindTemplate', function(value) { + element.textContent = value === undefined ? '' : value; + }); + }; + } + }; +}]; + + +/** + * @ngdoc directive + * @name ngBindHtml + * + * @description + * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default, + * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service. + * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link + * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize} + * in your module's dependencies, you need to include "angular-sanitize.js" in your application. + * + * You may also bypass sanitization for values you know are safe. To do so, bind to + * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example + * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}. + * + * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you + * will have an exception (instead of an exploit.) + * + * @element ANY + * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. + * + * @example + + <example module="bindHtmlExample" deps="angular-sanitize.js"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <p ng-bind-html="myHTML"></p> + </div> + </file> + + <file name="script.js"> + angular.module('bindHtmlExample', ['ngSanitize']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.myHTML = + 'I am an <code>HTML</code>string with ' + + '<a href="#">links!</a> and other <em>stuff</em>'; + }]); + </file> + + <file name="protractor.js" type="protractor"> + it('should check ng-bind-html', function() { + expect(element(by.binding('myHTML')).getText()).toBe( + 'I am an HTMLstring with links! and other stuff'); + }); + </file> + </example> + */ +var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) { + return { + restrict: 'A', + compile: function ngBindHtmlCompile(tElement, tAttrs) { + var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml); + var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) { + return (value || '').toString(); + }); + $compile.$$addBindingClass(tElement); + + return function ngBindHtmlLink(scope, element, attr) { + $compile.$$addBindingInfo(element, attr.ngBindHtml); + + scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() { + // we re-evaluate the expr because we want a TrustedValueHolderType + // for $sce, not a string + element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || ''); + }); + }; + } + }; +}]; + +/** + * @ngdoc directive + * @name ngChange + * + * @description + * Evaluate the given expression when the user changes the input. + * The expression is evaluated immediately, unlike the JavaScript onchange event + * which only triggers at the end of a change (usually, when the user leaves the + * form element or presses the return key). + * + * The `ngChange` expression is only evaluated when a change in the input value causes + * a new value to be committed to the model. + * + * It will not be evaluated: + * * if the value returned from the `$parsers` transformation pipeline has not changed + * * if the input has continued to be invalid since the model will stay `null` + * * if the model is changed programmatically and not by a change to the input value + * + * + * Note, this directive requires `ngModel` to be present. + * + * @element input + * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change + * in input value. + * + * @example + * <example name="ngChange-directive" module="changeExample"> + * <file name="index.html"> + * <script> + * angular.module('changeExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.counter = 0; + * $scope.change = function() { + * $scope.counter++; + * }; + * }]); + * </script> + * <div ng-controller="ExampleController"> + * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" /> + * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" /> + * <label for="ng-change-example2">Confirmed</label><br /> + * <tt>debug = {{confirmed}}</tt><br/> + * <tt>counter = {{counter}}</tt><br/> + * </div> + * </file> + * <file name="protractor.js" type="protractor"> + * var counter = element(by.binding('counter')); + * var debug = element(by.binding('confirmed')); + * + * it('should evaluate the expression if changing from view', function() { + * expect(counter.getText()).toContain('0'); + * + * element(by.id('ng-change-example1')).click(); + * + * expect(counter.getText()).toContain('1'); + * expect(debug.getText()).toContain('true'); + * }); + * + * it('should not evaluate the expression if changing from model', function() { + * element(by.id('ng-change-example2')).click(); + + * expect(counter.getText()).toContain('0'); + * expect(debug.getText()).toContain('true'); + * }); + * </file> + * </example> + */ +var ngChangeDirective = valueFn({ + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + ctrl.$viewChangeListeners.push(function() { + scope.$eval(attr.ngChange); + }); + } +}); + +function classDirective(name, selector) { + name = 'ngClass' + name; + return ['$animate', function($animate) { + return { + restrict: 'AC', + link: function(scope, element, attr) { + var oldVal; + + scope.$watch(attr[name], ngClassWatchAction, true); + + attr.$observe('class', function(value) { + ngClassWatchAction(scope.$eval(attr[name])); + }); + + + if (name !== 'ngClass') { + scope.$watch('$index', function($index, old$index) { + // jshint bitwise: false + var mod = $index & 1; + if (mod !== (old$index & 1)) { + var classes = arrayClasses(scope.$eval(attr[name])); + mod === selector ? + addClasses(classes) : + removeClasses(classes); + } + }); + } + + function addClasses(classes) { + var newClasses = digestClassCounts(classes, 1); + attr.$addClass(newClasses); + } + + function removeClasses(classes) { + var newClasses = digestClassCounts(classes, -1); + attr.$removeClass(newClasses); + } + + function digestClassCounts(classes, count) { + var classCounts = element.data('$classCounts') || {}; + var classesToUpdate = []; + forEach(classes, function(className) { + if (count > 0 || classCounts[className]) { + classCounts[className] = (classCounts[className] || 0) + count; + if (classCounts[className] === +(count > 0)) { + classesToUpdate.push(className); + } + } + }); + element.data('$classCounts', classCounts); + return classesToUpdate.join(' '); + } + + function updateClasses(oldClasses, newClasses) { + var toAdd = arrayDifference(newClasses, oldClasses); + var toRemove = arrayDifference(oldClasses, newClasses); + toAdd = digestClassCounts(toAdd, 1); + toRemove = digestClassCounts(toRemove, -1); + if (toAdd && toAdd.length) { + $animate.addClass(element, toAdd); + } + if (toRemove && toRemove.length) { + $animate.removeClass(element, toRemove); + } + } + + function ngClassWatchAction(newVal) { + if (selector === true || scope.$index % 2 === selector) { + var newClasses = arrayClasses(newVal || []); + if (!oldVal) { + addClasses(newClasses); + } else if (!equals(newVal,oldVal)) { + var oldClasses = arrayClasses(oldVal); + updateClasses(oldClasses, newClasses); + } + } + oldVal = shallowCopy(newVal); + } + } + }; + + function arrayDifference(tokens1, tokens2) { + var values = []; + + outer: + for (var i = 0; i < tokens1.length; i++) { + var token = tokens1[i]; + for (var j = 0; j < tokens2.length; j++) { + if (token == tokens2[j]) continue outer; + } + values.push(token); + } + return values; + } + + function arrayClasses(classVal) { + if (isArray(classVal)) { + return classVal; + } else if (isString(classVal)) { + return classVal.split(' '); + } else if (isObject(classVal)) { + var classes = []; + forEach(classVal, function(v, k) { + if (v) { + classes = classes.concat(k.split(' ')); + } + }); + return classes; + } + return classVal; + } + }]; +} + +/** + * @ngdoc directive + * @name ngClass + * @restrict AC + * + * @description + * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding + * an expression that represents all classes to be added. + * + * The directive operates in three different ways, depending on which of three types the expression + * evaluates to: + * + * 1. If the expression evaluates to a string, the string should be one or more space-delimited class + * names. + * + * 2. If the expression evaluates to an array, each element of the array should be a string that is + * one or more space-delimited class names. + * + * 3. If the expression evaluates to an object, then for each key-value pair of the + * object with a truthy value the corresponding key is used as a class name. + * + * The directive won't add duplicate classes if a particular class was already set. + * + * When the expression changes, the previously added classes are removed and only then the + * new classes are added. + * + * @animations + * **add** - happens just before the class is applied to the elements + * + * **remove** - happens just before the class is removed from the element + * + * @element ANY + * @param {expression} ngClass {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class + * names, an array, or a map of class names to boolean values. In the case of a map, the + * names of the properties whose values are truthy will be added as css classes to the + * element. + * + * @example Example that demonstrates basic bindings via ngClass directive. + <example> + <file name="index.html"> + <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p> + <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br> + <input type="checkbox" ng-model="important"> important (apply "bold" class)<br> + <input type="checkbox" ng-model="error"> error (apply "red" class) + <hr> + <p ng-class="style">Using String Syntax</p> + <input type="text" ng-model="style" placeholder="Type: bold strike red"> + <hr> + <p ng-class="[style1, style2, style3]">Using Array Syntax</p> + <input ng-model="style1" placeholder="Type: bold, strike or red"><br> + <input ng-model="style2" placeholder="Type: bold, strike or red"><br> + <input ng-model="style3" placeholder="Type: bold, strike or red"><br> + </file> + <file name="style.css"> + .strike { + text-decoration: line-through; + } + .bold { + font-weight: bold; + } + .red { + color: red; + } + </file> + <file name="protractor.js" type="protractor"> + var ps = element.all(by.css('p')); + + it('should let you toggle the class', function() { + + expect(ps.first().getAttribute('class')).not.toMatch(/bold/); + expect(ps.first().getAttribute('class')).not.toMatch(/red/); + + element(by.model('important')).click(); + expect(ps.first().getAttribute('class')).toMatch(/bold/); + + element(by.model('error')).click(); + expect(ps.first().getAttribute('class')).toMatch(/red/); + }); + + it('should let you toggle string example', function() { + expect(ps.get(1).getAttribute('class')).toBe(''); + element(by.model('style')).clear(); + element(by.model('style')).sendKeys('red'); + expect(ps.get(1).getAttribute('class')).toBe('red'); + }); + + it('array example should have 3 classes', function() { + expect(ps.last().getAttribute('class')).toBe(''); + element(by.model('style1')).sendKeys('bold'); + element(by.model('style2')).sendKeys('strike'); + element(by.model('style3')).sendKeys('red'); + expect(ps.last().getAttribute('class')).toBe('bold strike red'); + }); + </file> + </example> + + ## Animations + + The example below demonstrates how to perform animations using ngClass. + + <example module="ngAnimate" deps="angular-animate.js" animations="true"> + <file name="index.html"> + <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'"> + <input id="clearbtn" type="button" value="clear" ng-click="myVar=''"> + <br> + <span class="base-class" ng-class="myVar">Sample Text</span> + </file> + <file name="style.css"> + .base-class { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + } + + .base-class.my-class { + color: red; + font-size:3em; + } + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-class', function() { + expect(element(by.css('.base-class')).getAttribute('class')).not. + toMatch(/my-class/); + + element(by.id('setbtn')).click(); + + expect(element(by.css('.base-class')).getAttribute('class')). + toMatch(/my-class/); + + element(by.id('clearbtn')).click(); + + expect(element(by.css('.base-class')).getAttribute('class')).not. + toMatch(/my-class/); + }); + </file> + </example> + + + ## ngClass and pre-existing CSS3 Transitions/Animations + The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. + Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder + any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure + to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and + {@link ng.$animate#removeClass $animate.removeClass}. + */ +var ngClassDirective = classDirective('', true); + +/** + * @ngdoc directive + * @name ngClassOdd + * @restrict AC + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except they work in + * conjunction with `ngRepeat` and take effect only on odd (even) rows. + * + * This directive can be applied only within the scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result + * of the evaluation can be a string representing space delimited class names or an array. + * + * @example + <example> + <file name="index.html"> + <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> + <li ng-repeat="name in names"> + <span ng-class-odd="'odd'" ng-class-even="'even'"> + {{name}} + </span> + </li> + </ol> + </file> + <file name="style.css"> + .odd { + color: red; + } + .even { + color: blue; + } + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-class-odd and ng-class-even', function() { + expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). + toMatch(/odd/); + expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). + toMatch(/even/); + }); + </file> + </example> + */ +var ngClassOddDirective = classDirective('Odd', 0); + +/** + * @ngdoc directive + * @name ngClassEven + * @restrict AC + * + * @description + * The `ngClassOdd` and `ngClassEven` directives work exactly as + * {@link ng.directive:ngClass ngClass}, except they work in + * conjunction with `ngRepeat` and take effect only on odd (even) rows. + * + * This directive can be applied only within the scope of an + * {@link ng.directive:ngRepeat ngRepeat}. + * + * @element ANY + * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The + * result of the evaluation can be a string representing space delimited class names or an array. + * + * @example + <example> + <file name="index.html"> + <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> + <li ng-repeat="name in names"> + <span ng-class-odd="'odd'" ng-class-even="'even'"> + {{name}} + </span> + </li> + </ol> + </file> + <file name="style.css"> + .odd { + color: red; + } + .even { + color: blue; + } + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-class-odd and ng-class-even', function() { + expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). + toMatch(/odd/); + expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). + toMatch(/even/); + }); + </file> + </example> + */ +var ngClassEvenDirective = classDirective('Even', 1); + +/** + * @ngdoc directive + * @name ngCloak + * @restrict AC + * + * @description + * The `ngCloak` directive is used to prevent the Angular html template from being briefly + * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this + * directive to avoid the undesirable flicker effect caused by the html template display. + * + * The directive can be applied to the `<body>` element, but the preferred usage is to apply + * multiple `ngCloak` directives to small portions of the page to permit progressive rendering + * of the browser view. + * + * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and + * `angular.min.js`. + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * + * ```css + * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { + * display: none !important; + * } + * ``` + * + * When this css rule is loaded by the browser, all html elements (including their children) that + * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive + * during the compilation of the template it deletes the `ngCloak` element attribute, making + * the compiled element visible. + * + * For the best result, the `angular.js` script must be loaded in the head section of the html + * document; alternatively, the css rule above must be included in the external stylesheet of the + * application. + * + * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they + * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css + * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below. + * + * @element ANY + * + * @example + <example> + <file name="index.html"> + <div id="template1" ng-cloak>{{ 'hello' }}</div> + <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div> + </file> + <file name="protractor.js" type="protractor"> + it('should remove the template directive and css class', function() { + expect($('#template1').getAttribute('ng-cloak')). + toBeNull(); + expect($('#template2').getAttribute('ng-cloak')). + toBeNull(); + }); + </file> + </example> + * + */ +var ngCloakDirective = ngDirective({ + compile: function(element, attr) { + attr.$set('ngCloak', undefined); + element.removeClass('ng-cloak'); + } +}); + +/** + * @ngdoc directive + * @name ngController + * + * @description + * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular + * supports the principles behind the Model-View-Controller design pattern. + * + * MVC components in angular: + * + * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties + * are accessed through bindings. + * * View — The template (HTML with data bindings) that is rendered into the View. + * * Controller — The `ngController` directive specifies a Controller class; the class contains business + * logic behind the application to decorate the scope with functions and values + * + * Note that you can also attach controllers to the DOM by declaring it in a route definition + * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller + * again using `ng-controller` in the template itself. This will cause the controller to be attached + * and executed twice. + * + * @element ANY + * @scope + * @priority 500 + * @param {expression} ngController Name of a constructor function registered with the current + * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression} + * that on the current scope evaluates to a constructor function. + * + * The controller instance can be published into a scope property by specifying + * `ng-controller="as propertyName"`. + * + * If the current `$controllerProvider` is configured to use globals (via + * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may + * also be the name of a globally accessible constructor function (not recommended). + * + * @example + * Here is a simple form for editing user contact information. Adding, removing, clearing, and + * greeting are methods declared on the controller (see source tab). These methods can + * easily be called from the angular markup. Any changes to the data are automatically reflected + * in the View without the need for a manual update. + * + * Two different declaration styles are included below: + * + * * one binds methods and properties directly onto the controller using `this`: + * `ng-controller="SettingsController1 as settings"` + * * one injects `$scope` into the controller: + * `ng-controller="SettingsController2"` + * + * The second option is more common in the Angular community, and is generally used in boilerplates + * and in this guide. However, there are advantages to binding properties directly to the controller + * and avoiding scope. + * + * * Using `controller as` makes it obvious which controller you are accessing in the template when + * multiple controllers apply to an element. + * * If you are writing your controllers as classes you have easier access to the properties and + * methods, which will appear on the scope, from inside the controller code. + * * Since there is always a `.` in the bindings, you don't have to worry about prototypal + * inheritance masking primitives. + * + * This example demonstrates the `controller as` syntax. + * + * <example name="ngControllerAs" module="controllerAsExample"> + * <file name="index.html"> + * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings"> + * Name: <input type="text" ng-model="settings.name"/> + * [ <a href="" ng-click="settings.greet()">greet</a> ]<br/> + * Contact: + * <ul> + * <li ng-repeat="contact in settings.contacts"> + * <select ng-model="contact.type"> + * <option>phone</option> + * <option>email</option> + * </select> + * <input type="text" ng-model="contact.value"/> + * [ <a href="" ng-click="settings.clearContact(contact)">clear</a> + * | <a href="" ng-click="settings.removeContact(contact)">X</a> ] + * </li> + * <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li> + * </ul> + * </div> + * </file> + * <file name="app.js"> + * angular.module('controllerAsExample', []) + * .controller('SettingsController1', SettingsController1); + * + * function SettingsController1() { + * this.name = "John Smith"; + * this.contacts = [ + * {type: 'phone', value: '408 555 1212'}, + * {type: 'email', value: 'john.smith@example.org'} ]; + * } + * + * SettingsController1.prototype.greet = function() { + * alert(this.name); + * }; + * + * SettingsController1.prototype.addContact = function() { + * this.contacts.push({type: 'email', value: 'yourname@example.org'}); + * }; + * + * SettingsController1.prototype.removeContact = function(contactToRemove) { + * var index = this.contacts.indexOf(contactToRemove); + * this.contacts.splice(index, 1); + * }; + * + * SettingsController1.prototype.clearContact = function(contact) { + * contact.type = 'phone'; + * contact.value = ''; + * }; + * </file> + * <file name="protractor.js" type="protractor"> + * it('should check controller as', function() { + * var container = element(by.id('ctrl-as-exmpl')); + * expect(container.element(by.model('settings.name')) + * .getAttribute('value')).toBe('John Smith'); + * + * var firstRepeat = + * container.element(by.repeater('contact in settings.contacts').row(0)); + * var secondRepeat = + * container.element(by.repeater('contact in settings.contacts').row(1)); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('408 555 1212'); + * + * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('john.smith@example.org'); + * + * firstRepeat.element(by.linkText('clear')).click(); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe(''); + * + * container.element(by.linkText('add')).click(); + * + * expect(container.element(by.repeater('contact in settings.contacts').row(2)) + * .element(by.model('contact.value')) + * .getAttribute('value')) + * .toBe('yourname@example.org'); + * }); + * </file> + * </example> + * + * This example demonstrates the "attach to `$scope`" style of controller. + * + * <example name="ngController" module="controllerExample"> + * <file name="index.html"> + * <div id="ctrl-exmpl" ng-controller="SettingsController2"> + * Name: <input type="text" ng-model="name"/> + * [ <a href="" ng-click="greet()">greet</a> ]<br/> + * Contact: + * <ul> + * <li ng-repeat="contact in contacts"> + * <select ng-model="contact.type"> + * <option>phone</option> + * <option>email</option> + * </select> + * <input type="text" ng-model="contact.value"/> + * [ <a href="" ng-click="clearContact(contact)">clear</a> + * | <a href="" ng-click="removeContact(contact)">X</a> ] + * </li> + * <li>[ <a href="" ng-click="addContact()">add</a> ]</li> + * </ul> + * </div> + * </file> + * <file name="app.js"> + * angular.module('controllerExample', []) + * .controller('SettingsController2', ['$scope', SettingsController2]); + * + * function SettingsController2($scope) { + * $scope.name = "John Smith"; + * $scope.contacts = [ + * {type:'phone', value:'408 555 1212'}, + * {type:'email', value:'john.smith@example.org'} ]; + * + * $scope.greet = function() { + * alert($scope.name); + * }; + * + * $scope.addContact = function() { + * $scope.contacts.push({type:'email', value:'yourname@example.org'}); + * }; + * + * $scope.removeContact = function(contactToRemove) { + * var index = $scope.contacts.indexOf(contactToRemove); + * $scope.contacts.splice(index, 1); + * }; + * + * $scope.clearContact = function(contact) { + * contact.type = 'phone'; + * contact.value = ''; + * }; + * } + * </file> + * <file name="protractor.js" type="protractor"> + * it('should check controller', function() { + * var container = element(by.id('ctrl-exmpl')); + * + * expect(container.element(by.model('name')) + * .getAttribute('value')).toBe('John Smith'); + * + * var firstRepeat = + * container.element(by.repeater('contact in contacts').row(0)); + * var secondRepeat = + * container.element(by.repeater('contact in contacts').row(1)); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('408 555 1212'); + * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe('john.smith@example.org'); + * + * firstRepeat.element(by.linkText('clear')).click(); + * + * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) + * .toBe(''); + * + * container.element(by.linkText('add')).click(); + * + * expect(container.element(by.repeater('contact in contacts').row(2)) + * .element(by.model('contact.value')) + * .getAttribute('value')) + * .toBe('yourname@example.org'); + * }); + * </file> + *</example> + + */ +var ngControllerDirective = [function() { + return { + restrict: 'A', + scope: true, + controller: '@', + priority: 500 + }; +}]; + +/** + * @ngdoc directive + * @name ngCsp + * + * @element html + * @description + * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. + * + * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps. + * + * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). + * For Angular to be CSP compatible there are only two things that we need to do differently: + * + * - don't use `Function` constructor to generate optimized value getters + * - don't inject custom stylesheet into the document + * + * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp` + * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will + * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will + * be raised. + * + * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically + * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). + * To make those directives work in CSP mode, include the `angular-csp.css` manually. + * + * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This + * autodetection however triggers a CSP error to be logged in the console: + * + * ``` + * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of + * script in the following Content Security Policy directive: "default-src 'self'". Note that + * 'script-src' was not explicitly set, so 'default-src' is used as a fallback. + * ``` + * + * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` + * directive on the root element of the application or on the `angular.js` script tag, whichever + * appears first in the html document. + * + * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* + * + * @example + * This example shows how to apply the `ngCsp` directive to the `html` tag. + ```html + <!doctype html> + <html ng-app ng-csp> + ... + ... + </html> + ``` + * @example + // Note: the suffix `.csp` in the example name triggers + // csp mode in our http server! + <example name="example.csp" module="cspExample" ng-csp="true"> + <file name="index.html"> + <div ng-controller="MainController as ctrl"> + <div> + <button ng-click="ctrl.inc()" id="inc">Increment</button> + <span id="counter"> + {{ctrl.counter}} + </span> + </div> + + <div> + <button ng-click="ctrl.evil()" id="evil">Evil</button> + <span id="evilError"> + {{ctrl.evilError}} + </span> + </div> + </div> + </file> + <file name="script.js"> + angular.module('cspExample', []) + .controller('MainController', function() { + this.counter = 0; + this.inc = function() { + this.counter++; + }; + this.evil = function() { + // jshint evil:true + try { + eval('1+2'); + } catch (e) { + this.evilError = e.message; + } + }; + }); + </file> + <file name="protractor.js" type="protractor"> + var util, webdriver; + + var incBtn = element(by.id('inc')); + var counter = element(by.id('counter')); + var evilBtn = element(by.id('evil')); + var evilError = element(by.id('evilError')); + + function getAndClearSevereErrors() { + return browser.manage().logs().get('browser').then(function(browserLog) { + return browserLog.filter(function(logEntry) { + return logEntry.level.value > webdriver.logging.Level.WARNING.value; + }); + }); + } + + function clearErrors() { + getAndClearSevereErrors(); + } + + function expectNoErrors() { + getAndClearSevereErrors().then(function(filteredLog) { + expect(filteredLog.length).toEqual(0); + if (filteredLog.length) { + console.log('browser console errors: ' + util.inspect(filteredLog)); + } + }); + } + + function expectError(regex) { + getAndClearSevereErrors().then(function(filteredLog) { + var found = false; + filteredLog.forEach(function(log) { + if (log.message.match(regex)) { + found = true; + } + }); + if (!found) { + throw new Error('expected an error that matches ' + regex); + } + }); + } + + beforeEach(function() { + util = require('util'); + webdriver = require('protractor/node_modules/selenium-webdriver'); + }); + + // For now, we only test on Chrome, + // as Safari does not load the page with Protractor's injected scripts, + // and Firefox webdriver always disables content security policy (#6358) + if (browser.params.browser !== 'chrome') { + return; + } + + it('should not report errors when the page is loaded', function() { + // clear errors so we are not dependent on previous tests + clearErrors(); + // Need to reload the page as the page is already loaded when + // we come here + browser.driver.getCurrentUrl().then(function(url) { + browser.get(url); + }); + expectNoErrors(); + }); + + it('should evaluate expressions', function() { + expect(counter.getText()).toEqual('0'); + incBtn.click(); + expect(counter.getText()).toEqual('1'); + expectNoErrors(); + }); + + it('should throw and report an error when using "eval"', function() { + evilBtn.click(); + expect(evilError.getText()).toMatch(/Content Security Policy/); + expectError(/Content Security Policy/); + }); + </file> + </example> + */ + +// ngCsp is not implemented as a proper directive any more, because we need it be processed while we +// bootstrap the system (before $parse is instantiated), for this reason we just have +// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc + +/** + * @ngdoc directive + * @name ngClick + * + * @description + * The ngClick directive allows you to specify custom behavior when + * an element is clicked. + * + * @element ANY + * @priority 0 + * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon + * click. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-click="count = count + 1" ng-init="count=0"> + Increment + </button> + <span> + count: {{count}} + </span> + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-click', function() { + expect(element(by.binding('count')).getText()).toMatch('0'); + element(by.css('button')).click(); + expect(element(by.binding('count')).getText()).toMatch('1'); + }); + </file> + </example> + */ +/* + * A collection of directives that allows creation of custom event handlers that are defined as + * angular expressions and are compiled and executed within the current scope. + */ +var ngEventDirectives = {}; + +// For events that might fire synchronously during DOM manipulation +// we need to execute their event handlers asynchronously using $evalAsync, +// so that they are not executed in an inconsistent state. +var forceAsyncEvents = { + 'blur': true, + 'focus': true +}; +forEach( + 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), + function(eventName) { + var directiveName = directiveNormalize('ng-' + eventName); + ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { + return { + restrict: 'A', + compile: function($element, attr) { + // We expose the powerful $event object on the scope that provides access to the Window, + // etc. that isn't protected by the fast paths in $parse. We explicitly request better + // checks at the cost of speed since event handler expressions are not executed as + // frequently as regular change detection. + var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true); + return function ngEventHandler(scope, element) { + element.on(eventName, function(event) { + var callback = function() { + fn(scope, {$event:event}); + }; + if (forceAsyncEvents[eventName] && $rootScope.$$phase) { + scope.$evalAsync(callback); + } else { + scope.$apply(callback); + } + }); + }; + } + }; + }]; + } +); + +/** + * @ngdoc directive + * @name ngDblclick + * + * @description + * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event. + * + * @element ANY + * @priority 0 + * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon + * a dblclick. (The Event object is available as `$event`) + * + * @example + <example> + <file name="index.html"> + <button ng-dblclick="count = count + 1" ng-init="count=0"> + Increment (on double click) + </button> + count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngMousedown + * + * @description + * The ngMousedown directive allows you to specify custom behavior on mousedown event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon + * mousedown. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-mousedown="count = count + 1" ng-init="count=0"> + Increment (on mouse down) + </button> + count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngMouseup + * + * @description + * Specify custom behavior on mouseup event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon + * mouseup. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-mouseup="count = count + 1" ng-init="count=0"> + Increment (on mouse up) + </button> + count: {{count}} + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngMouseover + * + * @description + * Specify custom behavior on mouseover event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon + * mouseover. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-mouseover="count = count + 1" ng-init="count=0"> + Increment (when mouse is over) + </button> + count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngMouseenter + * + * @description + * Specify custom behavior on mouseenter event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon + * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-mouseenter="count = count + 1" ng-init="count=0"> + Increment (when mouse enters) + </button> + count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngMouseleave + * + * @description + * Specify custom behavior on mouseleave event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon + * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-mouseleave="count = count + 1" ng-init="count=0"> + Increment (when mouse leaves) + </button> + count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngMousemove + * + * @description + * Specify custom behavior on mousemove event. + * + * @element ANY + * @priority 0 + * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon + * mousemove. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <button ng-mousemove="count = count + 1" ng-init="count=0"> + Increment (when mouse moves) + </button> + count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngKeydown + * + * @description + * Specify custom behavior on keydown event. + * + * @element ANY + * @priority 0 + * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon + * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + <example> + <file name="index.html"> + <input ng-keydown="count = count + 1" ng-init="count=0"> + key down count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngKeyup + * + * @description + * Specify custom behavior on keyup event. + * + * @element ANY + * @priority 0 + * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon + * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) + * + * @example + <example> + <file name="index.html"> + <p>Typing in the input box below updates the key count</p> + <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}} + + <p>Typing in the input box below updates the keycode</p> + <input ng-keyup="event=$event"> + <p>event keyCode: {{ event.keyCode }}</p> + <p>event altKey: {{ event.altKey }}</p> + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngKeypress + * + * @description + * Specify custom behavior on keypress event. + * + * @element ANY + * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon + * keypress. ({@link guide/expression#-event- Event object is available as `$event`} + * and can be interrogated for keyCode, altKey, etc.) + * + * @example + <example> + <file name="index.html"> + <input ng-keypress="count = count + 1" ng-init="count=0"> + key press count: {{count}} + </file> + </example> + */ + + +/** + * @ngdoc directive + * @name ngSubmit + * + * @description + * Enables binding angular expressions to onsubmit events. + * + * Additionally it prevents the default action (which for form means sending the request to the + * server and reloading the current page), but only if the form does not contain `action`, + * `data-action`, or `x-action` attributes. + * + * <div class="alert alert-warning"> + * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and + * `ngSubmit` handlers together. See the + * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation} + * for a detailed discussion of when `ngSubmit` may be triggered. + * </div> + * + * @element form + * @priority 0 + * @param {expression} ngSubmit {@link guide/expression Expression} to eval. + * ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example module="submitExample"> + <file name="index.html"> + <script> + angular.module('submitExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.list = []; + $scope.text = 'hello'; + $scope.submit = function() { + if ($scope.text) { + $scope.list.push(this.text); + $scope.text = ''; + } + }; + }]); + </script> + <form ng-submit="submit()" ng-controller="ExampleController"> + Enter text and hit enter: + <input type="text" ng-model="text" name="text" /> + <input type="submit" id="submit" value="Submit" /> + <pre>list={{list}}</pre> + </form> + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-submit', function() { + expect(element(by.binding('list')).getText()).toBe('list=[]'); + element(by.css('#submit')).click(); + expect(element(by.binding('list')).getText()).toContain('hello'); + expect(element(by.model('text')).getAttribute('value')).toBe(''); + }); + it('should ignore empty strings', function() { + expect(element(by.binding('list')).getText()).toBe('list=[]'); + element(by.css('#submit')).click(); + element(by.css('#submit')).click(); + expect(element(by.binding('list')).getText()).toContain('hello'); + }); + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngFocus + * + * @description + * Specify custom behavior on focus event. + * + * Note: As the `focus` event is executed synchronously when calling `input.focus()` + * AngularJS executes the expression using `scope.$evalAsync` if the event is fired + * during an `$apply` to ensure a consistent state. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon + * focus. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + +/** + * @ngdoc directive + * @name ngBlur + * + * @description + * Specify custom behavior on blur event. + * + * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when + * an element has lost focus. + * + * Note: As the `blur` event is executed synchronously also during DOM manipulations + * (e.g. removing a focussed input), + * AngularJS executes the expression using `scope.$evalAsync` if the event is fired + * during an `$apply` to ensure a consistent state. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon + * blur. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + * See {@link ng.directive:ngClick ngClick} + */ + +/** + * @ngdoc directive + * @name ngCopy + * + * @description + * Specify custom behavior on copy event. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon + * copy. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value"> + copied: {{copied}} + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngCut + * + * @description + * Specify custom behavior on cut event. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon + * cut. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value"> + cut: {{cut}} + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngPaste + * + * @description + * Specify custom behavior on paste event. + * + * @element window, input, select, textarea, a + * @priority 0 + * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon + * paste. ({@link guide/expression#-event- Event object is available as `$event`}) + * + * @example + <example> + <file name="index.html"> + <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'> + pasted: {{paste}} + </file> + </example> + */ + +/** + * @ngdoc directive + * @name ngIf + * @restrict A + * + * @description + * The `ngIf` directive removes or recreates a portion of the DOM tree based on an + * {expression}. If the expression assigned to `ngIf` evaluates to a false + * value then the element is removed from the DOM, otherwise a clone of the + * element is reinserted into the DOM. + * + * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the + * element in the DOM rather than changing its visibility via the `display` css property. A common + * case when this difference is significant is when using css selectors that rely on an element's + * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes. + * + * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope + * is created when the element is restored. The scope created within `ngIf` inherits from + * its parent scope using + * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance). + * An important implication of this is if `ngModel` is used within `ngIf` to bind to + * a javascript primitive defined in the parent scope. In this case any modifications made to the + * variable within the child scope will override (hide) the value in the parent scope. + * + * Also, `ngIf` recreates elements using their compiled state. An example of this behavior + * is if an element's class attribute is directly modified after it's compiled, using something like + * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element + * the added class will be lost because the original compiled state is used to regenerate the element. + * + * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter` + * and `leave` effects. + * + * @animations + * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container + * leave - happens just before the `ngIf` contents are removed from the DOM + * + * @element ANY + * @scope + * @priority 600 + * @param {expression} ngIf If the {@link guide/expression expression} is falsy then + * the element is removed from the DOM tree. If it is truthy a copy of the compiled + * element is added to the DOM tree. + * + * @example + <example module="ngAnimate" deps="angular-animate.js" animations="true"> + <file name="index.html"> + Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/> + Show when checked: + <span ng-if="checked" class="animate-if"> + This is removed when the checkbox is unchecked. + </span> + </file> + <file name="animations.css"> + .animate-if { + background:white; + border:1px solid black; + padding:10px; + } + + .animate-if.ng-enter, .animate-if.ng-leave { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + } + + .animate-if.ng-enter, + .animate-if.ng-leave.ng-leave-active { + opacity:0; + } + + .animate-if.ng-leave, + .animate-if.ng-enter.ng-enter-active { + opacity:1; + } + </file> + </example> + */ +var ngIfDirective = ['$animate', function($animate) { + return { + multiElement: true, + transclude: 'element', + priority: 600, + terminal: true, + restrict: 'A', + $$tlb: true, + link: function($scope, $element, $attr, ctrl, $transclude) { + var block, childScope, previousElements; + $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { + + if (value) { + if (!childScope) { + $transclude(function(clone, newScope) { + childScope = newScope; + clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); + // Note: We only need the first/last node of the cloned nodes. + // However, we need to keep the reference to the jqlite wrapper as it might be changed later + // by a directive with templateUrl when its template arrives. + block = { + clone: clone + }; + $animate.enter(clone, $element.parent(), $element); + }); + } + } else { + if (previousElements) { + previousElements.remove(); + previousElements = null; + } + if (childScope) { + childScope.$destroy(); + childScope = null; + } + if (block) { + previousElements = getBlockNodes(block.clone); + $animate.leave(previousElements).then(function() { + previousElements = null; + }); + block = null; + } + } + }); + } + }; +}]; + +/** + * @ngdoc directive + * @name ngInclude + * @restrict ECA + * + * @description + * Fetches, compiles and includes an external HTML fragment. + * + * By default, the template URL is restricted to the same domain and protocol as the + * application document. This is done by calling {@link $sce#getTrustedResourceUrl + * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols + * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or + * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link + * ng.$sce Strict Contextual Escaping}. + * + * In addition, the browser's + * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) + * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) + * policy may further restrict whether the template is successfully loaded. + * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://` + * access on some browsers. + * + * @animations + * enter - animation is used to bring new content into the browser. + * leave - animation is used to animate existing content away. + * + * The enter and leave animation occur concurrently. + * + * @scope + * @priority 400 + * + * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, + * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. + * @param {string=} onload Expression to evaluate when a new partial is loaded. + * + * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll + * $anchorScroll} to scroll the viewport after the content is loaded. + * + * - If the attribute is not set, disable scrolling. + * - If the attribute is set without value, enable scrolling. + * - Otherwise enable scrolling only if the expression evaluates to truthy value. + * + * @example + <example module="includeExample" deps="angular-animate.js" animations="true"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <select ng-model="template" ng-options="t.name for t in templates"> + <option value="">(blank)</option> + </select> + url of the template: <code>{{template.url}}</code> + <hr/> + <div class="slide-animate-container"> + <div class="slide-animate" ng-include="template.url"></div> + </div> + </div> + </file> + <file name="script.js"> + angular.module('includeExample', ['ngAnimate']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.templates = + [ { name: 'template1.html', url: 'template1.html'}, + { name: 'template2.html', url: 'template2.html'} ]; + $scope.template = $scope.templates[0]; + }]); + </file> + <file name="template1.html"> + Content of template1.html + </file> + <file name="template2.html"> + Content of template2.html + </file> + <file name="animations.css"> + .slide-animate-container { + position:relative; + background:white; + border:1px solid black; + height:40px; + overflow:hidden; + } + + .slide-animate { + padding:10px; + } + + .slide-animate.ng-enter, .slide-animate.ng-leave { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + display:block; + padding:10px; + } + + .slide-animate.ng-enter { + top:-50px; + } + .slide-animate.ng-enter.ng-enter-active { + top:0; + } + + .slide-animate.ng-leave { + top:0; + } + .slide-animate.ng-leave.ng-leave-active { + top:50px; + } + </file> + <file name="protractor.js" type="protractor"> + var templateSelect = element(by.model('template')); + var includeElem = element(by.css('[ng-include]')); + + it('should load template1.html', function() { + expect(includeElem.getText()).toMatch(/Content of template1.html/); + }); + + it('should load template2.html', function() { + if (browser.params.browser == 'firefox') { + // Firefox can't handle using selects + // See https://github.com/angular/protractor/issues/480 + return; + } + templateSelect.click(); + templateSelect.all(by.css('option')).get(2).click(); + expect(includeElem.getText()).toMatch(/Content of template2.html/); + }); + + it('should change to blank', function() { + if (browser.params.browser == 'firefox') { + // Firefox can't handle using selects + return; + } + templateSelect.click(); + templateSelect.all(by.css('option')).get(0).click(); + expect(includeElem.isPresent()).toBe(false); + }); + </file> + </example> + */ + + +/** + * @ngdoc event + * @name ngInclude#$includeContentRequested + * @eventType emit on the scope ngInclude was declared in + * @description + * Emitted every time the ngInclude content is requested. + * + * @param {Object} angularEvent Synthetic event object. + * @param {String} src URL of content to load. + */ + + +/** + * @ngdoc event + * @name ngInclude#$includeContentLoaded + * @eventType emit on the current ngInclude scope + * @description + * Emitted every time the ngInclude content is reloaded. + * + * @param {Object} angularEvent Synthetic event object. + * @param {String} src URL of content to load. + */ + + +/** + * @ngdoc event + * @name ngInclude#$includeContentError + * @eventType emit on the scope ngInclude was declared in + * @description + * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299) + * + * @param {Object} angularEvent Synthetic event object. + * @param {String} src URL of content to load. + */ +var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce', + function($templateRequest, $anchorScroll, $animate, $sce) { + return { + restrict: 'ECA', + priority: 400, + terminal: true, + transclude: 'element', + controller: angular.noop, + compile: function(element, attr) { + var srcExp = attr.ngInclude || attr.src, + onloadExp = attr.onload || '', + autoScrollExp = attr.autoscroll; + + return function(scope, $element, $attr, ctrl, $transclude) { + var changeCounter = 0, + currentScope, + previousElement, + currentElement; + + var cleanupLastIncludeContent = function() { + if (previousElement) { + previousElement.remove(); + previousElement = null; + } + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + if (currentElement) { + $animate.leave(currentElement).then(function() { + previousElement = null; + }); + previousElement = currentElement; + currentElement = null; + } + }; + + scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) { + var afterAnimation = function() { + if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } + }; + var thisChangeId = ++changeCounter; + + if (src) { + //set the 2nd param to true to ignore the template request error so that the inner + //contents and scope can be cleaned up. + $templateRequest(src, true).then(function(response) { + if (thisChangeId !== changeCounter) return; + var newScope = scope.$new(); + ctrl.template = response; + + // Note: This will also link all children of ng-include that were contained in the original + // html. If that content contains controllers, ... they could pollute/change the scope. + // However, using ng-include on an element with additional content does not make sense... + // Note: We can't remove them in the cloneAttchFn of $transclude as that + // function is called before linking the content, which would apply child + // directives to non existing elements. + var clone = $transclude(newScope, function(clone) { + cleanupLastIncludeContent(); + $animate.enter(clone, null, $element).then(afterAnimation); + }); + + currentScope = newScope; + currentElement = clone; + + currentScope.$emit('$includeContentLoaded', src); + scope.$eval(onloadExp); + }, function() { + if (thisChangeId === changeCounter) { + cleanupLastIncludeContent(); + scope.$emit('$includeContentError', src); + } + }); + scope.$emit('$includeContentRequested', src); + } else { + cleanupLastIncludeContent(); + ctrl.template = null; + } + }); + }; + } + }; +}]; + +// This directive is called during the $transclude call of the first `ngInclude` directive. +// It will replace and compile the content of the element with the loaded template. +// We need this directive so that the element content is already filled when +// the link function of another directive on the same element as ngInclude +// is called. +var ngIncludeFillContentDirective = ['$compile', + function($compile) { + return { + restrict: 'ECA', + priority: -400, + require: 'ngInclude', + link: function(scope, $element, $attr, ctrl) { + if (/SVG/.test($element[0].toString())) { + // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not + // support innerHTML, so detect this here and try to generate the contents + // specially. + $element.empty(); + $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope, + function namespaceAdaptedClone(clone) { + $element.append(clone); + }, {futureParentElement: $element}); + return; + } + + $element.html(ctrl.template); + $compile($element.contents())(scope); + } + }; + }]; + +/** + * @ngdoc directive + * @name ngInit + * @restrict AC + * + * @description + * The `ngInit` directive allows you to evaluate an expression in the + * current scope. + * + * <div class="alert alert-error"> + * The only appropriate use of `ngInit` is for aliasing special properties of + * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you + * should use {@link guide/controller controllers} rather than `ngInit` + * to initialize values on a scope. + * </div> + * <div class="alert alert-warning"> + * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make + * sure you have parenthesis for correct precedence: + * <pre class="prettyprint"> + * `<div ng-init="test1 = (data | orderBy:'name')"></div>` + * </pre> + * </div> + * + * @priority 450 + * + * @element ANY + * @param {expression} ngInit {@link guide/expression Expression} to eval. + * + * @example + <example module="initExample"> + <file name="index.html"> + <script> + angular.module('initExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.list = [['a', 'b'], ['c', 'd']]; + }]); + </script> + <div ng-controller="ExampleController"> + <div ng-repeat="innerList in list" ng-init="outerIndex = $index"> + <div ng-repeat="value in innerList" ng-init="innerIndex = $index"> + <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span> + </div> + </div> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should alias index positions', function() { + var elements = element.all(by.css('.example-init')); + expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;'); + expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;'); + expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;'); + expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;'); + }); + </file> + </example> + */ +var ngInitDirective = ngDirective({ + priority: 450, + compile: function() { + return { + pre: function(scope, element, attrs) { + scope.$eval(attrs.ngInit); + } + }; + } +}); + +/** + * @ngdoc directive + * @name ngList + * + * @description + * Text input that converts between a delimited string and an array of strings. The default + * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom + * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`. + * + * The behaviour of the directive is affected by the use of the `ngTrim` attribute. + * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each + * list item is respected. This implies that the user of the directive is responsible for + * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a + * tab or newline character. + * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected + * when joining the list items back together) and whitespace around each list item is stripped + * before it is added to the model. + * + * ### Example with Validation + * + * <example name="ngList-directive" module="listExample"> + * <file name="app.js"> + * angular.module('listExample', []) + * .controller('ExampleController', ['$scope', function($scope) { + * $scope.names = ['morpheus', 'neo', 'trinity']; + * }]); + * </file> + * <file name="index.html"> + * <form name="myForm" ng-controller="ExampleController"> + * List: <input name="namesInput" ng-model="names" ng-list required> + * <span class="error" ng-show="myForm.namesInput.$error.required"> + * Required!</span> + * <br> + * <tt>names = {{names}}</tt><br/> + * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/> + * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/> + * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> + * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> + * </form> + * </file> + * <file name="protractor.js" type="protractor"> + * var listInput = element(by.model('names')); + * var names = element(by.exactBinding('names')); + * var valid = element(by.binding('myForm.namesInput.$valid')); + * var error = element(by.css('span.error')); + * + * it('should initialize to model', function() { + * expect(names.getText()).toContain('["morpheus","neo","trinity"]'); + * expect(valid.getText()).toContain('true'); + * expect(error.getCssValue('display')).toBe('none'); + * }); + * + * it('should be invalid if empty', function() { + * listInput.clear(); + * listInput.sendKeys(''); + * + * expect(names.getText()).toContain(''); + * expect(valid.getText()).toContain('false'); + * expect(error.getCssValue('display')).not.toBe('none'); + * }); + * </file> + * </example> + * + * ### Example - splitting on whitespace + * <example name="ngList-directive-newlines"> + * <file name="index.html"> + * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea> + * <pre>{{ list | json }}</pre> + * </file> + * <file name="protractor.js" type="protractor"> + * it("should split the text by newlines", function() { + * var listInput = element(by.model('list')); + * var output = element(by.binding('list | json')); + * listInput.sendKeys('abc\ndef\nghi'); + * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]'); + * }); + * </file> + * </example> + * + * @element input + * @param {string=} ngList optional delimiter that should be used to split the value. + */ +var ngListDirective = function() { + return { + restrict: 'A', + priority: 100, + require: 'ngModel', + link: function(scope, element, attr, ctrl) { + // We want to control whitespace trimming so we use this convoluted approach + // to access the ngList attribute, which doesn't pre-trim the attribute + var ngList = element.attr(attr.$attr.ngList) || ', '; + var trimValues = attr.ngTrim !== 'false'; + var separator = trimValues ? trim(ngList) : ngList; + + var parse = function(viewValue) { + // If the viewValue is invalid (say required but empty) it will be `undefined` + if (isUndefined(viewValue)) return; + + var list = []; + + if (viewValue) { + forEach(viewValue.split(separator), function(value) { + if (value) list.push(trimValues ? trim(value) : value); + }); + } + + return list; + }; + + ctrl.$parsers.push(parse); + ctrl.$formatters.push(function(value) { + if (isArray(value)) { + return value.join(ngList); + } + + return undefined; + }); + + // Override the standard $isEmpty because an empty array means the input is empty. + ctrl.$isEmpty = function(value) { + return !value || !value.length; + }; + } + }; +}; + +/* global VALID_CLASS: true, + INVALID_CLASS: true, + PRISTINE_CLASS: true, + DIRTY_CLASS: true, + UNTOUCHED_CLASS: true, + TOUCHED_CLASS: true, +*/ + +var VALID_CLASS = 'ng-valid', + INVALID_CLASS = 'ng-invalid', + PRISTINE_CLASS = 'ng-pristine', + DIRTY_CLASS = 'ng-dirty', + UNTOUCHED_CLASS = 'ng-untouched', + TOUCHED_CLASS = 'ng-touched', + PENDING_CLASS = 'ng-pending'; + + +var $ngModelMinErr = new minErr('ngModel'); + +/** + * @ngdoc type + * @name ngModel.NgModelController + * + * @property {string} $viewValue Actual string value in the view. + * @property {*} $modelValue The value in the model that the control is bound to. + * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever + the control reads value from the DOM. The functions are called in array order, each passing + its return value through to the next. The last return value is forwarded to the + {@link ngModel.NgModelController#$validators `$validators`} collection. + +Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue +`$viewValue`}. + +Returning `undefined` from a parser means a parse error occurred. In that case, +no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel` +will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`} +is set to `true`. The parse error is stored in `ngModel.$error.parse`. + + * + * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever + the model value changes. The functions are called in reverse array order, each passing the value through to the + next. The last return value is used as the actual DOM value. + Used to format / convert values for display in the control. + * ```js + * function formatter(value) { + * if (value) { + * return value.toUpperCase(); + * } + * } + * ngModel.$formatters.push(formatter); + * ``` + * + * @property {Object.<string, function>} $validators A collection of validators that are applied + * whenever the model value changes. The key value within the object refers to the name of the + * validator while the function refers to the validation operation. The validation operation is + * provided with the model value as an argument and must return a true or false value depending + * on the response of that validation. + * + * ```js + * ngModel.$validators.validCharacters = function(modelValue, viewValue) { + * var value = modelValue || viewValue; + * return /[0-9]+/.test(value) && + * /[a-z]+/.test(value) && + * /[A-Z]+/.test(value) && + * /\W+/.test(value); + * }; + * ``` + * + * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to + * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided + * is expected to return a promise when it is run during the model validation process. Once the promise + * is delivered then the validation status will be set to true when fulfilled and false when rejected. + * When the asynchronous validators are triggered, each of the validators will run in parallel and the model + * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator + * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators + * will only run once all synchronous validators have passed. + * + * Please note that if $http is used then it is important that the server returns a success HTTP response code + * in order to fulfill the validation and a status level of `4xx` in order to reject the validation. + * + * ```js + * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) { + * var value = modelValue || viewValue; + * + * // Lookup user by username + * return $http.get('/api/users/' + value). + * then(function resolved() { + * //username exists, this means validation fails + * return $q.reject('exists'); + * }, function rejected() { + * //username does not exist, therefore this validation passes + * return true; + * }); + * }; + * ``` + * + * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the + * view value has changed. It is called with no arguments, and its return value is ignored. + * This can be used in place of additional $watches against the model value. + * + * @property {Object} $error An object hash with all failing validator ids as keys. + * @property {Object} $pending An object hash with all pending validator ids as keys. + * + * @property {boolean} $untouched True if control has not lost focus yet. + * @property {boolean} $touched True if control has lost focus. + * @property {boolean} $pristine True if user has not interacted with the control yet. + * @property {boolean} $dirty True if user has already interacted with the control. + * @property {boolean} $valid True if there is no error. + * @property {boolean} $invalid True if at least one error on the control. + * @property {string} $name The name attribute of the control. + * + * @description + * + * `NgModelController` provides API for the {@link ngModel `ngModel`} directive. + * The controller contains services for data-binding, validation, CSS updates, and value formatting + * and parsing. It purposefully does not contain any logic which deals with DOM rendering or + * listening to DOM events. + * Such DOM related logic should be provided by other directives which make use of + * `NgModelController` for data-binding to control elements. + * Angular provides this DOM logic for most {@link input `input`} elements. + * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example + * custom control example} that uses `ngModelController` to bind to `contenteditable` elements. + * + * @example + * ### Custom Control Example + * This example shows how to use `NgModelController` with a custom control to achieve + * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) + * collaborate together to achieve the desired result. + * + * `contenteditable` is an HTML5 attribute, which tells the browser to let the element + * contents be edited in place by the user. + * + * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} + * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`). + * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks + * that content using the `$sce` service. + * + * <example name="NgModelController" module="customControl" deps="angular-sanitize.js"> + <file name="style.css"> + [contenteditable] { + border: 1px solid black; + background-color: white; + min-height: 20px; + } + + .ng-invalid { + border: 1px solid red; + } + + </file> + <file name="script.js"> + angular.module('customControl', ['ngSanitize']). + directive('contenteditable', ['$sce', function($sce) { + return { + restrict: 'A', // only activate on element attribute + require: '?ngModel', // get a hold of NgModelController + link: function(scope, element, attrs, ngModel) { + if (!ngModel) return; // do nothing if no ng-model + + // Specify how UI should be updated + ngModel.$render = function() { + element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); + }; + + // Listen for change events to enable binding + element.on('blur keyup change', function() { + scope.$evalAsync(read); + }); + read(); // initialize + + // Write data to the model + function read() { + var html = element.html(); + // When we clear the content editable the browser leaves a <br> behind + // If strip-br attribute is provided then we strip this out + if ( attrs.stripBr && html == '<br>' ) { + html = ''; + } + ngModel.$setViewValue(html); + } + } + }; + }]); + </file> + <file name="index.html"> + <form name="myForm"> + <div contenteditable + name="myWidget" ng-model="userContent" + strip-br="true" + required>Change me!</div> + <span ng-show="myForm.myWidget.$error.required">Required!</span> + <hr> + <textarea ng-model="userContent"></textarea> + </form> + </file> + <file name="protractor.js" type="protractor"> + it('should data-bind and become invalid', function() { + if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') { + // SafariDriver can't handle contenteditable + // and Firefox driver can't clear contenteditables very well + return; + } + var contentEditable = element(by.css('[contenteditable]')); + var content = 'Change me!'; + + expect(contentEditable.getText()).toEqual(content); + + contentEditable.clear(); + contentEditable.sendKeys(protractor.Key.BACK_SPACE); + expect(contentEditable.getText()).toEqual(''); + expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); + }); + </file> + * </example> + * + * + */ +var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate', + function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) { + this.$viewValue = Number.NaN; + this.$modelValue = Number.NaN; + this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity. + this.$validators = {}; + this.$asyncValidators = {}; + this.$parsers = []; + this.$formatters = []; + this.$viewChangeListeners = []; + this.$untouched = true; + this.$touched = false; + this.$pristine = true; + this.$dirty = false; + this.$valid = true; + this.$invalid = false; + this.$error = {}; // keep invalid keys here + this.$$success = {}; // keep valid keys here + this.$pending = undefined; // keep pending keys here + this.$name = $interpolate($attr.name || '', false)($scope); + + + var parsedNgModel = $parse($attr.ngModel), + parsedNgModelAssign = parsedNgModel.assign, + ngModelGet = parsedNgModel, + ngModelSet = parsedNgModelAssign, + pendingDebounce = null, + parserValid, + ctrl = this; + + this.$$setOptions = function(options) { + ctrl.$options = options; + if (options && options.getterSetter) { + var invokeModelGetter = $parse($attr.ngModel + '()'), + invokeModelSetter = $parse($attr.ngModel + '($$$p)'); + + ngModelGet = function($scope) { + var modelValue = parsedNgModel($scope); + if (isFunction(modelValue)) { + modelValue = invokeModelGetter($scope); + } + return modelValue; + }; + ngModelSet = function($scope, newValue) { + if (isFunction(parsedNgModel($scope))) { + invokeModelSetter($scope, {$$$p: ctrl.$modelValue}); + } else { + parsedNgModelAssign($scope, ctrl.$modelValue); + } + }; + } else if (!parsedNgModel.assign) { + throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}", + $attr.ngModel, startingTag($element)); + } + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$render + * + * @description + * Called when the view needs to be updated. It is expected that the user of the ng-model + * directive will implement this method. + * + * The `$render()` method is invoked in the following situations: + * + * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last + * committed value then `$render()` is called to update the input control. + * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and + * the `$viewValue` are different to last time. + * + * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of + * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue` + * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be + * invoked if you only change a property on the objects. + */ + this.$render = noop; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$isEmpty + * + * @description + * This is called when we need to determine if the value of an input is empty. + * + * For instance, the required directive does this to work out if the input has data or not. + * + * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. + * + * You can override this for input directives whose concept of being empty is different to the + * default. The `checkboxInputType` directive does this because in its case a value of `false` + * implies empty. + * + * @param {*} value The value of the input to check for emptiness. + * @returns {boolean} True if `value` is "empty". + */ + this.$isEmpty = function(value) { + return isUndefined(value) || value === '' || value === null || value !== value; + }; + + var parentForm = $element.inheritedData('$formController') || nullFormCtrl, + currentValidationRunId = 0; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setValidity + * + * @description + * Change the validity state, and notify the form. + * + * This method can be called within $parsers/$formatters or a custom validation implementation. + * However, in most cases it should be sufficient to use the `ngModel.$validators` and + * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically. + * + * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned + * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` + * (for unfulfilled `$asyncValidators`), so that it is available for data-binding. + * The `validationErrorKey` should be in camelCase and will get converted into dash-case + * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` + * class and can be bound to as `{{someForm.someControl.$error.myError}}` . + * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined), + * or skipped (null). Pending is used for unfulfilled `$asyncValidators`. + * Skipped is used by Angular when validators do not run because of parse errors and + * when `$asyncValidators` do not run because any of the `$validators` failed. + */ + addSetValidityMethod({ + ctrl: this, + $element: $element, + set: function(object, property) { + object[property] = true; + }, + unset: function(object, property) { + delete object[property]; + }, + parentForm: parentForm, + $animate: $animate + }); + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setPristine + * + * @description + * Sets the control to its pristine state. + * + * This method can be called to remove the `ng-dirty` class and set the control to its pristine + * state (`ng-pristine` class). A model is considered to be pristine when the control + * has not been changed from when first compiled. + */ + this.$setPristine = function() { + ctrl.$dirty = false; + ctrl.$pristine = true; + $animate.removeClass($element, DIRTY_CLASS); + $animate.addClass($element, PRISTINE_CLASS); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setDirty + * + * @description + * Sets the control to its dirty state. + * + * This method can be called to remove the `ng-pristine` class and set the control to its dirty + * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed + * from when first compiled. + */ + this.$setDirty = function() { + ctrl.$dirty = true; + ctrl.$pristine = false; + $animate.removeClass($element, PRISTINE_CLASS); + $animate.addClass($element, DIRTY_CLASS); + parentForm.$setDirty(); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setUntouched + * + * @description + * Sets the control to its untouched state. + * + * This method can be called to remove the `ng-touched` class and set the control to its + * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched + * by default, however this function can be used to restore that state if the model has + * already been touched by the user. + */ + this.$setUntouched = function() { + ctrl.$touched = false; + ctrl.$untouched = true; + $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setTouched + * + * @description + * Sets the control to its touched state. + * + * This method can be called to remove the `ng-untouched` class and set the control to its + * touched state (`ng-touched` class). A model is considered to be touched when the user has + * first focused the control element and then shifted focus away from the control (blur event). + */ + this.$setTouched = function() { + ctrl.$touched = true; + ctrl.$untouched = false; + $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$rollbackViewValue + * + * @description + * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`, + * which may be caused by a pending debounced event or because the input is waiting for a some + * future event. + * + * If you have an input that uses `ng-model-options` to set up debounced events or events such + * as blur you can have a situation where there is a period when the `$viewValue` + * is out of synch with the ngModel's `$modelValue`. + * + * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue` + * programmatically before these debounced/future events have resolved/occurred, because Angular's + * dirty checking mechanism is not able to tell whether the model has actually changed or not. + * + * The `$rollbackViewValue()` method should be called before programmatically changing the model of an + * input which may have such events pending. This is important in order to make sure that the + * input field will be updated with the new model value and any pending operations are cancelled. + * + * <example name="ng-model-cancel-update" module="cancel-update-example"> + * <file name="app.js"> + * angular.module('cancel-update-example', []) + * + * .controller('CancelUpdateController', ['$scope', function($scope) { + * $scope.resetWithCancel = function(e) { + * if (e.keyCode == 27) { + * $scope.myForm.myInput1.$rollbackViewValue(); + * $scope.myValue = ''; + * } + * }; + * $scope.resetWithoutCancel = function(e) { + * if (e.keyCode == 27) { + * $scope.myValue = ''; + * } + * }; + * }]); + * </file> + * <file name="index.html"> + * <div ng-controller="CancelUpdateController"> + * <p>Try typing something in each input. See that the model only updates when you + * blur off the input. + * </p> + * <p>Now see what happens if you start typing then press the Escape key</p> + * + * <form name="myForm" ng-model-options="{ updateOn: 'blur' }"> + * <p>With $rollbackViewValue()</p> + * <input name="myInput1" ng-model="myValue" ng-keydown="resetWithCancel($event)"><br/> + * myValue: "{{ myValue }}" + * + * <p>Without $rollbackViewValue()</p> + * <input name="myInput2" ng-model="myValue" ng-keydown="resetWithoutCancel($event)"><br/> + * myValue: "{{ myValue }}" + * </form> + * </div> + * </file> + * </example> + */ + this.$rollbackViewValue = function() { + $timeout.cancel(pendingDebounce); + ctrl.$viewValue = ctrl.$$lastCommittedViewValue; + ctrl.$render(); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$validate + * + * @description + * Runs each of the registered validators (first synchronous validators and then + * asynchronous validators). + * If the validity changes to invalid, the model will be set to `undefined`, + * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`. + * If the validity changes to valid, it will set the model to the last available valid + * modelValue, i.e. either the last parsed value or the last value set from the scope. + */ + this.$validate = function() { + // ignore $validate before model is initialized + if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + return; + } + + var viewValue = ctrl.$$lastCommittedViewValue; + // Note: we use the $$rawModelValue as $modelValue might have been + // set to undefined during a view -> model update that found validation + // errors. We can't parse the view here, since that could change + // the model although neither viewValue nor the model on the scope changed + var modelValue = ctrl.$$rawModelValue; + + var prevValid = ctrl.$valid; + var prevModelValue = ctrl.$modelValue; + + var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; + + ctrl.$$runValidators(modelValue, viewValue, function(allValid) { + // If there was no change in validity, don't update the model + // This prevents changing an invalid modelValue to undefined + if (!allowInvalid && prevValid !== allValid) { + // Note: Don't check ctrl.$valid here, as we could have + // external validators (e.g. calculated on the server), + // that just call $setValidity and need the model value + // to calculate their validity. + ctrl.$modelValue = allValid ? modelValue : undefined; + + if (ctrl.$modelValue !== prevModelValue) { + ctrl.$$writeModelToScope(); + } + } + }); + + }; + + this.$$runValidators = function(modelValue, viewValue, doneCallback) { + currentValidationRunId++; + var localValidationRunId = currentValidationRunId; + + // check parser error + if (!processParseErrors()) { + validationDone(false); + return; + } + if (!processSyncValidators()) { + validationDone(false); + return; + } + processAsyncValidators(); + + function processParseErrors() { + var errorKey = ctrl.$$parserName || 'parse'; + if (parserValid === undefined) { + setValidity(errorKey, null); + } else { + if (!parserValid) { + forEach(ctrl.$validators, function(v, name) { + setValidity(name, null); + }); + forEach(ctrl.$asyncValidators, function(v, name) { + setValidity(name, null); + }); + } + // Set the parse error last, to prevent unsetting it, should a $validators key == parserName + setValidity(errorKey, parserValid); + return parserValid; + } + return true; + } + + function processSyncValidators() { + var syncValidatorsValid = true; + forEach(ctrl.$validators, function(validator, name) { + var result = validator(modelValue, viewValue); + syncValidatorsValid = syncValidatorsValid && result; + setValidity(name, result); + }); + if (!syncValidatorsValid) { + forEach(ctrl.$asyncValidators, function(v, name) { + setValidity(name, null); + }); + return false; + } + return true; + } + + function processAsyncValidators() { + var validatorPromises = []; + var allValid = true; + forEach(ctrl.$asyncValidators, function(validator, name) { + var promise = validator(modelValue, viewValue); + if (!isPromiseLike(promise)) { + throw $ngModelMinErr("$asyncValidators", + "Expected asynchronous validator to return a promise but got '{0}' instead.", promise); + } + setValidity(name, undefined); + validatorPromises.push(promise.then(function() { + setValidity(name, true); + }, function(error) { + allValid = false; + setValidity(name, false); + })); + }); + if (!validatorPromises.length) { + validationDone(true); + } else { + $q.all(validatorPromises).then(function() { + validationDone(allValid); + }, noop); + } + } + + function setValidity(name, isValid) { + if (localValidationRunId === currentValidationRunId) { + ctrl.$setValidity(name, isValid); + } + } + + function validationDone(allValid) { + if (localValidationRunId === currentValidationRunId) { + + doneCallback(allValid); + } + } + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$commitViewValue + * + * @description + * Commit a pending update to the `$modelValue`. + * + * Updates may be pending by a debounced event or because the input is waiting for a some future + * event defined in `ng-model-options`. this method is rarely needed as `NgModelController` + * usually handles calling this in response to input events. + */ + this.$commitViewValue = function() { + var viewValue = ctrl.$viewValue; + + $timeout.cancel(pendingDebounce); + + // If the view value has not changed then we should just exit, except in the case where there is + // a native validator on the element. In this case the validation state may have changed even though + // the viewValue has stayed empty. + if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) { + return; + } + ctrl.$$lastCommittedViewValue = viewValue; + + // change to dirty + if (ctrl.$pristine) { + this.$setDirty(); + } + this.$$parseAndValidate(); + }; + + this.$$parseAndValidate = function() { + var viewValue = ctrl.$$lastCommittedViewValue; + var modelValue = viewValue; + parserValid = isUndefined(modelValue) ? undefined : true; + + if (parserValid) { + for (var i = 0; i < ctrl.$parsers.length; i++) { + modelValue = ctrl.$parsers[i](modelValue); + if (isUndefined(modelValue)) { + parserValid = false; + break; + } + } + } + if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) { + // ctrl.$modelValue has not been touched yet... + ctrl.$modelValue = ngModelGet($scope); + } + var prevModelValue = ctrl.$modelValue; + var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid; + ctrl.$$rawModelValue = modelValue; + + if (allowInvalid) { + ctrl.$modelValue = modelValue; + writeToModelIfNeeded(); + } + + // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date. + // This can happen if e.g. $setViewValue is called from inside a parser + ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) { + if (!allowInvalid) { + // Note: Don't check ctrl.$valid here, as we could have + // external validators (e.g. calculated on the server), + // that just call $setValidity and need the model value + // to calculate their validity. + ctrl.$modelValue = allValid ? modelValue : undefined; + writeToModelIfNeeded(); + } + }); + + function writeToModelIfNeeded() { + if (ctrl.$modelValue !== prevModelValue) { + ctrl.$$writeModelToScope(); + } + } + }; + + this.$$writeModelToScope = function() { + ngModelSet($scope, ctrl.$modelValue); + forEach(ctrl.$viewChangeListeners, function(listener) { + try { + listener(); + } catch (e) { + $exceptionHandler(e); + } + }); + }; + + /** + * @ngdoc method + * @name ngModel.NgModelController#$setViewValue + * + * @description + * Update the view value. + * + * This method should be called when an input directive want to change the view value; typically, + * this is done from within a DOM event handler. + * + * For example {@link ng.directive:input input} calls it when the value of the input changes and + * {@link ng.directive:select select} calls it when an option is selected. + * + * If the new `value` is an object (rather than a string or a number), we should make a copy of the + * object before passing it to `$setViewValue`. This is because `ngModel` does not perform a deep + * watch of objects, it only looks for a change of identity. If you only change the property of + * the object then ngModel will not realise that the object has changed and will not invoke the + * `$parsers` and `$validators` pipelines. + * + * For this reason, you should not change properties of the copy once it has been passed to + * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly. + * + * When this method is called, the new `value` will be staged for committing through the `$parsers` + * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged + * value sent directly for processing, finally to be applied to `$modelValue` and then the + * **expression** specified in the `ng-model` attribute. + * + * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called. + * + * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn` + * and the `default` trigger is not listed, all those actions will remain pending until one of the + * `updateOn` events is triggered on the DOM element. + * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions} + * directive is used with a custom debounce for this particular event. + * + * Note that calling this function does not trigger a `$digest`. + * + * @param {string} value Value from the view. + * @param {string} trigger Event that triggered the update. + */ + this.$setViewValue = function(value, trigger) { + ctrl.$viewValue = value; + if (!ctrl.$options || ctrl.$options.updateOnDefault) { + ctrl.$$debounceViewValueCommit(trigger); + } + }; + + this.$$debounceViewValueCommit = function(trigger) { + var debounceDelay = 0, + options = ctrl.$options, + debounce; + + if (options && isDefined(options.debounce)) { + debounce = options.debounce; + if (isNumber(debounce)) { + debounceDelay = debounce; + } else if (isNumber(debounce[trigger])) { + debounceDelay = debounce[trigger]; + } else if (isNumber(debounce['default'])) { + debounceDelay = debounce['default']; + } + } + + $timeout.cancel(pendingDebounce); + if (debounceDelay) { + pendingDebounce = $timeout(function() { + ctrl.$commitViewValue(); + }, debounceDelay); + } else if ($rootScope.$$phase) { + ctrl.$commitViewValue(); + } else { + $scope.$apply(function() { + ctrl.$commitViewValue(); + }); + } + }; + + // model -> value + // Note: we cannot use a normal scope.$watch as we want to detect the following: + // 1. scope value is 'a' + // 2. user enters 'b' + // 3. ng-change kicks in and reverts scope value to 'a' + // -> scope value did not change since the last digest as + // ng-change executes in apply phase + // 4. view should be changed back to 'a' + $scope.$watch(function ngModelWatch() { + var modelValue = ngModelGet($scope); + + // if scope model value and ngModel value are out of sync + // TODO(perf): why not move this to the action fn? + if (modelValue !== ctrl.$modelValue) { + ctrl.$modelValue = ctrl.$$rawModelValue = modelValue; + parserValid = undefined; + + var formatters = ctrl.$formatters, + idx = formatters.length; + + var viewValue = modelValue; + while (idx--) { + viewValue = formatters[idx](viewValue); + } + if (ctrl.$viewValue !== viewValue) { + ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue; + ctrl.$render(); + + ctrl.$$runValidators(modelValue, viewValue, noop); + } + } + + return modelValue; + }); +}]; + + +/** + * @ngdoc directive + * @name ngModel + * + * @element input + * @priority 1 + * + * @description + * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a + * property on the scope using {@link ngModel.NgModelController NgModelController}, + * which is created and exposed by this directive. + * + * `ngModel` is responsible for: + * + * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` + * require. + * - Providing validation behavior (i.e. required, number, email, url). + * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors). + * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations. + * - Registering the control with its parent {@link ng.directive:form form}. + * + * Note: `ngModel` will try to bind to the property given by evaluating the expression on the + * current scope. If the property doesn't already exist on this scope, it will be created + * implicitly and added to the scope. + * + * For best practices on using `ngModel`, see: + * + * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) + * + * For basic examples, how to use `ngModel`, see: + * + * - {@link ng.directive:input input} + * - {@link input[text] text} + * - {@link input[checkbox] checkbox} + * - {@link input[radio] radio} + * - {@link input[number] number} + * - {@link input[email] email} + * - {@link input[url] url} + * - {@link input[date] date} + * - {@link input[datetime-local] datetime-local} + * - {@link input[time] time} + * - {@link input[month] month} + * - {@link input[week] week} + * - {@link ng.directive:select select} + * - {@link ng.directive:textarea textarea} + * + * # CSS classes + * The following CSS classes are added and removed on the associated input/select/textarea element + * depending on the validity of the model. + * + * - `ng-valid`: the model is valid + * - `ng-invalid`: the model is invalid + * - `ng-valid-[key]`: for each valid key added by `$setValidity` + * - `ng-invalid-[key]`: for each invalid key added by `$setValidity` + * - `ng-pristine`: the control hasn't been interacted with yet + * - `ng-dirty`: the control has been interacted with + * - `ng-touched`: the control has been blurred + * - `ng-untouched`: the control hasn't been blurred + * - `ng-pending`: any `$asyncValidators` are unfulfilled + * + * Keep in mind that ngAnimate can detect each of these classes when added and removed. + * + * ## Animation Hooks + * + * Animations within models are triggered when any of the associated CSS classes are added and removed + * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`, + * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. + * The animations that are triggered within ngModel are similar to how they work in ngClass and + * animations can be hooked into using CSS transitions, keyframes as well as JS animations. + * + * The following example shows a simple way to utilize CSS transitions to style an input element + * that has been rendered as invalid after it has been validated: + * + * <pre> + * //be sure to include ngAnimate as a module to hook into more + * //advanced animations + * .my-input { + * transition:0.5s linear all; + * background: white; + * } + * .my-input.ng-invalid { + * background: red; + * color:white; + * } + * </pre> + * + * @example + * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample"> + <file name="index.html"> + <script> + angular.module('inputExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.val = '1'; + }]); + </script> + <style> + .my-input { + -webkit-transition:all linear 0.5s; + transition:all linear 0.5s; + background: transparent; + } + .my-input.ng-invalid { + color:white; + background: red; + } + </style> + Update input to see transitions when valid/invalid. + Integer is a valid value. + <form name="testForm" ng-controller="ExampleController"> + <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" /> + </form> + </file> + * </example> + * + * ## Binding to a getter/setter + * + * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a + * function that returns a representation of the model when called with zero arguments, and sets + * the internal state of a model when called with an argument. It's sometimes useful to use this + * for models that have an internal representation that's different than what the model exposes + * to the view. + * + * <div class="alert alert-success"> + * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more + * frequently than other parts of your code. + * </div> + * + * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that + * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to + * a `<form>`, which will enable this behavior for all `<input>`s within it. See + * {@link ng.directive:ngModelOptions `ngModelOptions`} for more. + * + * The following example shows how to use `ngModel` with a getter/setter: + * + * @example + * <example name="ngModel-getter-setter" module="getterSetterExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <form name="userForm"> + Name: + <input type="text" name="userName" + ng-model="user.name" + ng-model-options="{ getterSetter: true }" /> + </form> + <pre>user.name = <span ng-bind="user.name()"></span></pre> + </div> + </file> + <file name="app.js"> + angular.module('getterSetterExample', []) + .controller('ExampleController', ['$scope', function($scope) { + var _name = 'Brian'; + $scope.user = { + name: function(newName) { + if (angular.isDefined(newName)) { + _name = newName; + } + return _name; + } + }; + }]); + </file> + * </example> + */ +var ngModelDirective = ['$rootScope', function($rootScope) { + return { + restrict: 'A', + require: ['ngModel', '^?form', '^?ngModelOptions'], + controller: NgModelController, + // Prelink needs to run before any input directive + // so that we can set the NgModelOptions in NgModelController + // before anyone else uses it. + priority: 1, + compile: function ngModelCompile(element) { + // Setup initial state of the control + element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS); + + return { + pre: function ngModelPreLink(scope, element, attr, ctrls) { + var modelCtrl = ctrls[0], + formCtrl = ctrls[1] || nullFormCtrl; + + modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options); + + // notify others, especially parent forms + formCtrl.$addControl(modelCtrl); + + attr.$observe('name', function(newValue) { + if (modelCtrl.$name !== newValue) { + formCtrl.$$renameControl(modelCtrl, newValue); + } + }); + + scope.$on('$destroy', function() { + formCtrl.$removeControl(modelCtrl); + }); + }, + post: function ngModelPostLink(scope, element, attr, ctrls) { + var modelCtrl = ctrls[0]; + if (modelCtrl.$options && modelCtrl.$options.updateOn) { + element.on(modelCtrl.$options.updateOn, function(ev) { + modelCtrl.$$debounceViewValueCommit(ev && ev.type); + }); + } + + element.on('blur', function(ev) { + if (modelCtrl.$touched) return; + + if ($rootScope.$$phase) { + scope.$evalAsync(modelCtrl.$setTouched); + } else { + scope.$apply(modelCtrl.$setTouched); + } + }); + } + }; + } + }; +}]; + +var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/; + +/** + * @ngdoc directive + * @name ngModelOptions + * + * @description + * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of + * events that will trigger a model update and/or a debouncing delay so that the actual update only + * takes place when a timer expires; this timer will be reset after another change takes place. + * + * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might + * be different than the value in the actual model. This means that if you update the model you + * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in + * order to make sure it is synchronized with the model and that any debounced action is canceled. + * + * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`} + * method is by making sure the input is placed inside a form that has a `name` attribute. This is + * important because `form` controllers are published to the related scope under the name in their + * `name` attribute. + * + * Any pending changes will take place immediately when an enclosing form is submitted via the + * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit` + * to have access to the updated model. + * + * `ngModelOptions` has an effect on the element it's declared on and its descendants. + * + * @param {Object} ngModelOptions options to apply to the current model. Valid keys are: + * - `updateOn`: string specifying which event should the input be bound to. You can set several + * events using an space delimited list. There is a special event called `default` that + * matches the default events belonging of the control. + * - `debounce`: integer value which contains the debounce model update value in milliseconds. A + * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a + * custom value for each event. For example: + * `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"` + * - `allowInvalid`: boolean value which indicates that the model can be set with values that did + * not validate correctly instead of the default behavior of setting the model to undefined. + * - `getterSetter`: boolean value which determines whether or not to treat functions bound to + `ngModel` as getters/setters. + * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for + * `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`, + * otherwise the default timezone of the browser will be used. + * + * @example + + The following example shows how to override immediate updates. Changes on the inputs within the + form will update the model only when the control loses focus (blur event). If `escape` key is + pressed while the input field is focused, the value is reset to the value in the current model. + + <example name="ngModelOptions-directive-blur" module="optionsExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <form name="userForm"> + Name: + <input type="text" name="userName" + ng-model="user.name" + ng-model-options="{ updateOn: 'blur' }" + ng-keyup="cancel($event)" /><br /> + + Other data: + <input type="text" ng-model="user.data" /><br /> + </form> + <pre>user.name = <span ng-bind="user.name"></span></pre> + </div> + </file> + <file name="app.js"> + angular.module('optionsExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.user = { name: 'say', data: '' }; + + $scope.cancel = function(e) { + if (e.keyCode == 27) { + $scope.userForm.userName.$rollbackViewValue(); + } + }; + }]); + </file> + <file name="protractor.js" type="protractor"> + var model = element(by.binding('user.name')); + var input = element(by.model('user.name')); + var other = element(by.model('user.data')); + + it('should allow custom events', function() { + input.sendKeys(' hello'); + input.click(); + expect(model.getText()).toEqual('say'); + other.click(); + expect(model.getText()).toEqual('say hello'); + }); + + it('should $rollbackViewValue when model changes', function() { + input.sendKeys(' hello'); + expect(input.getAttribute('value')).toEqual('say hello'); + input.sendKeys(protractor.Key.ESCAPE); + expect(input.getAttribute('value')).toEqual('say'); + other.click(); + expect(model.getText()).toEqual('say'); + }); + </file> + </example> + + This one shows how to debounce model changes. Model will be updated only 1 sec after last change. + If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty. + + <example name="ngModelOptions-directive-debounce" module="optionsExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <form name="userForm"> + Name: + <input type="text" name="userName" + ng-model="user.name" + ng-model-options="{ debounce: 1000 }" /> + <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br /> + </form> + <pre>user.name = <span ng-bind="user.name"></span></pre> + </div> + </file> + <file name="app.js"> + angular.module('optionsExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.user = { name: 'say' }; + }]); + </file> + </example> + + This one shows how to bind to getter/setters: + + <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <form name="userForm"> + Name: + <input type="text" name="userName" + ng-model="user.name" + ng-model-options="{ getterSetter: true }" /> + </form> + <pre>user.name = <span ng-bind="user.name()"></span></pre> + </div> + </file> + <file name="app.js"> + angular.module('getterSetterExample', []) + .controller('ExampleController', ['$scope', function($scope) { + var _name = 'Brian'; + $scope.user = { + name: function(newName) { + return angular.isDefined(newName) ? (_name = newName) : _name; + } + }; + }]); + </file> + </example> + */ +var ngModelOptionsDirective = function() { + return { + restrict: 'A', + controller: ['$scope', '$attrs', function($scope, $attrs) { + var that = this; + this.$options = $scope.$eval($attrs.ngModelOptions); + // Allow adding/overriding bound events + if (this.$options.updateOn !== undefined) { + this.$options.updateOnDefault = false; + // extract "default" pseudo-event from list of events that can trigger a model update + this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() { + that.$options.updateOnDefault = true; + return ' '; + })); + } else { + this.$options.updateOnDefault = true; + } + }] + }; +}; + + + +// helper methods +function addSetValidityMethod(context) { + var ctrl = context.ctrl, + $element = context.$element, + classCache = {}, + set = context.set, + unset = context.unset, + parentForm = context.parentForm, + $animate = context.$animate; + + classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS)); + + ctrl.$setValidity = setValidity; + + function setValidity(validationErrorKey, state, controller) { + if (state === undefined) { + createAndSet('$pending', validationErrorKey, controller); + } else { + unsetAndCleanup('$pending', validationErrorKey, controller); + } + if (!isBoolean(state)) { + unset(ctrl.$error, validationErrorKey, controller); + unset(ctrl.$$success, validationErrorKey, controller); + } else { + if (state) { + unset(ctrl.$error, validationErrorKey, controller); + set(ctrl.$$success, validationErrorKey, controller); + } else { + set(ctrl.$error, validationErrorKey, controller); + unset(ctrl.$$success, validationErrorKey, controller); + } + } + if (ctrl.$pending) { + cachedToggleClass(PENDING_CLASS, true); + ctrl.$valid = ctrl.$invalid = undefined; + toggleValidationCss('', null); + } else { + cachedToggleClass(PENDING_CLASS, false); + ctrl.$valid = isObjectEmpty(ctrl.$error); + ctrl.$invalid = !ctrl.$valid; + toggleValidationCss('', ctrl.$valid); + } + + // re-read the state as the set/unset methods could have + // combined state in ctrl.$error[validationError] (used for forms), + // where setting/unsetting only increments/decrements the value, + // and does not replace it. + var combinedState; + if (ctrl.$pending && ctrl.$pending[validationErrorKey]) { + combinedState = undefined; + } else if (ctrl.$error[validationErrorKey]) { + combinedState = false; + } else if (ctrl.$$success[validationErrorKey]) { + combinedState = true; + } else { + combinedState = null; + } + + toggleValidationCss(validationErrorKey, combinedState); + parentForm.$setValidity(validationErrorKey, combinedState, ctrl); + } + + function createAndSet(name, value, controller) { + if (!ctrl[name]) { + ctrl[name] = {}; + } + set(ctrl[name], value, controller); + } + + function unsetAndCleanup(name, value, controller) { + if (ctrl[name]) { + unset(ctrl[name], value, controller); + } + if (isObjectEmpty(ctrl[name])) { + ctrl[name] = undefined; + } + } + + function cachedToggleClass(className, switchValue) { + if (switchValue && !classCache[className]) { + $animate.addClass($element, className); + classCache[className] = true; + } else if (!switchValue && classCache[className]) { + $animate.removeClass($element, className); + classCache[className] = false; + } + } + + function toggleValidationCss(validationErrorKey, isValid) { + validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; + + cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true); + cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false); + } +} + +function isObjectEmpty(obj) { + if (obj) { + for (var prop in obj) { + return false; + } + } + return true; +} + +/** + * @ngdoc directive + * @name ngNonBindable + * @restrict AC + * @priority 1000 + * + * @description + * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current + * DOM element. This is useful if the element contains what appears to be Angular directives and + * bindings but which should be ignored by Angular. This could be the case if you have a site that + * displays snippets of code, for instance. + * + * @element ANY + * + * @example + * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, + * but the one wrapped in `ngNonBindable` is left alone. + * + * @example + <example> + <file name="index.html"> + <div>Normal: {{1 + 2}}</div> + <div ng-non-bindable>Ignored: {{1 + 2}}</div> + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-non-bindable', function() { + expect(element(by.binding('1 + 2')).getText()).toContain('3'); + expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); + }); + </file> + </example> + */ +var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); + +/** + * @ngdoc directive + * @name ngPluralize + * @restrict EA + * + * @description + * `ngPluralize` is a directive that displays messages according to en-US localization rules. + * These rules are bundled with angular.js, but can be overridden + * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive + * by specifying the mappings between + * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) + * and the strings to be displayed. + * + * # Plural categories and explicit number rules + * There are two + * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) + * in Angular's default en-US locale: "one" and "other". + * + * While a plural category may match many numbers (for example, in en-US locale, "other" can match + * any number that is not 1), an explicit number rule can only match one number. For example, the + * explicit number rule for "3" matches the number 3. There are examples of plural categories + * and explicit number rules throughout the rest of this documentation. + * + * # Configuring ngPluralize + * You configure ngPluralize by providing 2 attributes: `count` and `when`. + * You can also provide an optional attribute, `offset`. + * + * The value of the `count` attribute can be either a string or an {@link guide/expression + * Angular expression}; these are evaluated on the current scope for its bound value. + * + * The `when` attribute specifies the mappings between plural categories and the actual + * string to be displayed. The value of the attribute should be a JSON object. + * + * The following example shows how to configure ngPluralize: + * + * ```html + * <ng-pluralize count="personCount" + when="{'0': 'Nobody is viewing.', + * 'one': '1 person is viewing.', + * 'other': '{} people are viewing.'}"> + * </ng-pluralize> + *``` + * + * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not + * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" + * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for + * other numbers, for example 12, so that instead of showing "12 people are viewing", you can + * show "a dozen people are viewing". + * + * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted + * into pluralized strings. In the previous example, Angular will replace `{}` with + * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder + * for <span ng-non-bindable>{{numberExpression}}</span>. + * + * # Configuring ngPluralize with offset + * The `offset` attribute allows further customization of pluralized text, which can result in + * a better user experience. For example, instead of the message "4 people are viewing this document", + * you might display "John, Kate and 2 others are viewing this document". + * The offset attribute allows you to offset a number by any desired value. + * Let's take a look at an example: + * + * ```html + * <ng-pluralize count="personCount" offset=2 + * when="{'0': 'Nobody is viewing.', + * '1': '{{person1}} is viewing.', + * '2': '{{person1}} and {{person2}} are viewing.', + * 'one': '{{person1}}, {{person2}} and one other person are viewing.', + * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> + * </ng-pluralize> + * ``` + * + * Notice that we are still using two plural categories(one, other), but we added + * three explicit number rules 0, 1 and 2. + * When one person, perhaps John, views the document, "John is viewing" will be shown. + * When three people view the document, no explicit number rule is found, so + * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. + * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" + * is shown. + * + * Note that when you specify offsets, you must provide explicit number rules for + * numbers from 0 up to and including the offset. If you use an offset of 3, for example, + * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for + * plural categories "one" and "other". + * + * @param {string|expression} count The variable to be bound to. + * @param {string} when The mapping between plural category to its corresponding strings. + * @param {number=} offset Offset to deduct from the total number. + * + * @example + <example module="pluralizeExample"> + <file name="index.html"> + <script> + angular.module('pluralizeExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.person1 = 'Igor'; + $scope.person2 = 'Misko'; + $scope.personCount = 1; + }]); + </script> + <div ng-controller="ExampleController"> + Person 1:<input type="text" ng-model="person1" value="Igor" /><br/> + Person 2:<input type="text" ng-model="person2" value="Misko" /><br/> + Number of People:<input type="text" ng-model="personCount" value="1" /><br/> + + <!--- Example with simple pluralization rules for en locale ---> + Without Offset: + <ng-pluralize count="personCount" + when="{'0': 'Nobody is viewing.', + 'one': '1 person is viewing.', + 'other': '{} people are viewing.'}"> + </ng-pluralize><br> + + <!--- Example with offset ---> + With Offset(2): + <ng-pluralize count="personCount" offset=2 + when="{'0': 'Nobody is viewing.', + '1': '{{person1}} is viewing.', + '2': '{{person1}} and {{person2}} are viewing.', + 'one': '{{person1}}, {{person2}} and one other person are viewing.', + 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> + </ng-pluralize> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should show correct pluralized string', function() { + var withoutOffset = element.all(by.css('ng-pluralize')).get(0); + var withOffset = element.all(by.css('ng-pluralize')).get(1); + var countInput = element(by.model('personCount')); + + expect(withoutOffset.getText()).toEqual('1 person is viewing.'); + expect(withOffset.getText()).toEqual('Igor is viewing.'); + + countInput.clear(); + countInput.sendKeys('0'); + + expect(withoutOffset.getText()).toEqual('Nobody is viewing.'); + expect(withOffset.getText()).toEqual('Nobody is viewing.'); + + countInput.clear(); + countInput.sendKeys('2'); + + expect(withoutOffset.getText()).toEqual('2 people are viewing.'); + expect(withOffset.getText()).toEqual('Igor and Misko are viewing.'); + + countInput.clear(); + countInput.sendKeys('3'); + + expect(withoutOffset.getText()).toEqual('3 people are viewing.'); + expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.'); + + countInput.clear(); + countInput.sendKeys('4'); + + expect(withoutOffset.getText()).toEqual('4 people are viewing.'); + expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.'); + }); + it('should show data-bound names', function() { + var withOffset = element.all(by.css('ng-pluralize')).get(1); + var personCount = element(by.model('personCount')); + var person1 = element(by.model('person1')); + var person2 = element(by.model('person2')); + personCount.clear(); + personCount.sendKeys('4'); + person1.clear(); + person1.sendKeys('Di'); + person2.clear(); + person2.sendKeys('Vojta'); + expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.'); + }); + </file> + </example> + */ +var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { + var BRACE = /{}/g, + IS_WHEN = /^when(Minus)?(.+)$/; + + return { + restrict: 'EA', + link: function(scope, element, attr) { + var numberExp = attr.count, + whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs + offset = attr.offset || 0, + whens = scope.$eval(whenExp) || {}, + whensExpFns = {}, + startSymbol = $interpolate.startSymbol(), + endSymbol = $interpolate.endSymbol(), + braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol, + watchRemover = angular.noop, + lastCount; + + forEach(attr, function(expression, attributeName) { + var tmpMatch = IS_WHEN.exec(attributeName); + if (tmpMatch) { + var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]); + whens[whenKey] = element.attr(attr.$attr[attributeName]); + } + }); + forEach(whens, function(expression, key) { + whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement)); + + }); + + scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) { + var count = parseFloat(newVal); + var countIsNaN = isNaN(count); + + if (!countIsNaN && !(count in whens)) { + // If an explicit number rule such as 1, 2, 3... is defined, just use it. + // Otherwise, check it against pluralization rules in $locale service. + count = $locale.pluralCat(count - offset); + } + + // If both `count` and `lastCount` are NaN, we don't need to re-register a watch. + // In JS `NaN !== NaN`, so we have to exlicitly check. + if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) { + watchRemover(); + watchRemover = scope.$watch(whensExpFns[count], updateElementText); + lastCount = count; + } + }); + + function updateElementText(newText) { + element.text(newText || ''); + } + } + }; +}]; + +/** + * @ngdoc directive + * @name ngRepeat + * + * @description + * The `ngRepeat` directive instantiates a template once per item from a collection. Each template + * instance gets its own scope, where the given loop variable is set to the current collection item, + * and `$index` is set to the item index or key. + * + * Special properties are exposed on the local scope of each template instance, including: + * + * | Variable | Type | Details | + * |-----------|-----------------|-----------------------------------------------------------------------------| + * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) | + * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. | + * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. | + * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | + * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | + * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | + * + * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. + * This may be useful when, for instance, nesting ngRepeats. + * + * # Iterating over object properties + * + * It is possible to get `ngRepeat` to iterate over the properties of an object using the following + * syntax: + * + * ```js + * <div ng-repeat="(key, value) in myObj"> ... </div> + * ``` + * + * You need to be aware that the JavaScript specification does not define what order + * it will return the keys for an object. In order to have a guaranteed deterministic order + * for the keys, Angular versions up to and including 1.3 **sort the keys alphabetically**. + * + * If this is not desired, the recommended workaround is to convert your object into an array + * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could + * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter) + * or implement a `$watch` on the object yourself. + * + * In version 1.4 we will remove the sorting, since it seems that browsers generally follow the + * strategy of providing keys in the order in which they were defined, although there are exceptions + * when keys are deleted and reinstated. + * + * + * # Tracking and Duplicates + * + * When the contents of the collection change, `ngRepeat` makes the corresponding changes to the DOM: + * + * * When an item is added, a new instance of the template is added to the DOM. + * * When an item is removed, its template instance is removed from the DOM. + * * When items are reordered, their respective templates are reordered in the DOM. + * + * By default, `ngRepeat` does not allow duplicate items in arrays. This is because when + * there are duplicates, it is not possible to maintain a one-to-one mapping between collection + * items and DOM elements. + * + * If you do need to repeat duplicate items, you can substitute the default tracking behavior + * with your own using the `track by` expression. + * + * For example, you may track items by the index of each item in the collection, using the + * special scope property `$index`: + * ```html + * <div ng-repeat="n in [42, 42, 43, 43] track by $index"> + * {{n}} + * </div> + * ``` + * + * You may use arbitrary expressions in `track by`, including references to custom functions + * on the scope: + * ```html + * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)"> + * {{n}} + * </div> + * ``` + * + * If you are working with objects that have an identifier property, you can track + * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat` + * will not have to rebuild the DOM elements for items it has already rendered, even if the + * JavaScript objects in the collection have been substituted for new ones: + * ```html + * <div ng-repeat="model in collection track by model.id"> + * {{model.name}} + * </div> + * ``` + * + * When no `track by` expression is provided, it is equivalent to tracking by the built-in + * `$id` function, which tracks items by their identity: + * ```html + * <div ng-repeat="obj in collection track by $id(obj)"> + * {{obj.prop}} + * </div> + * ``` + * + * # Special repeat start and end points + * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending + * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively. + * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on) + * up to and including the ending HTML tag where **ng-repeat-end** is placed. + * + * The example below makes use of this feature: + * ```html + * <header ng-repeat-start="item in items"> + * Header {{ item }} + * </header> + * <div class="body"> + * Body {{ item }} + * </div> + * <footer ng-repeat-end> + * Footer {{ item }} + * </footer> + * ``` + * + * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to: + * ```html + * <header> + * Header A + * </header> + * <div class="body"> + * Body A + * </div> + * <footer> + * Footer A + * </footer> + * <header> + * Header B + * </header> + * <div class="body"> + * Body B + * </div> + * <footer> + * Footer B + * </footer> + * ``` + * + * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such + * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**). + * + * @animations + * **.enter** - when a new item is added to the list or when an item is revealed after a filter + * + * **.leave** - when an item is removed from the list or when an item is filtered out + * + * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered + * + * @element ANY + * @scope + * @priority 1000 + * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These + * formats are currently supported: + * + * * `variable in expression` – where variable is the user defined loop variable and `expression` + * is a scope expression giving the collection to enumerate. + * + * For example: `album in artist.albums`. + * + * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, + * and `expression` is the scope expression giving the collection to enumerate. + * + * For example: `(name, age) in {'adam':10, 'amalie':12}`. + * + * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression + * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression + * is specified, ng-repeat associates elements by identity. It is an error to have + * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are + * mapped to the same DOM element, which is not possible.) If filters are used in the expression, they should be + * applied before the tracking expression. + * + * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements + * will be associated by item identity in the array. + * + * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique + * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements + * with the corresponding item in the array by identity. Moving the same object in array would move the DOM + * element in the same way in the DOM. + * + * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this + * case the object identity does not matter. Two objects are considered equivalent as long as their `id` + * property is same. + * + * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter + * to items in conjunction with a tracking expression. + * + * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the + * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message + * when a filter is active on the repeater, but the filtered result set is empty. + * + * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after + * the items have been processed through the filter. + * + * @example + * This example initializes the scope to a list of names and + * then uses `ngRepeat` to display every person: + <example module="ngAnimate" deps="angular-animate.js" animations="true"> + <file name="index.html"> + <div ng-init="friends = [ + {name:'John', age:25, gender:'boy'}, + {name:'Jessie', age:30, gender:'girl'}, + {name:'Johanna', age:28, gender:'girl'}, + {name:'Joy', age:15, gender:'girl'}, + {name:'Mary', age:28, gender:'girl'}, + {name:'Peter', age:95, gender:'boy'}, + {name:'Sebastian', age:50, gender:'boy'}, + {name:'Erika', age:27, gender:'girl'}, + {name:'Patrick', age:40, gender:'boy'}, + {name:'Samantha', age:60, gender:'girl'} + ]"> + I have {{friends.length}} friends. They are: + <input type="search" ng-model="q" placeholder="filter friends..." /> + <ul class="example-animate-container"> + <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results"> + [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. + </li> + <li class="animate-repeat" ng-if="results.length == 0"> + <strong>No results found...</strong> + </li> + </ul> + </div> + </file> + <file name="animations.css"> + .example-animate-container { + background:white; + border:1px solid black; + list-style:none; + margin:0; + padding:0 10px; + } + + .animate-repeat { + line-height:40px; + list-style:none; + box-sizing:border-box; + } + + .animate-repeat.ng-move, + .animate-repeat.ng-enter, + .animate-repeat.ng-leave { + -webkit-transition:all linear 0.5s; + transition:all linear 0.5s; + } + + .animate-repeat.ng-leave.ng-leave-active, + .animate-repeat.ng-move, + .animate-repeat.ng-enter { + opacity:0; + max-height:0; + } + + .animate-repeat.ng-leave, + .animate-repeat.ng-move.ng-move-active, + .animate-repeat.ng-enter.ng-enter-active { + opacity:1; + max-height:40px; + } + </file> + <file name="protractor.js" type="protractor"> + var friends = element.all(by.repeater('friend in friends')); + + it('should render initial data set', function() { + expect(friends.count()).toBe(10); + expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.'); + expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.'); + expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.'); + expect(element(by.binding('friends.length')).getText()) + .toMatch("I have 10 friends. They are:"); + }); + + it('should update repeater when filter predicate changes', function() { + expect(friends.count()).toBe(10); + + element(by.model('q')).sendKeys('ma'); + + expect(friends.count()).toBe(2); + expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.'); + expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.'); + }); + </file> + </example> + */ +var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { + var NG_REMOVED = '$$NG_REMOVED'; + var ngRepeatMinErr = minErr('ngRepeat'); + + var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) { + // TODO(perf): generate setters to shave off ~40ms or 1-1.5% + scope[valueIdentifier] = value; + if (keyIdentifier) scope[keyIdentifier] = key; + scope.$index = index; + scope.$first = (index === 0); + scope.$last = (index === (arrayLength - 1)); + scope.$middle = !(scope.$first || scope.$last); + // jshint bitwise: false + scope.$odd = !(scope.$even = (index&1) === 0); + // jshint bitwise: true + }; + + var getBlockStart = function(block) { + return block.clone[0]; + }; + + var getBlockEnd = function(block) { + return block.clone[block.clone.length - 1]; + }; + + + return { + restrict: 'A', + multiElement: true, + transclude: 'element', + priority: 1000, + terminal: true, + $$tlb: true, + compile: function ngRepeatCompile($element, $attr) { + var expression = $attr.ngRepeat; + var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' '); + + var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/); + + if (!match) { + throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", + expression); + } + + var lhs = match[1]; + var rhs = match[2]; + var aliasAs = match[3]; + var trackByExp = match[4]; + + match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/); + + if (!match) { + throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", + lhs); + } + var valueIdentifier = match[3] || match[1]; + var keyIdentifier = match[2]; + + if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) || + /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) { + throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.", + aliasAs); + } + + var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn; + var hashFnLocals = {$id: hashKey}; + + if (trackByExp) { + trackByExpGetter = $parse(trackByExp); + } else { + trackByIdArrayFn = function(key, value) { + return hashKey(value); + }; + trackByIdObjFn = function(key) { + return key; + }; + } + + return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) { + + if (trackByExpGetter) { + trackByIdExpFn = function(key, value, index) { + // assign key, value, and $index to the locals so that they can be used in hash functions + if (keyIdentifier) hashFnLocals[keyIdentifier] = key; + hashFnLocals[valueIdentifier] = value; + hashFnLocals.$index = index; + return trackByExpGetter($scope, hashFnLocals); + }; + } + + // Store a list of elements from previous run. This is a hash where key is the item from the + // iterator, and the value is objects with following properties. + // - scope: bound scope + // - element: previous element. + // - index: position + // + // We are using no-proto object so that we don't need to guard against inherited props via + // hasOwnProperty. + var lastBlockMap = createMap(); + + //watch props + $scope.$watchCollection(rhs, function ngRepeatAction(collection) { + var index, length, + previousNode = $element[0], // node that cloned nodes should be inserted after + // initialized to the comment node anchor + nextNode, + // Same as lastBlockMap but it has the current state. It will become the + // lastBlockMap on the next iteration. + nextBlockMap = createMap(), + collectionLength, + key, value, // key/value of iteration + trackById, + trackByIdFn, + collectionKeys, + block, // last object information {scope, element, id} + nextBlockOrder, + elementsToRemove; + + if (aliasAs) { + $scope[aliasAs] = collection; + } + + if (isArrayLike(collection)) { + collectionKeys = collection; + trackByIdFn = trackByIdExpFn || trackByIdArrayFn; + } else { + trackByIdFn = trackByIdExpFn || trackByIdObjFn; + // if object, extract keys, sort them and use to determine order of iteration over obj props + collectionKeys = []; + for (var itemKey in collection) { + if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') { + collectionKeys.push(itemKey); + } + } + collectionKeys.sort(); + } + + collectionLength = collectionKeys.length; + nextBlockOrder = new Array(collectionLength); + + // locate existing items + for (index = 0; index < collectionLength; index++) { + key = (collection === collectionKeys) ? index : collectionKeys[index]; + value = collection[key]; + trackById = trackByIdFn(key, value, index); + if (lastBlockMap[trackById]) { + // found previously seen block + block = lastBlockMap[trackById]; + delete lastBlockMap[trackById]; + nextBlockMap[trackById] = block; + nextBlockOrder[index] = block; + } else if (nextBlockMap[trackById]) { + // if collision detected. restore lastBlockMap and throw an error + forEach(nextBlockOrder, function(block) { + if (block && block.scope) lastBlockMap[block.id] = block; + }); + throw ngRepeatMinErr('dupes', + "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", + expression, trackById, value); + } else { + // new never before seen block + nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined}; + nextBlockMap[trackById] = true; + } + } + + // remove leftover items + for (var blockKey in lastBlockMap) { + block = lastBlockMap[blockKey]; + elementsToRemove = getBlockNodes(block.clone); + $animate.leave(elementsToRemove); + if (elementsToRemove[0].parentNode) { + // if the element was not removed yet because of pending animation, mark it as deleted + // so that we can ignore it later + for (index = 0, length = elementsToRemove.length; index < length; index++) { + elementsToRemove[index][NG_REMOVED] = true; + } + } + block.scope.$destroy(); + } + + // we are not using forEach for perf reasons (trying to avoid #call) + for (index = 0; index < collectionLength; index++) { + key = (collection === collectionKeys) ? index : collectionKeys[index]; + value = collection[key]; + block = nextBlockOrder[index]; + + if (block.scope) { + // if we have already seen this object, then we need to reuse the + // associated scope/element + + nextNode = previousNode; + + // skip nodes that are already pending removal via leave animation + do { + nextNode = nextNode.nextSibling; + } while (nextNode && nextNode[NG_REMOVED]); + + if (getBlockStart(block) != nextNode) { + // existing item which got moved + $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode)); + } + previousNode = getBlockEnd(block); + updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); + } else { + // new item which we don't know about + $transclude(function ngRepeatTransclude(clone, scope) { + block.scope = scope; + // http://jsperf.com/clone-vs-createcomment + var endNode = ngRepeatEndComment.cloneNode(false); + clone[clone.length++] = endNode; + + // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper? + $animate.enter(clone, null, jqLite(previousNode)); + previousNode = endNode; + // Note: We only need the first/last node of the cloned nodes. + // However, we need to keep the reference to the jqlite wrapper as it might be changed later + // by a directive with templateUrl when its template arrives. + block.clone = clone; + nextBlockMap[block.id] = block; + updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); + }); + } + } + lastBlockMap = nextBlockMap; + }); + }; + } + }; +}]; + +var NG_HIDE_CLASS = 'ng-hide'; +var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate'; +/** + * @ngdoc directive + * @name ngShow + * + * @description + * The `ngShow` directive shows or hides the given HTML element based on the expression + * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding + * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined + * in AngularJS and sets the display style to none (using an !important flag). + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * + * ```html + * <!-- when $scope.myValue is truthy (element is visible) --> + * <div ng-show="myValue"></div> + * + * <!-- when $scope.myValue is falsy (element is hidden) --> + * <div ng-show="myValue" class="ng-hide"></div> + * ``` + * + * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class + * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed + * from the element causing the element not to appear hidden. + * + * ## Why is !important used? + * + * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector + * can be easily overridden by heavier selectors. For example, something as simple + * as changing the display style on a HTML list item would make hidden elements appear visible. + * This also becomes a bigger issue when dealing with CSS frameworks. + * + * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector + * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the + * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. + * + * ### Overriding `.ng-hide` + * + * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change + * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` + * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope + * with extra animation classes that can be added. + * + * ```css + * .ng-hide:not(.ng-hide-animate) { + * /* this is just another form of hiding an element */ + * display: block!important; + * position: absolute; + * top: -9999px; + * left: -9999px; + * } + * ``` + * + * By default you don't need to override in CSS anything and the animations will work around the display style. + * + * ## A note about animations with `ngShow` + * + * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression + * is true and false. This system works like the animation system present with ngClass except that + * you must also include the !important flag to override the display property + * so that you can perform an animation when the element is hidden during the time of the animation. + * + * ```css + * // + * //a working example can be found at the bottom of this page + * // + * .my-element.ng-hide-add, .my-element.ng-hide-remove { + * /* this is required as of 1.3x to properly + * apply all styling in a show/hide animation */ + * transition: 0s linear all; + * } + * + * .my-element.ng-hide-add-active, + * .my-element.ng-hide-remove-active { + * /* the transition is defined in the active class */ + * transition: 1s linear all; + * } + * + * .my-element.ng-hide-add { ... } + * .my-element.ng-hide-add.ng-hide-add-active { ... } + * .my-element.ng-hide-remove { ... } + * .my-element.ng-hide-remove.ng-hide-remove-active { ... } + * ``` + * + * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display + * property to block during animation states--ngAnimate will handle the style toggling automatically for you. + * + * @animations + * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible + * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden + * + * @element ANY + * @param {expression} ngShow If the {@link guide/expression expression} is truthy + * then the element is shown or hidden respectively. + * + * @example + <example module="ngAnimate" deps="angular-animate.js" animations="true"> + <file name="index.html"> + Click me: <input type="checkbox" ng-model="checked"><br/> + <div> + Show: + <div class="check-element animate-show" ng-show="checked"> + <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. + </div> + </div> + <div> + Hide: + <div class="check-element animate-show" ng-hide="checked"> + <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. + </div> + </div> + </file> + <file name="glyphicons.css"> + @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); + </file> + <file name="animations.css"> + .animate-show { + line-height: 20px; + opacity: 1; + padding: 10px; + border: 1px solid black; + background: white; + } + + .animate-show.ng-hide-add.ng-hide-add-active, + .animate-show.ng-hide-remove.ng-hide-remove-active { + -webkit-transition: all linear 0.5s; + transition: all linear 0.5s; + } + + .animate-show.ng-hide { + line-height: 0; + opacity: 0; + padding: 0 10px; + } + + .check-element { + padding: 10px; + border: 1px solid black; + background: white; + } + </file> + <file name="protractor.js" type="protractor"> + var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); + var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); + + it('should check ng-show / ng-hide', function() { + expect(thumbsUp.isDisplayed()).toBeFalsy(); + expect(thumbsDown.isDisplayed()).toBeTruthy(); + + element(by.model('checked')).click(); + + expect(thumbsUp.isDisplayed()).toBeTruthy(); + expect(thumbsDown.isDisplayed()).toBeFalsy(); + }); + </file> + </example> + */ +var ngShowDirective = ['$animate', function($animate) { + return { + restrict: 'A', + multiElement: true, + link: function(scope, element, attr) { + scope.$watch(attr.ngShow, function ngShowWatchAction(value) { + // we're adding a temporary, animation-specific class for ng-hide since this way + // we can control when the element is actually displayed on screen without having + // to have a global/greedy CSS selector that breaks when other animations are run. + // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845 + $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, { + tempClasses: NG_HIDE_IN_PROGRESS_CLASS + }); + }); + } + }; +}]; + + +/** + * @ngdoc directive + * @name ngHide + * + * @description + * The `ngHide` directive shows or hides the given HTML element based on the expression + * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding + * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined + * in AngularJS and sets the display style to none (using an !important flag). + * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). + * + * ```html + * <!-- when $scope.myValue is truthy (element is hidden) --> + * <div ng-hide="myValue" class="ng-hide"></div> + * + * <!-- when $scope.myValue is falsy (element is visible) --> + * <div ng-hide="myValue"></div> + * ``` + * + * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class + * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed + * from the element causing the element not to appear hidden. + * + * ## Why is !important used? + * + * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector + * can be easily overridden by heavier selectors. For example, something as simple + * as changing the display style on a HTML list item would make hidden elements appear visible. + * This also becomes a bigger issue when dealing with CSS frameworks. + * + * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector + * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the + * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. + * + * ### Overriding `.ng-hide` + * + * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change + * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` + * class in CSS: + * + * ```css + * .ng-hide { + * /* this is just another form of hiding an element */ + * display: block!important; + * position: absolute; + * top: -9999px; + * left: -9999px; + * } + * ``` + * + * By default you don't need to override in CSS anything and the animations will work around the display style. + * + * ## A note about animations with `ngHide` + * + * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression + * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide` + * CSS class is added and removed for you instead of your own CSS class. + * + * ```css + * // + * //a working example can be found at the bottom of this page + * // + * .my-element.ng-hide-add, .my-element.ng-hide-remove { + * transition: 0.5s linear all; + * } + * + * .my-element.ng-hide-add { ... } + * .my-element.ng-hide-add.ng-hide-add-active { ... } + * .my-element.ng-hide-remove { ... } + * .my-element.ng-hide-remove.ng-hide-remove-active { ... } + * ``` + * + * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display + * property to block during animation states--ngAnimate will handle the style toggling automatically for you. + * + * @animations + * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden + * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible + * + * @element ANY + * @param {expression} ngHide If the {@link guide/expression expression} is truthy then + * the element is shown or hidden respectively. + * + * @example + <example module="ngAnimate" deps="angular-animate.js" animations="true"> + <file name="index.html"> + Click me: <input type="checkbox" ng-model="checked"><br/> + <div> + Show: + <div class="check-element animate-hide" ng-show="checked"> + <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked. + </div> + </div> + <div> + Hide: + <div class="check-element animate-hide" ng-hide="checked"> + <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked. + </div> + </div> + </file> + <file name="glyphicons.css"> + @import url(../../components/bootstrap-3.1.1/css/bootstrap.css); + </file> + <file name="animations.css"> + .animate-hide { + -webkit-transition: all linear 0.5s; + transition: all linear 0.5s; + line-height: 20px; + opacity: 1; + padding: 10px; + border: 1px solid black; + background: white; + } + + .animate-hide.ng-hide { + line-height: 0; + opacity: 0; + padding: 0 10px; + } + + .check-element { + padding: 10px; + border: 1px solid black; + background: white; + } + </file> + <file name="protractor.js" type="protractor"> + var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); + var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); + + it('should check ng-show / ng-hide', function() { + expect(thumbsUp.isDisplayed()).toBeFalsy(); + expect(thumbsDown.isDisplayed()).toBeTruthy(); + + element(by.model('checked')).click(); + + expect(thumbsUp.isDisplayed()).toBeTruthy(); + expect(thumbsDown.isDisplayed()).toBeFalsy(); + }); + </file> + </example> + */ +var ngHideDirective = ['$animate', function($animate) { + return { + restrict: 'A', + multiElement: true, + link: function(scope, element, attr) { + scope.$watch(attr.ngHide, function ngHideWatchAction(value) { + // The comment inside of the ngShowDirective explains why we add and + // remove a temporary class for the show/hide animation + $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, { + tempClasses: NG_HIDE_IN_PROGRESS_CLASS + }); + }); + } + }; +}]; + +/** + * @ngdoc directive + * @name ngStyle + * @restrict AC + * + * @description + * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. + * + * @element ANY + * @param {expression} ngStyle + * + * {@link guide/expression Expression} which evals to an + * object whose keys are CSS style names and values are corresponding values for those CSS + * keys. + * + * Since some CSS style names are not valid keys for an object, they must be quoted. + * See the 'background-color' style in the example below. + * + * @example + <example> + <file name="index.html"> + <input type="button" value="set color" ng-click="myStyle={color:'red'}"> + <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}"> + <input type="button" value="clear" ng-click="myStyle={}"> + <br/> + <span ng-style="myStyle">Sample Text</span> + <pre>myStyle={{myStyle}}</pre> + </file> + <file name="style.css"> + span { + color: black; + } + </file> + <file name="protractor.js" type="protractor"> + var colorSpan = element(by.css('span')); + + it('should check ng-style', function() { + expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); + element(by.css('input[value=\'set color\']')).click(); + expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)'); + element(by.css('input[value=clear]')).click(); + expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); + }); + </file> + </example> + */ +var ngStyleDirective = ngDirective(function(scope, element, attr) { + scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { + if (oldStyles && (newStyles !== oldStyles)) { + forEach(oldStyles, function(val, style) { element.css(style, '');}); + } + if (newStyles) element.css(newStyles); + }); +}); + +/** + * @ngdoc directive + * @name ngSwitch + * @restrict EA + * + * @description + * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression. + * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location + * as specified in the template. + * + * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it + * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element + * matches the value obtained from the evaluated expression. In other words, you define a container element + * (where you place the directive), place an expression on the **`on="..."` attribute** + * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place + * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on + * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default + * attribute is displayed. + * + * <div class="alert alert-info"> + * Be aware that the attribute values to match against cannot be expressions. They are interpreted + * as literal string values to match against. + * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the + * value of the expression `$scope.someVal`. + * </div> + + * @animations + * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container + * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM + * + * @usage + * + * ``` + * <ANY ng-switch="expression"> + * <ANY ng-switch-when="matchValue1">...</ANY> + * <ANY ng-switch-when="matchValue2">...</ANY> + * <ANY ng-switch-default>...</ANY> + * </ANY> + * ``` + * + * + * @scope + * @priority 1200 + * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>. + * On child elements add: + * + * * `ngSwitchWhen`: the case statement to match against. If match then this + * case will be displayed. If the same match appears multiple times, all the + * elements will be displayed. + * * `ngSwitchDefault`: the default case when no other case match. If there + * are multiple default cases, all of them will be displayed when no other + * case match. + * + * + * @example + <example module="switchExample" deps="angular-animate.js" animations="true"> + <file name="index.html"> + <div ng-controller="ExampleController"> + <select ng-model="selection" ng-options="item for item in items"> + </select> + <tt>selection={{selection}}</tt> + <hr/> + <div class="animate-switch-container" + ng-switch on="selection"> + <div class="animate-switch" ng-switch-when="settings">Settings Div</div> + <div class="animate-switch" ng-switch-when="home">Home Span</div> + <div class="animate-switch" ng-switch-default>default</div> + </div> + </div> + </file> + <file name="script.js"> + angular.module('switchExample', ['ngAnimate']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.items = ['settings', 'home', 'other']; + $scope.selection = $scope.items[0]; + }]); + </file> + <file name="animations.css"> + .animate-switch-container { + position:relative; + background:white; + border:1px solid black; + height:40px; + overflow:hidden; + } + + .animate-switch { + padding:10px; + } + + .animate-switch.ng-animate { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + + position:absolute; + top:0; + left:0; + right:0; + bottom:0; + } + + .animate-switch.ng-leave.ng-leave-active, + .animate-switch.ng-enter { + top:-50px; + } + .animate-switch.ng-leave, + .animate-switch.ng-enter.ng-enter-active { + top:0; + } + </file> + <file name="protractor.js" type="protractor"> + var switchElem = element(by.css('[ng-switch]')); + var select = element(by.model('selection')); + + it('should start in settings', function() { + expect(switchElem.getText()).toMatch(/Settings Div/); + }); + it('should change to home', function() { + select.all(by.css('option')).get(1).click(); + expect(switchElem.getText()).toMatch(/Home Span/); + }); + it('should select default', function() { + select.all(by.css('option')).get(2).click(); + expect(switchElem.getText()).toMatch(/default/); + }); + </file> + </example> + */ +var ngSwitchDirective = ['$animate', function($animate) { + return { + restrict: 'EA', + require: 'ngSwitch', + + // asks for $scope to fool the BC controller module + controller: ['$scope', function ngSwitchController() { + this.cases = {}; + }], + link: function(scope, element, attr, ngSwitchController) { + var watchExpr = attr.ngSwitch || attr.on, + selectedTranscludes = [], + selectedElements = [], + previousLeaveAnimations = [], + selectedScopes = []; + + var spliceFactory = function(array, index) { + return function() { array.splice(index, 1); }; + }; + + scope.$watch(watchExpr, function ngSwitchWatchAction(value) { + var i, ii; + for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) { + $animate.cancel(previousLeaveAnimations[i]); + } + previousLeaveAnimations.length = 0; + + for (i = 0, ii = selectedScopes.length; i < ii; ++i) { + var selected = getBlockNodes(selectedElements[i].clone); + selectedScopes[i].$destroy(); + var promise = previousLeaveAnimations[i] = $animate.leave(selected); + promise.then(spliceFactory(previousLeaveAnimations, i)); + } + + selectedElements.length = 0; + selectedScopes.length = 0; + + if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { + forEach(selectedTranscludes, function(selectedTransclude) { + selectedTransclude.transclude(function(caseElement, selectedScope) { + selectedScopes.push(selectedScope); + var anchor = selectedTransclude.element; + caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: '); + var block = { clone: caseElement }; + + selectedElements.push(block); + $animate.enter(caseElement, anchor.parent(), anchor); + }); + }); + } + }); + } + }; +}]; + +var ngSwitchWhenDirective = ngDirective({ + transclude: 'element', + priority: 1200, + require: '^ngSwitch', + multiElement: true, + link: function(scope, element, attrs, ctrl, $transclude) { + ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []); + ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element }); + } +}); + +var ngSwitchDefaultDirective = ngDirective({ + transclude: 'element', + priority: 1200, + require: '^ngSwitch', + multiElement: true, + link: function(scope, element, attr, ctrl, $transclude) { + ctrl.cases['?'] = (ctrl.cases['?'] || []); + ctrl.cases['?'].push({ transclude: $transclude, element: element }); + } +}); + +/** + * @ngdoc directive + * @name ngTransclude + * @restrict EAC + * + * @description + * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion. + * + * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted. + * + * @element ANY + * + * @example + <example module="transcludeExample"> + <file name="index.html"> + <script> + angular.module('transcludeExample', []) + .directive('pane', function(){ + return { + restrict: 'E', + transclude: true, + scope: { title:'@' }, + template: '<div style="border: 1px solid black;">' + + '<div style="background-color: gray">{{title}}</div>' + + '<ng-transclude></ng-transclude>' + + '</div>' + }; + }) + .controller('ExampleController', ['$scope', function($scope) { + $scope.title = 'Lorem Ipsum'; + $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; + }]); + </script> + <div ng-controller="ExampleController"> + <input ng-model="title"> <br/> + <textarea ng-model="text"></textarea> <br/> + <pane title="{{title}}">{{text}}</pane> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should have transcluded', function() { + var titleElement = element(by.model('title')); + titleElement.clear(); + titleElement.sendKeys('TITLE'); + var textElement = element(by.model('text')); + textElement.clear(); + textElement.sendKeys('TEXT'); + expect(element(by.binding('title')).getText()).toEqual('TITLE'); + expect(element(by.binding('text')).getText()).toEqual('TEXT'); + }); + </file> + </example> + * + */ +var ngTranscludeDirective = ngDirective({ + restrict: 'EAC', + link: function($scope, $element, $attrs, controller, $transclude) { + if (!$transclude) { + throw minErr('ngTransclude')('orphan', + 'Illegal use of ngTransclude directive in the template! ' + + 'No parent directive that requires a transclusion found. ' + + 'Element: {0}', + startingTag($element)); + } + + $transclude(function(clone) { + $element.empty(); + $element.append(clone); + }); + } +}); + +/** + * @ngdoc directive + * @name script + * @restrict E + * + * @description + * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the + * template can be used by {@link ng.directive:ngInclude `ngInclude`}, + * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the + * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be + * assigned through the element's `id`, which can then be used as a directive's `templateUrl`. + * + * @param {string} type Must be set to `'text/ng-template'`. + * @param {string} id Cache name of the template. + * + * @example + <example> + <file name="index.html"> + <script type="text/ng-template" id="/tpl.html"> + Content of the template. + </script> + + <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a> + <div id="tpl-content" ng-include src="currentTpl"></div> + </file> + <file name="protractor.js" type="protractor"> + it('should load template defined inside script tag', function() { + element(by.css('#tpl-link')).click(); + expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/); + }); + </file> + </example> + */ +var scriptDirective = ['$templateCache', function($templateCache) { + return { + restrict: 'E', + terminal: true, + compile: function(element, attr) { + if (attr.type == 'text/ng-template') { + var templateUrl = attr.id, + text = element[0].text; + + $templateCache.put(templateUrl, text); + } + } + }; +}]; + +var ngOptionsMinErr = minErr('ngOptions'); +/** + * @ngdoc directive + * @name select + * @restrict E + * + * @description + * HTML `SELECT` element with angular data-binding. + * + * # `ngOptions` + * + * The `ngOptions` attribute can be used to dynamically generate a list of `<option>` + * elements for the `<select>` element using the array or object obtained by evaluating the + * `ngOptions` comprehension expression. + * + * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a + * similar result. However, `ngOptions` provides some benefits such as reducing memory and + * increasing speed by not creating a new scope for each repeated instance, as well as providing + * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the + * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound + * to a non-string value. This is because an option element can only be bound to string values at + * present. + * + * When an item in the `<select>` menu is selected, the array element or object property + * represented by the selected option will be bound to the model identified by the `ngModel` + * directive. + * + * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can + * be nested into the `<select>` element. This element will then represent the `null` or "not selected" + * option. See example below for demonstration. + * + * <div class="alert alert-warning"> + * **Note:** `ngModel` compares by reference, not value. This is important when binding to an + * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/). + * </div> + * + * ## `select` **`as`** + * + * Using `select` **`as`** will bind the result of the `select` expression to the model, but + * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources) + * or property name (for object data sources) of the value within the collection. If a **`track by`** expression + * is used, the result of that expression will be set as the value of the `option` and `select` elements. + * + * + * ### `select` **`as`** and **`track by`** + * + * <div class="alert alert-warning"> + * Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together. + * </div> + * + * Consider the following example: + * + * ```html + * <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"> + * ``` + * + * ```js + * $scope.values = [{ + * id: 1, + * label: 'aLabel', + * subItem: { name: 'aSubItem' } + * }, { + * id: 2, + * label: 'bLabel', + * subItem: { name: 'bSubItem' } + * }]; + * + * $scope.selected = { name: 'aSubItem' }; + * ``` + * + * With the purpose of preserving the selection, the **`track by`** expression is always applied to the element + * of the data source (to `item` in this example). To calculate whether an element is selected, we do the + * following: + * + * 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]` + * 2. Apply **`track by`** to the already selected value in `ngModel`. + * In the example: this is not possible as **`track by`** refers to `item.id`, but the selected + * value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to + * a wrong object, the selected element can't be found, `<select>` is always reset to the "not + * selected" option. + * + * + * @param {string} ngModel Assignable angular expression to data-bind to. + * @param {string=} name Property name of the form under which the control is published. + * @param {string=} required The control is considered valid only if value is entered. + * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to + * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of + * `required` when you want to data-bind to the `required` attribute. + * @param {comprehension_expression=} ngOptions in one of the following forms: + * + * * for array data sources: + * * `label` **`for`** `value` **`in`** `array` + * * `select` **`as`** `label` **`for`** `value` **`in`** `array` + * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` + * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr` + * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr` + * (for including a filter with `track by`) + * * for object data sources: + * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` + * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` + * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` + * * `select` **`as`** `label` **`group by`** `group` + * **`for` `(`**`key`**`,`** `value`**`) in`** `object` + * + * Where: + * + * * `array` / `object`: an expression which evaluates to an array / object to iterate over. + * * `value`: local variable which will refer to each item in the `array` or each property value + * of `object` during iteration. + * * `key`: local variable which will refer to a property name in `object` during iteration. + * * `label`: The result of this expression will be the label for `<option>` element. The + * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). + * * `select`: The result of this expression will be bound to the model of the parent `<select>` + * element. If not specified, `select` expression will default to `value`. + * * `group`: The result of this expression will be used to group options using the `<optgroup>` + * DOM element. + * * `trackexpr`: Used when working with an array of objects. The result of this expression will be + * used to identify the objects in the array. The `trackexpr` will most likely refer to the + * `value` variable (e.g. `value.propertyName`). With this the selection is preserved + * even when the options are recreated (e.g. reloaded from the server). + * + * @example + <example module="selectExample"> + <file name="index.html"> + <script> + angular.module('selectExample', []) + .controller('ExampleController', ['$scope', function($scope) { + $scope.colors = [ + {name:'black', shade:'dark'}, + {name:'white', shade:'light'}, + {name:'red', shade:'dark'}, + {name:'blue', shade:'dark'}, + {name:'yellow', shade:'light'} + ]; + $scope.myColor = $scope.colors[2]; // red + }]); + </script> + <div ng-controller="ExampleController"> + <ul> + <li ng-repeat="color in colors"> + Name: <input ng-model="color.name"> + [<a href ng-click="colors.splice($index, 1)">X</a>] + </li> + <li> + [<a href ng-click="colors.push({})">add</a>] + </li> + </ul> + <hr/> + Color (null not allowed): + <select ng-model="myColor" ng-options="color.name for color in colors"></select><br> + + Color (null allowed): + <span class="nullable"> + <select ng-model="myColor" ng-options="color.name for color in colors"> + <option value="">-- choose color --</option> + </select> + </span><br/> + + Color grouped by shade: + <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors"> + </select><br/> + + + Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br> + <hr/> + Currently selected: {{ {selected_color:myColor} }} + <div style="border:solid 1px black; height:20px" + ng-style="{'background-color':myColor.name}"> + </div> + </div> + </file> + <file name="protractor.js" type="protractor"> + it('should check ng-options', function() { + expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red'); + element.all(by.model('myColor')).first().click(); + element.all(by.css('select[ng-model="myColor"] option')).first().click(); + expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black'); + element(by.css('.nullable select[ng-model="myColor"]')).click(); + element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click(); + expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null'); + }); + </file> + </example> + */ + +var ngOptionsDirective = valueFn({ + restrict: 'A', + terminal: true +}); + +// jshint maxlen: false +var selectDirective = ['$compile', '$parse', function($compile, $parse) { + //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888 + var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/, + nullModelCtrl = {$setViewValue: noop}; +// jshint maxlen: 100 + + return { + restrict: 'E', + require: ['select', '?ngModel'], + controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { + var self = this, + optionsMap = {}, + ngModelCtrl = nullModelCtrl, + nullOption, + unknownOption; + + + self.databound = $attrs.ngModel; + + + self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { + ngModelCtrl = ngModelCtrl_; + nullOption = nullOption_; + unknownOption = unknownOption_; + }; + + + self.addOption = function(value, element) { + assertNotHasOwnProperty(value, '"option value"'); + optionsMap[value] = true; + + if (ngModelCtrl.$viewValue == value) { + $element.val(value); + if (unknownOption.parent()) unknownOption.remove(); + } + // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459 + // Adding an <option selected="selected"> element to a <select required="required"> should + // automatically select the new element + if (element && element[0].hasAttribute('selected')) { + element[0].selected = true; + } + }; + + + self.removeOption = function(value) { + if (this.hasOption(value)) { + delete optionsMap[value]; + if (ngModelCtrl.$viewValue === value) { + this.renderUnknownOption(value); + } + } + }; + + + self.renderUnknownOption = function(val) { + var unknownVal = '? ' + hashKey(val) + ' ?'; + unknownOption.val(unknownVal); + $element.prepend(unknownOption); + $element.val(unknownVal); + unknownOption.prop('selected', true); // needed for IE + }; + + + self.hasOption = function(value) { + return optionsMap.hasOwnProperty(value); + }; + + $scope.$on('$destroy', function() { + // disable unknown option so that we don't do work when the whole select is being destroyed + self.renderUnknownOption = noop; + }); + }], + + link: function(scope, element, attr, ctrls) { + // if ngModel is not defined, we don't need to do anything + if (!ctrls[1]) return; + + var selectCtrl = ctrls[0], + ngModelCtrl = ctrls[1], + multiple = attr.multiple, + optionsExp = attr.ngOptions, + nullOption = false, // if false, user will not be able to select it (used by ngOptions) + emptyOption, + renderScheduled = false, + // we can't just jqLite('<option>') since jqLite is not smart enough + // to create it in <select> and IE barfs otherwise. + optionTemplate = jqLite(document.createElement('option')), + optGroupTemplate =jqLite(document.createElement('optgroup')), + unknownOption = optionTemplate.clone(); + + // find "null" option + for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) { + if (children[i].value === '') { + emptyOption = nullOption = children.eq(i); + break; + } + } + + selectCtrl.init(ngModelCtrl, nullOption, unknownOption); + + // required validator + if (multiple) { + ngModelCtrl.$isEmpty = function(value) { + return !value || value.length === 0; + }; + } + + if (optionsExp) setupAsOptions(scope, element, ngModelCtrl); + else if (multiple) setupAsMultiple(scope, element, ngModelCtrl); + else setupAsSingle(scope, element, ngModelCtrl, selectCtrl); + + + //////////////////////////// + + + + function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) { + ngModelCtrl.$render = function() { + var viewValue = ngModelCtrl.$viewValue; + + if (selectCtrl.hasOption(viewValue)) { + if (unknownOption.parent()) unknownOption.remove(); + selectElement.val(viewValue); + if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy + } else { + if (isUndefined(viewValue) && emptyOption) { + selectElement.val(''); + } else { + selectCtrl.renderUnknownOption(viewValue); + } + } + }; + + selectElement.on('change', function() { + scope.$apply(function() { + if (unknownOption.parent()) unknownOption.remove(); + ngModelCtrl.$setViewValue(selectElement.val()); + }); + }); + } + + function setupAsMultiple(scope, selectElement, ctrl) { + var lastView; + ctrl.$render = function() { + var items = new HashMap(ctrl.$viewValue); + forEach(selectElement.find('option'), function(option) { + option.selected = isDefined(items.get(option.value)); + }); + }; + + // we have to do it on each watch since ngModel watches reference, but + // we need to work of an array, so we need to see if anything was inserted/removed + scope.$watch(function selectMultipleWatch() { + if (!equals(lastView, ctrl.$viewValue)) { + lastView = shallowCopy(ctrl.$viewValue); + ctrl.$render(); + } + }); + + selectElement.on('change', function() { + scope.$apply(function() { + var array = []; + forEach(selectElement.find('option'), function(option) { + if (option.selected) { + array.push(option.value); + } + }); + ctrl.$setViewValue(array); + }); + }); + } + + function setupAsOptions(scope, selectElement, ctrl) { + var match; + + if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) { + throw ngOptionsMinErr('iexp', + "Expected expression in form of " + + "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + + " but got '{0}'. Element: {1}", + optionsExp, startingTag(selectElement)); + } + + var displayFn = $parse(match[2] || match[1]), + valueName = match[4] || match[6], + selectAs = / as /.test(match[0]) && match[1], + selectAsFn = selectAs ? $parse(selectAs) : null, + keyName = match[5], + groupByFn = $parse(match[3] || ''), + valueFn = $parse(match[2] ? match[1] : valueName), + valuesFn = $parse(match[7]), + track = match[8], + trackFn = track ? $parse(match[8]) : null, + trackKeysCache = {}, + // This is an array of array of existing option groups in DOM. + // We try to reuse these if possible + // - optionGroupsCache[0] is the options with no option group + // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element + optionGroupsCache = [[{element: selectElement, label:''}]], + //re-usable object to represent option's locals + locals = {}; + + if (nullOption) { + // compile the element since there might be bindings in it + $compile(nullOption)(scope); + + // remove the class, which is added automatically because we recompile the element and it + // becomes the compilation root + nullOption.removeClass('ng-scope'); + + // we need to remove it before calling selectElement.empty() because otherwise IE will + // remove the label from the element. wtf? + nullOption.remove(); + } + + // clear contents, we'll add what's needed based on the model + selectElement.empty(); + + selectElement.on('change', selectionChanged); + + ctrl.$render = render; + + scope.$watchCollection(valuesFn, scheduleRendering); + scope.$watchCollection(getLabels, scheduleRendering); + + if (multiple) { + scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering); + } + + // ------------------------------------------------------------------ // + + function callExpression(exprFn, key, value) { + locals[valueName] = value; + if (keyName) locals[keyName] = key; + return exprFn(scope, locals); + } + + function selectionChanged() { + scope.$apply(function() { + var collection = valuesFn(scope) || []; + var viewValue; + if (multiple) { + viewValue = []; + forEach(selectElement.val(), function(selectedKey) { + selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey; + viewValue.push(getViewValue(selectedKey, collection[selectedKey])); + }); + } else { + var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val(); + viewValue = getViewValue(selectedKey, collection[selectedKey]); + } + ctrl.$setViewValue(viewValue); + render(); + }); + } + + function getViewValue(key, value) { + if (key === '?') { + return undefined; + } else if (key === '') { + return null; + } else { + var viewValueFn = selectAsFn ? selectAsFn : valueFn; + return callExpression(viewValueFn, key, value); + } + } + + function getLabels() { + var values = valuesFn(scope); + var toDisplay; + if (values && isArray(values)) { + toDisplay = new Array(values.length); + for (var i = 0, ii = values.length; i < ii; i++) { + toDisplay[i] = callExpression(displayFn, i, values[i]); + } + return toDisplay; + } else if (values) { + // TODO: Add a test for this case + toDisplay = {}; + for (var prop in values) { + if (values.hasOwnProperty(prop)) { + toDisplay[prop] = callExpression(displayFn, prop, values[prop]); + } + } + } + return toDisplay; + } + + function createIsSelectedFn(viewValue) { + var selectedSet; + if (multiple) { + if (trackFn && isArray(viewValue)) { + + selectedSet = new HashMap([]); + for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) { + // tracking by key + selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true); + } + } else { + selectedSet = new HashMap(viewValue); + } + } else if (trackFn) { + viewValue = callExpression(trackFn, null, viewValue); + } + + return function isSelected(key, value) { + var compareValueFn; + if (trackFn) { + compareValueFn = trackFn; + } else if (selectAsFn) { + compareValueFn = selectAsFn; + } else { + compareValueFn = valueFn; + } + + if (multiple) { + return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value))); + } else { + return viewValue === callExpression(compareValueFn, key, value); + } + }; + } + + function scheduleRendering() { + if (!renderScheduled) { + scope.$$postDigest(render); + renderScheduled = true; + } + } + + /** + * A new labelMap is created with each render. + * This function is called for each existing option with added=false, + * and each new option with added=true. + * - Labels that are passed to this method twice, + * (once with added=true and once with added=false) will end up with a value of 0, and + * will cause no change to happen to the corresponding option. + * - Labels that are passed to this method only once with added=false will end up with a + * value of -1 and will eventually be passed to selectCtrl.removeOption() + * - Labels that are passed to this method only once with added=true will end up with a + * value of 1 and will eventually be passed to selectCtrl.addOption() + */ + function updateLabelMap(labelMap, label, added) { + labelMap[label] = labelMap[label] || 0; + labelMap[label] += (added ? 1 : -1); + } + + function render() { + renderScheduled = false; + + // Temporary location for the option groups before we render them + var optionGroups = {'':[]}, + optionGroupNames = [''], + optionGroupName, + optionGroup, + option, + existingParent, existingOptions, existingOption, + viewValue = ctrl.$viewValue, + values = valuesFn(scope) || [], + keys = keyName ? sortedKeys(values) : values, + key, + value, + groupLength, length, + groupIndex, index, + labelMap = {}, + selected, + isSelected = createIsSelectedFn(viewValue), + anySelected = false, + lastElement, + element, + label, + optionId; + + trackKeysCache = {}; + + // We now build up the list of options we need (we merge later) + for (index = 0; length = keys.length, index < length; index++) { + key = index; + if (keyName) { + key = keys[index]; + if (key.charAt(0) === '$') continue; + } + value = values[key]; + + optionGroupName = callExpression(groupByFn, key, value) || ''; + if (!(optionGroup = optionGroups[optionGroupName])) { + optionGroup = optionGroups[optionGroupName] = []; + optionGroupNames.push(optionGroupName); + } + + selected = isSelected(key, value); + anySelected = anySelected || selected; + + label = callExpression(displayFn, key, value); // what will be seen by the user + + // doing displayFn(scope, locals) || '' overwrites zero values + label = isDefined(label) ? label : ''; + optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index); + if (trackFn) { + trackKeysCache[optionId] = key; + } + + optionGroup.push({ + // either the index into array or key from object + id: optionId, + label: label, + selected: selected // determine if we should be selected + }); + } + if (!multiple) { + if (nullOption || viewValue === null) { + // insert null option if we have a placeholder, or the model is null + optionGroups[''].unshift({id:'', label:'', selected:!anySelected}); + } else if (!anySelected) { + // option could not be found, we have to insert the undefined item + optionGroups[''].unshift({id:'?', label:'', selected:true}); + } + } + + // Now we need to update the list of DOM nodes to match the optionGroups we computed above + for (groupIndex = 0, groupLength = optionGroupNames.length; + groupIndex < groupLength; + groupIndex++) { + // current option group name or '' if no group + optionGroupName = optionGroupNames[groupIndex]; + + // list of options for that group. (first item has the parent) + optionGroup = optionGroups[optionGroupName]; + + if (optionGroupsCache.length <= groupIndex) { + // we need to grow the optionGroups + existingParent = { + element: optGroupTemplate.clone().attr('label', optionGroupName), + label: optionGroup.label + }; + existingOptions = [existingParent]; + optionGroupsCache.push(existingOptions); + selectElement.append(existingParent.element); + } else { + existingOptions = optionGroupsCache[groupIndex]; + existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element + + // update the OPTGROUP label if not the same. + if (existingParent.label != optionGroupName) { + existingParent.element.attr('label', existingParent.label = optionGroupName); + } + } + + lastElement = null; // start at the beginning + for (index = 0, length = optionGroup.length; index < length; index++) { + option = optionGroup[index]; + if ((existingOption = existingOptions[index + 1])) { + // reuse elements + lastElement = existingOption.element; + if (existingOption.label !== option.label) { + updateLabelMap(labelMap, existingOption.label, false); + updateLabelMap(labelMap, option.label, true); + lastElement.text(existingOption.label = option.label); + lastElement.prop('label', existingOption.label); + } + if (existingOption.id !== option.id) { + lastElement.val(existingOption.id = option.id); + } + // lastElement.prop('selected') provided by jQuery has side-effects + if (lastElement[0].selected !== option.selected) { + lastElement.prop('selected', (existingOption.selected = option.selected)); + if (msie) { + // See #7692 + // The selected item wouldn't visually update on IE without this. + // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well + lastElement.prop('selected', existingOption.selected); + } + } + } else { + // grow elements + + // if it's a null option + if (option.id === '' && nullOption) { + // put back the pre-compiled element + element = nullOption; + } else { + // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but + // in this version of jQuery on some browser the .text() returns a string + // rather then the element. + (element = optionTemplate.clone()) + .val(option.id) + .prop('selected', option.selected) + .attr('selected', option.selected) + .prop('label', option.label) + .text(option.label); + } + + existingOptions.push(existingOption = { + element: element, + label: option.label, + id: option.id, + selected: option.selected + }); + updateLabelMap(labelMap, option.label, true); + if (lastElement) { + lastElement.after(element); + } else { + existingParent.element.append(element); + } + lastElement = element; + } + } + // remove any excessive OPTIONs in a group + index++; // increment since the existingOptions[0] is parent element not OPTION + while (existingOptions.length > index) { + option = existingOptions.pop(); + updateLabelMap(labelMap, option.label, false); + option.element.remove(); + } + } + // remove any excessive OPTGROUPs from select + while (optionGroupsCache.length > groupIndex) { + // remove all the labels in the option group + optionGroup = optionGroupsCache.pop(); + for (index = 1; index < optionGroup.length; ++index) { + updateLabelMap(labelMap, optionGroup[index].label, false); + } + optionGroup[0].element.remove(); + } + forEach(labelMap, function(count, label) { + if (count > 0) { + selectCtrl.addOption(label); + } else if (count < 0) { + selectCtrl.removeOption(label); + } + }); + } + } + } + }; +}]; + +var optionDirective = ['$interpolate', function($interpolate) { + var nullSelectCtrl = { + addOption: noop, + removeOption: noop + }; + + return { + restrict: 'E', + priority: 100, + compile: function(element, attr) { + if (isUndefined(attr.value)) { + var interpolateFn = $interpolate(element.text(), true); + if (!interpolateFn) { + attr.$set('value', element.text()); + } + } + + return function(scope, element, attr) { + var selectCtrlName = '$selectController', + parent = element.parent(), + selectCtrl = parent.data(selectCtrlName) || + parent.parent().data(selectCtrlName); // in case we are in optgroup + + if (!selectCtrl || !selectCtrl.databound) { + selectCtrl = nullSelectCtrl; + } + + if (interpolateFn) { + scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) { + attr.$set('value', newVal); + if (oldVal !== newVal) { + selectCtrl.removeOption(oldVal); + } + selectCtrl.addOption(newVal, element); + }); + } else { + selectCtrl.addOption(attr.value, element); + } + + element.on('$destroy', function() { + selectCtrl.removeOption(attr.value); + }); + }; + } + }; +}]; + +var styleDirective = valueFn({ + restrict: 'E', + terminal: false +}); + +var requiredDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + attr.required = true; // force truthy in case we are on non input element + + ctrl.$validators.required = function(modelValue, viewValue) { + return !attr.required || !ctrl.$isEmpty(viewValue); + }; + + attr.$observe('required', function() { + ctrl.$validate(); + }); + } + }; +}; + + +var patternDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + + var regexp, patternExp = attr.ngPattern || attr.pattern; + attr.$observe('pattern', function(regex) { + if (isString(regex) && regex.length > 0) { + regex = new RegExp('^' + regex + '$'); + } + + if (regex && !regex.test) { + throw minErr('ngPattern')('noregexp', + 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp, + regex, startingTag(elm)); + } + + regexp = regex || undefined; + ctrl.$validate(); + }); + + ctrl.$validators.pattern = function(value) { + return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value); + }; + } + }; +}; + + +var maxlengthDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + + var maxlength = -1; + attr.$observe('maxlength', function(value) { + var intVal = int(value); + maxlength = isNaN(intVal) ? -1 : intVal; + ctrl.$validate(); + }); + ctrl.$validators.maxlength = function(modelValue, viewValue) { + return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength); + }; + } + }; +}; + +var minlengthDirective = function() { + return { + restrict: 'A', + require: '?ngModel', + link: function(scope, elm, attr, ctrl) { + if (!ctrl) return; + + var minlength = 0; + attr.$observe('minlength', function(value) { + minlength = int(value) || 0; + ctrl.$validate(); + }); + ctrl.$validators.minlength = function(modelValue, viewValue) { + return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength; + }; + } + }; +}; + + if (window.angular.bootstrap) { + //AngularJS is already loaded, so we can return here... + console.log('WARNING: Tried to load angular more than once.'); + return; + } + + //try to bind to jquery now so that one can write jqLite(document).ready() + //but we will rebind on bootstrap again. + bindJQuery(); + + publishExternalAPI(angular); + + jqLite(document).ready(function() { + angularInit(document, bootstrap); + }); + +})(window, document); + +!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>');
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js new file mode 100644 index 000000000..0fb8002f9 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js @@ -0,0 +1,251 @@ +/* + AngularJS v1.3.15 + (c) 2010-2014 Google, Inc. http://angularjs.org + License: MIT +*/ +(function(Q,W,t){'use strict';function R(b){return function(){var a=arguments[0],c;c="["+(b?b+":":"")+a+"] http://errors.angularjs.org/1.3.15/"+(b?b+"/":"")+a;for(a=1;a<arguments.length;a++){c=c+(1==a?"?":"&")+"p"+(a-1)+"=";var d=encodeURIComponent,e;e=arguments[a];e="function"==typeof e?e.toString().replace(/ \{[\s\S]*$/,""):"undefined"==typeof e?"undefined":"string"!=typeof e?JSON.stringify(e):e;c+=d(e)}return Error(c)}}function Sa(b){if(null==b||Ta(b))return!1;var a=b.length;return b.nodeType=== +qa&&a?!0:C(b)||H(b)||0===a||"number"===typeof a&&0<a&&a-1 in b}function r(b,a,c){var d,e;if(b)if(G(b))for(d in b)"prototype"==d||"length"==d||"name"==d||b.hasOwnProperty&&!b.hasOwnProperty(d)||a.call(c,b[d],d,b);else if(H(b)||Sa(b)){var f="object"!==typeof b;d=0;for(e=b.length;d<e;d++)(f||d in b)&&a.call(c,b[d],d,b)}else if(b.forEach&&b.forEach!==r)b.forEach(a,c,b);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d,b);return b}function Ed(b,a,c){for(var d=Object.keys(b).sort(),e=0;e<d.length;e++)a.call(c, +b[d[e]],d[e]);return d}function mc(b){return function(a,c){b(c,a)}}function Fd(){return++ob}function nc(b,a){a?b.$$hashKey=a:delete b.$$hashKey}function w(b){for(var a=b.$$hashKey,c=1,d=arguments.length;c<d;c++){var e=arguments[c];if(e)for(var f=Object.keys(e),g=0,h=f.length;g<h;g++){var l=f[g];b[l]=e[l]}}nc(b,a);return b}function aa(b){return parseInt(b,10)}function Ob(b,a){return w(Object.create(b),a)}function E(){}function ra(b){return b}function ea(b){return function(){return b}}function x(b){return"undefined"=== +typeof b}function y(b){return"undefined"!==typeof b}function J(b){return null!==b&&"object"===typeof b}function C(b){return"string"===typeof b}function Y(b){return"number"===typeof b}function ga(b){return"[object Date]"===Ca.call(b)}function G(b){return"function"===typeof b}function Ua(b){return"[object RegExp]"===Ca.call(b)}function Ta(b){return b&&b.window===b}function Va(b){return b&&b.$evalAsync&&b.$watch}function Wa(b){return"boolean"===typeof b}function oc(b){return!(!b||!(b.nodeName||b.prop&& +b.attr&&b.find))}function Gd(b){var a={};b=b.split(",");var c;for(c=0;c<b.length;c++)a[b[c]]=!0;return a}function va(b){return z(b.nodeName||b[0]&&b[0].nodeName)}function Xa(b,a){var c=b.indexOf(a);0<=c&&b.splice(c,1);return a}function Da(b,a,c,d){if(Ta(b)||Va(b))throw Ja("cpws");if(a){if(b===a)throw Ja("cpi");c=c||[];d=d||[];if(J(b)){var e=c.indexOf(b);if(-1!==e)return d[e];c.push(b);d.push(a)}if(H(b))for(var f=a.length=0;f<b.length;f++)e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a.push(e); +else{var g=a.$$hashKey;H(a)?a.length=0:r(a,function(b,c){delete a[c]});for(f in b)b.hasOwnProperty(f)&&(e=Da(b[f],null,c,d),J(b[f])&&(c.push(b[f]),d.push(e)),a[f]=e);nc(a,g)}}else if(a=b)H(b)?a=Da(b,[],c,d):ga(b)?a=new Date(b.getTime()):Ua(b)?(a=new RegExp(b.source,b.toString().match(/[^\/]*$/)[0]),a.lastIndex=b.lastIndex):J(b)&&(e=Object.create(Object.getPrototypeOf(b)),a=Da(b,e,c,d));return a}function sa(b,a){if(H(b)){a=a||[];for(var c=0,d=b.length;c<d;c++)a[c]=b[c]}else if(J(b))for(c in a=a||{}, +b)if("$"!==c.charAt(0)||"$"!==c.charAt(1))a[c]=b[c];return a||b}function ha(b,a){if(b===a)return!0;if(null===b||null===a)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&&"object"==c)if(H(b)){if(!H(a))return!1;if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ha(b[d],a[d]))return!1;return!0}}else{if(ga(b))return ga(a)?ha(b.getTime(),a.getTime()):!1;if(Ua(b))return Ua(a)?b.toString()==a.toString():!1;if(Va(b)||Va(a)||Ta(b)||Ta(a)||H(a)||ga(a)||Ua(a))return!1;c={};for(d in b)if("$"!== +d.charAt(0)&&!G(b[d])){if(!ha(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c.hasOwnProperty(d)&&"$"!==d.charAt(0)&&a[d]!==t&&!G(a[d]))return!1;return!0}return!1}function Ya(b,a,c){return b.concat(Za.call(a,c))}function pc(b,a){var c=2<arguments.length?Za.call(arguments,2):[];return!G(a)||a instanceof RegExp?a:c.length?function(){return arguments.length?a.apply(b,Ya(c,arguments,0)):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}}function Hd(b,a){var c=a;"string"===typeof b&& +"$"===b.charAt(0)&&"$"===b.charAt(1)?c=t:Ta(a)?c="$WINDOW":a&&W===a?c="$DOCUMENT":Va(a)&&(c="$SCOPE");return c}function $a(b,a){if("undefined"===typeof b)return t;Y(a)||(a=a?2:null);return JSON.stringify(b,Hd,a)}function qc(b){return C(b)?JSON.parse(b):b}function wa(b){b=A(b).clone();try{b.empty()}catch(a){}var c=A("<div>").append(b).html();try{return b[0].nodeType===pb?z(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+z(b)})}catch(d){return z(c)}}function rc(b){try{return decodeURIComponent(b)}catch(a){}} +function sc(b){var a={},c,d;r((b||"").split("&"),function(b){b&&(c=b.replace(/\+/g,"%20").split("="),d=rc(c[0]),y(d)&&(b=y(c[1])?rc(c[1]):!0,tc.call(a,d)?H(a[d])?a[d].push(b):a[d]=[a[d],b]:a[d]=b))});return a}function Pb(b){var a=[];r(b,function(b,d){H(b)?r(b,function(b){a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))}):a.push(Ea(d,!0)+(!0===b?"":"="+Ea(b,!0)))});return a.length?a.join("&"):""}function qb(b){return Ea(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Ea(b,a){return encodeURIComponent(b).replace(/%40/gi, +"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%3B/gi,";").replace(/%20/g,a?"%20":"+")}function Id(b,a){var c,d,e=rb.length;b=A(b);for(d=0;d<e;++d)if(c=rb[d]+a,C(c=b.attr(c)))return c;return null}function Jd(b,a){var c,d,e={};r(rb,function(a){a+="app";!c&&b.hasAttribute&&b.hasAttribute(a)&&(c=b,d=b.getAttribute(a))});r(rb,function(a){a+="app";var e;!c&&(e=b.querySelector("["+a.replace(":","\\:")+"]"))&&(c=e,d=e.getAttribute(a))});c&&(e.strictDi=null!==Id(c,"strict-di"), +a(c,d?[d]:[],e))}function uc(b,a,c){J(c)||(c={});c=w({strictDi:!1},c);var d=function(){b=A(b);if(b.injector()){var d=b[0]===W?"document":wa(b);throw Ja("btstrpd",d.replace(/</,"<").replace(/>/,">"));}a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);c.debugInfoEnabled&&a.push(["$compileProvider",function(a){a.debugInfoEnabled(!0)}]);a.unshift("ng");d=ab(a,c.strictDi);d.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector", +d);c(b)(a)})}]);return d},e=/^NG_ENABLE_DEBUG_INFO!/,f=/^NG_DEFER_BOOTSTRAP!/;Q&&e.test(Q.name)&&(c.debugInfoEnabled=!0,Q.name=Q.name.replace(e,""));if(Q&&!f.test(Q.name))return d();Q.name=Q.name.replace(f,"");ca.resumeBootstrap=function(b){r(b,function(b){a.push(b)});return d()};G(ca.resumeDeferredBootstrap)&&ca.resumeDeferredBootstrap()}function Kd(){Q.name="NG_ENABLE_DEBUG_INFO!"+Q.name;Q.location.reload()}function Ld(b){b=ca.element(b).injector();if(!b)throw Ja("test");return b.get("$$testability")} +function vc(b,a){a=a||"_";return b.replace(Md,function(b,d){return(d?a:"")+b.toLowerCase()})}function Nd(){var b;wc||((ta=Q.jQuery)&&ta.fn.on?(A=ta,w(ta.fn,{scope:Ka.scope,isolateScope:Ka.isolateScope,controller:Ka.controller,injector:Ka.injector,inheritedData:Ka.inheritedData}),b=ta.cleanData,ta.cleanData=function(a){var c;if(Qb)Qb=!1;else for(var d=0,e;null!=(e=a[d]);d++)(c=ta._data(e,"events"))&&c.$destroy&&ta(e).triggerHandler("$destroy");b(a)}):A=T,ca.element=A,wc=!0)}function Rb(b,a,c){if(!b)throw Ja("areq", +a||"?",c||"required");return b}function sb(b,a,c){c&&H(b)&&(b=b[b.length-1]);Rb(G(b),a,"not a function, got "+(b&&"object"===typeof b?b.constructor.name||"Object":typeof b));return b}function La(b,a){if("hasOwnProperty"===b)throw Ja("badname",a);}function xc(b,a,c){if(!a)return b;a=a.split(".");for(var d,e=b,f=a.length,g=0;g<f;g++)d=a[g],b&&(b=(e=b)[d]);return!c&&G(b)?pc(e,b):b}function tb(b){var a=b[0];b=b[b.length-1];var c=[a];do{a=a.nextSibling;if(!a)break;c.push(a)}while(a!==b);return A(c)}function ia(){return Object.create(null)} +function Od(b){function a(a,b,c){return a[b]||(a[b]=c())}var c=R("$injector"),d=R("ng");b=a(b,"angular",Object);b.$$minErr=b.$$minErr||R;return a(b,"module",function(){var b={};return function(f,g,h){if("hasOwnProperty"===f)throw d("badname","module");g&&b.hasOwnProperty(f)&&(b[f]=null);return a(b,f,function(){function a(c,d,e,f){f||(f=b);return function(){f[e||"push"]([c,d,arguments]);return u}}if(!g)throw c("nomod",f);var b=[],d=[],e=[],q=a("$injector","invoke","push",d),u={_invokeQueue:b,_configBlocks:d, +_runBlocks:e,requires:g,name:f,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),animation:a("$animateProvider","register"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:q,run:function(a){e.push(a);return this}};h&&q(h);return u})}})}function Pd(b){w(b,{bootstrap:uc,copy:Da,extend:w,equals:ha, +element:A,forEach:r,injector:ab,noop:E,bind:pc,toJson:$a,fromJson:qc,identity:ra,isUndefined:x,isDefined:y,isString:C,isFunction:G,isObject:J,isNumber:Y,isElement:oc,isArray:H,version:Qd,isDate:ga,lowercase:z,uppercase:ub,callbacks:{counter:0},getTestability:Ld,$$minErr:R,$$csp:bb,reloadWithDebugInfo:Kd});cb=Od(Q);try{cb("ngLocale")}catch(a){cb("ngLocale",[]).provider("$locale",Rd)}cb("ng",["ngLocale"],["$provide",function(a){a.provider({$$sanitizeUri:Sd});a.provider("$compile",yc).directive({a:Td, +input:zc,textarea:zc,form:Ud,script:Vd,select:Wd,style:Xd,option:Yd,ngBind:Zd,ngBindHtml:$d,ngBindTemplate:ae,ngClass:be,ngClassEven:ce,ngClassOdd:de,ngCloak:ee,ngController:fe,ngForm:ge,ngHide:he,ngIf:ie,ngInclude:je,ngInit:ke,ngNonBindable:le,ngPluralize:me,ngRepeat:ne,ngShow:oe,ngStyle:pe,ngSwitch:qe,ngSwitchWhen:re,ngSwitchDefault:se,ngOptions:te,ngTransclude:ue,ngModel:ve,ngList:we,ngChange:xe,pattern:Ac,ngPattern:Ac,required:Bc,ngRequired:Bc,minlength:Cc,ngMinlength:Cc,maxlength:Dc,ngMaxlength:Dc, +ngValue:ye,ngModelOptions:ze}).directive({ngInclude:Ae}).directive(vb).directive(Ec);a.provider({$anchorScroll:Be,$animate:Ce,$browser:De,$cacheFactory:Ee,$controller:Fe,$document:Ge,$exceptionHandler:He,$filter:Fc,$interpolate:Ie,$interval:Je,$http:Ke,$httpBackend:Le,$location:Me,$log:Ne,$parse:Oe,$rootScope:Pe,$q:Qe,$$q:Re,$sce:Se,$sceDelegate:Te,$sniffer:Ue,$templateCache:Ve,$templateRequest:We,$$testability:Xe,$timeout:Ye,$window:Ze,$$rAF:$e,$$asyncCallback:af,$$jqLite:bf})}])}function db(b){return b.replace(cf, +function(a,b,d,e){return e?d.toUpperCase():d}).replace(df,"Moz$1")}function Gc(b){b=b.nodeType;return b===qa||!b||9===b}function Hc(b,a){var c,d,e=a.createDocumentFragment(),f=[];if(Sb.test(b)){c=c||e.appendChild(a.createElement("div"));d=(ef.exec(b)||["",""])[1].toLowerCase();d=ja[d]||ja._default;c.innerHTML=d[1]+b.replace(ff,"<$1></$2>")+d[2];for(d=d[0];d--;)c=c.lastChild;f=Ya(f,c.childNodes);c=e.firstChild;c.textContent=""}else f.push(a.createTextNode(b));e.textContent="";e.innerHTML="";r(f,function(a){e.appendChild(a)}); +return e}function T(b){if(b instanceof T)return b;var a;C(b)&&(b=N(b),a=!0);if(!(this instanceof T)){if(a&&"<"!=b.charAt(0))throw Tb("nosel");return new T(b)}if(a){a=W;var c;b=(c=gf.exec(b))?[a.createElement(c[1])]:(c=Hc(b,a))?c.childNodes:[]}Ic(this,b)}function Ub(b){return b.cloneNode(!0)}function wb(b,a){a||xb(b);if(b.querySelectorAll)for(var c=b.querySelectorAll("*"),d=0,e=c.length;d<e;d++)xb(c[d])}function Jc(b,a,c,d){if(y(d))throw Tb("offargs");var e=(d=yb(b))&&d.events,f=d&&d.handle;if(f)if(a)r(a.split(" "), +function(a){if(y(c)){var d=e[a];Xa(d||[],c);if(d&&0<d.length)return}b.removeEventListener(a,f,!1);delete e[a]});else for(a in e)"$destroy"!==a&&b.removeEventListener(a,f,!1),delete e[a]}function xb(b,a){var c=b.ng339,d=c&&zb[c];d&&(a?delete d.data[a]:(d.handle&&(d.events.$destroy&&d.handle({},"$destroy"),Jc(b)),delete zb[c],b.ng339=t))}function yb(b,a){var c=b.ng339,c=c&&zb[c];a&&!c&&(b.ng339=c=++hf,c=zb[c]={events:{},data:{},handle:t});return c}function Vb(b,a,c){if(Gc(b)){var d=y(c),e=!d&&a&&!J(a), +f=!a;b=(b=yb(b,!e))&&b.data;if(d)b[a]=c;else{if(f)return b;if(e)return b&&b[a];w(b,a)}}}function Ab(b,a){return b.getAttribute?-1<(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" "):!1}function Bb(b,a){a&&b.setAttribute&&r(a.split(" "),function(a){b.setAttribute("class",N((" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," ").replace(" "+N(a)+" "," ")))})}function Cb(b,a){if(a&&b.setAttribute){var c=(" "+(b.getAttribute("class")||"")+" ").replace(/[\n\t]/g," "); +r(a.split(" "),function(a){a=N(a);-1===c.indexOf(" "+a+" ")&&(c+=a+" ")});b.setAttribute("class",N(c))}}function Ic(b,a){if(a)if(a.nodeType)b[b.length++]=a;else{var c=a.length;if("number"===typeof c&&a.window!==a){if(c)for(var d=0;d<c;d++)b[b.length++]=a[d]}else b[b.length++]=a}}function Kc(b,a){return Db(b,"$"+(a||"ngController")+"Controller")}function Db(b,a,c){9==b.nodeType&&(b=b.documentElement);for(a=H(a)?a:[a];b;){for(var d=0,e=a.length;d<e;d++)if((c=A.data(b,a[d]))!==t)return c;b=b.parentNode|| +11===b.nodeType&&b.host}}function Lc(b){for(wb(b,!0);b.firstChild;)b.removeChild(b.firstChild)}function Mc(b,a){a||wb(b);var c=b.parentNode;c&&c.removeChild(b)}function jf(b,a){a=a||Q;if("complete"===a.document.readyState)a.setTimeout(b);else A(a).on("load",b)}function Nc(b,a){var c=Eb[a.toLowerCase()];return c&&Oc[va(b)]&&c}function kf(b,a){var c=b.nodeName;return("INPUT"===c||"TEXTAREA"===c)&&Pc[a]}function lf(b,a){var c=function(c,e){c.isDefaultPrevented=function(){return c.defaultPrevented};var f= +a[e||c.type],g=f?f.length:0;if(g){if(x(c.immediatePropagationStopped)){var h=c.stopImmediatePropagation;c.stopImmediatePropagation=function(){c.immediatePropagationStopped=!0;c.stopPropagation&&c.stopPropagation();h&&h.call(c)}}c.isImmediatePropagationStopped=function(){return!0===c.immediatePropagationStopped};1<g&&(f=sa(f));for(var l=0;l<g;l++)c.isImmediatePropagationStopped()||f[l].call(b,c)}};c.elem=b;return c}function bf(){this.$get=function(){return w(T,{hasClass:function(b,a){b.attr&&(b=b[0]); +return Ab(b,a)},addClass:function(b,a){b.attr&&(b=b[0]);return Cb(b,a)},removeClass:function(b,a){b.attr&&(b=b[0]);return Bb(b,a)}})}}function Ma(b,a){var c=b&&b.$$hashKey;if(c)return"function"===typeof c&&(c=b.$$hashKey()),c;c=typeof b;return c="function"==c||"object"==c&&null!==b?b.$$hashKey=c+":"+(a||Fd)():c+":"+b}function eb(b,a){if(a){var c=0;this.nextUid=function(){return++c}}r(b,this.put,this)}function mf(b){return(b=b.toString().replace(Qc,"").match(Rc))?"function("+(b[1]||"").replace(/[\s\r\n]+/, +" ")+")":"fn"}function ab(b,a){function c(a){return function(b,c){if(J(b))r(b,mc(a));else return a(b,c)}}function d(a,b){La(a,"service");if(G(b)||H(b))b=q.instantiate(b);if(!b.$get)throw Fa("pget",a);return p[a+"Provider"]=b}function e(a,b){return function(){var c=s.invoke(b,this);if(x(c))throw Fa("undef",a);return c}}function f(a,b,c){return d(a,{$get:!1!==c?e(a,b):b})}function g(a){var b=[],c;r(a,function(a){function d(a){var b,c;b=0;for(c=a.length;b<c;b++){var e=a[b],f=q.get(e[0]);f[e[1]].apply(f, +e[2])}}if(!n.get(a)){n.put(a,!0);try{C(a)?(c=cb(a),b=b.concat(g(c.requires)).concat(c._runBlocks),d(c._invokeQueue),d(c._configBlocks)):G(a)?b.push(q.invoke(a)):H(a)?b.push(q.invoke(a)):sb(a,"module")}catch(e){throw H(a)&&(a=a[a.length-1]),e.message&&e.stack&&-1==e.stack.indexOf(e.message)&&(e=e.message+"\n"+e.stack),Fa("modulerr",a,e.stack||e.message||e);}}});return b}function h(b,c){function d(a,e){if(b.hasOwnProperty(a)){if(b[a]===l)throw Fa("cdep",a+" <- "+k.join(" <- "));return b[a]}try{return k.unshift(a), +b[a]=l,b[a]=c(a,e)}catch(f){throw b[a]===l&&delete b[a],f;}finally{k.shift()}}function e(b,c,f,g){"string"===typeof f&&(g=f,f=null);var k=[],h=ab.$$annotate(b,a,g),l,q,p;q=0;for(l=h.length;q<l;q++){p=h[q];if("string"!==typeof p)throw Fa("itkn",p);k.push(f&&f.hasOwnProperty(p)?f[p]:d(p,g))}H(b)&&(b=b[l]);return b.apply(c,k)}return{invoke:e,instantiate:function(a,b,c){var d=Object.create((H(a)?a[a.length-1]:a).prototype||null);a=e(a,d,b,c);return J(a)||G(a)?a:d},get:d,annotate:ab.$$annotate,has:function(a){return p.hasOwnProperty(a+ +"Provider")||b.hasOwnProperty(a)}}}a=!0===a;var l={},k=[],n=new eb([],!0),p={$provide:{provider:c(d),factory:c(f),service:c(function(a,b){return f(a,["$injector",function(a){return a.instantiate(b)}])}),value:c(function(a,b){return f(a,ea(b),!1)}),constant:c(function(a,b){La(a,"constant");p[a]=b;u[a]=b}),decorator:function(a,b){var c=q.get(a+"Provider"),d=c.$get;c.$get=function(){var a=s.invoke(d,c);return s.invoke(b,null,{$delegate:a})}}}},q=p.$injector=h(p,function(a,b){ca.isString(b)&&k.push(b); +throw Fa("unpr",k.join(" <- "));}),u={},s=u.$injector=h(u,function(a,b){var c=q.get(a+"Provider",b);return s.invoke(c.$get,c,t,a)});r(g(b),function(a){s.invoke(a||E)});return s}function Be(){var b=!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;Array.prototype.some.call(a,function(a){if("a"===va(a))return b=a,!0});return b}function f(b){if(b){b.scrollIntoView();var c;c=g.yOffset;G(c)?c=c():oc(c)?(c=c[0],c="fixed"!== +a.getComputedStyle(c).position?0:c.getBoundingClientRect().bottom):Y(c)||(c=0);c&&(b=b.getBoundingClientRect().top,a.scrollBy(0,b-c))}else a.scrollTo(0,0)}function g(){var a=c.hash(),b;a?(b=h.getElementById(a))?f(b):(b=e(h.getElementsByName(a)))?f(b):"top"===a&&f(null):f(null)}var h=a.document;b&&d.$watch(function(){return c.hash()},function(a,b){a===b&&""===a||jf(function(){d.$evalAsync(g)})});return g}]}function af(){this.$get=["$$rAF","$timeout",function(b,a){return b.supported?function(a){return b(a)}: +function(b){return a(b,0,!1)}}]}function nf(b,a,c,d){function e(a){try{a.apply(null,Za.call(arguments,1))}finally{if(m--,0===m)for(;F.length;)try{F.pop()()}catch(b){c.error(b)}}}function f(a,b){(function da(){r(Z,function(a){a()});L=b(da,a)})()}function g(){h();l()}function h(){a:{try{B=u.state;break a}catch(a){}B=void 0}B=x(B)?null:B;ha(B,O)&&(B=O);O=B}function l(){if(D!==n.url()||I!==B)D=n.url(),I=B,r(X,function(a){a(n.url(),B)})}function k(a){try{return decodeURIComponent(a)}catch(b){return a}} +var n=this,p=a[0],q=b.location,u=b.history,s=b.setTimeout,M=b.clearTimeout,v={};n.isMock=!1;var m=0,F=[];n.$$completeOutstandingRequest=e;n.$$incOutstandingRequestCount=function(){m++};n.notifyWhenNoOutstandingRequests=function(a){r(Z,function(a){a()});0===m?a():F.push(a)};var Z=[],L;n.addPollFn=function(a){x(L)&&f(100,s);Z.push(a);return a};var B,I,D=q.href,S=a.find("base"),P=null;h();I=B;n.url=function(a,c,e){x(e)&&(e=null);q!==b.location&&(q=b.location);u!==b.history&&(u=b.history);if(a){var f= +I===e;if(D===a&&(!d.history||f))return n;var g=D&&Ga(D)===Ga(a);D=a;I=e;!d.history||g&&f?(g||(P=a),c?q.replace(a):g?(c=q,e=a.indexOf("#"),a=-1===e?"":a.substr(e+1),c.hash=a):q.href=a):(u[c?"replaceState":"pushState"](e,"",a),h(),I=B);return n}return P||q.href.replace(/%27/g,"'")};n.state=function(){return B};var X=[],ba=!1,O=null;n.onUrlChange=function(a){if(!ba){if(d.history)A(b).on("popstate",g);A(b).on("hashchange",g);ba=!0}X.push(a);return a};n.$$checkUrlChange=l;n.baseHref=function(){var a=S.attr("href"); +return a?a.replace(/^(https?\:)?\/\/[^\/]*/,""):""};var fa={},y="",ka=n.baseHref();n.cookies=function(a,b){var d,e,f,g;if(a)b===t?p.cookie=encodeURIComponent(a)+"=;path="+ka+";expires=Thu, 01 Jan 1970 00:00:00 GMT":C(b)&&(d=(p.cookie=encodeURIComponent(a)+"="+encodeURIComponent(b)+";path="+ka).length+1,4096<d&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"));else{if(p.cookie!==y)for(y=p.cookie,d=y.split("; "),fa={},f=0;f<d.length;f++)e=d[f],g= +e.indexOf("="),0<g&&(a=k(e.substring(0,g)),fa[a]===t&&(fa[a]=k(e.substring(g+1))));return fa}};n.defer=function(a,b){var c;m++;c=s(function(){delete v[c];e(a)},b||0);v[c]=!0;return c};n.defer.cancel=function(a){return v[a]?(delete v[a],M(a),e(E),!0):!1}}function De(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new nf(b,d,a,c)}]}function Ee(){this.$get=function(){function b(b,d){function e(a){a!=p&&(q?q==a&&(q=a.n):q=a,f(a.n,a.p),f(a,p),p=a,p.n=null)}function f(a,b){a!= +b&&(a&&(a.p=b),b&&(b.n=a))}if(b in a)throw R("$cacheFactory")("iid",b);var g=0,h=w({},d,{id:b}),l={},k=d&&d.capacity||Number.MAX_VALUE,n={},p=null,q=null;return a[b]={put:function(a,b){if(k<Number.MAX_VALUE){var c=n[a]||(n[a]={key:a});e(c)}if(!x(b))return a in l||g++,l[a]=b,g>k&&this.remove(q.key),b},get:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;e(b)}return l[a]},remove:function(a){if(k<Number.MAX_VALUE){var b=n[a];if(!b)return;b==p&&(p=b.p);b==q&&(q=b.n);f(b.n,b.p);delete n[a]}delete l[a]; +g--},removeAll:function(){l={};g=0;n={};p=q=null},destroy:function(){n=h=l=null;delete a[b]},info:function(){return w({},h,{size:g})}}}var a={};b.info=function(){var b={};r(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function Ve(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function yc(b,a){function c(a,b){var c=/^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/,d={};r(a,function(a,e){var f=a.match(c);if(!f)throw la("iscp",b,e,a);d[e]={mode:f[1][0],collection:"*"=== +f[2],optional:"?"===f[3],attrName:f[4]||e}});return d}var d={},e=/^\s*directive\:\s*([\w\-]+)\s+(.*)$/,f=/(([\w\-]+)(?:\:([^;]+))?;?)/,g=Gd("ngSrc,ngSrcset,src,srcset"),h=/^(?:(\^\^?)?(\?)?(\^\^?)?)?/,l=/^(on[a-z]+|formaction)$/;this.directive=function p(a,e){La(a,"directive");C(a)?(Rb(e,"directiveFactory"),d.hasOwnProperty(a)||(d[a]=[],b.factory(a+"Directive",["$injector","$exceptionHandler",function(b,e){var f=[];r(d[a],function(d,g){try{var h=b.invoke(d);G(h)?h={compile:ea(h)}:!h.compile&&h.link&& +(h.compile=ea(h.link));h.priority=h.priority||0;h.index=g;h.name=h.name||a;h.require=h.require||h.controller&&h.name;h.restrict=h.restrict||"EA";J(h.scope)&&(h.$$isolateBindings=c(h.scope,h.name));f.push(h)}catch(k){e(k)}});return f}])),d[a].push(e)):r(a,mc(p));return this};this.aHrefSanitizationWhitelist=function(b){return y(b)?(a.aHrefSanitizationWhitelist(b),this):a.aHrefSanitizationWhitelist()};this.imgSrcSanitizationWhitelist=function(b){return y(b)?(a.imgSrcSanitizationWhitelist(b),this):a.imgSrcSanitizationWhitelist()}; +var k=!0;this.debugInfoEnabled=function(a){return y(a)?(k=a,this):k};this.$get=["$injector","$interpolate","$exceptionHandler","$templateRequest","$parse","$controller","$rootScope","$document","$sce","$animate","$$sanitizeUri",function(a,b,c,s,M,v,m,F,Z,L,B){function I(a,b){try{a.addClass(b)}catch(c){}}function D(a,b,c,d,e){a instanceof A||(a=A(a));r(a,function(b,c){b.nodeType==pb&&b.nodeValue.match(/\S+/)&&(a[c]=A(b).wrap("<span></span>").parent()[0])});var f=S(a,b,a,c,d,e);D.$$addScopeClass(a); +var g=null;return function(b,c,d){Rb(b,"scope");d=d||{};var e=d.parentBoundTranscludeFn,h=d.transcludeControllers;d=d.futureParentElement;e&&e.$$boundTransclude&&(e=e.$$boundTransclude);g||(g=(d=d&&d[0])?"foreignobject"!==va(d)&&d.toString().match(/SVG/)?"svg":"html":"html");d="html"!==g?A(Xb(g,A("<div>").append(a).html())):c?Ka.clone.call(a):a;if(h)for(var k in h)d.data("$"+k+"Controller",h[k].instance);D.$$addScopeInfo(d,b);c&&c(d,b);f&&f(b,d,d,e);return d}}function S(a,b,c,d,e,f){function g(a, +c,d,e){var f,k,l,q,p,s,M;if(m)for(M=Array(c.length),q=0;q<h.length;q+=3)f=h[q],M[f]=c[f];else M=c;q=0;for(p=h.length;q<p;)k=M[h[q++]],c=h[q++],f=h[q++],c?(c.scope?(l=a.$new(),D.$$addScopeInfo(A(k),l)):l=a,s=c.transcludeOnThisElement?P(a,c.transclude,e,c.elementTranscludeOnThisElement):!c.templateOnThisElement&&e?e:!e&&b?P(a,b):null,c(f,l,k,d,s)):f&&f(a,k.childNodes,t,e)}for(var h=[],k,l,q,p,m,s=0;s<a.length;s++){k=new Yb;l=X(a[s],[],k,0===s?d:t,e);(f=l.length?fa(l,a[s],k,b,c,null,[],[],f):null)&& +f.scope&&D.$$addScopeClass(k.$$element);k=f&&f.terminal||!(q=a[s].childNodes)||!q.length?null:S(q,f?(f.transcludeOnThisElement||!f.templateOnThisElement)&&f.transclude:b);if(f||k)h.push(s,f,k),p=!0,m=m||f;f=null}return p?g:null}function P(a,b,c,d){return function(d,e,f,g,h){d||(d=a.$new(!1,h),d.$$transcluded=!0);return b(d,e,{parentBoundTranscludeFn:c,transcludeControllers:f,futureParentElement:g})}}function X(a,b,c,d,g){var h=c.$attr,k;switch(a.nodeType){case qa:ka(b,xa(va(a)),"E",d,g);for(var l, +q,p,m=a.attributes,s=0,M=m&&m.length;s<M;s++){var u=!1,L=!1;l=m[s];k=l.name;q=N(l.value);l=xa(k);if(p=U.test(l))k=k.replace(Sc,"").substr(8).replace(/_(.)/g,function(a,b){return b.toUpperCase()});var B=l.replace(/(Start|End)$/,"");x(B)&&l===B+"Start"&&(u=k,L=k.substr(0,k.length-5)+"end",k=k.substr(0,k.length-6));l=xa(k.toLowerCase());h[l]=k;if(p||!c.hasOwnProperty(l))c[l]=q,Nc(a,l)&&(c[l]=!0);Oa(a,b,q,l,p);ka(b,l,"A",d,g,u,L)}a=a.className;J(a)&&(a=a.animVal);if(C(a)&&""!==a)for(;k=f.exec(a);)l=xa(k[2]), +ka(b,l,"C",d,g)&&(c[l]=N(k[3])),a=a.substr(k.index+k[0].length);break;case pb:za(b,a.nodeValue);break;case 8:try{if(k=e.exec(a.nodeValue))l=xa(k[1]),ka(b,l,"M",d,g)&&(c[l]=N(k[2]))}catch(v){}}b.sort(da);return b}function ba(a,b,c){var d=[],e=0;if(b&&a.hasAttribute&&a.hasAttribute(b)){do{if(!a)throw la("uterdir",b,c);a.nodeType==qa&&(a.hasAttribute(b)&&e++,a.hasAttribute(c)&&e--);d.push(a);a=a.nextSibling}while(0<e)}else d.push(a);return A(d)}function O(a,b,c){return function(d,e,f,g,h){e=ba(e[0], +b,c);return a(d,e,f,g,h)}}function fa(a,d,e,f,g,k,l,p,m){function s(a,b,c,d){if(a){c&&(a=O(a,c,d));a.require=K.require;a.directiveName=da;if(P===K||K.$$isolateScope)a=Y(a,{isolateScope:!0});l.push(a)}if(b){c&&(b=O(b,c,d));b.require=K.require;b.directiveName=da;if(P===K||K.$$isolateScope)b=Y(b,{isolateScope:!0});p.push(b)}}function L(a,b,c,d){var e,f="data",g=!1,k=c,l;if(C(b)){l=b.match(h);b=b.substring(l[0].length);l[3]&&(l[1]?l[3]=null:l[1]=l[3]);"^"===l[1]?f="inheritedData":"^^"===l[1]&&(f="inheritedData", +k=c.parent());"?"===l[2]&&(g=!0);e=null;d&&"data"===f&&(e=d[b])&&(e=e.instance);e=e||k[f]("$"+b+"Controller");if(!e&&!g)throw la("ctreq",b,a);return e||null}H(b)&&(e=[],r(b,function(b){e.push(L(a,b,c,d))}));return e}function B(a,c,f,g,h){function k(a,b,c){var d;Va(a)||(c=b,b=a,a=t);E&&(d=F);c||(c=E?X.parent():X);return h(a,b,d,c,Wb)}var m,s,u,I,F,gb,X,O;d===f?(O=e,X=e.$$element):(X=A(f),O=new Yb(X,e));P&&(I=c.$new(!0));h&&(gb=k,gb.$$boundTransclude=h);S&&(Z={},F={},r(S,function(a){var b={$scope:a=== +P||a.$$isolateScope?I:c,$element:X,$attrs:O,$transclude:gb};u=a.controller;"@"==u&&(u=O[a.name]);b=v(u,b,!0,a.controllerAs);F[a.name]=b;E||X.data("$"+a.name+"Controller",b.instance);Z[a.name]=b}));if(P){D.$$addScopeInfo(X,I,!0,!(ma&&(ma===P||ma===P.$$originalDirective)));D.$$addScopeClass(X,!0);g=Z&&Z[P.name];var ba=I;g&&g.identifier&&!0===P.bindToController&&(ba=g.instance);r(I.$$isolateBindings=P.$$isolateBindings,function(a,d){var e=a.attrName,f=a.optional,g,h,k,l;switch(a.mode){case "@":O.$observe(e, +function(a){ba[d]=a});O.$$observers[e].$$scope=c;O[e]&&(ba[d]=b(O[e])(c));break;case "=":if(f&&!O[e])break;h=M(O[e]);l=h.literal?ha:function(a,b){return a===b||a!==a&&b!==b};k=h.assign||function(){g=ba[d]=h(c);throw la("nonassign",O[e],P.name);};g=ba[d]=h(c);f=function(a){l(a,ba[d])||(l(a,g)?k(c,a=ba[d]):ba[d]=a);return g=a};f.$stateful=!0;f=a.collection?c.$watchCollection(O[e],f):c.$watch(M(O[e],f),null,h.literal);I.$on("$destroy",f);break;case "&":h=M(O[e]),ba[d]=function(a){return h(c,a)}}})}Z&& +(r(Z,function(a){a()}),Z=null);g=0;for(m=l.length;g<m;g++)s=l[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb);var Wb=c;P&&(P.template||null===P.templateUrl)&&(Wb=I);a&&a(Wb,f.childNodes,t,h);for(g=p.length-1;0<=g;g--)s=p[g],$(s,s.isolateScope?I:c,X,O,s.require&&L(s.directiveName,s.require,X,F),gb)}m=m||{};for(var I=-Number.MAX_VALUE,F,S=m.controllerDirectives,Z,P=m.newIsolateScopeDirective,ma=m.templateDirective,fa=m.nonTlbTranscludeDirective,ka=!1,x=!1,E=m.hasElementTranscludeDirective, +w=e.$$element=A(d),K,da,V,fb=f,za,z=0,Q=a.length;z<Q;z++){K=a[z];var Oa=K.$$start,U=K.$$end;Oa&&(w=ba(d,Oa,U));V=t;if(I>K.priority)break;if(V=K.scope)K.templateUrl||(J(V)?(Na("new/isolated scope",P||F,K,w),P=K):Na("new/isolated scope",P,K,w)),F=F||K;da=K.name;!K.templateUrl&&K.controller&&(V=K.controller,S=S||{},Na("'"+da+"' controller",S[da],K,w),S[da]=K);if(V=K.transclude)ka=!0,K.$$tlb||(Na("transclusion",fa,K,w),fa=K),"element"==V?(E=!0,I=K.priority,V=w,w=e.$$element=A(W.createComment(" "+da+": "+ +e[da]+" ")),d=w[0],T(g,Za.call(V,0),d),fb=D(V,f,I,k&&k.name,{nonTlbTranscludeDirective:fa})):(V=A(Ub(d)).contents(),w.empty(),fb=D(V,f));if(K.template)if(x=!0,Na("template",ma,K,w),ma=K,V=G(K.template)?K.template(w,e):K.template,V=Tc(V),K.replace){k=K;V=Sb.test(V)?Uc(Xb(K.templateNamespace,N(V))):[];d=V[0];if(1!=V.length||d.nodeType!==qa)throw la("tplrt",da,"");T(g,w,d);Q={$attr:{}};V=X(d,[],Q);var aa=a.splice(z+1,a.length-(z+1));P&&y(V);a=a.concat(V).concat(aa);R(e,Q);Q=a.length}else w.html(V);if(K.templateUrl)x= +!0,Na("template",ma,K,w),ma=K,K.replace&&(k=K),B=of(a.splice(z,a.length-z),w,e,g,ka&&fb,l,p,{controllerDirectives:S,newIsolateScopeDirective:P,templateDirective:ma,nonTlbTranscludeDirective:fa}),Q=a.length;else if(K.compile)try{za=K.compile(w,e,fb),G(za)?s(null,za,Oa,U):za&&s(za.pre,za.post,Oa,U)}catch(pf){c(pf,wa(w))}K.terminal&&(B.terminal=!0,I=Math.max(I,K.priority))}B.scope=F&&!0===F.scope;B.transcludeOnThisElement=ka;B.elementTranscludeOnThisElement=E;B.templateOnThisElement=x;B.transclude=fb; +m.hasElementTranscludeDirective=E;return B}function y(a){for(var b=0,c=a.length;b<c;b++)a[b]=Ob(a[b],{$$isolateScope:!0})}function ka(b,e,f,g,h,k,l){if(e===h)return null;h=null;if(d.hasOwnProperty(e)){var q;e=a.get(e+"Directive");for(var m=0,s=e.length;m<s;m++)try{q=e[m],(g===t||g>q.priority)&&-1!=q.restrict.indexOf(f)&&(k&&(q=Ob(q,{$$start:k,$$end:l})),b.push(q),h=q)}catch(M){c(M)}}return h}function x(b){if(d.hasOwnProperty(b))for(var c=a.get(b+"Directive"),e=0,f=c.length;e<f;e++)if(b=c[e],b.multiElement)return!0; +return!1}function R(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;r(a,function(d,e){"$"!=e.charAt(0)&&(b[e]&&b[e]!==d&&(d+=("style"===e?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});r(b,function(b,f){"class"==f?(I(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):"style"==f?(e.attr("style",e.attr("style")+";"+b),a.style=(a.style?a.style+";":"")+b):"$"==f.charAt(0)||a.hasOwnProperty(f)||(a[f]=b,d[f]=c[f])})}function of(a,b,c,d,e,f,g,h){var k=[],l,q,p=b[0],m=a.shift(),M=Ob(m,{templateUrl:null,transclude:null, +replace:null,$$originalDirective:m}),u=G(m.templateUrl)?m.templateUrl(b,c):m.templateUrl,L=m.templateNamespace;b.empty();s(Z.getTrustedResourceUrl(u)).then(function(s){var B,v;s=Tc(s);if(m.replace){s=Sb.test(s)?Uc(Xb(L,N(s))):[];B=s[0];if(1!=s.length||B.nodeType!==qa)throw la("tplrt",m.name,u);s={$attr:{}};T(d,b,B);var D=X(B,[],s);J(m.scope)&&y(D);a=D.concat(a);R(c,s)}else B=p,b.html(s);a.unshift(M);l=fa(a,B,c,e,b,m,f,g,h);r(d,function(a,c){a==B&&(d[c]=b[0])});for(q=S(b[0].childNodes,e);k.length;){s= +k.shift();v=k.shift();var F=k.shift(),O=k.shift(),D=b[0];if(!s.$$destroyed){if(v!==p){var Z=v.className;h.hasElementTranscludeDirective&&m.replace||(D=Ub(B));T(F,A(v),D);I(A(D),Z)}v=l.transcludeOnThisElement?P(s,l.transclude,O):O;l(q,s,D,d,v)}}k=null});return function(a,b,c,d,e){a=e;b.$$destroyed||(k?k.push(b,c,d,a):(l.transcludeOnThisElement&&(a=P(b,l.transclude,e)),l(q,b,c,d,a)))}}function da(a,b){var c=b.priority-a.priority;return 0!==c?c:a.name!==b.name?a.name<b.name?-1:1:a.index-b.index}function Na(a, +b,c,d){if(b)throw la("multidir",b.name,c.name,a,wa(d));}function za(a,c){var d=b(c,!0);d&&a.push({priority:0,compile:function(a){a=a.parent();var b=!!a.length;b&&D.$$addBindingClass(a);return function(a,c){var e=c.parent();b||D.$$addBindingClass(e);D.$$addBindingInfo(e,d.expressions);a.$watch(d,function(a){c[0].nodeValue=a})}}})}function Xb(a,b){a=z(a||"html");switch(a){case "svg":case "math":var c=W.createElement("div");c.innerHTML="<"+a+">"+b+"</"+a+">";return c.childNodes[0].childNodes;default:return b}} +function Q(a,b){if("srcdoc"==b)return Z.HTML;var c=va(a);if("xlinkHref"==b||"form"==c&&"action"==b||"img"!=c&&("src"==b||"ngSrc"==b))return Z.RESOURCE_URL}function Oa(a,c,d,e,f){var h=Q(a,e);f=g[e]||f;var k=b(d,!0,h,f);if(k){if("multiple"===e&&"select"===va(a))throw la("selmulti",wa(a));c.push({priority:100,compile:function(){return{pre:function(a,c,g){c=g.$$observers||(g.$$observers={});if(l.test(e))throw la("nodomevents");var m=g[e];m!==d&&(k=m&&b(m,!0,h,f),d=m);k&&(g[e]=k(a),(c[e]||(c[e]=[])).$$inter= +!0,(g.$$observers&&g.$$observers[e].$$scope||a).$watch(k,function(a,b){"class"===e&&a!=b?g.$updateClass(a,b):g.$set(e,a)}))}}}})}}function T(a,b,c){var d=b[0],e=b.length,f=d.parentNode,g,h;if(a)for(g=0,h=a.length;g<h;g++)if(a[g]==d){a[g++]=c;h=g+e-1;for(var k=a.length;g<k;g++,h++)h<k?a[g]=a[h]:delete a[g];a.length-=e-1;a.context===d&&(a.context=c);break}f&&f.replaceChild(c,d);a=W.createDocumentFragment();a.appendChild(d);A(c).data(A(d).data());ta?(Qb=!0,ta.cleanData([d])):delete A.cache[d[A.expando]]; +d=1;for(e=b.length;d<e;d++)f=b[d],A(f).remove(),a.appendChild(f),delete b[d];b[0]=c;b.length=1}function Y(a,b){return w(function(){return a.apply(null,arguments)},a,b)}function $(a,b,d,e,f,g){try{a(b,d,e,f,g)}catch(h){c(h,wa(d))}}var Yb=function(a,b){if(b){var c=Object.keys(b),d,e,f;d=0;for(e=c.length;d<e;d++)f=c[d],this[f]=b[f]}else this.$attr={};this.$$element=a};Yb.prototype={$normalize:xa,$addClass:function(a){a&&0<a.length&&L.addClass(this.$$element,a)},$removeClass:function(a){a&&0<a.length&& +L.removeClass(this.$$element,a)},$updateClass:function(a,b){var c=Vc(a,b);c&&c.length&&L.addClass(this.$$element,c);(c=Vc(b,a))&&c.length&&L.removeClass(this.$$element,c)},$set:function(a,b,d,e){var f=this.$$element[0],g=Nc(f,a),h=kf(f,a),f=a;g?(this.$$element.prop(a,b),e=g):h&&(this[h]=b,f=h);this[a]=b;e?this.$attr[a]=e:(e=this.$attr[a])||(this.$attr[a]=e=vc(a,"-"));g=va(this.$$element);if("a"===g&&"href"===a||"img"===g&&"src"===a)this[a]=b=B(b,"src"===a);else if("img"===g&&"srcset"===a){for(var g= +"",h=N(b),k=/(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/,k=/\s/.test(h)?k:/(,)/,h=h.split(k),k=Math.floor(h.length/2),l=0;l<k;l++)var q=2*l,g=g+B(N(h[q]),!0),g=g+(" "+N(h[q+1]));h=N(h[2*l]).split(/\s/);g+=B(N(h[0]),!0);2===h.length&&(g+=" "+N(h[1]));this[a]=b=g}!1!==d&&(null===b||b===t?this.$$element.removeAttr(e):this.$$element.attr(e,b));(a=this.$$observers)&&r(a[f],function(a){try{a(b)}catch(d){c(d)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers=ia()),e=d[a]||(d[a]=[]);e.push(b); +m.$evalAsync(function(){!e.$$inter&&c.hasOwnProperty(a)&&b(c[a])});return function(){Xa(e,b)}}};var V=b.startSymbol(),ma=b.endSymbol(),Tc="{{"==V||"}}"==ma?ra:function(a){return a.replace(/\{\{/g,V).replace(/}}/g,ma)},U=/^ngAttr[A-Z]/;D.$$addBindingInfo=k?function(a,b){var c=a.data("$binding")||[];H(b)?c=c.concat(b):c.push(b);a.data("$binding",c)}:E;D.$$addBindingClass=k?function(a){I(a,"ng-binding")}:E;D.$$addScopeInfo=k?function(a,b,c,d){a.data(c?d?"$isolateScopeNoTemplate":"$isolateScope":"$scope", +b)}:E;D.$$addScopeClass=k?function(a,b){I(a,b?"ng-isolate-scope":"ng-scope")}:E;return D}]}function xa(b){return db(b.replace(Sc,""))}function Vc(b,a){var c="",d=b.split(/\s+/),e=a.split(/\s+/),f=0;a:for(;f<d.length;f++){for(var g=d[f],h=0;h<e.length;h++)if(g==e[h])continue a;c+=(0<c.length?" ":"")+g}return c}function Uc(b){b=A(b);var a=b.length;if(1>=a)return b;for(;a--;)8===b[a].nodeType&&qf.call(b,a,1);return b}function Fe(){var b={},a=!1,c=/^(\S+)(\s+as\s+(\w+))?$/;this.register=function(a,c){La(a, +"controller");J(a)?w(b,a):b[a]=c};this.allowGlobals=function(){a=!0};this.$get=["$injector","$window",function(d,e){function f(a,b,c,d){if(!a||!J(a.$scope))throw R("$controller")("noscp",d,b);a.$scope[b]=c}return function(g,h,l,k){var n,p,q;l=!0===l;k&&C(k)&&(q=k);if(C(g)){k=g.match(c);if(!k)throw rf("ctrlfmt",g);p=k[1];q=q||k[3];g=b.hasOwnProperty(p)?b[p]:xc(h.$scope,p,!0)||(a?xc(e,p,!0):t);sb(g,p,!0)}if(l)return l=(H(g)?g[g.length-1]:g).prototype,n=Object.create(l||null),q&&f(h,q,n,p||g.name),w(function(){d.invoke(g, +n,h,p);return n},{instance:n,identifier:q});n=d.instantiate(g,h,p);q&&f(h,q,n,p||g.name);return n}}]}function Ge(){this.$get=["$window",function(b){return A(b.document)}]}function He(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Zb(b,a){if(C(b)){var c=b.replace(sf,"").trim();if(c){var d=a("Content-Type");(d=d&&0===d.indexOf(Wc))||(d=(d=c.match(tf))&&uf[d[0]].test(c));d&&(b=qc(c))}}return b}function Xc(b){var a=ia(),c,d,e;if(!b)return a;r(b.split("\n"), +function(b){e=b.indexOf(":");c=z(N(b.substr(0,e)));d=N(b.substr(e+1));c&&(a[c]=a[c]?a[c]+", "+d:d)});return a}function Yc(b){var a=J(b)?b:t;return function(c){a||(a=Xc(b));return c?(c=a[z(c)],void 0===c&&(c=null),c):a}}function Zc(b,a,c,d){if(G(d))return d(b,a,c);r(d,function(d){b=d(b,a,c)});return b}function Ke(){var b=this.defaults={transformResponse:[Zb],transformRequest:[function(a){return J(a)&&"[object File]"!==Ca.call(a)&&"[object Blob]"!==Ca.call(a)&&"[object FormData]"!==Ca.call(a)?$a(a): +a}],headers:{common:{Accept:"application/json, text/plain, */*"},post:sa($b),put:sa($b),patch:sa($b)},xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN"},a=!1;this.useApplyAsync=function(b){return y(b)?(a=!!b,this):a};var c=this.interceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(d,e,f,g,h,l){function k(a){function c(a){var b=w({},a);b.data=a.data?Zc(a.data,a.headers,a.status,e.transformResponse):a.data;a=a.status;return 200<=a&&300>a? +b:h.reject(b)}function d(a){var b,c={};r(a,function(a,d){G(a)?(b=a(),null!=b&&(c[d]=b)):c[d]=a});return c}if(!ca.isObject(a))throw R("$http")("badreq",a);var e=w({method:"get",transformRequest:b.transformRequest,transformResponse:b.transformResponse},a);e.headers=function(a){var c=b.headers,e=w({},a.headers),f,g,c=w({},c.common,c[z(a.method)]);a:for(f in c){a=z(f);for(g in e)if(z(g)===a)continue a;e[f]=c[f]}return d(e)}(a);e.method=ub(e.method);var f=[function(a){var d=a.headers,e=Zc(a.data,Yc(d), +t,a.transformRequest);x(e)&&r(d,function(a,b){"content-type"===z(b)&&delete d[b]});x(a.withCredentials)&&!x(b.withCredentials)&&(a.withCredentials=b.withCredentials);return n(a,e).then(c,c)},t],g=h.when(e);for(r(u,function(a){(a.request||a.requestError)&&f.unshift(a.request,a.requestError);(a.response||a.responseError)&&f.push(a.response,a.responseError)});f.length;){a=f.shift();var k=f.shift(),g=g.then(a,k)}g.success=function(a){g.then(function(b){a(b.data,b.status,b.headers,e)});return g};g.error= +function(a){g.then(null,function(b){a(b.data,b.status,b.headers,e)});return g};return g}function n(c,f){function l(b,c,d,e){function f(){m(c,b,d,e)}I&&(200<=b&&300>b?I.put(P,[b,c,Xc(d),e]):I.remove(P));a?g.$applyAsync(f):(f(),g.$$phase||g.$apply())}function m(a,b,d,e){b=Math.max(b,0);(200<=b&&300>b?L.resolve:L.reject)({data:a,status:b,headers:Yc(d),config:c,statusText:e})}function n(a){m(a.data,a.status,sa(a.headers()),a.statusText)}function u(){var a=k.pendingRequests.indexOf(c);-1!==a&&k.pendingRequests.splice(a, +1)}var L=h.defer(),B=L.promise,I,D,S=c.headers,P=p(c.url,c.params);k.pendingRequests.push(c);B.then(u,u);!c.cache&&!b.cache||!1===c.cache||"GET"!==c.method&&"JSONP"!==c.method||(I=J(c.cache)?c.cache:J(b.cache)?b.cache:q);I&&(D=I.get(P),y(D)?D&&G(D.then)?D.then(n,n):H(D)?m(D[1],D[0],sa(D[2]),D[3]):m(D,200,{},"OK"):I.put(P,B));x(D)&&((D=$c(c.url)?e.cookies()[c.xsrfCookieName||b.xsrfCookieName]:t)&&(S[c.xsrfHeaderName||b.xsrfHeaderName]=D),d(c.method,P,f,l,S,c.timeout,c.withCredentials,c.responseType)); +return B}function p(a,b){if(!b)return a;var c=[];Ed(b,function(a,b){null===a||x(a)||(H(a)||(a=[a]),r(a,function(a){J(a)&&(a=ga(a)?a.toISOString():$a(a));c.push(Ea(b)+"="+Ea(a))}))});0<c.length&&(a+=(-1==a.indexOf("?")?"?":"&")+c.join("&"));return a}var q=f("$http"),u=[];r(c,function(a){u.unshift(C(a)?l.get(a):l.invoke(a))});k.pendingRequests=[];(function(a){r(arguments,function(a){k[a]=function(b,c){return k(w(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){r(arguments,function(a){k[a]= +function(b,c,d){return k(w(d||{},{method:a,url:b,data:c}))}})})("post","put","patch");k.defaults=b;return k}]}function vf(){return new Q.XMLHttpRequest}function Le(){this.$get=["$browser","$window","$document",function(b,a,c){return wf(b,vf,b.defer,a.angular.callbacks,c[0])}]}function wf(b,a,c,d,e){function f(a,b,c){var f=e.createElement("script"),n=null;f.type="text/javascript";f.src=a;f.async=!0;n=function(a){f.removeEventListener("load",n,!1);f.removeEventListener("error",n,!1);e.body.removeChild(f); +f=null;var g=-1,u="unknown";a&&("load"!==a.type||d[b].called||(a={type:"error"}),u=a.type,g="error"===a.type?404:200);c&&c(g,u)};f.addEventListener("load",n,!1);f.addEventListener("error",n,!1);e.body.appendChild(f);return n}return function(e,h,l,k,n,p,q,u){function s(){m&&m();F&&F.abort()}function M(a,d,e,f,g){L!==t&&c.cancel(L);m=F=null;a(d,e,f,g);b.$$completeOutstandingRequest(E)}b.$$incOutstandingRequestCount();h=h||b.url();if("jsonp"==z(e)){var v="_"+(d.counter++).toString(36);d[v]=function(a){d[v].data= +a;d[v].called=!0};var m=f(h.replace("JSON_CALLBACK","angular.callbacks."+v),v,function(a,b){M(k,a,d[v].data,"",b);d[v]=E})}else{var F=a();F.open(e,h,!0);r(n,function(a,b){y(a)&&F.setRequestHeader(b,a)});F.onload=function(){var a=F.statusText||"",b="response"in F?F.response:F.responseText,c=1223===F.status?204:F.status;0===c&&(c=b?200:"file"==Aa(h).protocol?404:0);M(k,c,b,F.getAllResponseHeaders(),a)};e=function(){M(k,-1,null,null,"")};F.onerror=e;F.onabort=e;q&&(F.withCredentials=!0);if(u)try{F.responseType= +u}catch(Z){if("json"!==u)throw Z;}F.send(l||null)}if(0<p)var L=c(s,p);else p&&G(p.then)&&p.then(s)}}function Ie(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse","$exceptionHandler","$sce",function(c,d,e){function f(a){return"\\\\\\"+a}function g(f,g,u,s){function M(c){return c.replace(k,b).replace(n,a)}function v(a){try{var b=a;a=u?e.getTrusted(u,b):e.valueOf(b);var c;if(s&&!y(a))c=a;else if(null==a)c=""; +else{switch(typeof a){case "string":break;case "number":a=""+a;break;default:a=$a(a)}c=a}return c}catch(g){c=ac("interr",f,g.toString()),d(c)}}s=!!s;for(var m,F,r=0,L=[],B=[],I=f.length,D=[],S=[];r<I;)if(-1!=(m=f.indexOf(b,r))&&-1!=(F=f.indexOf(a,m+h)))r!==m&&D.push(M(f.substring(r,m))),r=f.substring(m+h,F),L.push(r),B.push(c(r,v)),r=F+l,S.push(D.length),D.push("");else{r!==I&&D.push(M(f.substring(r)));break}if(u&&1<D.length)throw ac("noconcat",f);if(!g||L.length){var P=function(a){for(var b=0,c= +L.length;b<c;b++){if(s&&x(a[b]))return;D[S[b]]=a[b]}return D.join("")};return w(function(a){var b=0,c=L.length,e=Array(c);try{for(;b<c;b++)e[b]=B[b](a);return P(e)}catch(g){a=ac("interr",f,g.toString()),d(a)}},{exp:f,expressions:L,$$watchDelegate:function(a,b,c){var d;return a.$watchGroup(B,function(c,e){var f=P(c);G(b)&&b.call(this,f,c!==e?d:f,a);d=f},c)}})}}var h=b.length,l=a.length,k=new RegExp(b.replace(/./g,f),"g"),n=new RegExp(a.replace(/./g,f),"g");g.startSymbol=function(){return b};g.endSymbol= +function(){return a};return g}]}function Je(){this.$get=["$rootScope","$window","$q","$$q",function(b,a,c,d){function e(e,h,l,k){var n=a.setInterval,p=a.clearInterval,q=0,u=y(k)&&!k,s=(u?d:c).defer(),M=s.promise;l=y(l)?l:0;M.then(null,null,e);M.$$intervalId=n(function(){s.notify(q++);0<l&&q>=l&&(s.resolve(q),p(M.$$intervalId),delete f[M.$$intervalId]);u||b.$apply()},h);f[M.$$intervalId]=s;return M}var f={};e.cancel=function(b){return b&&b.$$intervalId in f?(f[b.$$intervalId].reject("canceled"),a.clearInterval(b.$$intervalId), +delete f[b.$$intervalId],!0):!1};return e}]}function Rd(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January February March April May June July August September October November December".split(" "),SHORTMONTH:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "), +DAY:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),SHORTDAY:"Sun Mon Tue Wed Thu Fri Sat".split(" "),AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a",ERANAMES:["Before Christ","Anno Domini"],ERAS:["BC","AD"]},pluralCat:function(b){return 1===b?"one":"other"}}}}function bc(b){b=b.split("/");for(var a=b.length;a--;)b[a]=qb(b[a]); +return b.join("/")}function ad(b,a){var c=Aa(b);a.$$protocol=c.protocol;a.$$host=c.hostname;a.$$port=aa(c.port)||xf[c.protocol]||null}function bd(b,a){var c="/"!==b.charAt(0);c&&(b="/"+b);var d=Aa(b);a.$$path=decodeURIComponent(c&&"/"===d.pathname.charAt(0)?d.pathname.substring(1):d.pathname);a.$$search=sc(d.search);a.$$hash=decodeURIComponent(d.hash);a.$$path&&"/"!=a.$$path.charAt(0)&&(a.$$path="/"+a.$$path)}function ya(b,a){if(0===a.indexOf(b))return a.substr(b.length)}function Ga(b){var a=b.indexOf("#"); +return-1==a?b:b.substr(0,a)}function Fb(b){return b.replace(/(#.+)|#$/,"$1")}function cc(b){return b.substr(0,Ga(b).lastIndexOf("/")+1)}function dc(b,a){this.$$html5=!0;a=a||"";var c=cc(b);ad(b,this);this.$$parse=function(a){var b=ya(c,a);if(!C(b))throw Gb("ipthprfx",a,c);bd(b,this);this.$$path||(this.$$path="/");this.$$compose()};this.$$compose=function(){var a=Pb(this.$$search),b=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(a?"?"+a:"")+b;this.$$absUrl=c+this.$$url.substr(1)};this.$$parseLinkUrl= +function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;(f=ya(b,d))!==t?(g=f,g=(f=ya(a,f))!==t?c+(ya("/",f)||f):b+g):(f=ya(c,d))!==t?g=c+f:c==d+"/"&&(g=c);g&&this.$$parse(g);return!!g}}function ec(b,a){var c=cc(b);ad(b,this);this.$$parse=function(d){d=ya(b,d)||ya(c,d);var e;"#"===d.charAt(0)?(e=ya(a,d),x(e)&&(e=d)):e=this.$$html5?d:"";bd(e,this);d=this.$$path;var f=/^\/[A-Z]:(\/.*)/;0===e.indexOf(b)&&(e=e.replace(b,""));f.exec(e)||(d=(e=f.exec(d))?e[1]:d);this.$$path=d;this.$$compose()}; +this.$$compose=function(){var c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+(this.$$url?a+this.$$url:"")};this.$$parseLinkUrl=function(a,c){return Ga(b)==Ga(a)?(this.$$parse(a),!0):!1}}function cd(b,a){this.$$html5=!0;ec.apply(this,arguments);var c=cc(b);this.$$parseLinkUrl=function(d,e){if(e&&"#"===e[0])return this.hash(e.slice(1)),!0;var f,g;b==Ga(d)?f=d:(g=ya(c,d))?f=b+a+g:c===d+"/"&&(f=c);f&&this.$$parse(f);return!!f};this.$$compose= +function(){var c=Pb(this.$$search),e=this.$$hash?"#"+qb(this.$$hash):"";this.$$url=bc(this.$$path)+(c?"?"+c:"")+e;this.$$absUrl=b+a+this.$$url}}function Hb(b){return function(){return this[b]}}function dd(b,a){return function(c){if(x(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Me(){var b="",a={enabled:!1,requireBase:!0,rewriteLinks:!0};this.hashPrefix=function(a){return y(a)?(b=a,this):b};this.html5Mode=function(b){return Wa(b)?(a.enabled=b,this):J(b)?(Wa(b.enabled)&&(a.enabled= +b.enabled),Wa(b.requireBase)&&(a.requireBase=b.requireBase),Wa(b.rewriteLinks)&&(a.rewriteLinks=b.rewriteLinks),this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement","$window",function(c,d,e,f,g){function h(a,b,c){var e=k.url(),f=k.$$state;try{d.url(a,b,c),k.$$state=d.state()}catch(g){throw k.url(e),k.$$state=f,g;}}function l(a,b){c.$broadcast("$locationChangeSuccess",k.absUrl(),a,k.$$state,b)}var k,n;n=d.baseHref();var p=d.url(),q;if(a.enabled){if(!n&&a.requireBase)throw Gb("nobase"); +q=p.substring(0,p.indexOf("/",p.indexOf("//")+2))+(n||"/");n=e.history?dc:cd}else q=Ga(p),n=ec;k=new n(q,"#"+b);k.$$parseLinkUrl(p,p);k.$$state=d.state();var u=/^\s*(javascript|mailto):/i;f.on("click",function(b){if(a.rewriteLinks&&!b.ctrlKey&&!b.metaKey&&!b.shiftKey&&2!=b.which&&2!=b.button){for(var e=A(b.target);"a"!==va(e[0]);)if(e[0]===f[0]||!(e=e.parent())[0])return;var h=e.prop("href"),l=e.attr("href")||e.attr("xlink:href");J(h)&&"[object SVGAnimatedString]"===h.toString()&&(h=Aa(h.animVal).href); +u.test(h)||!h||e.attr("target")||b.isDefaultPrevented()||!k.$$parseLinkUrl(h,l)||(b.preventDefault(),k.absUrl()!=d.url()&&(c.$apply(),g.angular["ff-684208-preventDefault"]=!0))}});Fb(k.absUrl())!=Fb(p)&&d.url(k.absUrl(),!0);var s=!0;d.onUrlChange(function(a,b){c.$evalAsync(function(){var d=k.absUrl(),e=k.$$state,f;k.$$parse(a);k.$$state=b;f=c.$broadcast("$locationChangeStart",a,d,b,e).defaultPrevented;k.absUrl()===a&&(f?(k.$$parse(d),k.$$state=e,h(d,!1,e)):(s=!1,l(d,e)))});c.$$phase||c.$digest()}); +c.$watch(function(){var a=Fb(d.url()),b=Fb(k.absUrl()),f=d.state(),g=k.$$replace,q=a!==b||k.$$html5&&e.history&&f!==k.$$state;if(s||q)s=!1,c.$evalAsync(function(){var b=k.absUrl(),d=c.$broadcast("$locationChangeStart",b,a,k.$$state,f).defaultPrevented;k.absUrl()===b&&(d?(k.$$parse(a),k.$$state=f):(q&&h(b,g,f===k.$$state?null:k.$$state),l(a,f)))});k.$$replace=!1});return k}]}function Ne(){var b=!0,a=this;this.debugEnabled=function(a){return y(a)?(b=a,this):b};this.$get=["$window",function(c){function d(a){a instanceof +Error&&(a.stack?a=a.message&&-1===a.stack.indexOf(a.message)?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function e(a){var b=c.console||{},e=b[a]||b.log||E;a=!1;try{a=!!e.apply}catch(l){}return a?function(){var a=[];r(arguments,function(b){a.push(d(b))});return e.apply(b,a)}:function(a,b){e(a,null==b?"":b)}}return{log:e("log"),info:e("info"),warn:e("warn"),error:e("error"),debug:function(){var c=e("debug");return function(){b&&c.apply(a, +arguments)}}()}}]}function ua(b,a){if("__defineGetter__"===b||"__defineSetter__"===b||"__lookupGetter__"===b||"__lookupSetter__"===b||"__proto__"===b)throw na("isecfld",a);return b}function oa(b,a){if(b){if(b.constructor===b)throw na("isecfn",a);if(b.window===b)throw na("isecwindow",a);if(b.children&&(b.nodeName||b.prop&&b.attr&&b.find))throw na("isecdom",a);if(b===Object)throw na("isecobj",a);}return b}function fc(b){return b.constant}function hb(b,a,c,d,e){oa(b,e);oa(a,e);c=c.split(".");for(var f, +g=0;1<c.length;g++){f=ua(c.shift(),e);var h=0===g&&a&&a[f]||b[f];h||(h={},b[f]=h);b=oa(h,e)}f=ua(c.shift(),e);oa(b[f],e);return b[f]=d}function Pa(b){return"constructor"==b}function ed(b,a,c,d,e,f,g){ua(b,f);ua(a,f);ua(c,f);ua(d,f);ua(e,f);var h=function(a){return oa(a,f)},l=g||Pa(b)?h:ra,k=g||Pa(a)?h:ra,n=g||Pa(c)?h:ra,p=g||Pa(d)?h:ra,q=g||Pa(e)?h:ra;return function(f,g){var h=g&&g.hasOwnProperty(b)?g:f;if(null==h)return h;h=l(h[b]);if(!a)return h;if(null==h)return t;h=k(h[a]);if(!c)return h;if(null== +h)return t;h=n(h[c]);if(!d)return h;if(null==h)return t;h=p(h[d]);return e?null==h?t:h=q(h[e]):h}}function yf(b,a){return function(c,d){return b(c,d,oa,a)}}function zf(b,a,c){var d=a.expensiveChecks,e=d?Af:Bf,f=e[b];if(f)return f;var g=b.split("."),h=g.length;if(a.csp)f=6>h?ed(g[0],g[1],g[2],g[3],g[4],c,d):function(a,b){var e=0,f;do f=ed(g[e++],g[e++],g[e++],g[e++],g[e++],c,d)(a,b),b=t,a=f;while(e<h);return f};else{var l="";d&&(l+="s = eso(s, fe);\nl = eso(l, fe);\n");var k=d;r(g,function(a,b){ua(a, +c);var e=(b?"s":'((l&&l.hasOwnProperty("'+a+'"))?l:s)')+"."+a;if(d||Pa(a))e="eso("+e+", fe)",k=!0;l+="if(s == null) return undefined;\ns="+e+";\n"});l+="return s;";a=new Function("s","l","eso","fe",l);a.toString=ea(l);k&&(a=yf(a,c));f=a}f.sharedGetter=!0;f.assign=function(a,c,d){return hb(a,d,b,c,b)};return e[b]=f}function gc(b){return G(b.valueOf)?b.valueOf():Cf.call(b)}function Oe(){var b=ia(),a=ia();this.$get=["$filter","$sniffer",function(c,d){function e(a){var b=a;a.sharedGetter&&(b=function(b, +c){return a(b,c)},b.literal=a.literal,b.constant=a.constant,b.assign=a.assign);return b}function f(a,b){for(var c=0,d=a.length;c<d;c++){var e=a[c];e.constant||(e.inputs?f(e.inputs,b):-1===b.indexOf(e)&&b.push(e))}return b}function g(a,b){return null==a||null==b?a===b:"object"===typeof a&&(a=gc(a),"object"===typeof a)?!1:a===b||a!==a&&b!==b}function h(a,b,c,d){var e=d.$$inputs||(d.$$inputs=f(d.inputs,[])),h;if(1===e.length){var k=g,e=e[0];return a.$watch(function(a){var b=e(a);g(b,k)||(h=d(a),k=b&& +gc(b));return h},b,c)}for(var l=[],q=0,p=e.length;q<p;q++)l[q]=g;return a.$watch(function(a){for(var b=!1,c=0,f=e.length;c<f;c++){var k=e[c](a);if(b||(b=!g(k,l[c])))l[c]=k&&gc(k)}b&&(h=d(a));return h},b,c)}function l(a,b,c,d){var e,f;return e=a.$watch(function(a){return d(a)},function(a,c,d){f=a;G(b)&&b.apply(this,arguments);y(a)&&d.$$postDigest(function(){y(f)&&e()})},c)}function k(a,b,c,d){function e(a){var b=!0;r(a,function(a){y(a)||(b=!1)});return b}var f,g;return f=a.$watch(function(a){return d(a)}, +function(a,c,d){g=a;G(b)&&b.call(this,a,c,d);e(a)&&d.$$postDigest(function(){e(g)&&f()})},c)}function n(a,b,c,d){var e;return e=a.$watch(function(a){return d(a)},function(a,c,d){G(b)&&b.apply(this,arguments);e()},c)}function p(a,b){if(!b)return a;var c=a.$$watchDelegate,c=c!==k&&c!==l?function(c,d){var e=a(c,d);return b(e,c,d)}:function(c,d){var e=a(c,d),f=b(e,c,d);return y(e)?f:e};a.$$watchDelegate&&a.$$watchDelegate!==h?c.$$watchDelegate=a.$$watchDelegate:b.$stateful||(c.$$watchDelegate=h,c.inputs= +[a]);return c}var q={csp:d.csp,expensiveChecks:!1},u={csp:d.csp,expensiveChecks:!0};return function(d,f,g){var m,r,t;switch(typeof d){case "string":t=d=d.trim();var L=g?a:b;m=L[t];m||(":"===d.charAt(0)&&":"===d.charAt(1)&&(r=!0,d=d.substring(2)),g=g?u:q,m=new hc(g),m=(new ib(m,c,g)).parse(d),m.constant?m.$$watchDelegate=n:r?(m=e(m),m.$$watchDelegate=m.literal?k:l):m.inputs&&(m.$$watchDelegate=h),L[t]=m);return p(m,f);case "function":return p(d,f);default:return p(E,f)}}}]}function Qe(){this.$get= +["$rootScope","$exceptionHandler",function(b,a){return fd(function(a){b.$evalAsync(a)},a)}]}function Re(){this.$get=["$browser","$exceptionHandler",function(b,a){return fd(function(a){b.defer(a)},a)}]}function fd(b,a){function c(a,b,c){function d(b){return function(c){e||(e=!0,b.call(a,c))}}var e=!1;return[d(b),d(c)]}function d(){this.$$state={status:0}}function e(a,b){return function(c){b.call(a,c)}}function f(c){!c.processScheduled&&c.pending&&(c.processScheduled=!0,b(function(){var b,d,e;e=c.pending; +c.processScheduled=!1;c.pending=t;for(var f=0,g=e.length;f<g;++f){d=e[f][0];b=e[f][c.status];try{G(b)?d.resolve(b(c.value)):1===c.status?d.resolve(c.value):d.reject(c.value)}catch(h){d.reject(h),a(h)}}}))}function g(){this.promise=new d;this.resolve=e(this,this.resolve);this.reject=e(this,this.reject);this.notify=e(this,this.notify)}var h=R("$q",TypeError);d.prototype={then:function(a,b,c){var d=new g;this.$$state.pending=this.$$state.pending||[];this.$$state.pending.push([d,a,b,c]);0<this.$$state.status&& +f(this.$$state);return d.promise},"catch":function(a){return this.then(null,a)},"finally":function(a,b){return this.then(function(b){return k(b,!0,a)},function(b){return k(b,!1,a)},b)}};g.prototype={resolve:function(a){this.promise.$$state.status||(a===this.promise?this.$$reject(h("qcycle",a)):this.$$resolve(a))},$$resolve:function(b){var d,e;e=c(this,this.$$resolve,this.$$reject);try{if(J(b)||G(b))d=b&&b.then;G(d)?(this.promise.$$state.status=-1,d.call(b,e[0],e[1],this.notify)):(this.promise.$$state.value= +b,this.promise.$$state.status=1,f(this.promise.$$state))}catch(g){e[1](g),a(g)}},reject:function(a){this.promise.$$state.status||this.$$reject(a)},$$reject:function(a){this.promise.$$state.value=a;this.promise.$$state.status=2;f(this.promise.$$state)},notify:function(c){var d=this.promise.$$state.pending;0>=this.promise.$$state.status&&d&&d.length&&b(function(){for(var b,e,f=0,g=d.length;f<g;f++){e=d[f][0];b=d[f][3];try{e.notify(G(b)?b(c):c)}catch(h){a(h)}}})}};var l=function(a,b){var c=new g;b?c.resolve(a): +c.reject(a);return c.promise},k=function(a,b,c){var d=null;try{G(c)&&(d=c())}catch(e){return l(e,!1)}return d&&G(d.then)?d.then(function(){return l(a,b)},function(a){return l(a,!1)}):l(a,b)},n=function(a,b,c,d){var e=new g;e.resolve(a);return e.promise.then(b,c,d)},p=function u(a){if(!G(a))throw h("norslvr",a);if(!(this instanceof u))return new u(a);var b=new g;a(function(a){b.resolve(a)},function(a){b.reject(a)});return b.promise};p.defer=function(){return new g};p.reject=function(a){var b=new g; +b.reject(a);return b.promise};p.when=n;p.all=function(a){var b=new g,c=0,d=H(a)?[]:{};r(a,function(a,e){c++;n(a).then(function(a){d.hasOwnProperty(e)||(d[e]=a,--c||b.resolve(d))},function(a){d.hasOwnProperty(e)||b.reject(a)})});0===c&&b.resolve(d);return b.promise};return p}function $e(){this.$get=["$window","$timeout",function(b,a){var c=b.requestAnimationFrame||b.webkitRequestAnimationFrame,d=b.cancelAnimationFrame||b.webkitCancelAnimationFrame||b.webkitCancelRequestAnimationFrame,e=!!c,f=e?function(a){var b= +c(a);return function(){d(b)}}:function(b){var c=a(b,16.66,!1);return function(){a.cancel(c)}};f.supported=e;return f}]}function Pe(){function b(a){function b(){this.$$watchers=this.$$nextSibling=this.$$childHead=this.$$childTail=null;this.$$listeners={};this.$$listenerCount={};this.$$watchersCount=0;this.$id=++ob;this.$$ChildScope=null}b.prototype=a;return b}var a=10,c=R("$rootScope"),d=null,e=null;this.digestTtl=function(b){arguments.length&&(a=b);return a};this.$get=["$injector","$exceptionHandler", +"$parse","$browser",function(f,g,h,l){function k(a){a.currentScope.$$destroyed=!0}function n(){this.$id=++ob;this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this.$root=this;this.$$destroyed=!1;this.$$listeners={};this.$$listenerCount={};this.$$isolateBindings=null}function p(a){if(v.$$phase)throw c("inprog",v.$$phase);v.$$phase=a}function q(a,b,c){do a.$$listenerCount[c]-=b,0===a.$$listenerCount[c]&&delete a.$$listenerCount[c]; +while(a=a.$parent)}function u(){}function s(){for(;t.length;)try{t.shift()()}catch(a){g(a)}e=null}function M(){null===e&&(e=l.defer(function(){v.$apply(s)}))}n.prototype={constructor:n,$new:function(a,c){var d;c=c||this;a?(d=new n,d.$root=this.$root):(this.$$ChildScope||(this.$$ChildScope=b(this)),d=new this.$$ChildScope);d.$parent=c;d.$$prevSibling=c.$$childTail;c.$$childHead?(c.$$childTail.$$nextSibling=d,c.$$childTail=d):c.$$childHead=c.$$childTail=d;(a||c!=this)&&d.$on("$destroy",k);return d}, +$watch:function(a,b,c){var e=h(a);if(e.$$watchDelegate)return e.$$watchDelegate(this,b,c,e);var f=this.$$watchers,g={fn:b,last:u,get:e,exp:a,eq:!!c};d=null;G(b)||(g.fn=E);f||(f=this.$$watchers=[]);f.unshift(g);return function(){Xa(f,g);d=null}},$watchGroup:function(a,b){function c(){h=!1;k?(k=!1,b(e,e,g)):b(e,d,g)}var d=Array(a.length),e=Array(a.length),f=[],g=this,h=!1,k=!0;if(!a.length){var l=!0;g.$evalAsync(function(){l&&b(e,e,g)});return function(){l=!1}}if(1===a.length)return this.$watch(a[0], +function(a,c,f){e[0]=a;d[0]=c;b(e,a===c?e:d,f)});r(a,function(a,b){var k=g.$watch(a,function(a,f){e[b]=a;d[b]=f;h||(h=!0,g.$evalAsync(c))});f.push(k)});return function(){for(;f.length;)f.shift()()}},$watchCollection:function(a,b){function c(a){e=a;var b,d,g,h;if(!x(e)){if(J(e))if(Sa(e))for(f!==p&&(f=p,u=f.length=0,l++),a=e.length,u!==a&&(l++,f.length=u=a),b=0;b<a;b++)h=f[b],g=e[b],d=h!==h&&g!==g,d||h===g||(l++,f[b]=g);else{f!==n&&(f=n={},u=0,l++);a=0;for(b in e)e.hasOwnProperty(b)&&(a++,g=e[b],h= +f[b],b in f?(d=h!==h&&g!==g,d||h===g||(l++,f[b]=g)):(u++,f[b]=g,l++));if(u>a)for(b in l++,f)e.hasOwnProperty(b)||(u--,delete f[b])}else f!==e&&(f=e,l++);return l}}c.$stateful=!0;var d=this,e,f,g,k=1<b.length,l=0,q=h(a,c),p=[],n={},m=!0,u=0;return this.$watch(q,function(){m?(m=!1,b(e,e,d)):b(e,g,d);if(k)if(J(e))if(Sa(e)){g=Array(e.length);for(var a=0;a<e.length;a++)g[a]=e[a]}else for(a in g={},e)tc.call(e,a)&&(g[a]=e[a]);else g=e})},$digest:function(){var b,f,h,k,q,n,r=a,t,O=[],M,y;p("$digest");l.$$checkUrlChange(); +this===v&&null!==e&&(l.defer.cancel(e),s());d=null;do{n=!1;for(t=this;m.length;){try{y=m.shift(),y.scope.$eval(y.expression,y.locals)}catch(w){g(w)}d=null}a:do{if(k=t.$$watchers)for(q=k.length;q--;)try{if(b=k[q])if((f=b.get(t))!==(h=b.last)&&!(b.eq?ha(f,h):"number"===typeof f&&"number"===typeof h&&isNaN(f)&&isNaN(h)))n=!0,d=b,b.last=b.eq?Da(f,null):f,b.fn(f,h===u?f:h,t),5>r&&(M=4-r,O[M]||(O[M]=[]),O[M].push({msg:G(b.exp)?"fn: "+(b.exp.name||b.exp.toString()):b.exp,newVal:f,oldVal:h}));else if(b=== +d){n=!1;break a}}catch(A){g(A)}if(!(k=t.$$childHead||t!==this&&t.$$nextSibling))for(;t!==this&&!(k=t.$$nextSibling);)t=t.$parent}while(t=k);if((n||m.length)&&!r--)throw v.$$phase=null,c("infdig",a,O);}while(n||m.length);for(v.$$phase=null;F.length;)try{F.shift()()}catch(x){g(x)}},$destroy:function(){if(!this.$$destroyed){var a=this.$parent;this.$broadcast("$destroy");this.$$destroyed=!0;if(this!==v){for(var b in this.$$listenerCount)q(this,this.$$listenerCount[b],b);a.$$childHead==this&&(a.$$childHead= +this.$$nextSibling);a.$$childTail==this&&(a.$$childTail=this.$$prevSibling);this.$$prevSibling&&(this.$$prevSibling.$$nextSibling=this.$$nextSibling);this.$$nextSibling&&(this.$$nextSibling.$$prevSibling=this.$$prevSibling);this.$destroy=this.$digest=this.$apply=this.$evalAsync=this.$applyAsync=E;this.$on=this.$watch=this.$watchGroup=function(){return E};this.$$listeners={};this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=this.$root=this.$$watchers=null}}},$eval:function(a, +b){return h(a)(this,b)},$evalAsync:function(a,b){v.$$phase||m.length||l.defer(function(){m.length&&v.$digest()});m.push({scope:this,expression:a,locals:b})},$$postDigest:function(a){F.push(a)},$apply:function(a){try{return p("$apply"),this.$eval(a)}catch(b){g(b)}finally{v.$$phase=null;try{v.$digest()}catch(c){throw g(c),c;}}},$applyAsync:function(a){function b(){c.$eval(a)}var c=this;a&&t.push(b);M()},$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);var d=this;do d.$$listenerCount[a]|| +(d.$$listenerCount[a]=0),d.$$listenerCount[a]++;while(d=d.$parent);var e=this;return function(){var d=c.indexOf(b);-1!==d&&(c[d]=null,q(e,1,a))}},$emit:function(a,b){var c=[],d,e=this,f=!1,h={name:a,targetScope:e,stopPropagation:function(){f=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},k=Ya([h],arguments,1),l,q;do{d=e.$$listeners[a]||c;h.currentScope=e;l=0;for(q=d.length;l<q;l++)if(d[l])try{d[l].apply(null,k)}catch(p){g(p)}else d.splice(l,1),l--,q--;if(f)return h.currentScope= +null,h;e=e.$parent}while(e);h.currentScope=null;return h},$broadcast:function(a,b){var c=this,d=this,e={name:a,targetScope:this,preventDefault:function(){e.defaultPrevented=!0},defaultPrevented:!1};if(!this.$$listenerCount[a])return e;for(var f=Ya([e],arguments,1),h,l;c=d;){e.currentScope=c;d=c.$$listeners[a]||[];h=0;for(l=d.length;h<l;h++)if(d[h])try{d[h].apply(null,f)}catch(k){g(k)}else d.splice(h,1),h--,l--;if(!(d=c.$$listenerCount[a]&&c.$$childHead||c!==this&&c.$$nextSibling))for(;c!==this&&!(d= +c.$$nextSibling);)c=c.$parent}e.currentScope=null;return e}};var v=new n,m=v.$$asyncQueue=[],F=v.$$postDigestQueue=[],t=v.$$applyAsyncQueue=[];return v}]}function Sd(){var b=/^\s*(https?|ftp|mailto|tel|file):/,a=/^\s*((https?|ftp|file|blob):|data:image\/)/;this.aHrefSanitizationWhitelist=function(a){return y(a)?(b=a,this):b};this.imgSrcSanitizationWhitelist=function(b){return y(b)?(a=b,this):a};this.$get=function(){return function(c,d){var e=d?a:b,f;f=Aa(c).href;return""===f||f.match(e)?c:"unsafe:"+ +f}}}function Df(b){if("self"===b)return b;if(C(b)){if(-1<b.indexOf("***"))throw Ba("iwcard",b);b=gd(b).replace("\\*\\*",".*").replace("\\*","[^:/.?&;]*");return new RegExp("^"+b+"$")}if(Ua(b))return new RegExp("^"+b.source+"$");throw Ba("imatcher");}function hd(b){var a=[];y(b)&&r(b,function(b){a.push(Df(b))});return a}function Te(){this.SCE_CONTEXTS=pa;var b=["self"],a=[];this.resourceUrlWhitelist=function(a){arguments.length&&(b=hd(a));return b};this.resourceUrlBlacklist=function(b){arguments.length&& +(a=hd(b));return a};this.$get=["$injector",function(c){function d(a,b){return"self"===a?$c(b):!!a.exec(b.href)}function e(a){var b=function(a){this.$$unwrapTrustedValue=function(){return a}};a&&(b.prototype=new a);b.prototype.valueOf=function(){return this.$$unwrapTrustedValue()};b.prototype.toString=function(){return this.$$unwrapTrustedValue().toString()};return b}var f=function(a){throw Ba("unsafe");};c.has("$sanitize")&&(f=c.get("$sanitize"));var g=e(),h={};h[pa.HTML]=e(g);h[pa.CSS]=e(g);h[pa.URL]= +e(g);h[pa.JS]=e(g);h[pa.RESOURCE_URL]=e(h[pa.URL]);return{trustAs:function(a,b){var c=h.hasOwnProperty(a)?h[a]:null;if(!c)throw Ba("icontext",a,b);if(null===b||b===t||""===b)return b;if("string"!==typeof b)throw Ba("itype",a);return new c(b)},getTrusted:function(c,e){if(null===e||e===t||""===e)return e;var g=h.hasOwnProperty(c)?h[c]:null;if(g&&e instanceof g)return e.$$unwrapTrustedValue();if(c===pa.RESOURCE_URL){var g=Aa(e.toString()),p,q,u=!1;p=0;for(q=b.length;p<q;p++)if(d(b[p],g)){u=!0;break}if(u)for(p= +0,q=a.length;p<q;p++)if(d(a[p],g)){u=!1;break}if(u)return e;throw Ba("insecurl",e.toString());}if(c===pa.HTML)return f(e);throw Ba("unsafe");},valueOf:function(a){return a instanceof g?a.$$unwrapTrustedValue():a}}}]}function Se(){var b=!0;this.enabled=function(a){arguments.length&&(b=!!a);return b};this.$get=["$parse","$sceDelegate",function(a,c){if(b&&8>Qa)throw Ba("iequirks");var d=sa(pa);d.isEnabled=function(){return b};d.trustAs=c.trustAs;d.getTrusted=c.getTrusted;d.valueOf=c.valueOf;b||(d.trustAs= +d.getTrusted=function(a,b){return b},d.valueOf=ra);d.parseAs=function(b,c){var e=a(c);return e.literal&&e.constant?e:a(c,function(a){return d.getTrusted(b,a)})};var e=d.parseAs,f=d.getTrusted,g=d.trustAs;r(pa,function(a,b){var c=z(b);d[db("parse_as_"+c)]=function(b){return e(a,b)};d[db("get_trusted_"+c)]=function(b){return f(a,b)};d[db("trust_as_"+c)]=function(b){return g(a,b)}});return d}]}function Ue(){this.$get=["$window","$document",function(b,a){var c={},d=aa((/android (\d+)/.exec(z((b.navigator|| +{}).userAgent))||[])[1]),e=/Boxee/i.test((b.navigator||{}).userAgent),f=a[0]||{},g,h=/^(Moz|webkit|ms)(?=[A-Z])/,l=f.body&&f.body.style,k=!1,n=!1;if(l){for(var p in l)if(k=h.exec(p)){g=k[0];g=g.substr(0,1).toUpperCase()+g.substr(1);break}g||(g="WebkitOpacity"in l&&"webkit");k=!!("transition"in l||g+"Transition"in l);n=!!("animation"in l||g+"Animation"in l);!d||k&&n||(k=C(f.body.style.webkitTransition),n=C(f.body.style.webkitAnimation))}return{history:!(!b.history||!b.history.pushState||4>d||e),hasEvent:function(a){if("input"=== +a&&11>=Qa)return!1;if(x(c[a])){var b=f.createElement("div");c[a]="on"+a in b}return c[a]},csp:bb(),vendorPrefix:g,transitions:k,animations:n,android:d}}]}function We(){this.$get=["$templateCache","$http","$q",function(b,a,c){function d(e,f){d.totalPendingRequests++;var g=a.defaults&&a.defaults.transformResponse;H(g)?g=g.filter(function(a){return a!==Zb}):g===Zb&&(g=null);return a.get(e,{cache:b,transformResponse:g})["finally"](function(){d.totalPendingRequests--}).then(function(a){return a.data}, +function(a){if(!f)throw la("tpload",e);return c.reject(a)})}d.totalPendingRequests=0;return d}]}function Xe(){this.$get=["$rootScope","$browser","$location",function(b,a,c){return{findBindings:function(a,b,c){a=a.getElementsByClassName("ng-binding");var g=[];r(a,function(a){var d=ca.element(a).data("$binding");d&&r(d,function(d){c?(new RegExp("(^|\\s)"+gd(b)+"(\\s|\\||$)")).test(d)&&g.push(a):-1!=d.indexOf(b)&&g.push(a)})});return g},findModels:function(a,b,c){for(var g=["ng-","data-ng-","ng\\:"], +h=0;h<g.length;++h){var l=a.querySelectorAll("["+g[h]+"model"+(c?"=":"*=")+'"'+b+'"]');if(l.length)return l}},getLocation:function(){return c.url()},setLocation:function(a){a!==c.url()&&(c.url(a),b.$digest())},whenStable:function(b){a.notifyWhenNoOutstandingRequests(b)}}}]}function Ye(){this.$get=["$rootScope","$browser","$q","$$q","$exceptionHandler",function(b,a,c,d,e){function f(f,l,k){var n=y(k)&&!k,p=(n?d:c).defer(),q=p.promise;l=a.defer(function(){try{p.resolve(f())}catch(a){p.reject(a),e(a)}finally{delete g[q.$$timeoutId]}n|| +b.$apply()},l);q.$$timeoutId=l;g[l]=p;return q}var g={};f.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),delete g[b.$$timeoutId],a.defer.cancel(b.$$timeoutId)):!1};return f}]}function Aa(b){Qa&&($.setAttribute("href",b),b=$.href);$.setAttribute("href",b);return{href:$.href,protocol:$.protocol?$.protocol.replace(/:$/,""):"",host:$.host,search:$.search?$.search.replace(/^\?/,""):"",hash:$.hash?$.hash.replace(/^#/,""):"",hostname:$.hostname,port:$.port,pathname:"/"=== +$.pathname.charAt(0)?$.pathname:"/"+$.pathname}}function $c(b){b=C(b)?Aa(b):b;return b.protocol===id.protocol&&b.host===id.host}function Ze(){this.$get=ea(Q)}function Fc(b){function a(c,d){if(J(c)){var e={};r(c,function(b,c){e[c]=a(c,b)});return e}return b.factory(c+"Filter",d)}this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+"Filter")}}];a("currency",jd);a("date",kd);a("filter",Ef);a("json",Ff);a("limitTo",Gf);a("lowercase",Hf);a("number",ld);a("orderBy",md);a("uppercase", +If)}function Ef(){return function(b,a,c){if(!H(b))return b;var d;switch(typeof a){case "function":break;case "boolean":case "number":case "string":d=!0;case "object":a=Jf(a,c,d);break;default:return b}return b.filter(a)}}function Jf(b,a,c){var d=J(b)&&"$"in b;!0===a?a=ha:G(a)||(a=function(a,b){if(J(a)||J(b))return!1;a=z(""+a);b=z(""+b);return-1!==a.indexOf(b)});return function(e){return d&&!J(e)?Ha(e,b.$,a,!1):Ha(e,b,a,c)}}function Ha(b,a,c,d,e){var f=null!==b?typeof b:"null",g=null!==a?typeof a: +"null";if("string"===g&&"!"===a.charAt(0))return!Ha(b,a.substring(1),c,d);if(H(b))return b.some(function(b){return Ha(b,a,c,d)});switch(f){case "object":var h;if(d){for(h in b)if("$"!==h.charAt(0)&&Ha(b[h],a,c,!0))return!0;return e?!1:Ha(b,a,c,!1)}if("object"===g){for(h in a)if(e=a[h],!G(e)&&!x(e)&&(f="$"===h,!Ha(f?b:b[h],e,c,f,f)))return!1;return!0}return c(b,a);case "function":return!1;default:return c(b,a)}}function jd(b){var a=b.NUMBER_FORMATS;return function(b,d,e){x(d)&&(d=a.CURRENCY_SYM);x(e)&& +(e=a.PATTERNS[1].maxFrac);return null==b?b:nd(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,e).replace(/\u00A4/g,d)}}function ld(b){var a=b.NUMBER_FORMATS;return function(b,d){return null==b?b:nd(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function nd(b,a,c,d,e){if(!isFinite(b)||J(b))return"";var f=0>b;b=Math.abs(b);var g=b+"",h="",l=[],k=!1;if(-1!==g.indexOf("e")){var n=g.match(/([\d\.]+)e(-?)(\d+)/);n&&"-"==n[2]&&n[3]>e+1?b=0:(h=g,k=!0)}if(k)0<e&&1>b&&(h=b.toFixed(e),b=parseFloat(h));else{g=(g.split(od)[1]|| +"").length;x(e)&&(e=Math.min(Math.max(a.minFrac,g),a.maxFrac));b=+(Math.round(+(b.toString()+"e"+e)).toString()+"e"+-e);var g=(""+b).split(od),k=g[0],g=g[1]||"",p=0,q=a.lgSize,u=a.gSize;if(k.length>=q+u)for(p=k.length-q,n=0;n<p;n++)0===(p-n)%u&&0!==n&&(h+=c),h+=k.charAt(n);for(n=p;n<k.length;n++)0===(k.length-n)%q&&0!==n&&(h+=c),h+=k.charAt(n);for(;g.length<e;)g+="0";e&&"0"!==e&&(h+=d+g.substr(0,e))}0===b&&(f=!1);l.push(f?a.negPre:a.posPre,h,f?a.negSuf:a.posSuf);return l.join("")}function Ib(b,a, +c){var d="";0>b&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function U(b,a,c,d){c=c||0;return function(e){e=e["get"+b]();if(0<c||e>-c)e+=c;0===e&&-12==c&&(e=12);return Ib(e,a,d)}}function Jb(b,a){return function(c,d){var e=c["get"+b](),f=ub(a?"SHORT"+b:b);return d[f][e]}}function pd(b){var a=(new Date(b,0,1)).getDay();return new Date(b,0,(4>=a?5:12)-a)}function qd(b){return function(a){var c=pd(a.getFullYear());a=+new Date(a.getFullYear(),a.getMonth(),a.getDate()+ +(4-a.getDay()))-+c;a=1+Math.round(a/6048E5);return Ib(a,b)}}function ic(b,a){return 0>=b.getFullYear()?a.ERAS[0]:a.ERAS[1]}function kd(b){function a(a){var b;if(b=a.match(c)){a=new Date(0);var f=0,g=0,h=b[8]?a.setUTCFullYear:a.setFullYear,l=b[8]?a.setUTCHours:a.setHours;b[9]&&(f=aa(b[9]+b[10]),g=aa(b[9]+b[11]));h.call(a,aa(b[1]),aa(b[2])-1,aa(b[3]));f=aa(b[4]||0)-f;g=aa(b[5]||0)-g;h=aa(b[6]||0);b=Math.round(1E3*parseFloat("0."+(b[7]||0)));l.call(a,f,g,h,b)}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; +return function(c,e,f){var g="",h=[],l,k;e=e||"mediumDate";e=b.DATETIME_FORMATS[e]||e;C(c)&&(c=Kf.test(c)?aa(c):a(c));Y(c)&&(c=new Date(c));if(!ga(c))return c;for(;e;)(k=Lf.exec(e))?(h=Ya(h,k,1),e=h.pop()):(h.push(e),e=null);f&&"UTC"===f&&(c=new Date(c.getTime()),c.setMinutes(c.getMinutes()+c.getTimezoneOffset()));r(h,function(a){l=Mf[a];g+=l?l(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function Ff(){return function(b,a){x(a)&&(a=2);return $a(b,a)}}function Gf(){return function(b, +a){Y(b)&&(b=b.toString());return H(b)||C(b)?(a=Infinity===Math.abs(Number(a))?Number(a):aa(a))?0<a?b.slice(0,a):b.slice(a):C(b)?"":[]:b}}function md(b){return function(a,c,d){function e(a,b){return b?function(b,c){return a(c,b)}:a}function f(a){switch(typeof a){case "number":case "boolean":case "string":return!0;default:return!1}}function g(a){return null===a?"null":"function"===typeof a.valueOf&&(a=a.valueOf(),f(a))||"function"===typeof a.toString&&(a=a.toString(),f(a))?a:""}function h(a,b){var c= +typeof a,d=typeof b;c===d&&"object"===c&&(a=g(a),b=g(b));return c===d?("string"===c&&(a=a.toLowerCase(),b=b.toLowerCase()),a===b?0:a<b?-1:1):c<d?-1:1}if(!Sa(a))return a;c=H(c)?c:[c];0===c.length&&(c=["+"]);c=c.map(function(a){var c=!1,d=a||ra;if(C(a)){if("+"==a.charAt(0)||"-"==a.charAt(0))c="-"==a.charAt(0),a=a.substring(1);if(""===a)return e(h,c);d=b(a);if(d.constant){var f=d();return e(function(a,b){return h(a[f],b[f])},c)}}return e(function(a,b){return h(d(a),d(b))},c)});return Za.call(a).sort(e(function(a, +b){for(var d=0;d<c.length;d++){var e=c[d](a,b);if(0!==e)return e}return 0},d))}}function Ia(b){G(b)&&(b={link:b});b.restrict=b.restrict||"AC";return ea(b)}function rd(b,a,c,d,e){var f=this,g=[],h=f.$$parentForm=b.parent().controller("form")||Kb;f.$error={};f.$$success={};f.$pending=t;f.$name=e(a.name||a.ngForm||"")(c);f.$dirty=!1;f.$pristine=!0;f.$valid=!0;f.$invalid=!1;f.$submitted=!1;h.$addControl(f);f.$rollbackViewValue=function(){r(g,function(a){a.$rollbackViewValue()})};f.$commitViewValue=function(){r(g, +function(a){a.$commitViewValue()})};f.$addControl=function(a){La(a.$name,"input");g.push(a);a.$name&&(f[a.$name]=a)};f.$$renameControl=function(a,b){var c=a.$name;f[c]===a&&delete f[c];f[b]=a;a.$name=b};f.$removeControl=function(a){a.$name&&f[a.$name]===a&&delete f[a.$name];r(f.$pending,function(b,c){f.$setValidity(c,null,a)});r(f.$error,function(b,c){f.$setValidity(c,null,a)});r(f.$$success,function(b,c){f.$setValidity(c,null,a)});Xa(g,a)};sd({ctrl:this,$element:b,set:function(a,b,c){var d=a[b]; +d?-1===d.indexOf(c)&&d.push(c):a[b]=[c]},unset:function(a,b,c){var d=a[b];d&&(Xa(d,c),0===d.length&&delete a[b])},parentForm:h,$animate:d});f.$setDirty=function(){d.removeClass(b,Ra);d.addClass(b,Lb);f.$dirty=!0;f.$pristine=!1;h.$setDirty()};f.$setPristine=function(){d.setClass(b,Ra,Lb+" ng-submitted");f.$dirty=!1;f.$pristine=!0;f.$submitted=!1;r(g,function(a){a.$setPristine()})};f.$setUntouched=function(){r(g,function(a){a.$setUntouched()})};f.$setSubmitted=function(){d.addClass(b,"ng-submitted"); +f.$submitted=!0;h.$setSubmitted()}}function jc(b){b.$formatters.push(function(a){return b.$isEmpty(a)?a:a.toString()})}function jb(b,a,c,d,e,f){var g=z(a[0].type);if(!e.android){var h=!1;a.on("compositionstart",function(a){h=!0});a.on("compositionend",function(){h=!1;l()})}var l=function(b){k&&(f.defer.cancel(k),k=null);if(!h){var e=a.val();b=b&&b.type;"password"===g||c.ngTrim&&"false"===c.ngTrim||(e=N(e));(d.$viewValue!==e||""===e&&d.$$hasNativeValidators)&&d.$setViewValue(e,b)}};if(e.hasEvent("input"))a.on("input", +l);else{var k,n=function(a,b,c){k||(k=f.defer(function(){k=null;b&&b.value===c||l(a)}))};a.on("keydown",function(a){var b=a.keyCode;91===b||15<b&&19>b||37<=b&&40>=b||n(a,this,this.value)});if(e.hasEvent("paste"))a.on("paste cut",n)}a.on("change",l);d.$render=function(){a.val(d.$isEmpty(d.$viewValue)?"":d.$viewValue)}}function Mb(b,a){return function(c,d){var e,f;if(ga(c))return c;if(C(c)){'"'==c.charAt(0)&&'"'==c.charAt(c.length-1)&&(c=c.substring(1,c.length-1));if(Nf.test(c))return new Date(c);b.lastIndex= +0;if(e=b.exec(c))return e.shift(),f=d?{yyyy:d.getFullYear(),MM:d.getMonth()+1,dd:d.getDate(),HH:d.getHours(),mm:d.getMinutes(),ss:d.getSeconds(),sss:d.getMilliseconds()/1E3}:{yyyy:1970,MM:1,dd:1,HH:0,mm:0,ss:0,sss:0},r(e,function(b,c){c<a.length&&(f[a[c]]=+b)}),new Date(f.yyyy,f.MM-1,f.dd,f.HH,f.mm,f.ss||0,1E3*f.sss||0)}return NaN}}function kb(b,a,c,d){return function(e,f,g,h,l,k,n){function p(a){return a&&!(a.getTime&&a.getTime()!==a.getTime())}function q(a){return y(a)?ga(a)?a:c(a):t}td(e,f,g,h); +jb(e,f,g,h,l,k);var u=h&&h.$options&&h.$options.timezone,s;h.$$parserName=b;h.$parsers.push(function(b){return h.$isEmpty(b)?null:a.test(b)?(b=c(b,s),"UTC"===u&&b.setMinutes(b.getMinutes()-b.getTimezoneOffset()),b):t});h.$formatters.push(function(a){if(a&&!ga(a))throw Nb("datefmt",a);if(p(a)){if((s=a)&&"UTC"===u){var b=6E4*s.getTimezoneOffset();s=new Date(s.getTime()+b)}return n("date")(a,d,u)}s=null;return""});if(y(g.min)||g.ngMin){var r;h.$validators.min=function(a){return!p(a)||x(r)||c(a)>=r}; +g.$observe("min",function(a){r=q(a);h.$validate()})}if(y(g.max)||g.ngMax){var v;h.$validators.max=function(a){return!p(a)||x(v)||c(a)<=v};g.$observe("max",function(a){v=q(a);h.$validate()})}}}function td(b,a,c,d){(d.$$hasNativeValidators=J(a[0].validity))&&d.$parsers.push(function(b){var c=a.prop("validity")||{};return c.badInput&&!c.typeMismatch?t:b})}function ud(b,a,c,d,e){if(y(d)){b=b(d);if(!b.constant)throw R("ngModel")("constexpr",c,d);return b(a)}return e}function kc(b,a){b="ngClass"+b;return["$animate", +function(c){function d(a,b){var c=[],d=0;a:for(;d<a.length;d++){for(var e=a[d],n=0;n<b.length;n++)if(e==b[n])continue a;c.push(e)}return c}function e(a){if(!H(a)){if(C(a))return a.split(" ");if(J(a)){var b=[];r(a,function(a,c){a&&(b=b.concat(c.split(" ")))});return b}}return a}return{restrict:"AC",link:function(f,g,h){function l(a,b){var c=g.data("$classCounts")||{},d=[];r(a,function(a){if(0<b||c[a])c[a]=(c[a]||0)+b,c[a]===+(0<b)&&d.push(a)});g.data("$classCounts",c);return d.join(" ")}function k(b){if(!0=== +a||f.$index%2===a){var k=e(b||[]);if(!n){var u=l(k,1);h.$addClass(u)}else if(!ha(b,n)){var s=e(n),u=d(k,s),k=d(s,k),u=l(u,1),k=l(k,-1);u&&u.length&&c.addClass(g,u);k&&k.length&&c.removeClass(g,k)}}n=sa(b)}var n;f.$watch(h[b],k,!0);h.$observe("class",function(a){k(f.$eval(h[b]))});"ngClass"!==b&&f.$watch("$index",function(c,d){var g=c&1;if(g!==(d&1)){var k=e(f.$eval(h[b]));g===a?(g=l(k,1),h.$addClass(g)):(g=l(k,-1),h.$removeClass(g))}})}}}]}function sd(b){function a(a,b){b&&!f[a]?(k.addClass(e,a), +f[a]=!0):!b&&f[a]&&(k.removeClass(e,a),f[a]=!1)}function c(b,c){b=b?"-"+vc(b,"-"):"";a(lb+b,!0===c);a(vd+b,!1===c)}var d=b.ctrl,e=b.$element,f={},g=b.set,h=b.unset,l=b.parentForm,k=b.$animate;f[vd]=!(f[lb]=e.hasClass(lb));d.$setValidity=function(b,e,f){e===t?(d.$pending||(d.$pending={}),g(d.$pending,b,f)):(d.$pending&&h(d.$pending,b,f),wd(d.$pending)&&(d.$pending=t));Wa(e)?e?(h(d.$error,b,f),g(d.$$success,b,f)):(g(d.$error,b,f),h(d.$$success,b,f)):(h(d.$error,b,f),h(d.$$success,b,f));d.$pending?(a(xd, +!0),d.$valid=d.$invalid=t,c("",null)):(a(xd,!1),d.$valid=wd(d.$error),d.$invalid=!d.$valid,c("",d.$valid));e=d.$pending&&d.$pending[b]?t:d.$error[b]?!1:d.$$success[b]?!0:null;c(b,e);l.$setValidity(b,e,d)}}function wd(b){if(b)for(var a in b)return!1;return!0}var Of=/^\/(.+)\/([a-z]*)$/,z=function(b){return C(b)?b.toLowerCase():b},tc=Object.prototype.hasOwnProperty,ub=function(b){return C(b)?b.toUpperCase():b},Qa,A,ta,Za=[].slice,qf=[].splice,Pf=[].push,Ca=Object.prototype.toString,Ja=R("ng"),ca=Q.angular|| +(Q.angular={}),cb,ob=0;Qa=W.documentMode;E.$inject=[];ra.$inject=[];var H=Array.isArray,N=function(b){return C(b)?b.trim():b},gd=function(b){return b.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g,"\\$1").replace(/\x08/g,"\\x08")},bb=function(){if(y(bb.isActive_))return bb.isActive_;var b=!(!W.querySelector("[ng-csp]")&&!W.querySelector("[data-ng-csp]"));if(!b)try{new Function("")}catch(a){b=!0}return bb.isActive_=b},rb=["ng-","data-ng-","ng:","x-ng-"],Md=/[A-Z]/g,wc=!1,Qb,qa=1,pb=3,Qd={full:"1.3.15",major:1, +minor:3,dot:15,codeName:"locality-filtration"};T.expando="ng339";var zb=T.cache={},hf=1;T._data=function(b){return this.cache[b[this.expando]]||{}};var cf=/([\:\-\_]+(.))/g,df=/^moz([A-Z])/,Qf={mouseleave:"mouseout",mouseenter:"mouseover"},Tb=R("jqLite"),gf=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,Sb=/<|&#?\w+;/,ef=/<([\w:]+)/,ff=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja={option:[1,'<select multiple="multiple">',"</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>", +"</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ja.optgroup=ja.option;ja.tbody=ja.tfoot=ja.colgroup=ja.caption=ja.thead;ja.th=ja.td;var Ka=T.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;"complete"===W.readyState?setTimeout(a):(this.on("DOMContentLoaded",a),T(Q).on("load",a))},toString:function(){var b=[];r(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return 0<= +b?A(this[b]):A(this[this.length+b])},length:0,push:Pf,sort:[].sort,splice:[].splice},Eb={};r("multiple selected checked disabled readOnly required open".split(" "),function(b){Eb[z(b)]=b});var Oc={};r("input select option textarea button form details".split(" "),function(b){Oc[b]=!0});var Pc={ngMinlength:"minlength",ngMaxlength:"maxlength",ngMin:"min",ngMax:"max",ngPattern:"pattern"};r({data:Vb,removeData:xb},function(b,a){T[a]=b});r({data:Vb,inheritedData:Db,scope:function(b){return A.data(b,"$scope")|| +Db(b.parentNode||b,["$isolateScope","$scope"])},isolateScope:function(b){return A.data(b,"$isolateScope")||A.data(b,"$isolateScopeNoTemplate")},controller:Kc,injector:function(b){return Db(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ab,css:function(b,a,c){a=db(a);if(y(c))b.style[a]=c;else return b.style[a]},attr:function(b,a,c){var d=z(a);if(Eb[d])if(y(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||E).specified? +d:t;else if(y(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),null===b?t:b},prop:function(b,a,c){if(y(c))b[a]=c;else return b[a]},text:function(){function b(a,b){if(x(b)){var d=a.nodeType;return d===qa||d===pb?a.textContent:""}a.textContent=b}b.$dv="";return b}(),val:function(b,a){if(x(a)){if(b.multiple&&"select"===va(b)){var c=[];r(b.options,function(a){a.selected&&c.push(a.value||a.text)});return 0===c.length?null:c}return b.value}b.value=a},html:function(b,a){if(x(a))return b.innerHTML; +wb(b,!0);b.innerHTML=a},empty:Lc},function(b,a){T.prototype[a]=function(a,d){var e,f,g=this.length;if(b!==Lc&&(2==b.length&&b!==Ab&&b!==Kc?a:d)===t){if(J(a)){for(e=0;e<g;e++)if(b===Vb)b(this[e],a);else for(f in a)b(this[e],f,a[f]);return this}e=b.$dv;g=e===t?Math.min(g,1):g;for(f=0;f<g;f++){var h=b(this[f],a,d);e=e?e+h:h}return e}for(e=0;e<g;e++)b(this[e],a,d);return this}});r({removeData:xb,on:function a(c,d,e,f){if(y(f))throw Tb("onargs");if(Gc(c)){var g=yb(c,!0);f=g.events;var h=g.handle;h||(h= +g.handle=lf(c,f));for(var g=0<=d.indexOf(" ")?d.split(" "):[d],l=g.length;l--;){d=g[l];var k=f[d];k||(f[d]=[],"mouseenter"===d||"mouseleave"===d?a(c,Qf[d],function(a){var c=a.relatedTarget;c&&(c===this||this.contains(c))||h(a,d)}):"$destroy"!==d&&c.addEventListener(d,h,!1),k=f[d]);k.push(e)}}},off:Jc,one:function(a,c,d){a=A(a);a.on(c,function f(){a.off(c,d);a.off(c,f)});a.on(c,d)},replaceWith:function(a,c){var d,e=a.parentNode;wb(a);r(new T(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c, +a);d=c})},children:function(a){var c=[];r(a.childNodes,function(a){a.nodeType===qa&&c.push(a)});return c},contents:function(a){return a.contentDocument||a.childNodes||[]},append:function(a,c){var d=a.nodeType;if(d===qa||11===d){c=new T(c);for(var d=0,e=c.length;d<e;d++)a.appendChild(c[d])}},prepend:function(a,c){if(a.nodeType===qa){var d=a.firstChild;r(new T(c),function(c){a.insertBefore(c,d)})}},wrap:function(a,c){c=A(c).eq(0).clone()[0];var d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)}, +remove:Mc,detach:function(a){Mc(a,!0)},after:function(a,c){var d=a,e=a.parentNode;c=new T(c);for(var f=0,g=c.length;f<g;f++){var h=c[f];e.insertBefore(h,d.nextSibling);d=h}},addClass:Cb,removeClass:Bb,toggleClass:function(a,c,d){c&&r(c.split(" "),function(c){var f=d;x(f)&&(f=!Ab(a,c));(f?Cb:Bb)(a,c)})},parent:function(a){return(a=a.parentNode)&&11!==a.nodeType?a:null},next:function(a){return a.nextElementSibling},find:function(a,c){return a.getElementsByTagName?a.getElementsByTagName(c):[]},clone:Ub, +triggerHandler:function(a,c,d){var e,f,g=c.type||c,h=yb(a);if(h=(h=h&&h.events)&&h[g])e={preventDefault:function(){this.defaultPrevented=!0},isDefaultPrevented:function(){return!0===this.defaultPrevented},stopImmediatePropagation:function(){this.immediatePropagationStopped=!0},isImmediatePropagationStopped:function(){return!0===this.immediatePropagationStopped},stopPropagation:E,type:g,target:a},c.type&&(e=w(e,c)),c=sa(h),f=d?[e].concat(d):[e],r(c,function(c){e.isImmediatePropagationStopped()||c.apply(a, +f)})}},function(a,c){T.prototype[c]=function(c,e,f){for(var g,h=0,l=this.length;h<l;h++)x(g)?(g=a(this[h],c,e,f),y(g)&&(g=A(g))):Ic(g,a(this[h],c,e,f));return y(g)?g:this};T.prototype.bind=T.prototype.on;T.prototype.unbind=T.prototype.off});eb.prototype={put:function(a,c){this[Ma(a,this.nextUid)]=c},get:function(a){return this[Ma(a,this.nextUid)]},remove:function(a){var c=this[a=Ma(a,this.nextUid)];delete this[a];return c}};var Rc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,Rf=/,/,Sf=/^\s*(_?)(\S+?)\1\s*$/, +Qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Fa=R("$injector");ab.$$annotate=function(a,c,d){var e;if("function"===typeof a){if(!(e=a.$inject)){e=[];if(a.length){if(c)throw C(d)&&d||(d=a.name||mf(a)),Fa("strictdi",d);c=a.toString().replace(Qc,"");c=c.match(Rc);r(c[1].split(Rf),function(a){a.replace(Sf,function(a,c,d){e.push(d)})})}a.$inject=e}}else H(a)?(c=a.length-1,sb(a[c],"fn"),e=a.slice(0,c)):sb(a,"fn",!0);return e};var Tf=R("$animate"),Ce=["$provide",function(a){this.$$selectors={};this.register=function(c, +d){var e=c+"-animation";if(c&&"."!=c.charAt(0))throw Tf("notcsel",c);this.$$selectors[c.substr(1)]=e;a.factory(e,d)};this.classNameFilter=function(a){1===arguments.length&&(this.$$classNameFilter=a instanceof RegExp?a:null);return this.$$classNameFilter};this.$get=["$$q","$$asyncCallback","$rootScope",function(a,d,e){function f(d){var f,g=a.defer();g.promise.$$cancelFn=function(){f&&f()};e.$$postDigest(function(){f=d(function(){g.resolve()})});return g.promise}function g(a,c){var d=[],e=[],f=ia(); +r((a.attr("class")||"").split(/\s+/),function(a){f[a]=!0});r(c,function(a,c){var g=f[c];!1===a&&g?e.push(c):!0!==a||g||d.push(c)});return 0<d.length+e.length&&[d.length?d:null,e.length?e:null]}function h(a,c,d){for(var e=0,f=c.length;e<f;++e)a[c[e]]=d}function l(){n||(n=a.defer(),d(function(){n.resolve();n=null}));return n.promise}function k(a,c){if(ca.isObject(c)){var d=w(c.from||{},c.to||{});a.css(d)}}var n;return{animate:function(a,c,d){k(a,{from:c,to:d});return l()},enter:function(a,c,d,e){k(a, +e);d?d.after(a):c.prepend(a);return l()},leave:function(a,c){k(a,c);a.remove();return l()},move:function(a,c,d,e){return this.enter(a,c,d,e)},addClass:function(a,c,d){return this.setClass(a,c,[],d)},$$addClassImmediately:function(a,c,d){a=A(a);c=C(c)?c:H(c)?c.join(" "):"";r(a,function(a){Cb(a,c)});k(a,d);return l()},removeClass:function(a,c,d){return this.setClass(a,[],c,d)},$$removeClassImmediately:function(a,c,d){a=A(a);c=C(c)?c:H(c)?c.join(" "):"";r(a,function(a){Bb(a,c)});k(a,d);return l()},setClass:function(a, +c,d,e){var k=this,l=!1;a=A(a);var m=a.data("$$animateClasses");m?e&&m.options&&(m.options=ca.extend(m.options||{},e)):(m={classes:{},options:e},l=!0);e=m.classes;c=H(c)?c:c.split(" ");d=H(d)?d:d.split(" ");h(e,c,!0);h(e,d,!1);l&&(m.promise=f(function(c){var d=a.data("$$animateClasses");a.removeData("$$animateClasses");if(d){var e=g(a,d.classes);e&&k.$$setClassImmediately(a,e[0],e[1],d.options)}c()}),a.data("$$animateClasses",m));return m.promise},$$setClassImmediately:function(a,c,d,e){c&&this.$$addClassImmediately(a, +c);d&&this.$$removeClassImmediately(a,d);k(a,e);return l()},enabled:E,cancel:E}}]}],la=R("$compile");yc.$inject=["$provide","$$sanitizeUriProvider"];var Sc=/^((?:x|data)[\:\-_])/i,rf=R("$controller"),Wc="application/json",$b={"Content-Type":Wc+";charset=utf-8"},tf=/^\[|^\{(?!\{)/,uf={"[":/]$/,"{":/}$/},sf=/^\)\]\}',?\n/,ac=R("$interpolate"),Uf=/^([^\?#]*)(\?([^#]*))?(#(.*))?$/,xf={http:80,https:443,ftp:21},Gb=R("$location"),Vf={$$html5:!1,$$replace:!1,absUrl:Hb("$$absUrl"),url:function(a){if(x(a))return this.$$url; +var c=Uf.exec(a);(c[1]||""===a)&&this.path(decodeURIComponent(c[1]));(c[2]||c[1]||""===a)&&this.search(c[3]||"");this.hash(c[5]||"");return this},protocol:Hb("$$protocol"),host:Hb("$$host"),port:Hb("$$port"),path:dd("$$path",function(a){a=null!==a?a.toString():"";return"/"==a.charAt(0)?a:"/"+a}),search:function(a,c){switch(arguments.length){case 0:return this.$$search;case 1:if(C(a)||Y(a))a=a.toString(),this.$$search=sc(a);else if(J(a))a=Da(a,{}),r(a,function(c,e){null==c&&delete a[e]}),this.$$search= +a;else throw Gb("isrcharg");break;default:x(c)||null===c?delete this.$$search[a]:this.$$search[a]=c}this.$$compose();return this},hash:dd("$$hash",function(a){return null!==a?a.toString():""}),replace:function(){this.$$replace=!0;return this}};r([cd,ec,dc],function(a){a.prototype=Object.create(Vf);a.prototype.state=function(c){if(!arguments.length)return this.$$state;if(a!==dc||!this.$$html5)throw Gb("nostate");this.$$state=x(c)?null:c;return this}});var na=R("$parse"),Wf=Function.prototype.call, +Xf=Function.prototype.apply,Yf=Function.prototype.bind,mb=ia();r({"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:function(){}},function(a,c){a.constant=a.literal=a.sharedGetter=!0;mb[c]=a});mb["this"]=function(a){return a};mb["this"].sharedGetter=!0;var nb=w(ia(),{"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return y(d)?y(e)?d+e:d:y(e)?e:t},"-":function(a,c,d,e){d=d(a,c);e=e(a,c);return(y(d)?d:0)-(y(e)?e:0)},"*":function(a,c,d,e){return d(a,c)*e(a,c)}, +"/":function(a,c,d,e){return d(a,c)/e(a,c)},"%":function(a,c,d,e){return d(a,c)%e(a,c)},"===":function(a,c,d,e){return d(a,c)===e(a,c)},"!==":function(a,c,d,e){return d(a,c)!==e(a,c)},"==":function(a,c,d,e){return d(a,c)==e(a,c)},"!=":function(a,c,d,e){return d(a,c)!=e(a,c)},"<":function(a,c,d,e){return d(a,c)<e(a,c)},">":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)}, +"||":function(a,c,d,e){return d(a,c)||e(a,c)},"!":function(a,c,d){return!d(a,c)},"=":!0,"|":!0}),Zf={n:"\n",f:"\f",r:"\r",t:"\t",v:"\v","'":"'",'"':'"'},hc=function(a){this.options=a};hc.prototype={constructor:hc,lex:function(a){this.text=a;this.index=0;for(this.tokens=[];this.index<this.text.length;)if(a=this.text.charAt(this.index),'"'===a||"'"===a)this.readString(a);else if(this.isNumber(a)||"."===a&&this.isNumber(this.peek()))this.readNumber();else if(this.isIdent(a))this.readIdent();else if(this.is(a, +"(){}[].,;:?"))this.tokens.push({index:this.index,text:a}),this.index++;else if(this.isWhitespace(a))this.index++;else{var c=a+this.peek(),d=c+this.peek(2),e=nb[c],f=nb[d];nb[a]||e||f?(a=f?d:e?c:a,this.tokens.push({index:this.index,text:a,operator:!0}),this.index+=a.length):this.throwError("Unexpected next character ",this.index,this.index+1)}return this.tokens},is:function(a,c){return-1!==c.indexOf(a)},peek:function(a){a=a||1;return this.index+a<this.text.length?this.text.charAt(this.index+a):!1}, +isNumber:function(a){return"0"<=a&&"9">=a&&"string"===typeof a},isWhitespace:function(a){return" "===a||"\r"===a||"\t"===a||"\n"===a||"\v"===a||"\u00a0"===a},isIdent:function(a){return"a"<=a&&"z">=a||"A"<=a&&"Z">=a||"_"===a||"$"===a},isExpOperator:function(a){return"-"===a||"+"===a||this.isNumber(a)},throwError:function(a,c,d){d=d||this.index;c=y(c)?"s "+c+"-"+this.index+" ["+this.text.substring(c,d)+"]":" "+d;throw na("lexerr",a,c,this.text);},readNumber:function(){for(var a="",c=this.index;this.index< +this.text.length;){var d=z(this.text.charAt(this.index));if("."==d||this.isNumber(d))a+=d;else{var e=this.peek();if("e"==d&&this.isExpOperator(e))a+=d;else if(this.isExpOperator(d)&&e&&this.isNumber(e)&&"e"==a.charAt(a.length-1))a+=d;else if(!this.isExpOperator(d)||e&&this.isNumber(e)||"e"!=a.charAt(a.length-1))break;else this.throwError("Invalid exponent")}this.index++}this.tokens.push({index:c,text:a,constant:!0,value:Number(a)})},readIdent:function(){for(var a=this.index;this.index<this.text.length;){var c= +this.text.charAt(this.index);if(!this.isIdent(c)&&!this.isNumber(c))break;this.index++}this.tokens.push({index:a,text:this.text.slice(a,this.index),identifier:!0})},readString:function(a){var c=this.index;this.index++;for(var d="",e=a,f=!1;this.index<this.text.length;){var g=this.text.charAt(this.index),e=e+g;if(f)"u"===g?(f=this.text.substring(this.index+1,this.index+5),f.match(/[\da-f]{4}/i)||this.throwError("Invalid unicode escape [\\u"+f+"]"),this.index+=4,d+=String.fromCharCode(parseInt(f,16))): +d+=Zf[g]||g,f=!1;else if("\\"===g)f=!0;else{if(g===a){this.index++;this.tokens.push({index:c,text:e,constant:!0,value:d});return}d+=g}this.index++}this.throwError("Unterminated quote",c)}};var ib=function(a,c,d){this.lexer=a;this.$filter=c;this.options=d};ib.ZERO=w(function(){return 0},{sharedGetter:!0,constant:!0});ib.prototype={constructor:ib,parse:function(a){this.text=a;this.tokens=this.lexer.lex(a);a=this.statements();0!==this.tokens.length&&this.throwError("is an unexpected token",this.tokens[0]); +a.literal=!!a.literal;a.constant=!!a.constant;return a},primary:function(){var a;this.expect("(")?(a=this.filterChain(),this.consume(")")):this.expect("[")?a=this.arrayDeclaration():this.expect("{")?a=this.object():this.peek().identifier&&this.peek().text in mb?a=mb[this.consume().text]:this.peek().identifier?a=this.identifier():this.peek().constant?a=this.constant():this.throwError("not a primary expression",this.peek());for(var c,d;c=this.expect("(","[",".");)"("===c.text?(a=this.functionCall(a, +d),d=null):"["===c.text?(d=a,a=this.objectIndex(a)):"."===c.text?(d=a,a=this.fieldAccess(a)):this.throwError("IMPOSSIBLE");return a},throwError:function(a,c){throw na("syntax",c.text,a,c.index+1,this.text,this.text.substring(c.index));},peekToken:function(){if(0===this.tokens.length)throw na("ueoe",this.text);return this.tokens[0]},peek:function(a,c,d,e){return this.peekAhead(0,a,c,d,e)},peekAhead:function(a,c,d,e,f){if(this.tokens.length>a){a=this.tokens[a];var g=a.text;if(g===c||g===d||g===e||g=== +f||!(c||d||e||f))return a}return!1},expect:function(a,c,d,e){return(a=this.peek(a,c,d,e))?(this.tokens.shift(),a):!1},consume:function(a){if(0===this.tokens.length)throw na("ueoe",this.text);var c=this.expect(a);c||this.throwError("is unexpected, expecting ["+a+"]",this.peek());return c},unaryFn:function(a,c){var d=nb[a];return w(function(a,f){return d(a,f,c)},{constant:c.constant,inputs:[c]})},binaryFn:function(a,c,d,e){var f=nb[c];return w(function(c,e){return f(c,e,a,d)},{constant:a.constant&& +d.constant,inputs:!e&&[a,d]})},identifier:function(){for(var a=this.consume().text;this.peek(".")&&this.peekAhead(1).identifier&&!this.peekAhead(2,"(");)a+=this.consume().text+this.consume().text;return zf(a,this.options,this.text)},constant:function(){var a=this.consume().value;return w(function(){return a},{constant:!0,literal:!0})},statements:function(){for(var a=[];;)if(0<this.tokens.length&&!this.peek("}",")",";","]")&&a.push(this.filterChain()),!this.expect(";"))return 1===a.length?a[0]:function(c, +d){for(var e,f=0,g=a.length;f<g;f++)e=a[f](c,d);return e}},filterChain:function(){for(var a=this.expression();this.expect("|");)a=this.filter(a);return a},filter:function(a){var c=this.$filter(this.consume().text),d,e;if(this.peek(":"))for(d=[],e=[];this.expect(":");)d.push(this.expression());var f=[a].concat(d||[]);return w(function(f,h){var l=a(f,h);if(e){e[0]=l;for(l=d.length;l--;)e[l+1]=d[l](f,h);return c.apply(t,e)}return c(l)},{constant:!c.$stateful&&f.every(fc),inputs:!c.$stateful&&f})},expression:function(){return this.assignment()}, +assignment:function(){var a=this.ternary(),c,d;return(d=this.expect("="))?(a.assign||this.throwError("implies assignment but ["+this.text.substring(0,d.index)+"] can not be assigned to",d),c=this.ternary(),w(function(d,f){return a.assign(d,c(d,f),f)},{inputs:[a,c]})):a},ternary:function(){var a=this.logicalOR(),c;if(this.expect("?")&&(c=this.assignment(),this.consume(":"))){var d=this.assignment();return w(function(e,f){return a(e,f)?c(e,f):d(e,f)},{constant:a.constant&&c.constant&&d.constant})}return a}, +logicalOR:function(){for(var a=this.logicalAND(),c;c=this.expect("||");)a=this.binaryFn(a,c.text,this.logicalAND(),!0);return a},logicalAND:function(){for(var a=this.equality(),c;c=this.expect("&&");)a=this.binaryFn(a,c.text,this.equality(),!0);return a},equality:function(){for(var a=this.relational(),c;c=this.expect("==","!=","===","!==");)a=this.binaryFn(a,c.text,this.relational());return a},relational:function(){for(var a=this.additive(),c;c=this.expect("<",">","<=",">=");)a=this.binaryFn(a,c.text, +this.additive());return a},additive:function(){for(var a=this.multiplicative(),c;c=this.expect("+","-");)a=this.binaryFn(a,c.text,this.multiplicative());return a},multiplicative:function(){for(var a=this.unary(),c;c=this.expect("*","/","%");)a=this.binaryFn(a,c.text,this.unary());return a},unary:function(){var a;return this.expect("+")?this.primary():(a=this.expect("-"))?this.binaryFn(ib.ZERO,a.text,this.unary()):(a=this.expect("!"))?this.unaryFn(a.text,this.unary()):this.primary()},fieldAccess:function(a){var c= +this.identifier();return w(function(d,e,f){d=f||a(d,e);return null==d?t:c(d)},{assign:function(d,e,f){var g=a(d,f);g||a.assign(d,g={},f);return c.assign(g,e)}})},objectIndex:function(a){var c=this.text,d=this.expression();this.consume("]");return w(function(e,f){var g=a(e,f),h=d(e,f);ua(h,c);return g?oa(g[h],c):t},{assign:function(e,f,g){var h=ua(d(e,g),c),l=oa(a(e,g),c);l||a.assign(e,l={},g);return l[h]=f}})},functionCall:function(a,c){var d=[];if(")"!==this.peekToken().text){do d.push(this.expression()); +while(this.expect(","))}this.consume(")");var e=this.text,f=d.length?[]:null;return function(g,h){var l=c?c(g,h):y(c)?t:g,k=a(g,h,l)||E;if(f)for(var n=d.length;n--;)f[n]=oa(d[n](g,h),e);oa(l,e);if(k){if(k.constructor===k)throw na("isecfn",e);if(k===Wf||k===Xf||k===Yf)throw na("isecff",e);}l=k.apply?k.apply(l,f):k(f[0],f[1],f[2],f[3],f[4]);f&&(f.length=0);return oa(l,e)}},arrayDeclaration:function(){var a=[];if("]"!==this.peekToken().text){do{if(this.peek("]"))break;a.push(this.expression())}while(this.expect(",")) +}this.consume("]");return w(function(c,d){for(var e=[],f=0,g=a.length;f<g;f++)e.push(a[f](c,d));return e},{literal:!0,constant:a.every(fc),inputs:a})},object:function(){var a=[],c=[];if("}"!==this.peekToken().text){do{if(this.peek("}"))break;var d=this.consume();d.constant?a.push(d.value):d.identifier?a.push(d.text):this.throwError("invalid key",d);this.consume(":");c.push(this.expression())}while(this.expect(","))}this.consume("}");return w(function(d,f){for(var g={},h=0,l=c.length;h<l;h++)g[a[h]]= +c[h](d,f);return g},{literal:!0,constant:c.every(fc),inputs:c})}};var Bf=ia(),Af=ia(),Cf=Object.prototype.valueOf,Ba=R("$sce"),pa={HTML:"html",CSS:"css",URL:"url",RESOURCE_URL:"resourceUrl",JS:"js"},la=R("$compile"),$=W.createElement("a"),id=Aa(Q.location.href);Fc.$inject=["$provide"];jd.$inject=["$locale"];ld.$inject=["$locale"];var od=".",Mf={yyyy:U("FullYear",4),yy:U("FullYear",2,0,!0),y:U("FullYear",1),MMMM:Jb("Month"),MMM:Jb("Month",!0),MM:U("Month",2,1),M:U("Month",1,1),dd:U("Date",2),d:U("Date", +1),HH:U("Hours",2),H:U("Hours",1),hh:U("Hours",2,-12),h:U("Hours",1,-12),mm:U("Minutes",2),m:U("Minutes",1),ss:U("Seconds",2),s:U("Seconds",1),sss:U("Milliseconds",3),EEEE:Jb("Day"),EEE:Jb("Day",!0),a:function(a,c){return 12>a.getHours()?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=-1*a.getTimezoneOffset();return a=(0<=a?"+":"")+(Ib(Math[0<a?"floor":"ceil"](a/60),2)+Ib(Math.abs(a%60),2))},ww:qd(2),w:qd(1),G:ic,GG:ic,GGG:ic,GGGG:function(a,c){return 0>=a.getFullYear()?c.ERANAMES[0]:c.ERANAMES[1]}},Lf=/((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/, +Kf=/^\-?\d+$/;kd.$inject=["$locale"];var Hf=ea(z),If=ea(ub);md.$inject=["$parse"];var Td=ea({restrict:"E",compile:function(a,c){if(!c.href&&!c.xlinkHref&&!c.name)return function(a,c){if("a"===c[0].nodeName.toLowerCase()){var f="[object SVGAnimatedString]"===Ca.call(c.prop("href"))?"xlink:href":"href";c.on("click",function(a){c.attr(f)||a.preventDefault()})}}}}),vb={};r(Eb,function(a,c){if("multiple"!=a){var d=xa("ng-"+c);vb[d]=function(){return{restrict:"A",priority:100,link:function(a,f,g){a.$watch(g[d], +function(a){g.$set(c,!!a)})}}}}});r(Pc,function(a,c){vb[c]=function(){return{priority:100,link:function(a,e,f){if("ngPattern"===c&&"/"==f.ngPattern.charAt(0)&&(e=f.ngPattern.match(Of))){f.$set("ngPattern",new RegExp(e[1],e[2]));return}a.$watch(f[c],function(a){f.$set(c,a)})}}}});r(["src","srcset","href"],function(a){var c=xa("ng-"+a);vb[c]=function(){return{priority:99,link:function(d,e,f){var g=a,h=a;"href"===a&&"[object SVGAnimatedString]"===Ca.call(e.prop("href"))&&(h="xlinkHref",f.$attr[h]="xlink:href", +g=null);f.$observe(c,function(c){c?(f.$set(h,c),Qa&&g&&e.prop(g,f[h])):"href"===a&&f.$set(h,null)})}}}});var Kb={$addControl:E,$$renameControl:function(a,c){a.$name=c},$removeControl:E,$setValidity:E,$setDirty:E,$setPristine:E,$setSubmitted:E};rd.$inject=["$element","$attrs","$scope","$animate","$interpolate"];var yd=function(a){return["$timeout",function(c){return{name:"form",restrict:a?"EAC":"E",controller:rd,compile:function(d,e){d.addClass(Ra).addClass(lb);var f=e.name?"name":a&&e.ngForm?"ngForm": +!1;return{pre:function(a,d,e,k){if(!("action"in e)){var n=function(c){a.$apply(function(){k.$commitViewValue();k.$setSubmitted()});c.preventDefault()};d[0].addEventListener("submit",n,!1);d.on("$destroy",function(){c(function(){d[0].removeEventListener("submit",n,!1)},0,!1)})}var p=k.$$parentForm;f&&(hb(a,null,k.$name,k,k.$name),e.$observe(f,function(c){k.$name!==c&&(hb(a,null,k.$name,t,k.$name),p.$$renameControl(k,c),hb(a,null,k.$name,k,k.$name))}));d.on("$destroy",function(){p.$removeControl(k); +f&&hb(a,null,e[f],t,k.$name);w(k,Kb)})}}}}}]},Ud=yd(),ge=yd(!0),Nf=/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/,$f=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,ag=/^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,bg=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,zd=/^(\d{4})-(\d{2})-(\d{2})$/,Ad=/^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,lc=/^(\d{4})-W(\d\d)$/,Bd=/^(\d{4})-(\d\d)$/, +Cd=/^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/,Dd={text:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e)},date:kb("date",zd,Mb(zd,["yyyy","MM","dd"]),"yyyy-MM-dd"),"datetime-local":kb("datetimelocal",Ad,Mb(Ad,"yyyy MM dd HH mm ss sss".split(" ")),"yyyy-MM-ddTHH:mm:ss.sss"),time:kb("time",Cd,Mb(Cd,["HH","mm","ss","sss"]),"HH:mm:ss.sss"),week:kb("week",lc,function(a,c){if(ga(a))return a;if(C(a)){lc.lastIndex=0;var d=lc.exec(a);if(d){var e=+d[1],f=+d[2],g=d=0,h=0,l=0,k=pd(e),f=7*(f-1);c&&(d=c.getHours(),g= +c.getMinutes(),h=c.getSeconds(),l=c.getMilliseconds());return new Date(e,0,k.getDate()+f,d,g,h,l)}}return NaN},"yyyy-Www"),month:kb("month",Bd,Mb(Bd,["yyyy","MM"]),"yyyy-MM"),number:function(a,c,d,e,f,g){td(a,c,d,e);jb(a,c,d,e,f,g);e.$$parserName="number";e.$parsers.push(function(a){return e.$isEmpty(a)?null:bg.test(a)?parseFloat(a):t});e.$formatters.push(function(a){if(!e.$isEmpty(a)){if(!Y(a))throw Nb("numfmt",a);a=a.toString()}return a});if(y(d.min)||d.ngMin){var h;e.$validators.min=function(a){return e.$isEmpty(a)|| +x(h)||a>=h};d.$observe("min",function(a){y(a)&&!Y(a)&&(a=parseFloat(a,10));h=Y(a)&&!isNaN(a)?a:t;e.$validate()})}if(y(d.max)||d.ngMax){var l;e.$validators.max=function(a){return e.$isEmpty(a)||x(l)||a<=l};d.$observe("max",function(a){y(a)&&!Y(a)&&(a=parseFloat(a,10));l=Y(a)&&!isNaN(a)?a:t;e.$validate()})}},url:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e);e.$$parserName="url";e.$validators.url=function(a,c){var d=a||c;return e.$isEmpty(d)||$f.test(d)}},email:function(a,c,d,e,f,g){jb(a,c,d,e,f,g);jc(e); +e.$$parserName="email";e.$validators.email=function(a,c){var d=a||c;return e.$isEmpty(d)||ag.test(d)}},radio:function(a,c,d,e){x(d.name)&&c.attr("name",++ob);c.on("click",function(a){c[0].checked&&e.$setViewValue(d.value,a&&a.type)});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e,f,g,h,l){var k=ud(l,a,"ngTrueValue",d.ngTrueValue,!0),n=ud(l,a,"ngFalseValue",d.ngFalseValue,!1);c.on("click",function(a){e.$setViewValue(c[0].checked,a&& +a.type)});e.$render=function(){c[0].checked=e.$viewValue};e.$isEmpty=function(a){return!1===a};e.$formatters.push(function(a){return ha(a,k)});e.$parsers.push(function(a){return a?k:n})},hidden:E,button:E,submit:E,reset:E,file:E},zc=["$browser","$sniffer","$filter","$parse",function(a,c,d,e){return{restrict:"E",require:["?ngModel"],link:{pre:function(f,g,h,l){l[0]&&(Dd[z(h.type)]||Dd.text)(f,g,h,l[0],c,a,d,e)}}}}],cg=/^(true|false|\d+)$/,ye=function(){return{restrict:"A",priority:100,compile:function(a, +c){return cg.test(c.ngValue)?function(a,c,f){f.$set("value",a.$eval(f.ngValue))}:function(a,c,f){a.$watch(f.ngValue,function(a){f.$set("value",a)})}}}},Zd=["$compile",function(a){return{restrict:"AC",compile:function(c){a.$$addBindingClass(c);return function(c,e,f){a.$$addBindingInfo(e,f.ngBind);e=e[0];c.$watch(f.ngBind,function(a){e.textContent=a===t?"":a})}}}}],ae=["$interpolate","$compile",function(a,c){return{compile:function(d){c.$$addBindingClass(d);return function(d,f,g){d=a(f.attr(g.$attr.ngBindTemplate)); +c.$$addBindingInfo(f,d.expressions);f=f[0];g.$observe("ngBindTemplate",function(a){f.textContent=a===t?"":a})}}}}],$d=["$sce","$parse","$compile",function(a,c,d){return{restrict:"A",compile:function(e,f){var g=c(f.ngBindHtml),h=c(f.ngBindHtml,function(a){return(a||"").toString()});d.$$addBindingClass(e);return function(c,e,f){d.$$addBindingInfo(e,f.ngBindHtml);c.$watch(h,function(){e.html(a.getTrustedHtml(g(c))||"")})}}}}],xe=ea({restrict:"A",require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}), +be=kc("",!0),de=kc("Odd",0),ce=kc("Even",1),ee=Ia({compile:function(a,c){c.$set("ngCloak",t);a.removeClass("ng-cloak")}}),fe=[function(){return{restrict:"A",scope:!0,controller:"@",priority:500}}],Ec={},dg={blur:!0,focus:!0};r("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste".split(" "),function(a){var c=xa("ng-"+a);Ec[c]=["$parse","$rootScope",function(d,e){return{restrict:"A",compile:function(f,g){var h= +d(g[c],null,!0);return function(c,d){d.on(a,function(d){var f=function(){h(c,{$event:d})};dg[a]&&e.$$phase?c.$evalAsync(f):c.$apply(f)})}}}}]});var ie=["$animate",function(a){return{multiElement:!0,transclude:"element",priority:600,terminal:!0,restrict:"A",$$tlb:!0,link:function(c,d,e,f,g){var h,l,k;c.$watch(e.ngIf,function(c){c?l||g(function(c,f){l=f;c[c.length++]=W.createComment(" end ngIf: "+e.ngIf+" ");h={clone:c};a.enter(c,d.parent(),d)}):(k&&(k.remove(),k=null),l&&(l.$destroy(),l=null),h&&(k= +tb(h.clone),a.leave(k).then(function(){k=null}),h=null))})}}}],je=["$templateRequest","$anchorScroll","$animate","$sce",function(a,c,d,e){return{restrict:"ECA",priority:400,terminal:!0,transclude:"element",controller:ca.noop,compile:function(f,g){var h=g.ngInclude||g.src,l=g.onload||"",k=g.autoscroll;return function(f,g,q,r,s){var t=0,v,m,F,w=function(){m&&(m.remove(),m=null);v&&(v.$destroy(),v=null);F&&(d.leave(F).then(function(){m=null}),m=F,F=null)};f.$watch(e.parseAsResourceUrl(h),function(e){var h= +function(){!y(k)||k&&!f.$eval(k)||c()},m=++t;e?(a(e,!0).then(function(a){if(m===t){var c=f.$new();r.template=a;a=s(c,function(a){w();d.enter(a,null,g).then(h)});v=c;F=a;v.$emit("$includeContentLoaded",e);f.$eval(l)}},function(){m===t&&(w(),f.$emit("$includeContentError",e))}),f.$emit("$includeContentRequested",e)):(w(),r.template=null)})}}}}],Ae=["$compile",function(a){return{restrict:"ECA",priority:-400,require:"ngInclude",link:function(c,d,e,f){/SVG/.test(d[0].toString())?(d.empty(),a(Hc(f.template, +W).childNodes)(c,function(a){d.append(a)},{futureParentElement:d})):(d.html(f.template),a(d.contents())(c))}}}],ke=Ia({priority:450,compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),we=function(){return{restrict:"A",priority:100,require:"ngModel",link:function(a,c,d,e){var f=c.attr(d.$attr.ngList)||", ",g="false"!==d.ngTrim,h=g?N(f):f;e.$parsers.push(function(a){if(!x(a)){var c=[];a&&r(a.split(h),function(a){a&&c.push(g?N(a):a)});return c}});e.$formatters.push(function(a){return H(a)? +a.join(f):t});e.$isEmpty=function(a){return!a||!a.length}}}},lb="ng-valid",vd="ng-invalid",Ra="ng-pristine",Lb="ng-dirty",xd="ng-pending",Nb=new R("ngModel"),eg=["$scope","$exceptionHandler","$attrs","$element","$parse","$animate","$timeout","$rootScope","$q","$interpolate",function(a,c,d,e,f,g,h,l,k,n){this.$modelValue=this.$viewValue=Number.NaN;this.$$rawModelValue=t;this.$validators={};this.$asyncValidators={};this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$untouched=!0; +this.$touched=!1;this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$error={};this.$$success={};this.$pending=t;this.$name=n(d.name||"",!1)(a);var p=f(d.ngModel),q=p.assign,u=p,s=q,M=null,v,m=this;this.$$setOptions=function(a){if((m.$options=a)&&a.getterSetter){var c=f(d.ngModel+"()"),g=f(d.ngModel+"($$$p)");u=function(a){var d=p(a);G(d)&&(d=c(a));return d};s=function(a,c){G(p(a))?g(a,{$$$p:m.$modelValue}):q(a,m.$modelValue)}}else if(!p.assign)throw Nb("nonassign",d.ngModel,wa(e)); +};this.$render=E;this.$isEmpty=function(a){return x(a)||""===a||null===a||a!==a};var F=e.inheritedData("$formController")||Kb,w=0;sd({ctrl:this,$element:e,set:function(a,c){a[c]=!0},unset:function(a,c){delete a[c]},parentForm:F,$animate:g});this.$setPristine=function(){m.$dirty=!1;m.$pristine=!0;g.removeClass(e,Lb);g.addClass(e,Ra)};this.$setDirty=function(){m.$dirty=!0;m.$pristine=!1;g.removeClass(e,Ra);g.addClass(e,Lb);F.$setDirty()};this.$setUntouched=function(){m.$touched=!1;m.$untouched=!0;g.setClass(e, +"ng-untouched","ng-touched")};this.$setTouched=function(){m.$touched=!0;m.$untouched=!1;g.setClass(e,"ng-touched","ng-untouched")};this.$rollbackViewValue=function(){h.cancel(M);m.$viewValue=m.$$lastCommittedViewValue;m.$render()};this.$validate=function(){if(!Y(m.$modelValue)||!isNaN(m.$modelValue)){var a=m.$$rawModelValue,c=m.$valid,d=m.$modelValue,e=m.$options&&m.$options.allowInvalid;m.$$runValidators(a,m.$$lastCommittedViewValue,function(f){e||c===f||(m.$modelValue=f?a:t,m.$modelValue!==d&&m.$$writeModelToScope())})}}; +this.$$runValidators=function(a,c,d){function e(){var d=!0;r(m.$validators,function(e,f){var h=e(a,c);d=d&&h;g(f,h)});return d?!0:(r(m.$asyncValidators,function(a,c){g(c,null)}),!1)}function f(){var d=[],e=!0;r(m.$asyncValidators,function(f,h){var k=f(a,c);if(!k||!G(k.then))throw Nb("$asyncValidators",k);g(h,t);d.push(k.then(function(){g(h,!0)},function(a){e=!1;g(h,!1)}))});d.length?k.all(d).then(function(){h(e)},E):h(!0)}function g(a,c){l===w&&m.$setValidity(a,c)}function h(a){l===w&&d(a)}w++;var l= +w;(function(){var a=m.$$parserName||"parse";if(v===t)g(a,null);else return v||(r(m.$validators,function(a,c){g(c,null)}),r(m.$asyncValidators,function(a,c){g(c,null)})),g(a,v),v;return!0})()?e()?f():h(!1):h(!1)};this.$commitViewValue=function(){var a=m.$viewValue;h.cancel(M);if(m.$$lastCommittedViewValue!==a||""===a&&m.$$hasNativeValidators)m.$$lastCommittedViewValue=a,m.$pristine&&this.$setDirty(),this.$$parseAndValidate()};this.$$parseAndValidate=function(){var c=m.$$lastCommittedViewValue;if(v= +x(c)?t:!0)for(var d=0;d<m.$parsers.length;d++)if(c=m.$parsers[d](c),x(c)){v=!1;break}Y(m.$modelValue)&&isNaN(m.$modelValue)&&(m.$modelValue=u(a));var e=m.$modelValue,f=m.$options&&m.$options.allowInvalid;m.$$rawModelValue=c;f&&(m.$modelValue=c,m.$modelValue!==e&&m.$$writeModelToScope());m.$$runValidators(c,m.$$lastCommittedViewValue,function(a){f||(m.$modelValue=a?c:t,m.$modelValue!==e&&m.$$writeModelToScope())})};this.$$writeModelToScope=function(){s(a,m.$modelValue);r(m.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})}; +this.$setViewValue=function(a,c){m.$viewValue=a;m.$options&&!m.$options.updateOnDefault||m.$$debounceViewValueCommit(c)};this.$$debounceViewValueCommit=function(c){var d=0,e=m.$options;e&&y(e.debounce)&&(e=e.debounce,Y(e)?d=e:Y(e[c])?d=e[c]:Y(e["default"])&&(d=e["default"]));h.cancel(M);d?M=h(function(){m.$commitViewValue()},d):l.$$phase?m.$commitViewValue():a.$apply(function(){m.$commitViewValue()})};a.$watch(function(){var c=u(a);if(c!==m.$modelValue){m.$modelValue=m.$$rawModelValue=c;v=t;for(var d= +m.$formatters,e=d.length,f=c;e--;)f=d[e](f);m.$viewValue!==f&&(m.$viewValue=m.$$lastCommittedViewValue=f,m.$render(),m.$$runValidators(c,f,E))}return c})}],ve=["$rootScope",function(a){return{restrict:"A",require:["ngModel","^?form","^?ngModelOptions"],controller:eg,priority:1,compile:function(c){c.addClass(Ra).addClass("ng-untouched").addClass(lb);return{pre:function(a,c,f,g){var h=g[0],l=g[1]||Kb;h.$$setOptions(g[2]&&g[2].$options);l.$addControl(h);f.$observe("name",function(a){h.$name!==a&&l.$$renameControl(h, +a)});a.$on("$destroy",function(){l.$removeControl(h)})},post:function(c,e,f,g){var h=g[0];if(h.$options&&h.$options.updateOn)e.on(h.$options.updateOn,function(a){h.$$debounceViewValueCommit(a&&a.type)});e.on("blur",function(e){h.$touched||(a.$$phase?c.$evalAsync(h.$setTouched):c.$apply(h.$setTouched))})}}}}}],fg=/(\s+|^)default(\s+|$)/,ze=function(){return{restrict:"A",controller:["$scope","$attrs",function(a,c){var d=this;this.$options=a.$eval(c.ngModelOptions);this.$options.updateOn!==t?(this.$options.updateOnDefault= +!1,this.$options.updateOn=N(this.$options.updateOn.replace(fg,function(){d.$options.updateOnDefault=!0;return" "}))):this.$options.updateOnDefault=!0}]}},le=Ia({terminal:!0,priority:1E3}),me=["$locale","$interpolate",function(a,c){var d=/{}/g,e=/^when(Minus)?(.+)$/;return{restrict:"EA",link:function(f,g,h){function l(a){g.text(a||"")}var k=h.count,n=h.$attr.when&&g.attr(h.$attr.when),p=h.offset||0,q=f.$eval(n)||{},u={},n=c.startSymbol(),s=c.endSymbol(),t=n+k+"-"+p+s,v=ca.noop,m;r(h,function(a,c){var d= +e.exec(c);d&&(d=(d[1]?"-":"")+z(d[2]),q[d]=g.attr(h.$attr[c]))});r(q,function(a,e){u[e]=c(a.replace(d,t))});f.$watch(k,function(c){c=parseFloat(c);var d=isNaN(c);d||c in q||(c=a.pluralCat(c-p));c===m||d&&isNaN(m)||(v(),v=f.$watch(u[c],l),m=c)})}}}],ne=["$parse","$animate",function(a,c){var d=R("ngRepeat"),e=function(a,c,d,e,k,n,p){a[d]=e;k&&(a[k]=n);a.$index=c;a.$first=0===c;a.$last=c===p-1;a.$middle=!(a.$first||a.$last);a.$odd=!(a.$even=0===(c&1))};return{restrict:"A",multiElement:!0,transclude:"element", +priority:1E3,terminal:!0,$$tlb:!0,compile:function(f,g){var h=g.ngRepeat,l=W.createComment(" end ngRepeat: "+h+" "),k=h.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);if(!k)throw d("iexp",h);var n=k[1],p=k[2],q=k[3],u=k[4],k=n.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);if(!k)throw d("iidexp",n);var s=k[3]||k[1],y=k[2];if(q&&(!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(q)||/^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(q)))throw d("badident", +q);var v,m,w,x,E={$id:Ma};u?v=a(u):(w=function(a,c){return Ma(c)},x=function(a){return a});return function(a,f,g,k,n){v&&(m=function(c,d,e){y&&(E[y]=c);E[s]=d;E.$index=e;return v(a,E)});var u=ia();a.$watchCollection(p,function(g){var k,p,v=f[0],D,E=ia(),G,H,L,S,J,C,z;q&&(a[q]=g);if(Sa(g))J=g,p=m||w;else{p=m||x;J=[];for(z in g)g.hasOwnProperty(z)&&"$"!=z.charAt(0)&&J.push(z);J.sort()}G=J.length;z=Array(G);for(k=0;k<G;k++)if(H=g===J?k:J[k],L=g[H],S=p(H,L,k),u[S])C=u[S],delete u[S],E[S]=C,z[k]=C;else{if(E[S])throw r(z, +function(a){a&&a.scope&&(u[a.id]=a)}),d("dupes",h,S,L);z[k]={id:S,scope:t,clone:t};E[S]=!0}for(D in u){C=u[D];S=tb(C.clone);c.leave(S);if(S[0].parentNode)for(k=0,p=S.length;k<p;k++)S[k].$$NG_REMOVED=!0;C.scope.$destroy()}for(k=0;k<G;k++)if(H=g===J?k:J[k],L=g[H],C=z[k],C.scope){D=v;do D=D.nextSibling;while(D&&D.$$NG_REMOVED);C.clone[0]!=D&&c.move(tb(C.clone),null,A(v));v=C.clone[C.clone.length-1];e(C.scope,k,s,L,y,H,G)}else n(function(a,d){C.scope=d;var f=l.cloneNode(!1);a[a.length++]=f;c.enter(a, +null,A(v));v=f;C.clone=a;E[C.id]=C;e(C.scope,k,s,L,y,H,G)});u=E})}}}}],oe=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngShow,function(c){a[c?"removeClass":"addClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],he=["$animate",function(a){return{restrict:"A",multiElement:!0,link:function(c,d,e){c.$watch(e.ngHide,function(c){a[c?"addClass":"removeClass"](d,"ng-hide",{tempClasses:"ng-hide-animate"})})}}}],pe=Ia(function(a,c,d){a.$watchCollection(d.ngStyle, +function(a,d){d&&a!==d&&r(d,function(a,d){c.css(d,"")});a&&c.css(a)})}),qe=["$animate",function(a){return{restrict:"EA",require:"ngSwitch",controller:["$scope",function(){this.cases={}}],link:function(c,d,e,f){var g=[],h=[],l=[],k=[],n=function(a,c){return function(){a.splice(c,1)}};c.$watch(e.ngSwitch||e.on,function(c){var d,e;d=0;for(e=l.length;d<e;++d)a.cancel(l[d]);d=l.length=0;for(e=k.length;d<e;++d){var s=tb(h[d].clone);k[d].$destroy();(l[d]=a.leave(s)).then(n(l,d))}h.length=0;k.length=0;(g= +f.cases["!"+c]||f.cases["?"])&&r(g,function(c){c.transclude(function(d,e){k.push(e);var f=c.element;d[d.length++]=W.createComment(" end ngSwitchWhen: ");h.push({clone:d});a.enter(d,f.parent(),f)})})})}}}],re=Ia({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0,link:function(a,c,d,e,f){e.cases["!"+d.ngSwitchWhen]=e.cases["!"+d.ngSwitchWhen]||[];e.cases["!"+d.ngSwitchWhen].push({transclude:f,element:c})}}),se=Ia({transclude:"element",priority:1200,require:"^ngSwitch",multiElement:!0, +link:function(a,c,d,e,f){e.cases["?"]=e.cases["?"]||[];e.cases["?"].push({transclude:f,element:c})}}),ue=Ia({restrict:"EAC",link:function(a,c,d,e,f){if(!f)throw R("ngTransclude")("orphan",wa(c));f(function(a){c.empty();c.append(a)})}}),Vd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){"text/ng-template"==d.type&&a.put(d.id,c[0].text)}}}],gg=R("ngOptions"),te=ea({restrict:"A",terminal:!0}),Wd=["$compile","$parse",function(a,c){var d=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/, +e={$setViewValue:E};return{restrict:"E",require:["select","?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var l=this,k={},n=e,p;l.databound=d.ngModel;l.init=function(a,c,d){n=a;p=d};l.addOption=function(c,d){La(c,'"option value"');k[c]=!0;n.$viewValue==c&&(a.val(c),p.parent()&&p.remove());d&&d[0].hasAttribute("selected")&&(d[0].selected=!0)};l.removeOption=function(a){this.hasOption(a)&&(delete k[a],n.$viewValue===a&&this.renderUnknownOption(a))};l.renderUnknownOption=function(c){c= +"? "+Ma(c)+" ?";p.val(c);a.prepend(p);a.val(c);p.prop("selected",!0)};l.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy",function(){l.renderUnknownOption=E})}],link:function(e,g,h,l){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(C.parent()&&C.remove(),c.val(a),""===a&&v.prop("selected",!0)):x(a)&&v?c.val(""):e.renderUnknownOption(a)};c.on("change",function(){a.$apply(function(){C.parent()&&C.remove();d.$setViewValue(c.val())})})}function n(a,c,d){var e; +d.$render=function(){var a=new eb(d.$viewValue);r(c.find("option"),function(c){c.selected=y(a.get(c.value))})};a.$watch(function(){ha(e,d.$viewValue)||(e=sa(d.$viewValue),d.$render())});c.on("change",function(){a.$apply(function(){var a=[];r(c.find("option"),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function p(e,f,g){function h(a,c,d){T[x]=d;G&&(T[G]=c);return a(e,T)}function k(a){var c;if(u)if(I&&H(a)){c=new eb([]);for(var d=0;d<a.length;d++)c.put(h(I,null,a[d]),!0)}else c= +new eb(a);else I&&(a=h(I,null,a));return function(d,e){var f;f=I?I:B?B:z;return u?y(c.remove(h(f,d,e))):a===h(f,d,e)}}function l(){m||(e.$$postDigest(p),m=!0)}function n(a,c,d){a[c]=a[c]||0;a[c]+=d?1:-1}function p(){m=!1;var a={"":[]},c=[""],d,l,s,t,v;s=g.$viewValue;t=L(e)||[];var B=G?Object.keys(t).sort():t,x,A,H,z,O={};v=k(s);var N=!1,U,W;Q={};for(z=0;H=B.length,z<H;z++){x=z;if(G&&(x=B[z],"$"===x.charAt(0)))continue;A=t[x];d=h(J,x,A)||"";(l=a[d])||(l=a[d]=[],c.push(d));d=v(x,A);N=N||d;A=h(C,x,A); +A=y(A)?A:"";W=I?I(e,T):G?B[z]:z;I&&(Q[W]=x);l.push({id:W,label:A,selected:d})}u||(w||null===s?a[""].unshift({id:"",label:"",selected:!N}):N||a[""].unshift({id:"?",label:"",selected:!0}));x=0;for(B=c.length;x<B;x++){d=c[x];l=a[d];R.length<=x?(s={element:E.clone().attr("label",d),label:l.label},t=[s],R.push(t),f.append(s.element)):(t=R[x],s=t[0],s.label!=d&&s.element.attr("label",s.label=d));N=null;z=0;for(H=l.length;z<H;z++)d=l[z],(v=t[z+1])?(N=v.element,v.label!==d.label&&(n(O,v.label,!1),n(O,d.label, +!0),N.text(v.label=d.label),N.prop("label",v.label)),v.id!==d.id&&N.val(v.id=d.id),N[0].selected!==d.selected&&(N.prop("selected",v.selected=d.selected),Qa&&N.prop("selected",v.selected))):(""===d.id&&w?U=w:(U=F.clone()).val(d.id).prop("selected",d.selected).attr("selected",d.selected).prop("label",d.label).text(d.label),t.push(v={element:U,label:d.label,id:d.id,selected:d.selected}),n(O,d.label,!0),N?N.after(U):s.element.append(U),N=U);for(z++;t.length>z;)d=t.pop(),n(O,d.label,!1),d.element.remove()}for(;R.length> +x;){l=R.pop();for(z=1;z<l.length;++z)n(O,l[z].label,!1);l[0].element.remove()}r(O,function(a,c){0<a?q.addOption(c):0>a&&q.removeOption(c)})}var v;if(!(v=s.match(d)))throw gg("iexp",s,wa(f));var C=c(v[2]||v[1]),x=v[4]||v[6],A=/ as /.test(v[0])&&v[1],B=A?c(A):null,G=v[5],J=c(v[3]||""),z=c(v[2]?v[1]:x),L=c(v[7]),I=v[8]?c(v[8]):null,Q={},R=[[{element:f,label:""}]],T={};w&&(a(w)(e),w.removeClass("ng-scope"),w.remove());f.empty();f.on("change",function(){e.$apply(function(){var a=L(e)||[],c;if(u)c=[],r(f.val(), +function(d){d=I?Q[d]:d;c.push("?"===d?t:""===d?null:h(B?B:z,d,a[d]))});else{var d=I?Q[f.val()]:f.val();c="?"===d?t:""===d?null:h(B?B:z,d,a[d])}g.$setViewValue(c);p()})});g.$render=p;e.$watchCollection(L,l);e.$watchCollection(function(){var a=L(e),c;if(a&&H(a)){c=Array(a.length);for(var d=0,f=a.length;d<f;d++)c[d]=h(C,d,a[d])}else if(a)for(d in c={},a)a.hasOwnProperty(d)&&(c[d]=h(C,d,a[d]));return c},l);u&&e.$watchCollection(function(){return g.$modelValue},l)}if(l[1]){var q=l[0];l=l[1];var u=h.multiple, +s=h.ngOptions,w=!1,v,m=!1,F=A(W.createElement("option")),E=A(W.createElement("optgroup")),C=F.clone();h=0;for(var B=g.children(),G=B.length;h<G;h++)if(""===B[h].value){v=w=B.eq(h);break}q.init(l,w,C);u&&(l.$isEmpty=function(a){return!a||0===a.length});s?p(e,g,l):u?n(e,g,l):k(e,g,l,q)}}}}],Yd=["$interpolate",function(a){var c={addOption:E,removeOption:E};return{restrict:"E",priority:100,compile:function(d,e){if(x(e.value)){var f=a(d.text(),!0);f||e.$set("value",d.text())}return function(a,d,e){var k= +d.parent(),n=k.data("$selectController")||k.parent().data("$selectController");n&&n.databound||(n=c);f?a.$watch(f,function(a,c){e.$set("value",a);c!==a&&n.removeOption(c);n.addOption(a,d)}):n.addOption(e.value,d);d.on("$destroy",function(){n.removeOption(e.value)})}}}}],Xd=ea({restrict:"E",terminal:!1}),Bc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){e&&(d.required=!0,e.$validators.required=function(a,c){return!d.required||!e.$isEmpty(c)},d.$observe("required",function(){e.$validate()}))}}}, +Ac=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f,g=d.ngPattern||d.pattern;d.$observe("pattern",function(a){C(a)&&0<a.length&&(a=new RegExp("^"+a+"$"));if(a&&!a.test)throw R("ngPattern")("noregexp",g,a,wa(c));f=a||t;e.$validate()});e.$validators.pattern=function(a){return e.$isEmpty(a)||x(f)||f.test(a)}}}}},Dc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=-1;d.$observe("maxlength",function(a){a=aa(a);f=isNaN(a)?-1:a;e.$validate()}); +e.$validators.maxlength=function(a,c){return 0>f||e.$isEmpty(c)||c.length<=f}}}}},Cc=function(){return{restrict:"A",require:"?ngModel",link:function(a,c,d,e){if(e){var f=0;d.$observe("minlength",function(a){f=aa(a)||0;e.$validate()});e.$validators.minlength=function(a,c){return e.$isEmpty(c)||c.length>=f}}}}};Q.angular.bootstrap?console.log("WARNING: Tried to load angular more than once."):(Nd(),Pd(ca),A(W).ready(function(){Jd(W,uc)}))})(window,document);!window.angular.$$csp()&&window.angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>'); +//# sourceMappingURL=angular.min.js.map diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js.gzip b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js.gzip Binary files differnew file mode 100644 index 000000000..ffd226d59 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js.gzip diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js.map b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js.map new file mode 100644 index 000000000..b05460368 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/angular.min.js.map @@ -0,0 +1,8 @@ +{ +"version":3, +"file":"angular.min.js", +"lineCount":250, +"mappings":"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", +"sources":["angular.js"], +"names":["window","document","undefined","minErr","isArrayLike","obj","isWindow","length","nodeType","NODE_TYPE_ELEMENT","isString","isArray","forEach","iterator","context","key","isFunction","hasOwnProperty","call","isPrimitive","forEachSorted","keys","Object","sort","i","reverseParams","iteratorFn","value","nextUid","uid","setHashKey","h","$$hashKey","extend","dst","ii","arguments","j","jj","int","str","parseInt","inherit","parent","extra","create","noop","identity","$","valueFn","isUndefined","isDefined","isObject","isNumber","isDate","toString","isRegExp","isScope","$evalAsync","$watch","isBoolean","isElement","node","nodeName","prop","attr","find","makeMap","items","split","nodeName_","element","lowercase","arrayRemove","array","index","indexOf","splice","copy","source","destination","stackSource","stackDest","ngMinErr","push","result","Date","getTime","RegExp","match","lastIndex","emptyObject","getPrototypeOf","shallowCopy","src","charAt","equals","o1","o2","t1","t2","keySet","concat","array1","array2","slice","bind","self","fn","curryArgs","startIndex","apply","toJsonReplacer","val","toJson","pretty","JSON","stringify","fromJson","json","parse","startingTag","jqLite","clone","empty","e","elemHtml","append","html","NODE_TYPE_TEXT","replace","tryDecodeURIComponent","decodeURIComponent","parseKeyValue","keyValue","key_value","toKeyValue","parts","arrayValue","encodeUriQuery","join","encodeUriSegment","pctEncodeSpaces","encodeURIComponent","getNgAttribute","ngAttr","ngAttrPrefixes","angularInit","bootstrap","appElement","module","config","prefix","name","hasAttribute","getAttribute","candidate","querySelector","strictDi","modules","defaultConfig","doBootstrap","injector","tag","unshift","$provide","debugInfoEnabled","$compileProvider","createInjector","invoke","bootstrapApply","scope","compile","$apply","data","NG_ENABLE_DEBUG_INFO","NG_DEFER_BOOTSTRAP","test","angular","resumeBootstrap","angular.resumeBootstrap","extraModules","resumeDeferredBootstrap","reloadWithDebugInfo","location","reload","getTestability","rootElement","get","snake_case","separator","SNAKE_CASE_REGEXP","letter","pos","toLowerCase","bindJQuery","originalCleanData","bindJQueryFired","jQuery","on","JQLitePrototype","isolateScope","controller","inheritedData","cleanData","jQuery.cleanData","elems","events","skipDestroyOnNextJQueryCleanData","elem","_data","$destroy","triggerHandler","JQLite","assertArg","arg","reason","assertArgFn","acceptArrayAnnotation","constructor","assertNotHasOwnProperty","getter","path","bindFnToScope","lastInstance","len","getBlockNodes","nodes","endNode","blockNodes","nextSibling","createMap","setupModuleLoader","ensure","factory","$injectorMinErr","$$minErr","requires","configFn","invokeLater","provider","method","insertMethod","queue","invokeQueue","moduleInstance","configBlocks","runBlocks","_invokeQueue","_configBlocks","_runBlocks","service","constant","animation","filter","directive","run","block","publishExternalAPI","version","uppercase","counter","csp","angularModule","$LocaleProvider","ngModule","$$sanitizeUri","$$SanitizeUriProvider","$CompileProvider","a","htmlAnchorDirective","input","inputDirective","textarea","form","formDirective","script","scriptDirective","select","selectDirective","style","styleDirective","option","optionDirective","ngBind","ngBindDirective","ngBindHtml","ngBindHtmlDirective","ngBindTemplate","ngBindTemplateDirective","ngClass","ngClassDirective","ngClassEven","ngClassEvenDirective","ngClassOdd","ngClassOddDirective","ngCloak","ngCloakDirective","ngController","ngControllerDirective","ngForm","ngFormDirective","ngHide","ngHideDirective","ngIf","ngIfDirective","ngInclude","ngIncludeDirective","ngInit","ngInitDirective","ngNonBindable","ngNonBindableDirective","ngPluralize","ngPluralizeDirective","ngRepeat","ngRepeatDirective","ngShow","ngShowDirective","ngStyle","ngStyleDirective","ngSwitch","ngSwitchDirective","ngSwitchWhen","ngSwitchWhenDirective","ngSwitchDefault","ngSwitchDefaultDirective","ngOptions","ngOptionsDirective","ngTransclude","ngTranscludeDirective","ngModel","ngModelDirective","ngList","ngListDirective","ngChange","ngChangeDirective","pattern","patternDirective","ngPattern","required","requiredDirective","ngRequired","minlength","minlengthDirective","ngMinlength","maxlength","maxlengthDirective","ngMaxlength","ngValue","ngValueDirective","ngModelOptions","ngModelOptionsDirective","ngIncludeFillContentDirective","ngAttributeAliasDirectives","ngEventDirectives","$anchorScroll","$AnchorScrollProvider","$animate","$AnimateProvider","$browser","$BrowserProvider","$cacheFactory","$CacheFactoryProvider","$controller","$ControllerProvider","$document","$DocumentProvider","$exceptionHandler","$ExceptionHandlerProvider","$filter","$FilterProvider","$interpolate","$InterpolateProvider","$interval","$IntervalProvider","$http","$HttpProvider","$httpBackend","$HttpBackendProvider","$location","$LocationProvider","$log","$LogProvider","$parse","$ParseProvider","$rootScope","$RootScopeProvider","$q","$QProvider","$$q","$$QProvider","$sce","$SceProvider","$sceDelegate","$SceDelegateProvider","$sniffer","$SnifferProvider","$templateCache","$TemplateCacheProvider","$templateRequest","$TemplateRequestProvider","$$testability","$$TestabilityProvider","$timeout","$TimeoutProvider","$window","$WindowProvider","$$rAF","$$RAFProvider","$$asyncCallback","$$AsyncCallbackProvider","$$jqLite","$$jqLiteProvider","camelCase","SPECIAL_CHARS_REGEXP","_","offset","toUpperCase","MOZ_HACK_REGEXP","jqLiteAcceptsData","NODE_TYPE_DOCUMENT","jqLiteBuildFragment","tmp","fragment","createDocumentFragment","HTML_REGEXP","appendChild","createElement","TAG_NAME_REGEXP","exec","wrap","wrapMap","_default","innerHTML","XHTML_TAG_REGEXP","lastChild","childNodes","firstChild","textContent","createTextNode","argIsString","trim","jqLiteMinErr","parsed","SINGLE_TAG_REGEXP","jqLiteAddNodes","jqLiteClone","cloneNode","jqLiteDealoc","onlyDescendants","jqLiteRemoveData","querySelectorAll","descendants","l","jqLiteOff","type","unsupported","expandoStore","jqLiteExpandoStore","handle","listenerFns","removeEventListener","expandoId","ng339","jqCache","createIfNecessary","jqId","jqLiteData","isSimpleSetter","isSimpleGetter","massGetter","jqLiteHasClass","selector","jqLiteRemoveClass","cssClasses","setAttribute","cssClass","jqLiteAddClass","existingClasses","root","elements","jqLiteController","jqLiteInheritedData","documentElement","names","parentNode","NODE_TYPE_DOCUMENT_FRAGMENT","host","jqLiteEmpty","removeChild","jqLiteRemove","keepData","jqLiteDocumentLoaded","action","win","readyState","setTimeout","getBooleanAttrName","booleanAttr","BOOLEAN_ATTR","BOOLEAN_ELEMENTS","getAliasedAttrName","ALIASED_ATTR","createEventHandler","eventHandler","event","isDefaultPrevented","event.isDefaultPrevented","defaultPrevented","eventFns","eventFnsLength","immediatePropagationStopped","originalStopImmediatePropagation","stopImmediatePropagation","event.stopImmediatePropagation","stopPropagation","isImmediatePropagationStopped","event.isImmediatePropagationStopped","$get","this.$get","hasClass","classes","addClass","removeClass","hashKey","nextUidFn","objType","HashMap","isolatedUid","this.nextUid","put","anonFn","args","fnText","STRIP_COMMENTS","FN_ARGS","modulesToLoad","supportObject","delegate","provider_","providerInjector","instantiate","providerCache","providerSuffix","enforceReturnValue","enforcedReturnValue","instanceInjector","factoryFn","enforce","loadModules","moduleFn","runInvokeQueue","invokeArgs","loadedModules","message","stack","createInternalInjector","cache","getService","serviceName","caller","INSTANTIATING","err","shift","locals","$inject","$$annotate","Type","instance","prototype","returnedValue","annotate","has","$injector","instanceCache","decorator","decorFn","origProvider","orig$get","origProvider.$get","origInstance","$delegate","autoScrollingEnabled","disableAutoScrolling","this.disableAutoScrolling","getFirstAnchor","list","Array","some","scrollTo","scrollIntoView","scroll","yOffset","getComputedStyle","position","getBoundingClientRect","bottom","elemTop","top","scrollBy","hash","elm","getElementById","getElementsByName","autoScrollWatch","autoScrollWatchAction","newVal","oldVal","supported","Browser","completeOutstandingRequest","outstandingRequestCount","outstandingRequestCallbacks","pop","error","startPoller","interval","check","pollFns","pollFn","pollTimeout","cacheStateAndFireUrlChange","cacheState","fireUrlChange","history","state","cachedState","lastCachedState","lastBrowserUrl","url","lastHistoryState","urlChangeListeners","listener","safeDecodeURIComponent","rawDocument","clearTimeout","pendingDeferIds","isMock","$$completeOutstandingRequest","$$incOutstandingRequestCount","self.$$incOutstandingRequestCount","notifyWhenNoOutstandingRequests","self.notifyWhenNoOutstandingRequests","callback","addPollFn","self.addPollFn","href","baseElement","reloadLocation","self.url","sameState","sameBase","stripHash","substr","self.state","urlChangeInit","onUrlChange","self.onUrlChange","$$checkUrlChange","baseHref","self.baseHref","lastCookies","lastCookieString","cookiePath","cookies","self.cookies","cookieLength","cookie","warn","cookieArray","substring","defer","self.defer","delay","timeoutId","cancel","self.defer.cancel","deferId","cacheFactory","cacheId","options","refresh","entry","freshEnd","staleEnd","n","link","p","nextEntry","prevEntry","caches","size","stats","id","capacity","Number","MAX_VALUE","lruHash","lruEntry","remove","removeAll","destroy","info","cacheFactory.info","cacheFactory.get","$$sanitizeUriProvider","parseIsolateBindings","directiveName","LOCAL_REGEXP","bindings","definition","scopeName","$compileMinErr","mode","collection","optional","attrName","hasDirectives","COMMENT_DIRECTIVE_REGEXP","CLASS_DIRECTIVE_REGEXP","ALL_OR_NOTHING_ATTRS","REQUIRE_PREFIX_REGEXP","EVENT_HANDLER_ATTR_REGEXP","this.directive","registerDirective","directiveFactory","Suffix","directives","priority","require","restrict","$$isolateBindings","aHrefSanitizationWhitelist","this.aHrefSanitizationWhitelist","regexp","imgSrcSanitizationWhitelist","this.imgSrcSanitizationWhitelist","this.debugInfoEnabled","enabled","safeAddClass","$element","className","$compileNodes","transcludeFn","maxPriority","ignoreDirective","previousCompileContext","nodeValue","compositeLinkFn","compileNodes","$$addScopeClass","namespace","publicLinkFn","cloneConnectFn","parentBoundTranscludeFn","transcludeControllers","futureParentElement","$$boundTransclude","$linkNode","wrapTemplate","controllerName","$$addScopeInfo","nodeList","$rootElement","childLinkFn","childScope","childBoundTranscludeFn","stableNodeList","nodeLinkFnFound","linkFns","idx","nodeLinkFn","$new","transcludeOnThisElement","createBoundTranscludeFn","transclude","elementTranscludeOnThisElement","templateOnThisElement","attrs","linkFnFound","Attributes","collectDirectives","applyDirectivesToNode","$$element","terminal","previousBoundTranscludeFn","elementTransclusion","boundTranscludeFn","transcludedScope","cloneFn","controllers","containingScope","$$transcluded","attrsMap","$attr","addDirective","directiveNormalize","isNgAttr","nAttrs","attributes","attrStartName","attrEndName","ngAttrName","NG_ATTR_BINDING","PREFIX_REGEXP","directiveNName","directiveIsMultiElement","nName","addAttrInterpolateDirective","animVal","addTextInterpolateDirective","NODE_TYPE_COMMENT","byPriority","groupScan","attrStart","attrEnd","depth","groupElementsLinkFnWrapper","linkFn","compileNode","templateAttrs","jqCollection","originalReplaceDirective","preLinkFns","postLinkFns","addLinkFns","pre","post","newIsolateScopeDirective","$$isolateScope","cloneAndAnnotateFn","getControllers","elementControllers","retrievalMethod","$searchElement","linkNode","controllersBoundTransclude","cloneAttachFn","hasElementTranscludeDirective","scopeToChild","controllerDirectives","$scope","$attrs","$transclude","controllerInstance","controllerAs","templateDirective","$$originalDirective","isolateScopeController","isolateBindingContext","identifier","bindToController","lastValue","parentGet","parentSet","compare","$observe","$$observers","$$scope","literal","b","assign","parentValueWatch","parentValue","$stateful","unwatch","$watchCollection","$on","invokeLinkFn","template","templateUrl","terminalPriority","newScopeDirective","nonTlbTranscludeDirective","hasTranscludeDirective","hasTemplate","$compileNode","$template","childTranscludeFn","$$start","$$end","directiveValue","assertNoDuplicate","$$tlb","createComment","replaceWith","replaceDirective","contents","denormalizeTemplate","removeComments","templateNamespace","newTemplateAttrs","templateDirectives","unprocessedDirectives","markDirectivesAsIsolate","mergeTemplateAttributes","compileTemplateUrl","Math","max","tDirectives","startAttrName","endAttrName","multiElement","srcAttr","dstAttr","$set","tAttrs","linkQueue","afterTemplateNodeLinkFn","afterTemplateChildLinkFn","beforeTemplateCompileNode","origAsyncDirective","derivedSyncDirective","getTrustedResourceUrl","then","content","tempTemplateAttrs","beforeTemplateLinkNode","linkRootElement","$$destroyed","oldClasses","delayedNodeLinkFn","ignoreChildLinkFn","diff","what","previousDirective","text","interpolateFn","textInterpolateCompileFn","templateNode","templateNodeParent","hasCompileParent","$$addBindingClass","textInterpolateLinkFn","$$addBindingInfo","expressions","interpolateFnWatchAction","wrapper","getTrustedContext","attrNormalizedName","HTML","RESOURCE_URL","allOrNothing","trustedContext","attrInterpolatePreLinkFn","newValue","$$inter","oldValue","$updateClass","elementsToRemove","newNode","firstElementToRemove","removeCount","j2","replaceChild","expando","k","kk","annotation","attributesToCopy","$normalize","$addClass","classVal","$removeClass","newClasses","toAdd","tokenDifference","toRemove","writeAttr","booleanKey","aliasedKey","observer","trimmedSrcset","srcPattern","rawUris","nbrUrisWith2parts","floor","innerIdx","lastTuple","removeAttr","listeners","startSymbol","endSymbol","binding","isolated","noTemplate","dataName","str1","str2","values","tokens1","tokens2","token","jqNodes","globals","CNTRL_REG","register","this.register","allowGlobals","this.allowGlobals","addIdentifier","expression","later","ident","$controllerMinErr","controllerPrototype","exception","cause","defaultHttpResponseTransform","headers","tempData","JSON_PROTECTION_PREFIX","contentType","jsonStart","JSON_START","JSON_ENDS","parseHeaders","line","headersGetter","headersObj","transformData","status","fns","defaults","transformResponse","transformRequest","d","common","CONTENT_TYPE_APPLICATION_JSON","patch","xsrfCookieName","xsrfHeaderName","useApplyAsync","this.useApplyAsync","interceptorFactories","interceptors","requestConfig","response","resp","reject","executeHeaderFns","headerContent","processedHeaders","headerFn","header","mergeHeaders","defHeaders","reqHeaders","defHeaderName","reqHeaderName","lowercaseDefHeaderName","chain","serverRequest","reqData","withCredentials","sendReq","promise","when","reversedInterceptors","interceptor","request","requestError","responseError","thenFn","rejectFn","success","promise.success","promise.error","done","headersString","statusText","resolveHttpPromise","resolvePromise","$applyAsync","$$phase","deferred","resolve","resolvePromiseWithResult","removePendingReq","pendingRequests","cachedResp","buildUrl","params","defaultCache","xsrfValue","urlIsSameOrigin","timeout","responseType","v","toISOString","interceptorFactory","createShortMethods","createShortMethodsWithData","createXhr","XMLHttpRequest","createHttpBackend","callbacks","$browserDefer","jsonpReq","callbackId","async","body","called","addEventListener","timeoutRequest","jsonpDone","xhr","abort","completeRequest","open","setRequestHeader","onload","xhr.onload","responseText","urlResolve","protocol","getAllResponseHeaders","onerror","onabort","send","this.startSymbol","this.endSymbol","escape","ch","mustHaveExpression","unescapeText","escapedStartRegexp","escapedEndRegexp","parseStringifyInterceptor","getTrusted","valueOf","newErr","$interpolateMinErr","endIndex","parseFns","textLength","expressionPositions","startSymbolLength","exp","endSymbolLength","compute","interpolationFn","$$watchDelegate","objectEquality","$watchGroup","interpolateFnWatcher","oldValues","currValue","$interpolate.startSymbol","$interpolate.endSymbol","count","invokeApply","setInterval","clearInterval","iteration","skipApply","$$intervalId","tick","notify","intervals","interval.cancel","NUMBER_FORMATS","DECIMAL_SEP","GROUP_SEP","PATTERNS","minInt","minFrac","maxFrac","posPre","posSuf","negPre","negSuf","gSize","lgSize","CURRENCY_SYM","DATETIME_FORMATS","MONTH","SHORTMONTH","DAY","SHORTDAY","AMPMS","medium","fullDate","longDate","mediumDate","shortDate","mediumTime","shortTime","ERANAMES","ERAS","pluralCat","num","encodePath","segments","parseAbsoluteUrl","absoluteUrl","locationObj","parsedUrl","$$protocol","$$host","hostname","$$port","port","DEFAULT_PORTS","parseAppUrl","relativeUrl","prefixed","$$path","pathname","$$search","search","$$hash","beginsWith","begin","whole","trimEmptyHash","stripFile","lastIndexOf","LocationHtml5Url","appBase","basePrefix","$$html5","appBaseNoFile","$$parse","this.$$parse","pathUrl","$locationMinErr","$$compose","this.$$compose","$$url","$$absUrl","$$parseLinkUrl","this.$$parseLinkUrl","relHref","appUrl","prevAppUrl","rewrittenUrl","LocationHashbangUrl","hashPrefix","withoutBaseUrl","withoutHashUrl","windowsFilePathExp","firstPathSegmentMatch","LocationHashbangInHtml5Url","locationGetter","property","locationGetterSetter","preprocess","html5Mode","requireBase","rewriteLinks","this.hashPrefix","this.html5Mode","setBrowserUrlWithFallback","oldUrl","oldState","$$state","afterLocationChange","$broadcast","absUrl","LocationMode","initialUrl","IGNORE_URI_REGEXP","ctrlKey","metaKey","shiftKey","which","button","target","absHref","preventDefault","initializing","newUrl","newState","$digest","$locationWatch","currentReplace","$$replace","urlOrStateChanged","debug","debugEnabled","this.debugEnabled","flag","formatError","Error","sourceURL","consoleLog","console","logFn","log","hasApply","arg1","arg2","ensureSafeMemberName","fullExpression","$parseMinErr","ensureSafeObject","children","isConstant","setter","setValue","fullExp","propertyObj","isPossiblyDangerousMemberName","cspSafeGetterFn","key0","key1","key2","key3","key4","expensiveChecks","eso","o","eso0","eso1","eso2","eso3","eso4","cspSafeGetter","pathVal","getterFnWithEnsureSafeObject","s","getterFn","getterFnCache","getterFnCacheExpensive","getterFnCacheDefault","pathKeys","pathKeysLength","code","needsEnsureSafeObject","lookupJs","evaledFnGetter","Function","sharedGetter","fn.assign","getValueOf","objectValueOf","cacheDefault","cacheExpensive","wrapSharedExpression","wrapped","collectExpressionInputs","inputs","expressionInputDirtyCheck","oldValueOfValue","inputsWatchDelegate","parsedExpression","inputExpressions","$$inputs","lastResult","oldInputValue","expressionInputWatch","newInputValue","oldInputValueOfValues","expressionInputsWatch","changed","oneTimeWatchDelegate","oneTimeWatch","oneTimeListener","old","$$postDigest","oneTimeLiteralWatchDelegate","isAllDefined","allDefined","constantWatchDelegate","constantWatch","constantListener","addInterceptor","interceptorFn","watchDelegate","regularInterceptedExpression","oneTimeInterceptedExpression","$parseOptions","$parseOptionsExpensive","oneTime","cacheKey","parseOptions","lexer","Lexer","parser","Parser","qFactory","nextTick","exceptionHandler","callOnce","resolveFn","Promise","simpleBind","scheduleProcessQueue","processScheduled","pending","Deferred","$qMinErr","TypeError","onFulfilled","onRejected","progressBack","catch","finally","handleCallback","$$reject","$$resolve","progress","makePromise","resolved","isResolved","callbackOutput","errback","$Q","Q","resolver","all","promises","results","requestAnimationFrame","webkitRequestAnimationFrame","cancelAnimationFrame","webkitCancelAnimationFrame","webkitCancelRequestAnimationFrame","rafSupported","raf","timer","createChildScopeClass","ChildScope","$$watchers","$$nextSibling","$$childHead","$$childTail","$$listeners","$$listenerCount","$$watchersCount","$id","$$ChildScope","TTL","$rootScopeMinErr","lastDirtyWatch","applyAsyncId","digestTtl","this.digestTtl","destroyChildScope","$event","currentScope","Scope","$parent","$$prevSibling","$root","beginPhase","phase","decrementListenerCount","current","initWatchVal","flushApplyAsync","applyAsyncQueue","scheduleApplyAsync","isolate","child","watchExp","watcher","last","eq","deregisterWatch","watchExpressions","watchGroupAction","changeReactionScheduled","firstRun","newValues","deregisterFns","shouldCall","deregisterWatchGroup","expr","unwatchFn","watchGroupSubAction","$watchCollectionInterceptor","_value","bothNaN","newItem","oldItem","internalArray","oldLength","changeDetected","newLength","internalObject","veryOldValue","trackVeryOldValue","changeDetector","initRun","$watchCollectionAction","watch","watchers","dirty","ttl","watchLog","logIdx","asyncTask","asyncQueue","$eval","isNaN","msg","next","postDigestQueue","eventName","this.$watchGroup","$applyAsyncExpression","namedListeners","indexOfListener","$emit","targetScope","listenerArgs","$$asyncQueue","$$postDigestQueue","$$applyAsyncQueue","sanitizeUri","uri","isImage","regex","normalizedVal","adjustMatcher","matcher","$sceMinErr","escapeForRegexp","adjustMatchers","matchers","adjustedMatchers","SCE_CONTEXTS","resourceUrlWhitelist","resourceUrlBlacklist","this.resourceUrlWhitelist","this.resourceUrlBlacklist","matchUrl","generateHolderType","Base","holderType","trustedValue","$$unwrapTrustedValue","this.$$unwrapTrustedValue","holderType.prototype.valueOf","holderType.prototype.toString","htmlSanitizer","trustedValueHolderBase","byType","CSS","URL","JS","trustAs","Constructor","maybeTrusted","allowed","this.enabled","msie","sce","isEnabled","sce.isEnabled","sce.getTrusted","parseAs","sce.parseAs","enumValue","lName","eventSupport","android","userAgent","navigator","boxee","vendorPrefix","vendorRegex","bodyStyle","transitions","animations","webkitTransition","webkitAnimation","pushState","hasEvent","divElm","handleRequestFn","tpl","ignoreRequestError","totalPendingRequests","transformer","httpOptions","handleError","testability","testability.findBindings","opt_exactMatch","getElementsByClassName","matches","dataBinding","bindingName","testability.findModels","prefixes","attributeEquals","testability.getLocation","testability.setLocation","testability.whenStable","deferreds","$$timeoutId","timeout.cancel","urlParsingNode","requestUrl","originUrl","filters","suffix","currencyFilter","dateFilter","filterFilter","jsonFilter","limitToFilter","lowercaseFilter","numberFilter","orderByFilter","uppercaseFilter","comparator","matchAgainstAnyProp","predicateFn","createPredicateFn","shouldMatchPrimitives","actual","expected","item","deepCompare","dontMatchWholeObject","actualType","expectedType","expectedVal","matchAnyProperty","actualVal","$locale","formats","amount","currencySymbol","fractionSize","formatNumber","number","groupSep","decimalSep","isFinite","isNegative","abs","numStr","formatedText","hasExponent","toFixed","parseFloat","fractionLen","min","round","fraction","lgroup","group","padNumber","digits","neg","dateGetter","date","dateStrGetter","shortForm","getFirstThursdayOfYear","year","dayOfWeekOnFirst","getDay","weekGetter","firstThurs","getFullYear","thisThurs","getMonth","getDate","eraGetter","jsonStringToDate","string","R_ISO8601_STR","tzHour","tzMin","dateSetter","setUTCFullYear","setFullYear","timeSetter","setUTCHours","setHours","m","ms","format","timezone","NUMBER_STRING","DATE_FORMATS_SPLIT","setMinutes","getMinutes","getTimezoneOffset","DATE_FORMATS","object","spacing","limit","Infinity","sortPredicate","reverseOrder","reverseComparator","comp","descending","objectToString","v1","v2","map","predicate","ngDirective","FormController","controls","parentForm","$$parentForm","nullFormCtrl","$error","$$success","$pending","$name","$dirty","$pristine","$valid","$invalid","$submitted","$addControl","$rollbackViewValue","form.$rollbackViewValue","control","$commitViewValue","form.$commitViewValue","form.$addControl","$$renameControl","form.$$renameControl","newName","oldName","$removeControl","form.$removeControl","$setValidity","addSetValidityMethod","ctrl","set","unset","$setDirty","form.$setDirty","PRISTINE_CLASS","DIRTY_CLASS","$setPristine","form.$setPristine","setClass","SUBMITTED_CLASS","$setUntouched","form.$setUntouched","$setSubmitted","form.$setSubmitted","stringBasedInputType","$formatters","$isEmpty","baseInputType","composing","ev","ngTrim","$viewValue","$$hasNativeValidators","$setViewValue","deferListener","origValue","keyCode","$render","ctrl.$render","createDateParser","mapping","iso","ISO_DATE_REGEXP","yyyy","MM","dd","HH","getHours","mm","ss","getSeconds","sss","getMilliseconds","part","NaN","createDateInputType","parseDate","dynamicDateInputType","isValidDate","parseObservedDateValue","badInputChecker","$options","previousDate","$$parserName","$parsers","parsedDate","$ngModelMinErr","timezoneOffset","ngMin","minVal","$validators","ctrl.$validators.min","$validate","ngMax","maxVal","ctrl.$validators.max","validity","VALIDITY_STATE_PROPERTY","badInput","typeMismatch","parseConstantExpr","fallback","parseFn","classDirective","arrayDifference","arrayClasses","digestClassCounts","classCounts","classesToUpdate","ngClassWatchAction","$index","old$index","mod","cachedToggleClass","switchValue","classCache","toggleValidationCss","validationErrorKey","isValid","VALID_CLASS","INVALID_CLASS","setValidity","isObjectEmpty","PENDING_CLASS","combinedState","REGEX_STRING_REGEXP","documentMode","isActive_","active","full","major","minor","dot","codeName","JQLite._data","MOUSE_EVENT_MAP","mouseleave","mouseenter","optgroup","tbody","tfoot","colgroup","caption","thead","th","td","ready","trigger","fired","removeData","removeAttribute","css","lowercasedName","specified","getNamedItem","ret","getText","$dv","multiple","selected","nodeCount","jqLiteOn","types","related","relatedTarget","contains","off","one","onFn","replaceNode","insertBefore","contentDocument","prepend","wrapNode","detach","after","newElement","toggleClass","condition","classCondition","nextElementSibling","getElementsByTagName","extraParameters","dummyEvent","handlerArgs","eventFnsCopy","arg3","unbind","FN_ARG_SPLIT","FN_ARG","argDecl","underscore","$animateMinErr","$$selectors","classNameFilter","this.classNameFilter","$$classNameFilter","runAnimationPostDigest","cancelFn","$$cancelFn","defer.promise.$$cancelFn","ngAnimatePostDigest","ngAnimateNotifyComplete","resolveElementClasses","hasClasses","cachedClassManipulation","op","asyncPromise","currentDefer","applyStyles","styles","from","to","animate","enter","leave","move","$$addClassImmediately","$$removeClassImmediately","add","createdCache","STORAGE_KEY","$$setClassImmediately","APPLICATION_JSON","PATH_MATCH","locationPrototype","paramValue","Location","Location.prototype.state","CALL","APPLY","BIND","CONSTANTS","null","true","false","constantGetter","OPERATORS","+","-","*","/","%","===","!==","==","!=","<",">","<=",">=","&&","||","!","ESCAPE","lex","tokens","readString","peek","readNumber","isIdent","readIdent","is","isWhitespace","ch2","ch3","op2","op3","op1","operator","throwError","chars","isExpOperator","start","end","colStr","peekCh","quote","rawString","hex","String","fromCharCode","rep","ZERO","statements","primary","expect","filterChain","consume","arrayDeclaration","functionCall","objectIndex","fieldAccess","peekToken","e1","e2","e3","e4","peekAhead","t","unaryFn","right","$parseUnaryFn","binaryFn","left","isBranching","$parseBinaryFn","$parseConstant","$parseStatements","inputFn","argsFn","$parseFilter","every","assignment","ternary","$parseAssignment","logicalOR","middle","$parseTernary","logicalAND","equality","relational","additive","multiplicative","unary","$parseFieldAccess","indexFn","$parseObjectIndex","fnGetter","contextGetter","expressionText","$parseFunctionCall","elementFns","$parseArrayLiteral","valueFns","$parseObjectLiteral","yy","y","MMMM","MMM","M","H","hh","EEEE","EEE","ampmGetter","Z","timeZoneGetter","zone","paddedZone","ww","w","G","GG","GGG","GGGG","longEraGetter","xlinkHref","propName","normalized","ngBooleanAttrWatchAction","htmlAttr","ngAttrAliasWatchAction","nullFormRenameControl","formDirectiveFactory","isNgForm","ngFormCompile","formElement","nameAttr","ngFormPreLink","handleFormSubmission","parentFormCtrl","URL_REGEXP","EMAIL_REGEXP","NUMBER_REGEXP","DATE_REGEXP","DATETIMELOCAL_REGEXP","WEEK_REGEXP","MONTH_REGEXP","TIME_REGEXP","inputType","textInputType","weekParser","isoWeek","existingDate","week","minutes","hours","seconds","milliseconds","addDays","numberInputType","urlInputType","ctrl.$validators.url","modelValue","viewValue","emailInputType","email","ctrl.$validators.email","radioInputType","checked","checkboxInputType","trueValue","ngTrueValue","falseValue","ngFalseValue","ctrl.$isEmpty","ctrls","CONSTANT_VALUE_REGEXP","tplAttr","ngValueConstantLink","ngValueLink","valueWatchAction","$compile","ngBindCompile","templateElement","ngBindLink","ngBindWatchAction","ngBindTemplateCompile","ngBindTemplateLink","ngBindHtmlCompile","tElement","ngBindHtmlGetter","ngBindHtmlWatch","getStringValue","ngBindHtmlLink","ngBindHtmlWatchAction","getTrustedHtml","$viewChangeListeners","forceAsyncEvents","ngEventHandler","previousElements","ngIfWatchAction","newScope","srcExp","onloadExp","autoScrollExp","autoscroll","changeCounter","previousElement","currentElement","cleanupLastIncludeContent","parseAsResourceUrl","ngIncludeWatchAction","afterAnimation","thisChangeId","namespaceAdaptedClone","trimValues","NgModelController","$modelValue","$$rawModelValue","$asyncValidators","$untouched","$touched","parsedNgModel","parsedNgModelAssign","ngModelGet","ngModelSet","pendingDebounce","parserValid","$$setOptions","this.$$setOptions","getterSetter","invokeModelGetter","invokeModelSetter","$$$p","this.$isEmpty","currentValidationRunId","this.$setPristine","this.$setDirty","this.$setUntouched","UNTOUCHED_CLASS","TOUCHED_CLASS","$setTouched","this.$setTouched","this.$rollbackViewValue","$$lastCommittedViewValue","this.$validate","prevValid","prevModelValue","allowInvalid","$$runValidators","allValid","$$writeModelToScope","this.$$runValidators","doneCallback","processSyncValidators","syncValidatorsValid","validator","processAsyncValidators","validatorPromises","validationDone","localValidationRunId","processParseErrors","errorKey","this.$commitViewValue","$$parseAndValidate","this.$$parseAndValidate","this.$$writeModelToScope","this.$setViewValue","updateOnDefault","$$debounceViewValueCommit","this.$$debounceViewValueCommit","debounceDelay","debounce","ngModelWatch","formatters","ngModelCompile","ngModelPreLink","modelCtrl","formCtrl","ngModelPostLink","updateOn","DEFAULT_REGEXP","that","BRACE","IS_WHEN","updateElementText","newText","numberExp","whenExp","whens","whensExpFns","braceReplacement","watchRemover","lastCount","attributeName","tmpMatch","whenKey","ngPluralizeWatchAction","countIsNaN","ngRepeatMinErr","updateScope","valueIdentifier","keyIdentifier","arrayLength","$first","$last","$middle","$odd","$even","ngRepeatCompile","ngRepeatEndComment","lhs","rhs","aliasAs","trackByExp","trackByExpGetter","trackByIdExpFn","trackByIdArrayFn","trackByIdObjFn","hashFnLocals","ngRepeatLink","lastBlockMap","ngRepeatAction","previousNode","nextNode","nextBlockMap","collectionLength","trackById","collectionKeys","nextBlockOrder","trackByIdFn","itemKey","blockKey","ngRepeatTransclude","ngShowWatchAction","NG_HIDE_CLASS","tempClasses","NG_HIDE_IN_PROGRESS_CLASS","ngHideWatchAction","ngStyleWatchAction","newStyles","oldStyles","ngSwitchController","cases","selectedTranscludes","selectedElements","previousLeaveAnimations","selectedScopes","spliceFactory","ngSwitchWatchAction","selectedTransclude","caseElement","selectedScope","anchor","ngOptionsMinErr","NG_OPTIONS_REGEXP","nullModelCtrl","optionsMap","ngModelCtrl","unknownOption","databound","init","self.init","ngModelCtrl_","nullOption_","unknownOption_","addOption","self.addOption","removeOption","self.removeOption","hasOption","renderUnknownOption","self.renderUnknownOption","unknownVal","self.hasOption","setupAsSingle","selectElement","selectCtrl","ngModelCtrl.$render","emptyOption","setupAsMultiple","lastView","selectMultipleWatch","setupAsOptions","callExpression","exprFn","valueName","keyName","createIsSelectedFn","selectedSet","trackFn","trackIndex","isSelected","compareValueFn","selectAsFn","scheduleRendering","renderScheduled","render","updateLabelMap","labelMap","label","added","optionGroups","optionGroupNames","optionGroupName","optionGroup","existingParent","existingOptions","existingOption","valuesFn","anySelected","optionId","trackKeysCache","groupByFn","displayFn","nullOption","groupIndex","groupLength","optionGroupsCache","optGroupTemplate","lastElement","optionTemplate","optionsExp","selectAs","track","selectionChanged","selectedKey","viewValueFn","getLabels","toDisplay","ngModelCtrl.$isEmpty","nullSelectCtrl","selectCtrlName","interpolateWatchAction","ctrl.$validators.required","patternExp","ctrl.$validators.pattern","intVal","ctrl.$validators.maxlength","ctrl.$validators.minlength","$$csp"] +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/bower.json new file mode 100644 index 000000000..2039c744a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/bower.json @@ -0,0 +1,8 @@ +{ + "name": "angular", + "version": "1.3.15", + "main": "./angular.js", + "ignore": [], + "dependencies": { + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/index.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/index.js new file mode 100644 index 000000000..5c1aafcca --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/index.js @@ -0,0 +1,2 @@ +require('./angular'); +module.exports = angular; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/package.json new file mode 100644 index 000000000..abfd51463 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/angular/package.json @@ -0,0 +1,25 @@ +{ + "name": "angular", + "version": "1.3.15", + "description": "HTML enhanced for web apps", + "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", + "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" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/.bower.json new file mode 100644 index 000000000..beb6806e3 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/.bower.json @@ -0,0 +1,47 @@ +{ + "name": "bootstrap", + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "version": "3.3.2", + "keywords": [ + "css", + "js", + "less", + "mobile-first", + "responsive", + "front-end", + "framework", + "web" + ], + "homepage": "http://getbootstrap.com", + "main": [ + "less/bootstrap.less", + "dist/css/bootstrap.css", + "dist/js/bootstrap.js", + "dist/fonts/glyphicons-halflings-regular.eot", + "dist/fonts/glyphicons-halflings-regular.svg", + "dist/fonts/glyphicons-halflings-regular.ttf", + "dist/fonts/glyphicons-halflings-regular.woff" + ], + "ignore": [ + "/.*", + "_config.yml", + "CNAME", + "composer.json", + "CONTRIBUTING.md", + "docs", + "js/tests", + "test-infra" + ], + "dependencies": { + "jquery": ">= 1.9.1" + }, + "_release": "3.3.2", + "_resolution": { + "type": "version", + "tag": "v3.3.2", + "commit": "bcf7dd38b5ab180256e2e4fb5da0369551b3f082" + }, + "_source": "https://github.com/twbs/bootstrap.git", + "_target": "3.3.2", + "_originalSource": "bootstrap" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/Gruntfile.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/Gruntfile.js new file mode 100644 index 000000000..de945ab3c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/Gruntfile.js @@ -0,0 +1,509 @@ +/*! + * Bootstrap's Gruntfile + * http://getbootstrap.com + * Copyright 2013-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +module.exports = function (grunt) { + 'use strict'; + + // Force use of Unix newlines + grunt.util.linefeed = '\n'; + + RegExp.quote = function (string) { + return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'); + }; + + var fs = require('fs'); + var path = require('path'); + var npmShrinkwrap = require('npm-shrinkwrap'); + var generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js'); + var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js'); + var getLessVarsData = function () { + var filePath = path.join(__dirname, 'less/variables.less'); + var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' }); + var parser = new BsLessdocParser(fileContent); + return { sections: parser.parseFile() }; + }; + var generateRawFiles = require('./grunt/bs-raw-files-generator.js'); + var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js'); + var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' }); + + Object.keys(configBridge.paths).forEach(function (key) { + configBridge.paths[key].forEach(function (val, i, arr) { + arr[i] = path.join('./docs/assets', val); + }); + }); + + // Project configuration. + grunt.initConfig({ + + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/*!\n' + + ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' + + ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' + + ' * Licensed under <%= pkg.license.type %> (<%= pkg.license.url %>)\n' + + ' */\n', + jqueryCheck: configBridge.config.jqueryCheck.join('\n'), + jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'), + + // Task configuration. + clean: { + dist: 'dist', + docs: 'docs/dist' + }, + + jshint: { + options: { + jshintrc: 'js/.jshintrc' + }, + grunt: { + options: { + jshintrc: 'grunt/.jshintrc' + }, + src: ['Gruntfile.js', 'grunt/*.js'] + }, + core: { + src: 'js/*.js' + }, + test: { + options: { + jshintrc: 'js/tests/unit/.jshintrc' + }, + src: 'js/tests/unit/*.js' + }, + assets: { + src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js'] + } + }, + + jscs: { + options: { + config: 'js/.jscsrc' + }, + grunt: { + src: '<%= jshint.grunt.src %>' + }, + core: { + src: '<%= jshint.core.src %>' + }, + test: { + src: '<%= jshint.test.src %>' + }, + assets: { + options: { + requireCamelCaseOrUpperCaseIdentifiers: null + }, + src: '<%= jshint.assets.src %>' + } + }, + + concat: { + options: { + banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>', + stripBanners: false + }, + bootstrap: { + src: [ + 'js/transition.js', + 'js/alert.js', + 'js/button.js', + 'js/carousel.js', + 'js/collapse.js', + 'js/dropdown.js', + 'js/modal.js', + 'js/tooltip.js', + 'js/popover.js', + 'js/scrollspy.js', + 'js/tab.js', + 'js/affix.js' + ], + dest: 'dist/js/<%= pkg.name %>.js' + } + }, + + uglify: { + options: { + preserveComments: 'some' + }, + core: { + src: '<%= concat.bootstrap.dest %>', + dest: 'dist/js/<%= pkg.name %>.min.js' + }, + customize: { + src: configBridge.paths.customizerJs, + dest: 'docs/assets/js/customize.min.js' + }, + docsJs: { + src: configBridge.paths.docsJs, + dest: 'docs/assets/js/docs.min.js' + } + }, + + qunit: { + options: { + inject: 'js/tests/unit/phantom.js' + }, + files: 'js/tests/index.html' + }, + + less: { + compileCore: { + options: { + strictMath: true, + sourceMap: true, + outputSourceFiles: true, + sourceMapURL: '<%= pkg.name %>.css.map', + sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map' + }, + src: 'less/bootstrap.less', + dest: 'dist/css/<%= pkg.name %>.css' + }, + compileTheme: { + options: { + strictMath: true, + sourceMap: true, + outputSourceFiles: true, + sourceMapURL: '<%= pkg.name %>-theme.css.map', + sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map' + }, + src: 'less/theme.less', + dest: 'dist/css/<%= pkg.name %>-theme.css' + } + }, + + autoprefixer: { + options: { + browsers: configBridge.config.autoprefixerBrowsers + }, + core: { + options: { + map: true + }, + src: 'dist/css/<%= pkg.name %>.css' + }, + theme: { + options: { + map: true + }, + src: 'dist/css/<%= pkg.name %>-theme.css' + }, + docs: { + src: 'docs/assets/css/src/docs.css' + }, + examples: { + expand: true, + cwd: 'docs/examples/', + src: ['**/*.css'], + dest: 'docs/examples/' + } + }, + + csslint: { + options: { + csslintrc: 'less/.csslintrc' + }, + dist: [ + 'dist/css/bootstrap.css', + 'dist/css/bootstrap-theme.css' + ], + examples: [ + 'docs/examples/**/*.css' + ], + docs: { + options: { + ids: false, + 'overqualified-elements': false + }, + src: 'docs/assets/css/src/docs.css' + } + }, + + cssmin: { + options: { + compatibility: 'ie8', + keepSpecialComments: '*', + advanced: false + }, + minifyCore: { + src: 'dist/css/<%= pkg.name %>.css', + dest: 'dist/css/<%= pkg.name %>.min.css' + }, + minifyTheme: { + src: 'dist/css/<%= pkg.name %>-theme.css', + dest: 'dist/css/<%= pkg.name %>-theme.min.css' + }, + docs: { + src: [ + 'docs/assets/css/src/docs.css', + 'docs/assets/css/src/pygments-manni.css' + ], + dest: 'docs/assets/css/docs.min.css' + } + }, + + usebanner: { + options: { + position: 'top', + banner: '<%= banner %>' + }, + files: { + src: 'dist/css/*.css' + } + }, + + csscomb: { + options: { + config: 'less/.csscomb.json' + }, + dist: { + expand: true, + cwd: 'dist/css/', + src: ['*.css', '!*.min.css'], + dest: 'dist/css/' + }, + examples: { + expand: true, + cwd: 'docs/examples/', + src: '**/*.css', + dest: 'docs/examples/' + }, + docs: { + src: 'docs/assets/css/src/docs.css', + dest: 'docs/assets/css/src/docs.css' + } + }, + + copy: { + fonts: { + src: 'fonts/*', + dest: 'dist/' + }, + docs: { + src: 'dist/*/*', + dest: 'docs/' + } + }, + + connect: { + server: { + options: { + port: 3000, + base: '.' + } + } + }, + + jekyll: { + options: { + config: '_config.yml' + }, + docs: {}, + github: { + options: { + raw: 'github: true' + } + } + }, + + jade: { + options: { + pretty: true, + data: getLessVarsData + }, + customizerVars: { + src: 'docs/_jade/customizer-variables.jade', + dest: 'docs/_includes/customizer-variables.html' + }, + customizerNav: { + src: 'docs/_jade/customizer-nav.jade', + dest: 'docs/_includes/nav/customize.html' + } + }, + + validation: { + options: { + charset: 'utf-8', + doctype: 'HTML5', + failHard: true, + reset: true, + relaxerror: [ + 'Element img is missing required attribute src.', + 'Attribute autocomplete not allowed on element input at this point.', + 'Attribute autocomplete not allowed on element button at this point.', + 'Bad value separator for attribute role on element li.' + ] + }, + files: { + src: '_gh_pages/**/*.html' + } + }, + + watch: { + src: { + files: '<%= jshint.core.src %>', + tasks: ['jshint:src', 'qunit', 'concat'] + }, + test: { + files: '<%= jshint.test.src %>', + tasks: ['jshint:test', 'qunit'] + }, + less: { + files: 'less/**/*.less', + tasks: 'less' + } + }, + + sed: { + versionNumber: { + pattern: (function () { + var old = grunt.option('oldver'); + return old ? RegExp.quote(old) : old; + })(), + replacement: grunt.option('newver'), + recursive: true + } + }, + + 'saucelabs-qunit': { + all: { + options: { + build: process.env.TRAVIS_JOB_ID, + throttled: 10, + maxRetries: 3, + maxPollRetries: 4, + urls: ['http://127.0.0.1:3000/js/tests/index.html'], + browsers: grunt.file.readYAML('grunt/sauce_browsers.yml') + } + } + }, + + exec: { + npmUpdate: { + command: 'npm update' + } + }, + + compress: { + main: { + options: { + archive: 'bootstrap-<%= pkg.version %>-dist.zip', + mode: 'zip', + level: 9, + pretty: true + }, + files: [ + { + expand: true, + cwd: 'dist/', + src: ['**'], + dest: 'bootstrap-<%= pkg.version %>-dist' + } + ] + } + } + + }); + + + // These plugins provide necessary tasks. + require('load-grunt-tasks')(grunt, { scope: 'devDependencies' }); + require('time-grunt')(grunt); + + // Docs HTML validation task + grunt.registerTask('validate-html', ['jekyll:docs', 'validation']); + + var runSubset = function (subset) { + return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset; + }; + var isUndefOrNonZero = function (val) { + return val === undefined || val !== '0'; + }; + + // Test task. + var testSubtasks = []; + // Skip core tests if running a different subset of the test suite + if (runSubset('core') && + // Skip core tests if this is a Savage build + process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') { + testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'csslint:dist', 'test-js', 'docs']); + } + // Skip HTML validation if running a different subset of the test suite + if (runSubset('validate-html') && + // Skip HTML5 validator on Travis when [skip validator] is in the commit message + isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) { + testSubtasks.push('validate-html'); + } + // Only run Sauce Labs tests if there's a Sauce access key + if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' && + // Skip Sauce if running a different subset of the test suite + runSubset('sauce-js-unit') && + // Skip Sauce on Travis when [skip sauce] is in the commit message + isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) { + testSubtasks.push('connect'); + testSubtasks.push('saucelabs-qunit'); + } + grunt.registerTask('test', testSubtasks); + grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']); + + // JS distribution task. + grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']); + + // CSS distribution task. + grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']); + grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'usebanner', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']); + + // Full distribution task. + grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']); + + // Default task. + grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']); + + // Version numbering task. + // grunt change-version-number --oldver=A.B.C --newver=X.Y.Z + // This can be overzealous, so its changes should always be manually reviewed! + grunt.registerTask('change-version-number', 'sed'); + + grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); }); + + // task for building customizer + grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']); + grunt.registerTask('build-customizer-html', 'jade'); + grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () { + var banner = grunt.template.process('<%= banner %>'); + generateRawFiles(grunt, banner); + }); + + grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () { + var srcFiles = grunt.config.get('concat.bootstrap.src'); + var destFilepath = 'dist/js/npm.js'; + generateCommonJSModule(grunt, srcFiles, destFilepath); + }); + + // Docs task. + grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']); + grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']); + grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']); + grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']); + grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']); + + grunt.registerTask('prep-release', ['jekyll:github', 'compress']); + + // Task for updating the cached npm packages used by the Travis build (which are controlled by test-infra/npm-shrinkwrap.json). + // This task should be run and the updated file should be committed whenever Bootstrap's dependencies change. + grunt.registerTask('update-shrinkwrap', ['exec:npmUpdate', '_update-shrinkwrap']); + grunt.registerTask('_update-shrinkwrap', function () { + var done = this.async(); + npmShrinkwrap({ dev: true, dirname: __dirname }, function (err) { + if (err) { + grunt.fail.warn(err); + } + var dest = 'test-infra/npm-shrinkwrap.json'; + fs.renameSync('npm-shrinkwrap.json', dest); + grunt.log.writeln('File ' + dest.cyan + ' updated.'); + done(); + }); + }); +}; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/LICENSE b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/LICENSE new file mode 100644 index 000000000..f4c52d6a4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-2015 Twitter, Inc + +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. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/README.md new file mode 100644 index 000000000..9b0729f74 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/README.md @@ -0,0 +1,132 @@ +# [Bootstrap](http://getbootstrap.com) +![Bower version](https://img.shields.io/bower/v/bootstrap.svg?style=flat) +[![npm version](https://img.shields.io/npm/v/bootstrap.svg?style=flat)](https://www.npmjs.com/package/bootstrap) +[![Build Status](https://img.shields.io/travis/twbs/bootstrap/master.svg?style=flat)](https://travis-ci.org/twbs/bootstrap) +[![devDependency Status](https://img.shields.io/david/dev/twbs/bootstrap.svg?style=flat)](https://david-dm.org/twbs/bootstrap#info=devDependencies) +[![Selenium Test Status](https://saucelabs.com/browser-matrix/bootstrap.svg)](https://saucelabs.com/u/bootstrap) + +Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thornton](https://twitter.com/fat), and maintained by the [core team](https://github.com/orgs/twbs/people) with the massive support and involvement of the community. + +To get started, check out <http://getbootstrap.com>! + +## Table of contents + +- [Quick start](#quick-start) +- [Bugs and feature requests](#bugs-and-feature-requests) +- [Documentation](#documentation) +- [Contributing](#contributing) +- [Community](#community) +- [Versioning](#versioning) +- [Creators](#creators) +- [Copyright and license](#copyright-and-license) + +## Quick start + +Four quick start options are available: + +- [Download the latest release](https://github.com/twbs/bootstrap/archive/v3.3.2.zip). +- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`. +- Install with [Bower](http://bower.io): `bower install bootstrap`. +- Install with [npm](https://www.npmjs.org): `npm install bootstrap`. + +Read the [Getting started page](http://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more. + +### What's included + +Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: + +``` +bootstrap/ +├── css/ +│ ├── bootstrap.css +│ ├── bootstrap.css.map +│ ├── bootstrap.min.css +│ ├── bootstrap-theme.css +│ ├── bootstrap-theme.css.map +│ └── bootstrap-theme.min.css +├── js/ +│ ├── bootstrap.js +│ └── bootstrap.min.js +└── fonts/ + ├── glyphicons-halflings-regular.eot + ├── glyphicons-halflings-regular.svg + ├── glyphicons-halflings-regular.ttf + ├── glyphicons-halflings-regular.woff + └── glyphicons-halflings-regular.woff2 +``` + +We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). CSS [source maps](https://developers.google.com/chrome-developer-tools/docs/css-preprocessors) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Fonts from Glyphicons are included, as is the optional Bootstrap theme. + + + +## Bugs and feature requests + +Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new). + + +## Documentation + +Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at <http://getbootstrap.com>. The docs may also be run locally. + +### Running documentation locally + +1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v2.5.x). + - **Windows users:** Read [this unofficial guide](http://jekyll-windows.juthilo.com/) to get Jekyll up and running without problems. +2. Install the Ruby-based syntax highlighter, [Rouge](https://github.com/jneen/rouge), with `gem install rouge`. +3. From the root `/bootstrap` directory, run `jekyll serve` in the command line. +4. Open <http://localhost:9001> in your browser, and voilà. + +Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/). + +### Documentation for previous releases + +Documentation for v2.3.2 has been made available for the time being at <http://getbootstrap.com/2.3.2/> while folks transition to Bootstrap 3. + +[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. + + + +## Contributing + +Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. + +Moreover, if your pull request contains JavaScript patches or features, you must include relevant unit tests. All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo). + +Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at <http://editorconfig.org>. + + + +## Community + +Keep track of development and community news. + +- Follow [@twbootstrap on Twitter](https://twitter.com/twbootstrap). +- Read and subscribe to [The Official Bootstrap Blog](http://blog.getbootstrap.com). +- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel. +- Implementation help may be found at Stack Overflow (tagged [`twitter-bootstrap-3`](http://stackoverflow.com/questions/tagged/twitter-bootstrap-3)). + + + +## Versioning + +For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](http://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible. + + + +## Creators + +**Mark Otto** + +- <https://twitter.com/mdo> +- <https://github.com/mdo> + +**Jacob Thornton** + +- <https://twitter.com/fat> +- <https://github.com/fat> + + + +## Copyright and license + +Code and documentation copyright 2011-2015 Twitter, Inc. Code released under [the MIT license](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE). diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/bower.json new file mode 100644 index 000000000..1e8851412 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/bower.json @@ -0,0 +1,38 @@ +{ + "name": "bootstrap", + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "version": "3.3.2", + "keywords": [ + "css", + "js", + "less", + "mobile-first", + "responsive", + "front-end", + "framework", + "web" + ], + "homepage": "http://getbootstrap.com", + "main": [ + "less/bootstrap.less", + "dist/css/bootstrap.css", + "dist/js/bootstrap.js", + "dist/fonts/glyphicons-halflings-regular.eot", + "dist/fonts/glyphicons-halflings-regular.svg", + "dist/fonts/glyphicons-halflings-regular.ttf", + "dist/fonts/glyphicons-halflings-regular.woff" + ], + "ignore": [ + "/.*", + "_config.yml", + "CNAME", + "composer.json", + "CONTRIBUTING.md", + "docs", + "js/tests", + "test-infra" + ], + "dependencies": { + "jquery": ">= 1.9.1" + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.eot b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.eot Binary files differnew file mode 100644 index 000000000..b93a4953f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.eot diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.svg b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata></metadata> +<defs> +<font id="glyphicons_halflingsregular" horiz-adv-x="1200" > +<font-face units-per-em="1200" ascent="960" descent="-240" /> +<missing-glyph horiz-adv-x="500" /> +<glyph horiz-adv-x="0" /> +<glyph horiz-adv-x="400" /> +<glyph unicode=" " /> +<glyph unicode="*" d="M600 1100q15 0 34 -1.5t30 -3.5l11 -1q10 -2 17.5 -10.5t7.5 -18.5v-224l158 158q7 7 18 8t19 -6l106 -106q7 -8 6 -19t-8 -18l-158 -158h224q10 0 18.5 -7.5t10.5 -17.5q6 -41 6 -75q0 -15 -1.5 -34t-3.5 -30l-1 -11q-2 -10 -10.5 -17.5t-18.5 -7.5h-224l158 -158 q7 -7 8 -18t-6 -19l-106 -106q-8 -7 -19 -6t-18 8l-158 158v-224q0 -10 -7.5 -18.5t-17.5 -10.5q-41 -6 -75 -6q-15 0 -34 1.5t-30 3.5l-11 1q-10 2 -17.5 10.5t-7.5 18.5v224l-158 -158q-7 -7 -18 -8t-19 6l-106 106q-7 8 -6 19t8 18l158 158h-224q-10 0 -18.5 7.5 t-10.5 17.5q-6 41 -6 75q0 15 1.5 34t3.5 30l1 11q2 10 10.5 17.5t18.5 7.5h224l-158 158q-7 7 -8 18t6 19l106 106q8 7 19 6t18 -8l158 -158v224q0 10 7.5 18.5t17.5 10.5q41 6 75 6z" /> +<glyph unicode="+" d="M450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-350h350q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-350v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v350h-350q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5 h350v350q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode=" " /> +<glyph unicode="¥" d="M825 1100h250q10 0 12.5 -5t-5.5 -13l-364 -364q-6 -6 -11 -18h268q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-100h275q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-125v-174q0 -11 -7.5 -18.5t-18.5 -7.5h-148q-11 0 -18.5 7.5t-7.5 18.5v174 h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h125v100h-275q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h118q-5 12 -11 18l-364 364q-8 8 -5.5 13t12.5 5h250q25 0 43 -18l164 -164q8 -8 18 -8t18 8l164 164q18 18 43 18z" /> +<glyph unicode=" " horiz-adv-x="650" /> +<glyph unicode=" " horiz-adv-x="1300" /> +<glyph unicode=" " horiz-adv-x="650" /> +<glyph unicode=" " horiz-adv-x="1300" /> +<glyph unicode=" " horiz-adv-x="433" /> +<glyph unicode=" " horiz-adv-x="325" /> +<glyph unicode=" " horiz-adv-x="216" /> +<glyph unicode=" " horiz-adv-x="216" /> +<glyph unicode=" " horiz-adv-x="162" /> +<glyph unicode=" " horiz-adv-x="260" /> +<glyph unicode=" " horiz-adv-x="72" /> +<glyph unicode=" " horiz-adv-x="260" /> +<glyph unicode=" " horiz-adv-x="325" /> +<glyph unicode="€" d="M744 1198q242 0 354 -189q60 -104 66 -209h-181q0 45 -17.5 82.5t-43.5 61.5t-58 40.5t-60.5 24t-51.5 7.5q-19 0 -40.5 -5.5t-49.5 -20.5t-53 -38t-49 -62.5t-39 -89.5h379l-100 -100h-300q-6 -50 -6 -100h406l-100 -100h-300q9 -74 33 -132t52.5 -91t61.5 -54.5t59 -29 t47 -7.5q22 0 50.5 7.5t60.5 24.5t58 41t43.5 61t17.5 80h174q-30 -171 -128 -278q-107 -117 -274 -117q-206 0 -324 158q-36 48 -69 133t-45 204h-217l100 100h112q1 47 6 100h-218l100 100h134q20 87 51 153.5t62 103.5q117 141 297 141z" /> +<glyph unicode="₽" d="M428 1200h350q67 0 120 -13t86 -31t57 -49.5t35 -56.5t17 -64.5t6.5 -60.5t0.5 -57v-16.5v-16.5q0 -36 -0.5 -57t-6.5 -61t-17 -65t-35 -57t-57 -50.5t-86 -31.5t-120 -13h-178l-2 -100h288q10 0 13 -6t-3 -14l-120 -160q-6 -8 -18 -14t-22 -6h-138v-175q0 -11 -5.5 -18 t-15.5 -7h-149q-10 0 -17.5 7.5t-7.5 17.5v175h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v100h-267q-10 0 -13 6t3 14l120 160q6 8 18 14t22 6h117v475q0 10 7.5 17.5t17.5 7.5zM600 1000v-300h203q64 0 86.5 33t22.5 119q0 84 -22.5 116t-86.5 32h-203z" /> +<glyph unicode="−" d="M250 700h800q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="⌛" d="M1000 1200v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-50v-100q0 -91 -49.5 -165.5t-130.5 -109.5q81 -35 130.5 -109.5t49.5 -165.5v-150h50q21 0 35.5 -14.5t14.5 -35.5v-150h-800v150q0 21 14.5 35.5t35.5 14.5h50v150q0 91 49.5 165.5t130.5 109.5q-81 35 -130.5 109.5 t-49.5 165.5v100h-50q-21 0 -35.5 14.5t-14.5 35.5v150h800zM400 1000v-100q0 -60 32.5 -109.5t87.5 -73.5q28 -12 44 -37t16 -55t-16 -55t-44 -37q-55 -24 -87.5 -73.5t-32.5 -109.5v-150h400v150q0 60 -32.5 109.5t-87.5 73.5q-28 12 -44 37t-16 55t16 55t44 37 q55 24 87.5 73.5t32.5 109.5v100h-400z" /> +<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" /> +<glyph unicode="☁" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -206.5q0 -121 -85 -207.5t-205 -86.5h-750q-79 0 -135.5 57t-56.5 137q0 69 42.5 122.5t108.5 67.5q-2 12 -2 37q0 153 108 260.5t260 107.5z" /> +<glyph unicode="⛺" d="M774 1193.5q16 -9.5 20.5 -27t-5.5 -33.5l-136 -187l467 -746h30q20 0 35 -18.5t15 -39.5v-42h-1200v42q0 21 15 39.5t35 18.5h30l468 746l-135 183q-10 16 -5.5 34t20.5 28t34 5.5t28 -20.5l111 -148l112 150q9 16 27 20.5t34 -5zM600 200h377l-182 112l-195 534v-646z " /> +<glyph unicode="✉" d="M25 1100h1150q10 0 12.5 -5t-5.5 -13l-564 -567q-8 -8 -18 -8t-18 8l-564 567q-8 8 -5.5 13t12.5 5zM18 882l264 -264q8 -8 8 -18t-8 -18l-264 -264q-8 -8 -13 -5.5t-5 12.5v550q0 10 5 12.5t13 -5.5zM918 618l264 264q8 8 13 5.5t5 -12.5v-550q0 -10 -5 -12.5t-13 5.5 l-264 264q-8 8 -8 18t8 18zM818 482l364 -364q8 -8 5.5 -13t-12.5 -5h-1150q-10 0 -12.5 5t5.5 13l364 364q8 8 18 8t18 -8l164 -164q8 -8 18 -8t18 8l164 164q8 8 18 8t18 -8z" /> +<glyph unicode="✏" d="M1011 1210q19 0 33 -13l153 -153q13 -14 13 -33t-13 -33l-99 -92l-214 214l95 96q13 14 32 14zM1013 800l-615 -614l-214 214l614 614zM317 96l-333 -112l110 335z" /> +<glyph unicode="" d="M700 650v-550h250q21 0 35.5 -14.5t14.5 -35.5v-50h-800v50q0 21 14.5 35.5t35.5 14.5h250v550l-500 550h1200z" /> +<glyph unicode="" d="M368 1017l645 163q39 15 63 0t24 -49v-831q0 -55 -41.5 -95.5t-111.5 -63.5q-79 -25 -147 -4.5t-86 75t25.5 111.5t122.5 82q72 24 138 8v521l-600 -155v-606q0 -42 -44 -90t-109 -69q-79 -26 -147 -5.5t-86 75.5t25.5 111.5t122.5 82.5q72 24 138 7v639q0 38 14.5 59 t53.5 34z" /> +<glyph unicode="" d="M500 1191q100 0 191 -39t156.5 -104.5t104.5 -156.5t39 -191l-1 -2l1 -5q0 -141 -78 -262l275 -274q23 -26 22.5 -44.5t-22.5 -42.5l-59 -58q-26 -20 -46.5 -20t-39.5 20l-275 274q-119 -77 -261 -77l-5 1l-2 -1q-100 0 -191 39t-156.5 104.5t-104.5 156.5t-39 191 t39 191t104.5 156.5t156.5 104.5t191 39zM500 1022q-88 0 -162 -43t-117 -117t-43 -162t43 -162t117 -117t162 -43t162 43t117 117t43 162t-43 162t-117 117t-162 43z" /> +<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104z" /> +<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429z" /> +<glyph unicode="" d="M407 800l131 353q7 19 17.5 19t17.5 -19l129 -353h421q21 0 24 -8.5t-14 -20.5l-342 -249l130 -401q7 -20 -0.5 -25.5t-24.5 6.5l-343 246l-342 -247q-17 -12 -24.5 -6.5t-0.5 25.5l130 400l-347 251q-17 12 -14 20.5t23 8.5h429zM477 700h-240l197 -142l-74 -226 l193 139l195 -140l-74 229l192 140h-234l-78 211z" /> +<glyph unicode="" d="M600 1200q124 0 212 -88t88 -212v-250q0 -46 -31 -98t-69 -52v-75q0 -10 6 -21.5t15 -17.5l358 -230q9 -5 15 -16.5t6 -21.5v-93q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v93q0 10 6 21.5t15 16.5l358 230q9 6 15 17.5t6 21.5v75q-38 0 -69 52 t-31 98v250q0 124 88 212t212 88z" /> +<glyph unicode="" d="M25 1100h1150q10 0 17.5 -7.5t7.5 -17.5v-1050q0 -10 -7.5 -17.5t-17.5 -7.5h-1150q-10 0 -17.5 7.5t-7.5 17.5v1050q0 10 7.5 17.5t17.5 7.5zM100 1000v-100h100v100h-100zM875 1000h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5t17.5 -7.5h550 q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM1000 1000v-100h100v100h-100zM100 800v-100h100v100h-100zM1000 800v-100h100v100h-100zM100 600v-100h100v100h-100zM1000 600v-100h100v100h-100zM875 500h-550q-10 0 -17.5 -7.5t-7.5 -17.5v-350q0 -10 7.5 -17.5 t17.5 -7.5h550q10 0 17.5 7.5t7.5 17.5v350q0 10 -7.5 17.5t-17.5 7.5zM100 400v-100h100v100h-100zM1000 400v-100h100v100h-100zM100 200v-100h100v100h-100zM1000 200v-100h100v100h-100z" /> +<glyph unicode="" d="M50 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM50 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM650 500h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM850 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 700h200q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM850 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5 t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 1100h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200 q0 21 14.5 35.5t35.5 14.5zM50 700h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 700h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700 q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM50 300h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5zM450 300h700q21 0 35.5 -14.5t14.5 -35.5v-200 q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M465 477l571 571q8 8 18 8t17 -8l177 -177q8 -7 8 -17t-8 -18l-783 -784q-7 -8 -17.5 -8t-17.5 8l-384 384q-8 8 -8 18t8 17l177 177q7 8 17 8t18 -8l171 -171q7 -7 18 -7t18 7z" /> +<glyph unicode="" d="M904 1083l178 -179q8 -8 8 -18.5t-8 -17.5l-267 -268l267 -268q8 -7 8 -17.5t-8 -18.5l-178 -178q-8 -8 -18.5 -8t-17.5 8l-268 267l-268 -267q-7 -8 -17.5 -8t-18.5 8l-178 178q-8 8 -8 18.5t8 17.5l267 268l-267 268q-8 7 -8 17.5t8 18.5l178 178q8 8 18.5 8t17.5 -8 l268 -267l268 268q7 7 17.5 7t18.5 -7z" /> +<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM425 900h150q10 0 17.5 -7.5t7.5 -17.5v-75h75q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5 t-17.5 -7.5h-75v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-75q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v75q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M507 1177q98 0 187.5 -38.5t154.5 -103.5t103.5 -154.5t38.5 -187.5q0 -141 -78 -262l300 -299q8 -8 8 -18.5t-8 -18.5l-109 -108q-7 -8 -17.5 -8t-18.5 8l-300 299q-119 -77 -261 -77q-98 0 -188 38.5t-154.5 103t-103 154.5t-38.5 188t38.5 187.5t103 154.5 t154.5 103.5t188 38.5zM506.5 1023q-89.5 0 -165.5 -44t-120 -120.5t-44 -166t44 -165.5t120 -120t165.5 -44t166 44t120.5 120t44 165.5t-44 166t-120.5 120.5t-166 44zM325 800h350q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-350q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M550 1200h100q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM800 975v166q167 -62 272 -209.5t105 -331.5q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5 t-184.5 123t-123 184.5t-45.5 224q0 184 105 331.5t272 209.5v-166q-103 -55 -165 -155t-62 -220q0 -116 57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5q0 120 -62 220t-165 155z" /> +<glyph unicode="" d="M1025 1200h150q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM725 800h150q10 0 17.5 -7.5t7.5 -17.5v-750q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v750 q0 10 7.5 17.5t17.5 7.5zM425 500h150q10 0 17.5 -7.5t7.5 -17.5v-450q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v450q0 10 7.5 17.5t17.5 7.5zM125 300h150q10 0 17.5 -7.5t7.5 -17.5v-250q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5 v250q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M600 1174q33 0 74 -5l38 -152l5 -1q49 -14 94 -39l5 -2l134 80q61 -48 104 -105l-80 -134l3 -5q25 -44 39 -93l1 -6l152 -38q5 -43 5 -73q0 -34 -5 -74l-152 -38l-1 -6q-15 -49 -39 -93l-3 -5l80 -134q-48 -61 -104 -105l-134 81l-5 -3q-44 -25 -94 -39l-5 -2l-38 -151 q-43 -5 -74 -5q-33 0 -74 5l-38 151l-5 2q-49 14 -94 39l-5 3l-134 -81q-60 48 -104 105l80 134l-3 5q-25 45 -38 93l-2 6l-151 38q-6 42 -6 74q0 33 6 73l151 38l2 6q13 48 38 93l3 5l-80 134q47 61 105 105l133 -80l5 2q45 25 94 39l5 1l38 152q43 5 74 5zM600 815 q-89 0 -152 -63t-63 -151.5t63 -151.5t152 -63t152 63t63 151.5t-63 151.5t-152 63z" /> +<glyph unicode="" d="M500 1300h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-75h-1100v75q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5zM500 1200v-100h300v100h-300zM1100 900v-800q0 -41 -29.5 -70.5t-70.5 -29.5h-700q-41 0 -70.5 29.5t-29.5 70.5 v800h900zM300 800v-700h100v700h-100zM500 800v-700h100v700h-100zM700 800v-700h100v700h-100zM900 800v-700h100v700h-100z" /> +<glyph unicode="" d="M18 618l620 608q8 7 18.5 7t17.5 -7l608 -608q8 -8 5.5 -13t-12.5 -5h-175v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v375h-300v-375q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v575h-175q-10 0 -12.5 5t5.5 13z" /> +<glyph unicode="" d="M600 1200v-400q0 -41 29.5 -70.5t70.5 -29.5h300v-650q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5h450zM1000 800h-250q-21 0 -35.5 14.5t-14.5 35.5v250z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h50q10 0 17.5 -7.5t7.5 -17.5v-275h175q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M1300 0h-538l-41 400h-242l-41 -400h-538l431 1200h209l-21 -300h162l-20 300h208zM515 800l-27 -300h224l-27 300h-170z" /> +<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-450h191q20 0 25.5 -11.5t-7.5 -27.5l-327 -400q-13 -16 -32 -16t-32 16l-327 400q-13 16 -7.5 27.5t25.5 11.5h191v450q0 21 14.5 35.5t35.5 14.5zM1125 400h50q10 0 17.5 -7.5t7.5 -17.5v-350q0 -10 -7.5 -17.5t-17.5 -7.5 h-1050q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h50q10 0 17.5 -7.5t7.5 -17.5v-175h900v175q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM525 900h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -275q-13 -16 -32 -16t-32 16l-223 275q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z " /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM632 914l223 -275q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5l223 275q13 16 32 16 t32 -16z" /> +<glyph unicode="" d="M225 1200h750q10 0 19.5 -7t12.5 -17l186 -652q7 -24 7 -49v-425q0 -12 -4 -27t-9 -17q-12 -6 -37 -6h-1100q-12 0 -27 4t-17 8q-6 13 -6 38l1 425q0 25 7 49l185 652q3 10 12.5 17t19.5 7zM878 1000h-556q-10 0 -19 -7t-11 -18l-87 -450q-2 -11 4 -18t16 -7h150 q10 0 19.5 -7t11.5 -17l38 -152q2 -10 11.5 -17t19.5 -7h250q10 0 19.5 7t11.5 17l38 152q2 10 11.5 17t19.5 7h150q10 0 16 7t4 18l-87 450q-2 11 -11 18t-19 7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM540 820l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" /> +<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-362q0 -10 -7.5 -17.5t-17.5 -7.5h-362q-11 0 -13 5.5t5 12.5l133 133q-109 76 -238 76q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5h150q0 -117 -45.5 -224 t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117z" /> +<glyph unicode="" d="M947 1060l135 135q7 7 12.5 5t5.5 -13v-361q0 -11 -7.5 -18.5t-18.5 -7.5h-361q-11 0 -13 5.5t5 12.5l134 134q-110 75 -239 75q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5h-150q0 117 45.5 224t123 184.5t184.5 123t224 45.5q192 0 347 -117zM1027 600h150 q0 -117 -45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5q-192 0 -348 118l-134 -134q-7 -8 -12.5 -5.5t-5.5 12.5v360q0 11 7.5 18.5t18.5 7.5h360q10 0 12.5 -5.5t-5.5 -12.5l-133 -133q110 -76 240 -76q116 0 214.5 57t155.5 155.5t57 214.5z" /> +<glyph unicode="" d="M125 1200h1050q10 0 17.5 -7.5t7.5 -17.5v-1150q0 -10 -7.5 -17.5t-17.5 -7.5h-1050q-10 0 -17.5 7.5t-7.5 17.5v1150q0 10 7.5 17.5t17.5 7.5zM1075 1000h-850q-10 0 -17.5 -7.5t-7.5 -17.5v-850q0 -10 7.5 -17.5t17.5 -7.5h850q10 0 17.5 7.5t7.5 17.5v850 q0 10 -7.5 17.5t-17.5 7.5zM325 900h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 900h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 700h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 700h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 500h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 500h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5zM325 300h50q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM525 300h450q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-450q-10 0 -17.5 7.5t-7.5 17.5v50 q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M900 800v200q0 83 -58.5 141.5t-141.5 58.5h-300q-82 0 -141 -59t-59 -141v-200h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h900q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-100zM400 800v150q0 21 15 35.5t35 14.5h200 q20 0 35 -14.5t15 -35.5v-150h-300z" /> +<glyph unicode="" d="M125 1100h50q10 0 17.5 -7.5t7.5 -17.5v-1075h-100v1075q0 10 7.5 17.5t17.5 7.5zM1075 1052q4 0 9 -2q16 -6 16 -23v-421q0 -6 -3 -12q-33 -59 -66.5 -99t-65.5 -58t-56.5 -24.5t-52.5 -6.5q-26 0 -57.5 6.5t-52.5 13.5t-60 21q-41 15 -63 22.5t-57.5 15t-65.5 7.5 q-85 0 -160 -57q-7 -5 -15 -5q-6 0 -11 3q-14 7 -14 22v438q22 55 82 98.5t119 46.5q23 2 43 0.5t43 -7t32.5 -8.5t38 -13t32.5 -11q41 -14 63.5 -21t57 -14t63.5 -7q103 0 183 87q7 8 18 8z" /> +<glyph unicode="" d="M600 1175q116 0 227 -49.5t192.5 -131t131 -192.5t49.5 -227v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v300q0 127 -70.5 231.5t-184.5 161.5t-245 57t-245 -57t-184.5 -161.5t-70.5 -231.5v-300q0 -10 -7.5 -17.5t-17.5 -7.5h-50 q-10 0 -17.5 7.5t-7.5 17.5v300q0 116 49.5 227t131 192.5t192.5 131t227 49.5zM220 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460q0 8 6 14t14 6zM820 500h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14v460 q0 8 6 14t14 6z" /> +<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM900 668l120 120q7 7 17 7t17 -7l34 -34q7 -7 7 -17t-7 -17l-120 -120l120 -120q7 -7 7 -17 t-7 -17l-34 -34q-7 -7 -17 -7t-17 7l-120 119l-120 -119q-7 -7 -17 -7t-17 7l-34 34q-7 7 -7 17t7 17l119 120l-119 120q-7 7 -7 17t7 17l34 34q7 8 17 8t17 -8z" /> +<glyph unicode="" d="M321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6 l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238q-6 8 -4.5 18t9.5 17l29 22q7 5 15 5z" /> +<glyph unicode="" d="M967 1004h3q11 -1 17 -10q135 -179 135 -396q0 -105 -34 -206.5t-98 -185.5q-7 -9 -17 -10h-3q-9 0 -16 6l-42 34q-8 6 -9 16t5 18q111 150 111 328q0 90 -29.5 176t-84.5 157q-6 9 -5 19t10 16l42 33q7 5 15 5zM321 814l258 172q9 6 15 2.5t6 -13.5v-750q0 -10 -6 -13.5 t-15 2.5l-258 172q-21 14 -46 14h-250q-10 0 -17.5 7.5t-7.5 17.5v350q0 10 7.5 17.5t17.5 7.5h250q25 0 46 14zM766 900h4q10 -1 16 -10q96 -129 96 -290q0 -154 -90 -281q-6 -9 -17 -10l-3 -1q-9 0 -16 6l-29 23q-7 7 -8.5 16.5t4.5 17.5q72 103 72 229q0 132 -78 238 q-6 8 -4.5 18.5t9.5 16.5l29 22q7 5 15 5z" /> +<glyph unicode="" d="M500 900h100v-100h-100v-100h-400v-100h-100v600h500v-300zM1200 700h-200v-100h200v-200h-300v300h-200v300h-100v200h600v-500zM100 1100v-300h300v300h-300zM800 1100v-300h300v300h-300zM300 900h-100v100h100v-100zM1000 900h-100v100h100v-100zM300 500h200v-500 h-500v500h200v100h100v-100zM800 300h200v-100h-100v-100h-200v100h-100v100h100v200h-200v100h300v-300zM100 400v-300h300v300h-300zM300 200h-100v100h100v-100zM1200 200h-100v100h100v-100zM700 0h-100v100h100v-100zM1200 0h-300v100h300v-100z" /> +<glyph unicode="" d="M100 200h-100v1000h100v-1000zM300 200h-100v1000h100v-1000zM700 200h-200v1000h200v-1000zM900 200h-100v1000h100v-1000zM1200 200h-200v1000h200v-1000zM400 0h-300v100h300v-100zM600 0h-100v91h100v-91zM800 0h-100v91h100v-91zM1100 0h-200v91h200v-91z" /> +<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" /> +<glyph unicode="" d="M500 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-682 682l1 475q0 10 7.5 17.5t17.5 7.5h474zM800 1200l682 -682q8 -8 8 -18t-8 -18l-464 -464q-8 -8 -18 -8t-18 8l-56 56l424 426l-700 700h150zM319.5 1024.5q-29.5 29.5 -71 29.5t-71 -29.5 t-29.5 -71.5t29.5 -71.5t71 -29.5t71 29.5t29.5 71.5t-29.5 71.5z" /> +<glyph unicode="" d="M300 1200h825q75 0 75 -75v-900q0 -25 -18 -43l-64 -64q-8 -8 -13 -5.5t-5 12.5v950q0 10 -7.5 17.5t-17.5 7.5h-700q-25 0 -43 -18l-64 -64q-8 -8 -5.5 -13t12.5 -5h700q10 0 17.5 -7.5t7.5 -17.5v-950q0 -10 -7.5 -17.5t-17.5 -7.5h-850q-10 0 -17.5 7.5t-7.5 17.5v975 q0 25 18 43l139 139q18 18 43 18z" /> +<glyph unicode="" d="M250 1200h800q21 0 35.5 -14.5t14.5 -35.5v-1150l-450 444l-450 -445v1151q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M822 1200h-444q-11 0 -19 -7.5t-9 -17.5l-78 -301q-7 -24 7 -45l57 -108q6 -9 17.5 -15t21.5 -6h450q10 0 21.5 6t17.5 15l62 108q14 21 7 45l-83 301q-1 10 -9 17.5t-19 7.5zM1175 800h-150q-10 0 -21 -6.5t-15 -15.5l-78 -156q-4 -9 -15 -15.5t-21 -6.5h-550 q-10 0 -21 6.5t-15 15.5l-78 156q-4 9 -15 15.5t-21 6.5h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-650q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h750q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5 t7.5 17.5v650q0 10 -7.5 17.5t-17.5 7.5zM850 200h-500q-10 0 -19.5 -7t-11.5 -17l-38 -152q-2 -10 3.5 -17t15.5 -7h600q10 0 15.5 7t3.5 17l-38 152q-2 10 -11.5 17t-19.5 7z" /> +<glyph unicode="" d="M500 1100h200q56 0 102.5 -20.5t72.5 -50t44 -59t25 -50.5l6 -20h150q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h150q2 8 6.5 21.5t24 48t45 61t72 48t102.5 21.5zM900 800v-100 h100v100h-100zM600 730q-95 0 -162.5 -67.5t-67.5 -162.5t67.5 -162.5t162.5 -67.5t162.5 67.5t67.5 162.5t-67.5 162.5t-162.5 67.5zM600 603q43 0 73 -30t30 -73t-30 -73t-73 -30t-73 30t-30 73t30 73t73 30z" /> +<glyph unicode="" d="M681 1199l385 -998q20 -50 60 -92q18 -19 36.5 -29.5t27.5 -11.5l10 -2v-66h-417v66q53 0 75 43.5t5 88.5l-82 222h-391q-58 -145 -92 -234q-11 -34 -6.5 -57t25.5 -37t46 -20t55 -6v-66h-365v66q56 24 84 52q12 12 25 30.5t20 31.5l7 13l399 1006h93zM416 521h340 l-162 457z" /> +<glyph unicode="" d="M753 641q5 -1 14.5 -4.5t36 -15.5t50.5 -26.5t53.5 -40t50.5 -54.5t35.5 -70t14.5 -87q0 -67 -27.5 -125.5t-71.5 -97.5t-98.5 -66.5t-108.5 -40.5t-102 -13h-500v89q41 7 70.5 32.5t29.5 65.5v827q0 24 -0.5 34t-3.5 24t-8.5 19.5t-17 13.5t-28 12.5t-42.5 11.5v71 l471 -1q57 0 115.5 -20.5t108 -57t80.5 -94t31 -124.5q0 -51 -15.5 -96.5t-38 -74.5t-45 -50.5t-38.5 -30.5zM400 700h139q78 0 130.5 48.5t52.5 122.5q0 41 -8.5 70.5t-29.5 55.5t-62.5 39.5t-103.5 13.5h-118v-350zM400 200h216q80 0 121 50.5t41 130.5q0 90 -62.5 154.5 t-156.5 64.5h-159v-400z" /> +<glyph unicode="" d="M877 1200l2 -57q-83 -19 -116 -45.5t-40 -66.5l-132 -839q-9 -49 13 -69t96 -26v-97h-500v97q186 16 200 98l173 832q3 17 3 30t-1.5 22.5t-9 17.5t-13.5 12.5t-21.5 10t-26 8.5t-33.5 10q-13 3 -19 5v57h425z" /> +<glyph unicode="" d="M1300 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM175 1000h-75v-800h75l-125 -167l-125 167h75v800h-75l125 167z" /> +<glyph unicode="" d="M1100 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-650q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v650h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM1167 50l-167 -125v75h-800v-75l-167 125l167 125v-75h800v75z" /> +<glyph unicode="" d="M50 1100h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M250 1100h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM250 500h700q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000 q-21 0 -35.5 14.5t-14.5 35.5zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5zM0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5z" /> +<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 800h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 500h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 1100h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 800h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 500h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 500h800q21 0 35.5 -14.5t14.5 -35.5v-100 q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM350 200h800 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M400 0h-100v1100h100v-1100zM550 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM267 550l-167 -125v75h-200v100h200v75zM550 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM550 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM900 0h-100v1100h100v-1100zM50 800h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM1100 600h200v-100h-200v-75l-167 125l167 125v-75zM50 500h300q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5zM50 200h600 q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-600q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M75 1000h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53v650q0 31 22 53t53 22zM1200 300l-300 300l300 300v-600z" /> +<glyph unicode="" d="M44 1100h1112q18 0 31 -13t13 -31v-1012q0 -18 -13 -31t-31 -13h-1112q-18 0 -31 13t-13 31v1012q0 18 13 31t31 13zM100 1000v-737l247 182l298 -131l-74 156l293 318l236 -288v500h-1000zM342 884q56 0 95 -39t39 -94.5t-39 -95t-95 -39.5t-95 39.5t-39 95t39 94.5 t95 39z" /> +<glyph unicode="" d="M648 1169q117 0 216 -60t156.5 -161t57.5 -218q0 -115 -70 -258q-69 -109 -158 -225.5t-143 -179.5l-54 -62q-9 8 -25.5 24.5t-63.5 67.5t-91 103t-98.5 128t-95.5 148q-60 132 -60 249q0 88 34 169.5t91.5 142t137 96.5t166.5 36zM652.5 974q-91.5 0 -156.5 -65 t-65 -157t65 -156.5t156.5 -64.5t156.5 64.5t65 156.5t-65 157t-156.5 65z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 173v854q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57z" /> +<glyph unicode="" d="M554 1295q21 -72 57.5 -143.5t76 -130t83 -118t82.5 -117t70 -116t49.5 -126t18.5 -136.5q0 -71 -25.5 -135t-68.5 -111t-99 -82t-118.5 -54t-125.5 -23q-84 5 -161.5 34t-139.5 78.5t-99 125t-37 164.5q0 69 18 136.5t49.5 126.5t69.5 116.5t81.5 117.5t83.5 119 t76.5 131t58.5 143zM344 710q-23 -33 -43.5 -70.5t-40.5 -102.5t-17 -123q1 -37 14.5 -69.5t30 -52t41 -37t38.5 -24.5t33 -15q21 -7 32 -1t13 22l6 34q2 10 -2.5 22t-13.5 19q-5 4 -14 12t-29.5 40.5t-32.5 73.5q-26 89 6 271q2 11 -6 11q-8 1 -15 -10z" /> +<glyph unicode="" d="M1000 1013l108 115q2 1 5 2t13 2t20.5 -1t25 -9.5t28.5 -21.5q22 -22 27 -43t0 -32l-6 -10l-108 -115zM350 1100h400q50 0 105 -13l-187 -187h-368q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v182l200 200v-332 q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM1009 803l-362 -362l-161 -50l55 170l355 355z" /> +<glyph unicode="" d="M350 1100h361q-164 -146 -216 -200h-195q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-103q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M824 1073l339 -301q8 -7 8 -17.5t-8 -17.5l-340 -306q-7 -6 -12.5 -4t-6.5 11v203q-26 1 -54.5 0t-78.5 -7.5t-92 -17.5t-86 -35t-70 -57q10 59 33 108t51.5 81.5t65 58.5t68.5 40.5t67 24.5t56 13.5t40 4.5v210q1 10 6.5 12.5t13.5 -4.5z" /> +<glyph unicode="" d="M350 1100h350q60 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-219q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5z M643 639l395 395q7 7 17.5 7t17.5 -7l101 -101q7 -7 7 -17.5t-7 -17.5l-531 -532q-7 -7 -17.5 -7t-17.5 7l-248 248q-7 7 -7 17.5t7 17.5l101 101q7 7 17.5 7t17.5 -7l111 -111q8 -7 18 -7t18 7z" /> +<glyph unicode="" d="M318 918l264 264q8 8 18 8t18 -8l260 -264q7 -8 4.5 -13t-12.5 -5h-170v-200h200v173q0 10 5 12t13 -5l264 -260q8 -7 8 -17.5t-8 -17.5l-264 -265q-8 -7 -13 -5t-5 12v173h-200v-200h170q10 0 12.5 -5t-4.5 -13l-260 -264q-8 -8 -18 -8t-18 8l-264 264q-8 8 -5.5 13 t12.5 5h175v200h-200v-173q0 -10 -5 -12t-13 5l-264 265q-8 7 -8 17.5t8 17.5l264 260q8 7 13 5t5 -12v-173h200v200h-175q-10 0 -12.5 5t5.5 13z" /> +<glyph unicode="" d="M250 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M50 1100h100q21 0 35.5 -14.5t14.5 -35.5v-438l464 453q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5 t-14.5 35.5v1000q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1200 1050v-1000q0 -21 -10.5 -25t-25.5 10l-464 453v-438q0 -21 -10.5 -25t-25.5 10l-492 480q-15 14 -15 35t15 35l492 480q15 14 25.5 10t10.5 -25v-438l464 453q15 14 25.5 10t10.5 -25z" /> +<glyph unicode="" d="M243 1074l814 -498q18 -11 18 -26t-18 -26l-814 -498q-18 -11 -30.5 -4t-12.5 28v1000q0 21 12.5 28t30.5 -4z" /> +<glyph unicode="" d="M250 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM650 1000h200q21 0 35.5 -14.5t14.5 -35.5v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v800 q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1100 950v-800q0 -21 -14.5 -35.5t-35.5 -14.5h-800q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5z" /> +<glyph unicode="" d="M500 612v438q0 21 10.5 25t25.5 -10l492 -480q15 -14 15 -35t-15 -35l-492 -480q-15 -14 -25.5 -10t-10.5 25v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10z" /> +<glyph unicode="" d="M1048 1102l100 1q20 0 35 -14.5t15 -35.5l5 -1000q0 -21 -14.5 -35.5t-35.5 -14.5l-100 -1q-21 0 -35.5 14.5t-14.5 35.5l-2 437l-463 -454q-14 -15 -24.5 -10.5t-10.5 25.5l-2 437l-462 -455q-15 -14 -25.5 -9.5t-10.5 24.5l-5 1000q0 21 10.5 25.5t25.5 -10.5l466 -450 l-2 438q0 20 10.5 24.5t25.5 -9.5l466 -451l-2 438q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M850 1100h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-464 -453q-15 -14 -25.5 -10t-10.5 25v1000q0 21 10.5 25t25.5 -10l464 -453v438q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M686 1081l501 -540q15 -15 10.5 -26t-26.5 -11h-1042q-22 0 -26.5 11t10.5 26l501 540q15 15 36 15t36 -15zM150 400h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M885 900l-352 -353l352 -353l-197 -198l-552 552l552 550z" /> +<glyph unicode="" d="M1064 547l-551 -551l-198 198l353 353l-353 353l198 198z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM650 900h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-150 q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5h150v-150q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v150h150q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-150v150q0 21 -14.5 35.5t-35.5 14.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM850 700h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5 t35.5 -14.5h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM741.5 913q-12.5 0 -21.5 -9l-120 -120l-120 120q-9 9 -21.5 9 t-21.5 -9l-141 -141q-9 -9 -9 -21.5t9 -21.5l120 -120l-120 -120q-9 -9 -9 -21.5t9 -21.5l141 -141q9 -9 21.5 -9t21.5 9l120 120l120 -120q9 -9 21.5 -9t21.5 9l141 141q9 9 9 21.5t-9 21.5l-120 120l120 120q9 9 9 21.5t-9 21.5l-141 141q-9 9 -21.5 9z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM546 623l-84 85q-7 7 -17.5 7t-18.5 -7l-139 -139q-7 -8 -7 -18t7 -18 l242 -241q7 -8 17.5 -8t17.5 8l375 375q7 7 7 17.5t-7 18.5l-139 139q-7 7 -17.5 7t-17.5 -7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM588 941q-29 0 -59 -5.5t-63 -20.5t-58 -38.5t-41.5 -63t-16.5 -89.5 q0 -25 20 -25h131q30 -5 35 11q6 20 20.5 28t45.5 8q20 0 31.5 -10.5t11.5 -28.5q0 -23 -7 -34t-26 -18q-1 0 -13.5 -4t-19.5 -7.5t-20 -10.5t-22 -17t-18.5 -24t-15.5 -35t-8 -46q-1 -8 5.5 -16.5t20.5 -8.5h173q7 0 22 8t35 28t37.5 48t29.5 74t12 100q0 47 -17 83 t-42.5 57t-59.5 34.5t-64 18t-59 4.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM675 1000h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5 t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5zM675 700h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h75v-200h-75q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h350q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5 t-17.5 7.5h-75v275q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M525 1200h150q10 0 17.5 -7.5t7.5 -17.5v-194q103 -27 178.5 -102.5t102.5 -178.5h194q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-194q-27 -103 -102.5 -178.5t-178.5 -102.5v-194q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v194 q-103 27 -178.5 102.5t-102.5 178.5h-194q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h194q27 103 102.5 178.5t178.5 102.5v194q0 10 7.5 17.5t17.5 7.5zM700 893v-168q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v168q-68 -23 -119 -74 t-74 -119h168q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-168q23 -68 74 -119t119 -74v168q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-168q68 23 119 74t74 119h-168q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h168 q-23 68 -74 119t-119 74z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM759 823l64 -64q7 -7 7 -17.5t-7 -17.5l-124 -124l124 -124q7 -7 7 -17.5t-7 -17.5l-64 -64q-7 -7 -17.5 -7t-17.5 7l-124 124l-124 -124q-7 -7 -17.5 -7t-17.5 7l-64 64 q-7 7 -7 17.5t7 17.5l124 124l-124 124q-7 7 -7 17.5t7 17.5l64 64q7 7 17.5 7t17.5 -7l124 -124l124 124q7 7 17.5 7t17.5 -7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5t57 -214.5 t155.5 -155.5t214.5 -57t214.5 57t155.5 155.5t57 214.5t-57 214.5t-155.5 155.5t-214.5 57zM782 788l106 -106q7 -7 7 -17.5t-7 -17.5l-320 -321q-8 -7 -18 -7t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l197 197q7 7 17.5 7t17.5 -7z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM600 1027q-116 0 -214.5 -57t-155.5 -155.5t-57 -214.5q0 -120 65 -225 l587 587q-105 65 -225 65zM965 819l-584 -584q104 -62 219 -62q116 0 214.5 57t155.5 155.5t57 214.5q0 115 -62 219z" /> +<glyph unicode="" d="M39 582l522 427q16 13 27.5 8t11.5 -26v-291h550q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-550v-291q0 -21 -11.5 -26t-27.5 8l-522 427q-16 13 -16 32t16 32z" /> +<glyph unicode="" d="M639 1009l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291h-550q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h550v291q0 21 11.5 26t27.5 -8z" /> +<glyph unicode="" d="M682 1161l427 -522q13 -16 8 -27.5t-26 -11.5h-291v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v550h-291q-21 0 -26 11.5t8 27.5l427 522q13 16 32 16t32 -16z" /> +<glyph unicode="" d="M550 1200h200q21 0 35.5 -14.5t14.5 -35.5v-550h291q21 0 26 -11.5t-8 -27.5l-427 -522q-13 -16 -32 -16t-32 16l-427 522q-13 16 -8 27.5t26 11.5h291v550q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M639 1109l522 -427q16 -13 16 -32t-16 -32l-522 -427q-16 -13 -27.5 -8t-11.5 26v291q-94 -2 -182 -20t-170.5 -52t-147 -92.5t-100.5 -135.5q5 105 27 193.5t67.5 167t113 135t167 91.5t225.5 42v262q0 21 11.5 26t27.5 -8z" /> +<glyph unicode="" d="M850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5zM350 0h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249 q8 7 18 7t18 -7l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5z" /> +<glyph unicode="" d="M1014 1120l106 -106q7 -8 7 -18t-7 -18l-249 -249l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l249 249q8 7 18 7t18 -7zM250 600h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-249 -249q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l249 249l-94 94q-14 14 -10 24.5t25 10.5z" /> +<glyph unicode="" d="M600 1177q117 0 224 -45.5t184.5 -123t123 -184.5t45.5 -224t-45.5 -224t-123 -184.5t-184.5 -123t-224 -45.5t-224 45.5t-184.5 123t-123 184.5t-45.5 224t45.5 224t123 184.5t184.5 123t224 45.5zM704 900h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5 t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM675 400h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M260 1200q9 0 19 -2t15 -4l5 -2q22 -10 44 -23l196 -118q21 -13 36 -24q29 -21 37 -12q11 13 49 35l196 118q22 13 45 23q17 7 38 7q23 0 47 -16.5t37 -33.5l13 -16q14 -21 18 -45l25 -123l8 -44q1 -9 8.5 -14.5t17.5 -5.5h61q10 0 17.5 -7.5t7.5 -17.5v-50 q0 -10 -7.5 -17.5t-17.5 -7.5h-50q-10 0 -17.5 -7.5t-7.5 -17.5v-175h-400v300h-200v-300h-400v175q0 10 -7.5 17.5t-17.5 7.5h-50q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5h61q11 0 18 3t7 8q0 4 9 52l25 128q5 25 19 45q2 3 5 7t13.5 15t21.5 19.5t26.5 15.5 t29.5 7zM915 1079l-166 -162q-7 -7 -5 -12t12 -5h219q10 0 15 7t2 17l-51 149q-3 10 -11 12t-15 -6zM463 917l-177 157q-8 7 -16 5t-11 -12l-51 -143q-3 -10 2 -17t15 -7h231q11 0 12.5 5t-5.5 12zM500 0h-375q-10 0 -17.5 7.5t-7.5 17.5v375h400v-400zM1100 400v-375 q0 -10 -7.5 -17.5t-17.5 -7.5h-375v400h400z" /> +<glyph unicode="" d="M1165 1190q8 3 21 -6.5t13 -17.5q-2 -178 -24.5 -323.5t-55.5 -245.5t-87 -174.5t-102.5 -118.5t-118 -68.5t-118.5 -33t-120 -4.5t-105 9.5t-90 16.5q-61 12 -78 11q-4 1 -12.5 0t-34 -14.5t-52.5 -40.5l-153 -153q-26 -24 -37 -14.5t-11 43.5q0 64 42 102q8 8 50.5 45 t66.5 58q19 17 35 47t13 61q-9 55 -10 102.5t7 111t37 130t78 129.5q39 51 80 88t89.5 63.5t94.5 45t113.5 36t129 31t157.5 37t182 47.5zM1116 1098q-8 9 -22.5 -3t-45.5 -50q-38 -47 -119 -103.5t-142 -89.5l-62 -33q-56 -30 -102 -57t-104 -68t-102.5 -80.5t-85.5 -91 t-64 -104.5q-24 -56 -31 -86t2 -32t31.5 17.5t55.5 59.5q25 30 94 75.5t125.5 77.5t147.5 81q70 37 118.5 69t102 79.5t99 111t86.5 148.5q22 50 24 60t-6 19z" /> +<glyph unicode="" d="M653 1231q-39 -67 -54.5 -131t-10.5 -114.5t24.5 -96.5t47.5 -80t63.5 -62.5t68.5 -46.5t65 -30q-4 7 -17.5 35t-18.5 39.5t-17 39.5t-17 43t-13 42t-9.5 44.5t-2 42t4 43t13.5 39t23 38.5q96 -42 165 -107.5t105 -138t52 -156t13 -159t-19 -149.5q-13 -55 -44 -106.5 t-68 -87t-78.5 -64.5t-72.5 -45t-53 -22q-72 -22 -127 -11q-31 6 -13 19q6 3 17 7q13 5 32.5 21t41 44t38.5 63.5t21.5 81.5t-6.5 94.5t-50 107t-104 115.5q10 -104 -0.5 -189t-37 -140.5t-65 -93t-84 -52t-93.5 -11t-95 24.5q-80 36 -131.5 114t-53.5 171q-2 23 0 49.5 t4.5 52.5t13.5 56t27.5 60t46 64.5t69.5 68.5q-8 -53 -5 -102.5t17.5 -90t34 -68.5t44.5 -39t49 -2q31 13 38.5 36t-4.5 55t-29 64.5t-36 75t-26 75.5q-15 85 2 161.5t53.5 128.5t85.5 92.5t93.5 61t81.5 25.5z" /> +<glyph unicode="" d="M600 1094q82 0 160.5 -22.5t140 -59t116.5 -82.5t94.5 -95t68 -95t42.5 -82.5t14 -57.5t-14 -57.5t-43 -82.5t-68.5 -95t-94.5 -95t-116.5 -82.5t-140 -59t-159.5 -22.5t-159.5 22.5t-140 59t-116.5 82.5t-94.5 95t-68.5 95t-43 82.5t-14 57.5t14 57.5t42.5 82.5t68 95 t94.5 95t116.5 82.5t140 59t160.5 22.5zM888 829q-15 15 -18 12t5 -22q25 -57 25 -119q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 59 23 114q8 19 4.5 22t-17.5 -12q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q22 -36 47 -71t70 -82t92.5 -81t113 -58.5t133.5 -24.5 t133.5 24t113 58.5t92.5 81.5t70 81.5t47 70.5q11 18 9 42.5t-14 41.5q-90 117 -163 189zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l35 34q14 15 12.5 33.5t-16.5 33.5q-44 44 -89 117q-11 18 -28 20t-32 -12z" /> +<glyph unicode="" d="M592 0h-148l31 120q-91 20 -175.5 68.5t-143.5 106.5t-103.5 119t-66.5 110t-22 76q0 21 14 57.5t42.5 82.5t68 95t94.5 95t116.5 82.5t140 59t160.5 22.5q61 0 126 -15l32 121h148zM944 770l47 181q108 -85 176.5 -192t68.5 -159q0 -26 -19.5 -71t-59.5 -102t-93 -112 t-129 -104.5t-158 -75.5l46 173q77 49 136 117t97 131q11 18 9 42.5t-14 41.5q-54 70 -107 130zM310 824q-70 -69 -160 -184q-13 -16 -15 -40.5t9 -42.5q18 -30 39 -60t57 -70.5t74 -73t90 -61t105 -41.5l41 154q-107 18 -178.5 101.5t-71.5 193.5q0 59 23 114q8 19 4.5 22 t-17.5 -12zM448 727l-35 -36q-15 -15 -19.5 -38.5t4.5 -41.5q37 -68 93 -116q16 -13 38.5 -11t36.5 17l12 11l22 86l-3 4q-44 44 -89 117q-11 18 -28 20t-32 -12z" /> +<glyph unicode="" d="M-90 100l642 1066q20 31 48 28.5t48 -35.5l642 -1056q21 -32 7.5 -67.5t-50.5 -35.5h-1294q-37 0 -50.5 34t7.5 66zM155 200h345v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h345l-445 723zM496 700h208q20 0 32 -14.5t8 -34.5l-58 -252 q-4 -20 -21.5 -34.5t-37.5 -14.5h-54q-20 0 -37.5 14.5t-21.5 34.5l-58 252q-4 20 8 34.5t32 14.5z" /> +<glyph unicode="" d="M650 1200q62 0 106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -93 100 -113v-64q0 -21 -13 -29t-32 1l-205 128l-205 -128q-19 -9 -32 -1t-13 29v64q0 20 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5v41 q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44z" /> +<glyph unicode="" d="M850 1200h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-150h-1100v150q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-50h500v50q0 21 14.5 35.5t35.5 14.5zM1100 800v-750q0 -21 -14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v750h1100zM100 600v-100h100v100h-100zM300 600v-100h100v100h-100zM500 600v-100h100v100h-100zM700 600v-100h100v100h-100zM900 600v-100h100v100h-100zM100 400v-100h100v100h-100zM300 400v-100h100v100h-100zM500 400 v-100h100v100h-100zM700 400v-100h100v100h-100zM900 400v-100h100v100h-100zM100 200v-100h100v100h-100zM300 200v-100h100v100h-100zM500 200v-100h100v100h-100zM700 200v-100h100v100h-100zM900 200v-100h100v100h-100z" /> +<glyph unicode="" d="M1135 1165l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-159l-600 -600h-291q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h209l600 600h241v150q0 21 10.5 25t24.5 -10zM522 819l-141 -141l-122 122h-209q-21 0 -35.5 14.5 t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h291zM1135 565l249 -230q15 -14 15 -35t-15 -35l-249 -230q-14 -14 -24.5 -10t-10.5 25v150h-241l-181 181l141 141l122 -122h159v150q0 21 10.5 25t24.5 -10z" /> +<glyph unicode="" d="M100 1100h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5z" /> +<glyph unicode="" d="M150 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM850 1200h200q21 0 35.5 -14.5t14.5 -35.5v-250h-300v250q0 21 14.5 35.5t35.5 14.5zM1100 800v-300q0 -41 -3 -77.5t-15 -89.5t-32 -96t-58 -89t-89 -77t-129 -51t-174 -20t-174 20 t-129 51t-89 77t-58 89t-32 96t-15 89.5t-3 77.5v300h300v-250v-27v-42.5t1.5 -41t5 -38t10 -35t16.5 -30t25.5 -24.5t35 -19t46.5 -12t60 -4t60 4.5t46.5 12.5t35 19.5t25 25.5t17 30.5t10 35t5 38t2 40.5t-0.5 42v25v250h300z" /> +<glyph unicode="" d="M1100 411l-198 -199l-353 353l-353 -353l-197 199l551 551z" /> +<glyph unicode="" d="M1101 789l-550 -551l-551 551l198 199l353 -353l353 353z" /> +<glyph unicode="" d="M404 1000h746q21 0 35.5 -14.5t14.5 -35.5v-551h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v401h-381zM135 984l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-400h385l215 -200h-750q-21 0 -35.5 14.5 t-14.5 35.5v550h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M56 1200h94q17 0 31 -11t18 -27l38 -162h896q24 0 39 -18.5t10 -42.5l-100 -475q-5 -21 -27 -42.5t-55 -21.5h-633l48 -200h535q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-50q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-300v-50 q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v50h-31q-18 0 -32.5 10t-20.5 19l-5 10l-201 961h-54q-20 0 -35 14.5t-15 35.5t15 35.5t35 14.5z" /> +<glyph unicode="" d="M1200 1000v-100h-1200v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500zM0 800h1200v-800h-1200v800z" /> +<glyph unicode="" d="M200 800l-200 -400v600h200q0 41 29.5 70.5t70.5 29.5h300q42 0 71 -29.5t29 -70.5h500v-200h-1000zM1500 700l-300 -700h-1200l300 700h1200z" /> +<glyph unicode="" d="M635 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-601h150q21 0 25 -10.5t-10 -24.5l-230 -249q-14 -15 -35 -15t-35 15l-230 249q-14 14 -10 24.5t25 10.5h150v601h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M936 864l249 -229q14 -15 14 -35.5t-14 -35.5l-249 -229q-15 -15 -25.5 -10.5t-10.5 24.5v151h-600v-151q0 -20 -10.5 -24.5t-25.5 10.5l-249 229q-14 15 -14 35.5t14 35.5l249 229q15 15 25.5 10.5t10.5 -25.5v-149h600v149q0 21 10.5 25.5t25.5 -10.5z" /> +<glyph unicode="" d="M1169 400l-172 732q-5 23 -23 45.5t-38 22.5h-672q-20 0 -38 -20t-23 -41l-172 -739h1138zM1100 300h-1000q-41 0 -70.5 -29.5t-29.5 -70.5v-100q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v100q0 41 -29.5 70.5t-70.5 29.5zM800 100v100h100v-100h-100 zM1000 100v100h100v-100h-100z" /> +<glyph unicode="" d="M1150 1100q21 0 35.5 -14.5t14.5 -35.5v-850q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v850q0 21 14.5 35.5t35.5 14.5zM1000 200l-675 200h-38l47 -276q3 -16 -5.5 -20t-29.5 -4h-7h-84q-20 0 -34.5 14t-18.5 35q-55 337 -55 351v250v6q0 16 1 23.5t6.5 14 t17.5 6.5h200l675 250v-850zM0 750v-250q-4 0 -11 0.5t-24 6t-30 15t-24 30t-11 48.5v50q0 26 10.5 46t25 30t29 16t25.5 7z" /> +<glyph unicode="" d="M553 1200h94q20 0 29 -10.5t3 -29.5l-18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q19 0 33 -14.5t14 -35t-13 -40.5t-31 -27q-8 -4 -23 -9.5t-65 -19.5t-103 -25t-132.5 -20t-158.5 -9q-57 0 -115 5t-104 12t-88.5 15.5t-73.5 17.5t-54.5 16t-35.5 12l-11 4 q-18 8 -31 28t-13 40.5t14 35t33 14.5h17l118 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3.5 32t28.5 13zM498 110q50 -6 102 -6q53 0 102 6q-12 -49 -39.5 -79.5t-62.5 -30.5t-63 30.5t-39 79.5z" /> +<glyph unicode="" d="M800 946l224 78l-78 -224l234 -45l-180 -155l180 -155l-234 -45l78 -224l-224 78l-45 -234l-155 180l-155 -180l-45 234l-224 -78l78 224l-234 45l180 155l-180 155l234 45l-78 224l224 -78l45 234l155 -180l155 180z" /> +<glyph unicode="" d="M650 1200h50q40 0 70 -40.5t30 -84.5v-150l-28 -125h328q40 0 70 -40.5t30 -84.5v-100q0 -45 -29 -74l-238 -344q-16 -24 -38 -40.5t-45 -16.5h-250q-7 0 -42 25t-66 50l-31 25h-61q-45 0 -72.5 18t-27.5 57v400q0 36 20 63l145 196l96 198q13 28 37.5 48t51.5 20z M650 1100l-100 -212l-150 -213v-375h100l136 -100h214l250 375v125h-450l50 225v175h-50zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1100h250q23 0 45 -16.5t38 -40.5l238 -344q29 -29 29 -74v-100q0 -44 -30 -84.5t-70 -40.5h-328q28 -118 28 -125v-150q0 -44 -30 -84.5t-70 -40.5h-50q-27 0 -51.5 20t-37.5 48l-96 198l-145 196q-20 27 -20 63v400q0 39 27.5 57t72.5 18h61q124 100 139 100z M50 1000h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM636 1000l-136 -100h-100v-375l150 -213l100 -212h50v175l-50 225h450v125l-250 375h-214z" /> +<glyph unicode="" d="M356 873l363 230q31 16 53 -6l110 -112q13 -13 13.5 -32t-11.5 -34l-84 -121h302q84 0 138 -38t54 -110t-55 -111t-139 -39h-106l-131 -339q-6 -21 -19.5 -41t-28.5 -20h-342q-7 0 -90 81t-83 94v525q0 17 14 35.5t28 28.5zM400 792v-503l100 -89h293l131 339 q6 21 19.5 41t28.5 20h203q21 0 30.5 25t0.5 50t-31 25h-456h-7h-6h-5.5t-6 0.5t-5 1.5t-5 2t-4 2.5t-4 4t-2.5 4.5q-12 25 5 47l146 183l-86 83zM50 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v500 q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M475 1103l366 -230q2 -1 6 -3.5t14 -10.5t18 -16.5t14.5 -20t6.5 -22.5v-525q0 -13 -86 -94t-93 -81h-342q-15 0 -28.5 20t-19.5 41l-131 339h-106q-85 0 -139.5 39t-54.5 111t54 110t138 38h302l-85 121q-11 15 -10.5 34t13.5 32l110 112q22 22 53 6zM370 945l146 -183 q17 -22 5 -47q-2 -2 -3.5 -4.5t-4 -4t-4 -2.5t-5 -2t-5 -1.5t-6 -0.5h-6h-6.5h-6h-475v-100h221q15 0 29 -20t20 -41l130 -339h294l106 89v503l-342 236zM1050 800h100q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5 v500q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M550 1294q72 0 111 -55t39 -139v-106l339 -131q21 -6 41 -19.5t20 -28.5v-342q0 -7 -81 -90t-94 -83h-525q-17 0 -35.5 14t-28.5 28l-9 14l-230 363q-16 31 6 53l112 110q13 13 32 13.5t34 -11.5l121 -84v302q0 84 38 138t110 54zM600 972v203q0 21 -25 30.5t-50 0.5 t-25 -31v-456v-7v-6v-5.5t-0.5 -6t-1.5 -5t-2 -5t-2.5 -4t-4 -4t-4.5 -2.5q-25 -12 -47 5l-183 146l-83 -86l236 -339h503l89 100v293l-339 131q-21 6 -41 19.5t-20 28.5zM450 200h500q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-500 q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M350 1100h500q21 0 35.5 14.5t14.5 35.5v100q0 21 -14.5 35.5t-35.5 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100q0 -21 14.5 -35.5t35.5 -14.5zM600 306v-106q0 -84 -39 -139t-111 -55t-110 54t-38 138v302l-121 -84q-15 -12 -34 -11.5t-32 13.5l-112 110 q-22 22 -6 53l230 363q1 2 3.5 6t10.5 13.5t16.5 17t20 13.5t22.5 6h525q13 0 94 -83t81 -90v-342q0 -15 -20 -28.5t-41 -19.5zM308 900l-236 -339l83 -86l183 146q22 17 47 5q2 -1 4.5 -2.5t4 -4t2.5 -4t2 -5t1.5 -5t0.5 -6v-5.5v-6v-7v-456q0 -22 25 -31t50 0.5t25 30.5 v203q0 15 20 28.5t41 19.5l339 131v293l-89 100h-503z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM914 632l-275 223q-16 13 -27.5 8t-11.5 -26v-137h-275 q-10 0 -17.5 -7.5t-7.5 -17.5v-150q0 -10 7.5 -17.5t17.5 -7.5h275v-137q0 -21 11.5 -26t27.5 8l275 223q16 13 16 32t-16 32z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM561 855l-275 -223q-16 -13 -16 -32t16 -32l275 -223q16 -13 27.5 -8 t11.5 26v137h275q10 0 17.5 7.5t7.5 17.5v150q0 10 -7.5 17.5t-17.5 7.5h-275v137q0 21 -11.5 26t-27.5 -8z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM855 639l-223 275q-13 16 -32 16t-32 -16l-223 -275q-13 -16 -8 -27.5 t26 -11.5h137v-275q0 -10 7.5 -17.5t17.5 -7.5h150q10 0 17.5 7.5t7.5 17.5v275h137q21 0 26 11.5t-8 27.5z" /> +<glyph unicode="" d="M600 1178q118 0 225 -45.5t184.5 -123t123 -184.5t45.5 -225t-45.5 -225t-123 -184.5t-184.5 -123t-225 -45.5t-225 45.5t-184.5 123t-123 184.5t-45.5 225t45.5 225t123 184.5t184.5 123t225 45.5zM675 900h-150q-10 0 -17.5 -7.5t-7.5 -17.5v-275h-137q-21 0 -26 -11.5 t8 -27.5l223 -275q13 -16 32 -16t32 16l223 275q13 16 8 27.5t-26 11.5h-137v275q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M600 1176q116 0 222.5 -46t184 -123.5t123.5 -184t46 -222.5t-46 -222.5t-123.5 -184t-184 -123.5t-222.5 -46t-222.5 46t-184 123.5t-123.5 184t-46 222.5t46 222.5t123.5 184t184 123.5t222.5 46zM627 1101q-15 -12 -36.5 -20.5t-35.5 -12t-43 -8t-39 -6.5 q-15 -3 -45.5 0t-45.5 -2q-20 -7 -51.5 -26.5t-34.5 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -91t-29.5 -79q-9 -34 5 -93t8 -87q0 -9 17 -44.5t16 -59.5q12 0 23 -5t23.5 -15t19.5 -14q16 -8 33 -15t40.5 -15t34.5 -12q21 -9 52.5 -32t60 -38t57.5 -11 q7 -15 -3 -34t-22.5 -40t-9.5 -38q13 -21 23 -34.5t27.5 -27.5t36.5 -18q0 -7 -3.5 -16t-3.5 -14t5 -17q104 -2 221 112q30 29 46.5 47t34.5 49t21 63q-13 8 -37 8.5t-36 7.5q-15 7 -49.5 15t-51.5 19q-18 0 -41 -0.5t-43 -1.5t-42 -6.5t-38 -16.5q-51 -35 -66 -12 q-4 1 -3.5 25.5t0.5 25.5q-6 13 -26.5 17.5t-24.5 6.5q1 15 -0.5 30.5t-7 28t-18.5 11.5t-31 -21q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q7 -12 18 -24t21.5 -20.5t20 -15t15.5 -10.5l5 -3q2 12 7.5 30.5t8 34.5t-0.5 32q-3 18 3.5 29 t18 22.5t15.5 24.5q6 14 10.5 35t8 31t15.5 22.5t34 22.5q-6 18 10 36q8 0 24 -1.5t24.5 -1.5t20 4.5t20.5 15.5q-10 23 -31 42.5t-37.5 29.5t-49 27t-43.5 23q0 1 2 8t3 11.5t1.5 10.5t-1 9.5t-4.5 4.5q31 -13 58.5 -14.5t38.5 2.5l12 5q5 28 -9.5 46t-36.5 24t-50 15 t-41 20q-18 -4 -37 0zM613 994q0 -17 8 -42t17 -45t9 -23q-8 1 -39.5 5.5t-52.5 10t-37 16.5q3 11 16 29.5t16 25.5q10 -10 19 -10t14 6t13.5 14.5t16.5 12.5z" /> +<glyph unicode="" d="M756 1157q164 92 306 -9l-259 -138l145 -232l251 126q6 -89 -34 -156.5t-117 -110.5q-60 -34 -127 -39.5t-126 16.5l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5t15 37.5l600 599q-34 101 5.5 201.5t135.5 154.5z" /> +<glyph unicode="" horiz-adv-x="1220" d="M100 1196h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 1096h-200v-100h200v100zM100 796h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 696h-500v-100h500v100zM100 396h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5v100q0 41 29.5 70.5t70.5 29.5zM1100 296h-300v-100h300v100z " /> +<glyph unicode="" d="M150 1200h900q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM700 500v-300l-200 -200v500l-350 500h900z" /> +<glyph unicode="" d="M500 1200h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5zM500 1100v-100h200v100h-200zM1200 400v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5v200h1200z" /> +<glyph unicode="" d="M50 1200h300q21 0 25 -10.5t-10 -24.5l-94 -94l199 -199q7 -8 7 -18t-7 -18l-106 -106q-8 -7 -18 -7t-18 7l-199 199l-94 -94q-14 -14 -24.5 -10t-10.5 25v300q0 21 14.5 35.5t35.5 14.5zM850 1200h300q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -10.5 -25t-24.5 10l-94 94 l-199 -199q-8 -7 -18 -7t-18 7l-106 106q-7 8 -7 18t7 18l199 199l-94 94q-14 14 -10 24.5t25 10.5zM364 470l106 -106q7 -8 7 -18t-7 -18l-199 -199l94 -94q14 -14 10 -24.5t-25 -10.5h-300q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 10.5 25t24.5 -10l94 -94l199 199 q8 7 18 7t18 -7zM1071 271l94 94q14 14 24.5 10t10.5 -25v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -25 10.5t10 24.5l94 94l-199 199q-7 8 -7 18t7 18l106 106q8 7 18 7t18 -7z" /> +<glyph unicode="" d="M596 1192q121 0 231.5 -47.5t190 -127t127 -190t47.5 -231.5t-47.5 -231.5t-127 -190.5t-190 -127t-231.5 -47t-231.5 47t-190.5 127t-127 190.5t-47 231.5t47 231.5t127 190t190.5 127t231.5 47.5zM596 1010q-112 0 -207.5 -55.5t-151 -151t-55.5 -207.5t55.5 -207.5 t151 -151t207.5 -55.5t207.5 55.5t151 151t55.5 207.5t-55.5 207.5t-151 151t-207.5 55.5zM454.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38.5 -16.5t-38.5 16.5t-16 39t16 38.5t38.5 16zM754.5 905q22.5 0 38.5 -16t16 -38.5t-16 -39t-38 -16.5q-14 0 -29 10l-55 -145 q17 -23 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 23 16 39t38.5 16zM345.5 709q22.5 0 38.5 -16t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16zM854.5 709q22.5 0 38.5 -16 t16 -38.5t-16 -38.5t-38.5 -16t-38.5 16t-16 38.5t16 38.5t38.5 16z" /> +<glyph unicode="" d="M546 173l469 470q91 91 99 192q7 98 -52 175.5t-154 94.5q-22 4 -47 4q-34 0 -66.5 -10t-56.5 -23t-55.5 -38t-48 -41.5t-48.5 -47.5q-376 -375 -391 -390q-30 -27 -45 -41.5t-37.5 -41t-32 -46.5t-16 -47.5t-1.5 -56.5q9 -62 53.5 -95t99.5 -33q74 0 125 51l548 548 q36 36 20 75q-7 16 -21.5 26t-32.5 10q-26 0 -50 -23q-13 -12 -39 -38l-341 -338q-15 -15 -35.5 -15.5t-34.5 13.5t-14 34.5t14 34.5q327 333 361 367q35 35 67.5 51.5t78.5 16.5q14 0 29 -1q44 -8 74.5 -35.5t43.5 -68.5q14 -47 2 -96.5t-47 -84.5q-12 -11 -32 -32 t-79.5 -81t-114.5 -115t-124.5 -123.5t-123 -119.5t-96.5 -89t-57 -45q-56 -27 -120 -27q-70 0 -129 32t-93 89q-48 78 -35 173t81 163l511 511q71 72 111 96q91 55 198 55q80 0 152 -33q78 -36 129.5 -103t66.5 -154q17 -93 -11 -183.5t-94 -156.5l-482 -476 q-15 -15 -36 -16t-37 14t-17.5 34t14.5 35z" /> +<glyph unicode="" d="M649 949q48 68 109.5 104t121.5 38.5t118.5 -20t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-150 152.5t-126.5 127.5t-93.5 124.5t-33.5 117.5q0 64 28 123t73 100.5t104 64t119 20 t120.5 -38.5t104.5 -104zM896 972q-33 0 -64.5 -19t-56.5 -46t-47.5 -53.5t-43.5 -45.5t-37.5 -19t-36 19t-40 45.5t-43 53.5t-54 46t-65.5 19q-67 0 -122.5 -55.5t-55.5 -132.5q0 -23 13.5 -51t46 -65t57.5 -63t76 -75l22 -22q15 -14 44 -44t50.5 -51t46 -44t41 -35t23 -12 t23.5 12t42.5 36t46 44t52.5 52t44 43q4 4 12 13q43 41 63.5 62t52 55t46 55t26 46t11.5 44q0 79 -53 133.5t-120 54.5z" /> +<glyph unicode="" d="M776.5 1214q93.5 0 159.5 -66l141 -141q66 -66 66 -160q0 -42 -28 -95.5t-62 -87.5l-29 -29q-31 53 -77 99l-18 18l95 95l-247 248l-389 -389l212 -212l-105 -106l-19 18l-141 141q-66 66 -66 159t66 159l283 283q65 66 158.5 66zM600 706l105 105q10 -8 19 -17l141 -141 q66 -66 66 -159t-66 -159l-283 -283q-66 -66 -159 -66t-159 66l-141 141q-66 66 -66 159.5t66 159.5l55 55q29 -55 75 -102l18 -17l-95 -95l247 -248l389 389z" /> +<glyph unicode="" d="M603 1200q85 0 162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5v953q0 21 30 46.5t81 48t129 37.5t163 15zM300 1000v-700h600v700h-600zM600 254q-43 0 -73.5 -30.5t-30.5 -73.5t30.5 -73.5t73.5 -30.5t73.5 30.5 t30.5 73.5t-30.5 73.5t-73.5 30.5z" /> +<glyph unicode="" d="M902 1185l283 -282q15 -15 15 -36t-14.5 -35.5t-35.5 -14.5t-35 15l-36 35l-279 -267v-300l-212 210l-308 -307l-280 -203l203 280l307 308l-210 212h300l267 279l-35 36q-15 14 -15 35t14.5 35.5t35.5 14.5t35 -15z" /> +<glyph unicode="" d="M700 1248v-78q38 -5 72.5 -14.5t75.5 -31.5t71 -53.5t52 -84t24 -118.5h-159q-4 36 -10.5 59t-21 45t-40 35.5t-64.5 20.5v-307l64 -13q34 -7 64 -16.5t70 -32t67.5 -52.5t47.5 -80t20 -112q0 -139 -89 -224t-244 -97v-77h-100v79q-150 16 -237 103q-40 40 -52.5 93.5 t-15.5 139.5h139q5 -77 48.5 -126t117.5 -65v335l-27 8q-46 14 -79 26.5t-72 36t-63 52t-40 72.5t-16 98q0 70 25 126t67.5 92t94.5 57t110 27v77h100zM600 754v274q-29 -4 -50 -11t-42 -21.5t-31.5 -41.5t-10.5 -65q0 -29 7 -50.5t16.5 -34t28.5 -22.5t31.5 -14t37.5 -10 q9 -3 13 -4zM700 547v-310q22 2 42.5 6.5t45 15.5t41.5 27t29 42t12 59.5t-12.5 59.5t-38 44.5t-53 31t-66.5 24.5z" /> +<glyph unicode="" d="M561 1197q84 0 160.5 -40t123.5 -109.5t47 -147.5h-153q0 40 -19.5 71.5t-49.5 48.5t-59.5 26t-55.5 9q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -26 13.5 -63t26.5 -61t37 -66q6 -9 9 -14h241v-100h-197q8 -50 -2.5 -115t-31.5 -95q-45 -62 -99 -112 q34 10 83 17.5t71 7.5q32 1 102 -16t104 -17q83 0 136 30l50 -147q-31 -19 -58 -30.5t-55 -15.5t-42 -4.5t-46 -0.5q-23 0 -76 17t-111 32.5t-96 11.5q-39 -3 -82 -16t-67 -25l-23 -11l-55 145q4 3 16 11t15.5 10.5t13 9t15.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221v100h166q-23 47 -44 104q-7 20 -12 41.5t-6 55.5t6 66.5t29.5 70.5t58.5 71q97 88 263 88z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM935 1184l230 -249q14 -14 10 -24.5t-25 -10.5h-150v-900h-200v900h-150q-21 0 -25 10.5t10 24.5l230 249q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M1000 700h-100v100h-100v-100h-100v500h300v-500zM400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM801 1100v-200h100v200h-100zM1000 350l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150z " /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 1050l-200 -250h200v-100h-300v150l200 250h-200v100h300v-150zM1000 0h-100v100h-100v-100h-100v500h300v-500zM801 400v-200h100v200h-100z " /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1000 700h-100v400h-100v100h200v-500zM1100 0h-100v100h-200v400h300v-500zM901 400v-200h100v200h-100z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1100 700h-100v100h-200v400h300v-500zM901 1100v-200h100v200h-100zM1000 0h-100v400h-100v100h200v-500z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM900 1000h-200v200h200v-200zM1000 700h-300v200h300v-200zM1100 400h-400v200h400v-200zM1200 100h-500v200h500v-200z" /> +<glyph unicode="" d="M400 300h150q21 0 25 -11t-10 -25l-230 -250q-14 -15 -35 -15t-35 15l-230 250q-14 14 -10 25t25 11h150v900h200v-900zM1200 1000h-500v200h500v-200zM1100 700h-400v200h400v-200zM1000 400h-300v200h300v-200zM900 100h-200v200h200v-200z" /> +<glyph unicode="" d="M350 1100h400q162 0 256 -93.5t94 -256.5v-400q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5z" /> +<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-163 0 -256.5 92.5t-93.5 257.5v400q0 163 94 256.5t256 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM440 770l253 -190q17 -12 17 -30t-17 -30l-253 -190q-16 -12 -28 -6.5t-12 26.5v400q0 21 12 26.5t28 -6.5z" /> +<glyph unicode="" d="M350 1100h400q163 0 256.5 -94t93.5 -256v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 163 92.5 256.5t257.5 93.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM350 700h400q21 0 26.5 -12t-6.5 -28l-190 -253q-12 -17 -30 -17t-30 17l-190 253q-12 16 -6.5 28t26.5 12z" /> +<glyph unicode="" d="M350 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -163 -92.5 -256.5t-257.5 -93.5h-400q-163 0 -256.5 94t-93.5 256v400q0 165 92.5 257.5t257.5 92.5zM800 900h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5 v500q0 41 -29.5 70.5t-70.5 29.5zM580 693l190 -253q12 -16 6.5 -28t-26.5 -12h-400q-21 0 -26.5 12t6.5 28l190 253q12 17 30 17t30 -17z" /> +<glyph unicode="" d="M550 1100h400q165 0 257.5 -92.5t92.5 -257.5v-400q0 -165 -92.5 -257.5t-257.5 -92.5h-400q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h450q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-450q-21 0 -35.5 14.5t-14.5 35.5v100 q0 21 14.5 35.5t35.5 14.5zM338 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" /> +<glyph unicode="" d="M793 1182l9 -9q8 -10 5 -27q-3 -11 -79 -225.5t-78 -221.5l300 1q24 0 32.5 -17.5t-5.5 -35.5q-1 0 -133.5 -155t-267 -312.5t-138.5 -162.5q-12 -15 -26 -15h-9l-9 8q-9 11 -4 32q2 9 42 123.5t79 224.5l39 110h-302q-23 0 -31 19q-10 21 6 41q75 86 209.5 237.5 t228 257t98.5 111.5q9 16 25 16h9z" /> +<glyph unicode="" d="M350 1100h400q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-450q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h450q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400 q0 165 92.5 257.5t257.5 92.5zM938 867l324 -284q16 -14 16 -33t-16 -33l-324 -284q-16 -14 -27 -9t-11 26v150h-250q-21 0 -35.5 14.5t-14.5 35.5v200q0 21 14.5 35.5t35.5 14.5h250v150q0 21 11 26t27 -9z" /> +<glyph unicode="" d="M750 1200h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -10.5 -25t-24.5 10l-109 109l-312 -312q-15 -15 -35.5 -15t-35.5 15l-141 141q-15 15 -15 35.5t15 35.5l312 312l-109 109q-14 14 -10 24.5t25 10.5zM456 900h-156q-41 0 -70.5 -29.5t-29.5 -70.5v-500 q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v148l200 200v-298q0 -165 -93.5 -257.5t-256.5 -92.5h-400q-165 0 -257.5 92.5t-92.5 257.5v400q0 165 92.5 257.5t257.5 92.5h300z" /> +<glyph unicode="" d="M600 1186q119 0 227.5 -46.5t187 -125t125 -187t46.5 -227.5t-46.5 -227.5t-125 -187t-187 -125t-227.5 -46.5t-227.5 46.5t-187 125t-125 187t-46.5 227.5t46.5 227.5t125 187t187 125t227.5 46.5zM600 1022q-115 0 -212 -56.5t-153.5 -153.5t-56.5 -212t56.5 -212 t153.5 -153.5t212 -56.5t212 56.5t153.5 153.5t56.5 212t-56.5 212t-153.5 153.5t-212 56.5zM600 794q80 0 137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137t57 137t137 57z" /> +<glyph unicode="" d="M450 1200h200q21 0 35.5 -14.5t14.5 -35.5v-350h245q20 0 25 -11t-9 -26l-383 -426q-14 -15 -33.5 -15t-32.5 15l-379 426q-13 15 -8.5 26t25.5 11h250v350q0 21 14.5 35.5t35.5 14.5zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M583 1182l378 -435q14 -15 9 -31t-26 -16h-244v-250q0 -20 -17 -35t-39 -15h-200q-20 0 -32 14.5t-12 35.5v250h-250q-20 0 -25.5 16.5t8.5 31.5l383 431q14 16 33.5 17t33.5 -14zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5z M900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M396 723l369 369q7 7 17.5 7t17.5 -7l139 -139q7 -8 7 -18.5t-7 -17.5l-525 -525q-7 -8 -17.5 -8t-17.5 8l-292 291q-7 8 -7 18t7 18l139 139q8 7 18.5 7t17.5 -7zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50 h-100z" /> +<glyph unicode="" d="M135 1023l142 142q14 14 35 14t35 -14l77 -77l-212 -212l-77 76q-14 15 -14 36t14 35zM655 855l210 210q14 14 24.5 10t10.5 -25l-2 -599q-1 -20 -15.5 -35t-35.5 -15l-597 -1q-21 0 -25 10.5t10 24.5l208 208l-154 155l212 212zM50 300h1000q21 0 35.5 -14.5t14.5 -35.5 v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M350 1200l599 -2q20 -1 35 -15.5t15 -35.5l1 -597q0 -21 -10.5 -25t-24.5 10l-208 208l-155 -154l-212 212l155 154l-210 210q-14 14 -10 24.5t25 10.5zM524 512l-76 -77q-15 -14 -36 -14t-35 14l-142 142q-14 14 -14 35t14 35l77 77zM50 300h1000q21 0 35.5 -14.5 t14.5 -35.5v-250h-1100v250q0 21 14.5 35.5t35.5 14.5zM900 200v-50h100v50h-100z" /> +<glyph unicode="" d="M1200 103l-483 276l-314 -399v423h-399l1196 796v-1096zM483 424v-230l683 953z" /> +<glyph unicode="" d="M1100 1000v-850q0 -21 -14.5 -35.5t-35.5 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200z" /> +<glyph unicode="" d="M1100 1000l-2 -149l-299 -299l-95 95q-9 9 -21.5 9t-21.5 -9l-149 -147h-312v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1132 638l106 -106q7 -7 7 -17.5t-7 -17.5l-420 -421q-8 -7 -18 -7 t-18 7l-202 203q-8 7 -8 17.5t8 17.5l106 106q7 8 17.5 8t17.5 -8l79 -79l297 297q7 7 17.5 7t17.5 -7z" /> +<glyph unicode="" d="M1100 1000v-269l-103 -103l-134 134q-15 15 -33.5 16.5t-34.5 -12.5l-266 -266h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM1202 572l70 -70q15 -15 15 -35.5t-15 -35.5l-131 -131 l131 -131q15 -15 15 -35.5t-15 -35.5l-70 -70q-15 -15 -35.5 -15t-35.5 15l-131 131l-131 -131q-15 -15 -35.5 -15t-35.5 15l-70 70q-15 15 -15 35.5t15 35.5l131 131l-131 131q-15 15 -15 35.5t15 35.5l70 70q15 15 35.5 15t35.5 -15l131 -131l131 131q15 15 35.5 15 t35.5 -15z" /> +<glyph unicode="" d="M1100 1000v-300h-350q-21 0 -35.5 -14.5t-14.5 -35.5v-150h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM850 600h100q21 0 35.5 -14.5t14.5 -35.5v-250h150q21 0 25 -10.5t-10 -24.5 l-230 -230q-14 -14 -35 -14t-35 14l-230 230q-14 14 -10 24.5t25 10.5h150v250q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1100 1000v-400l-165 165q-14 15 -35 15t-35 -15l-263 -265h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100zM700 1000h-100v200h100v-200zM935 565l230 -229q14 -15 10 -25.5t-25 -10.5h-150v-250q0 -20 -14.5 -35 t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35v250h-150q-21 0 -25 10.5t10 25.5l230 229q14 15 35 15t35 -15z" /> +<glyph unicode="" d="M50 1100h1100q21 0 35.5 -14.5t14.5 -35.5v-150h-1200v150q0 21 14.5 35.5t35.5 14.5zM1200 800v-550q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v550h1200zM100 500v-200h400v200h-400z" /> +<glyph unicode="" d="M935 1165l248 -230q14 -14 14 -35t-14 -35l-248 -230q-14 -14 -24.5 -10t-10.5 25v150h-400v200h400v150q0 21 10.5 25t24.5 -10zM200 800h-50q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v-200zM400 800h-100v200h100v-200zM18 435l247 230 q14 14 24.5 10t10.5 -25v-150h400v-200h-400v-150q0 -21 -10.5 -25t-24.5 10l-247 230q-15 14 -15 35t15 35zM900 300h-100v200h100v-200zM1000 500h51q20 0 34.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-34.5 -14.5h-51v200z" /> +<glyph unicode="" d="M862 1073l276 116q25 18 43.5 8t18.5 -41v-1106q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v397q-4 1 -11 5t-24 17.5t-30 29t-24 42t-11 56.5v359q0 31 18.5 65t43.5 52zM550 1200q22 0 34.5 -12.5t14.5 -24.5l1 -13v-450q0 -28 -10.5 -59.5 t-25 -56t-29 -45t-25.5 -31.5l-10 -11v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447q-4 4 -11 11.5t-24 30.5t-30 46t-24 55t-11 60v450q0 2 0.5 5.5t4 12t8.5 15t14.5 12t22.5 5.5q20 0 32.5 -12.5t14.5 -24.5l3 -13v-350h100v350v5.5t2.5 12 t7 15t15 12t25.5 5.5q23 0 35.5 -12.5t13.5 -24.5l1 -13v-350h100v350q0 2 0.5 5.5t3 12t7 15t15 12t24.5 5.5z" /> +<glyph unicode="" d="M1200 1100v-56q-4 0 -11 -0.5t-24 -3t-30 -7.5t-24 -15t-11 -24v-888q0 -22 25 -34.5t50 -13.5l25 -2v-56h-400v56q75 0 87.5 6.5t12.5 43.5v394h-500v-394q0 -37 12.5 -43.5t87.5 -6.5v-56h-400v56q4 0 11 0.5t24 3t30 7.5t24 15t11 24v888q0 22 -25 34.5t-50 13.5 l-25 2v56h400v-56q-75 0 -87.5 -6.5t-12.5 -43.5v-394h500v394q0 37 -12.5 43.5t-87.5 6.5v56h400z" /> +<glyph unicode="" d="M675 1000h375q21 0 35.5 -14.5t14.5 -35.5v-150h-105l-295 -98v98l-200 200h-400l100 100h375zM100 900h300q41 0 70.5 -29.5t29.5 -70.5v-500q0 -41 -29.5 -70.5t-70.5 -29.5h-300q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5zM100 800v-200h300v200 h-300zM1100 535l-400 -133v163l400 133v-163zM100 500v-200h300v200h-300zM1100 398v-248q0 -21 -14.5 -35.5t-35.5 -14.5h-375l-100 -100h-375l-100 100h400l200 200h105z" /> +<glyph unicode="" d="M17 1007l162 162q17 17 40 14t37 -22l139 -194q14 -20 11 -44.5t-20 -41.5l-119 -118q102 -142 228 -268t267 -227l119 118q17 17 42.5 19t44.5 -12l192 -136q19 -14 22.5 -37.5t-13.5 -40.5l-163 -162q-3 -1 -9.5 -1t-29.5 2t-47.5 6t-62.5 14.5t-77.5 26.5t-90 42.5 t-101.5 60t-111 83t-119 108.5q-74 74 -133.5 150.5t-94.5 138.5t-60 119.5t-34.5 100t-15 74.5t-4.5 48z" /> +<glyph unicode="" d="M600 1100q92 0 175 -10.5t141.5 -27t108.5 -36.5t81.5 -40t53.5 -37t31 -27l9 -10v-200q0 -21 -14.5 -33t-34.5 -9l-202 34q-20 3 -34.5 20t-14.5 38v146q-141 24 -300 24t-300 -24v-146q0 -21 -14.5 -38t-34.5 -20l-202 -34q-20 -3 -34.5 9t-14.5 33v200q3 4 9.5 10.5 t31 26t54 37.5t80.5 39.5t109 37.5t141 26.5t175 10.5zM600 795q56 0 97 -9.5t60 -23.5t30 -28t12 -24l1 -10v-50l365 -303q14 -15 24.5 -40t10.5 -45v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v212q0 20 10.5 45t24.5 40l365 303v50 q0 4 1 10.5t12 23t30 29t60 22.5t97 10z" /> +<glyph unicode="" d="M1100 700l-200 -200h-600l-200 200v500h200v-200h200v200h200v-200h200v200h200v-500zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5 t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M700 1100h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-1000h300v1000q0 41 -29.5 70.5t-70.5 29.5zM1100 800h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-700h300v700q0 41 -29.5 70.5t-70.5 29.5zM400 0h-300v400q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-400z " /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 300h-100v200h-100v-200h-100v500h100v-200h100v200h100v-500zM900 700v-300l-100 -100h-200v500h200z M700 700v-300h100v300h-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-300h200v-100h-300v500h300v-100zM900 700h-200v-300h200v-100h-300v500h300v-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 400l-300 150l300 150v-300zM900 550l-300 -150v300z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM900 300h-700v500h700v-500zM800 700h-130q-38 0 -66.5 -43t-28.5 -108t27 -107t68 -42h130v300zM300 700v-300 h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 700h-200v-100h200v-300h-300v100h200v100h-200v300h300v-100zM900 300h-100v400h-100v100h200v-500z M700 300h-100v100h100v-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM300 700h200v-400h-300v500h100v-100zM900 300h-100v400h-100v100h200v-500zM300 600v-200h100v200h-100z M700 300h-100v100h100v-100z" /> +<glyph unicode="" d="M200 1100h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212v500q0 124 88 212t212 88zM100 900v-700h900v700h-900zM500 500l-199 -200h-100v50l199 200v150h-200v100h300v-300zM900 300h-100v400h-100v100h200v-500zM701 300h-100 v100h100v-100z" /> +<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700h-300v-200h300v-100h-300l-100 100v200l100 100h300v-100z" /> +<glyph unicode="" d="M600 1191q120 0 229.5 -47t188.5 -126t126 -188.5t47 -229.5t-47 -229.5t-126 -188.5t-188.5 -126t-229.5 -47t-229.5 47t-188.5 126t-126 188.5t-47 229.5t47 229.5t126 188.5t188.5 126t229.5 47zM600 1021q-114 0 -211 -56.5t-153.5 -153.5t-56.5 -211t56.5 -211 t153.5 -153.5t211 -56.5t211 56.5t153.5 153.5t56.5 211t-56.5 211t-153.5 153.5t-211 56.5zM800 700v-100l-50 -50l100 -100v-50h-100l-100 100h-150v-100h-100v400h300zM500 700v-100h200v100h-200z" /> +<glyph unicode="" d="M503 1089q110 0 200.5 -59.5t134.5 -156.5q44 14 90 14q120 0 205 -86.5t85 -207t-85 -207t-205 -86.5h-128v250q0 21 -14.5 35.5t-35.5 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-250h-222q-80 0 -136 57.5t-56 136.5q0 69 43 122.5t108 67.5q-2 19 -2 37q0 100 49 185 t134 134t185 49zM525 500h150q10 0 17.5 -7.5t7.5 -17.5v-275h137q21 0 26 -11.5t-8 -27.5l-223 -244q-13 -16 -32 -16t-32 16l-223 244q-13 16 -8 27.5t26 11.5h137v275q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M502 1089q110 0 201 -59.5t135 -156.5q43 15 89 15q121 0 206 -86.5t86 -206.5q0 -99 -60 -181t-150 -110l-378 360q-13 16 -31.5 16t-31.5 -16l-381 -365h-9q-79 0 -135.5 57.5t-56.5 136.5q0 69 43 122.5t108 67.5q-2 19 -2 38q0 100 49 184.5t133.5 134t184.5 49.5z M632 467l223 -228q13 -16 8 -27.5t-26 -11.5h-137v-275q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v275h-137q-21 0 -26 11.5t8 27.5q199 204 223 228q19 19 31.5 19t32.5 -19z" /> +<glyph unicode="" d="M700 100v100h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170l-270 -300h400v-100h-50q-21 0 -35.5 -14.5t-14.5 -35.5v-50h400v50q0 21 -14.5 35.5t-35.5 14.5h-50z" /> +<glyph unicode="" d="M600 1179q94 0 167.5 -56.5t99.5 -145.5q89 -6 150.5 -71.5t61.5 -155.5q0 -61 -29.5 -112.5t-79.5 -82.5q9 -29 9 -55q0 -74 -52.5 -126.5t-126.5 -52.5q-55 0 -100 30v-251q21 0 35.5 -14.5t14.5 -35.5v-50h-300v50q0 21 14.5 35.5t35.5 14.5v251q-45 -30 -100 -30 q-74 0 -126.5 52.5t-52.5 126.5q0 18 4 38q-47 21 -75.5 65t-28.5 97q0 74 52.5 126.5t126.5 52.5q5 0 23 -2q0 2 -1 10t-1 13q0 116 81.5 197.5t197.5 81.5z" /> +<glyph unicode="" d="M1010 1010q111 -111 150.5 -260.5t0 -299t-150.5 -260.5q-83 -83 -191.5 -126.5t-218.5 -43.5t-218.5 43.5t-191.5 126.5q-111 111 -150.5 260.5t0 299t150.5 260.5q83 83 191.5 126.5t218.5 43.5t218.5 -43.5t191.5 -126.5zM476 1065q-4 0 -8 -1q-121 -34 -209.5 -122.5 t-122.5 -209.5q-4 -12 2.5 -23t18.5 -14l36 -9q3 -1 7 -1q23 0 29 22q27 96 98 166q70 71 166 98q11 3 17.5 13.5t3.5 22.5l-9 35q-3 13 -14 19q-7 4 -15 4zM512 920q-4 0 -9 -2q-80 -24 -138.5 -82.5t-82.5 -138.5q-4 -13 2 -24t19 -14l34 -9q4 -1 8 -1q22 0 28 21 q18 58 58.5 98.5t97.5 58.5q12 3 18 13.5t3 21.5l-9 35q-3 12 -14 19q-7 4 -15 4zM719.5 719.5q-49.5 49.5 -119.5 49.5t-119.5 -49.5t-49.5 -119.5t49.5 -119.5t119.5 -49.5t119.5 49.5t49.5 119.5t-49.5 119.5zM855 551q-22 0 -28 -21q-18 -58 -58.5 -98.5t-98.5 -57.5 q-11 -4 -17 -14.5t-3 -21.5l9 -35q3 -12 14 -19q7 -4 15 -4q4 0 9 2q80 24 138.5 82.5t82.5 138.5q4 13 -2.5 24t-18.5 14l-34 9q-4 1 -8 1zM1000 515q-23 0 -29 -22q-27 -96 -98 -166q-70 -71 -166 -98q-11 -3 -17.5 -13.5t-3.5 -22.5l9 -35q3 -13 14 -19q7 -4 15 -4 q4 0 8 1q121 34 209.5 122.5t122.5 209.5q4 12 -2.5 23t-18.5 14l-36 9q-3 1 -7 1z" /> +<glyph unicode="" d="M700 800h300v-380h-180v200h-340v-200h-380v755q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM700 300h162l-212 -212l-212 212h162v200h100v-200zM520 0h-395q-10 0 -17.5 7.5t-7.5 17.5v395zM1000 220v-195q0 -10 -7.5 -17.5t-17.5 -7.5h-195z" /> +<glyph unicode="" d="M700 800h300v-520l-350 350l-550 -550v1095q0 10 7.5 17.5t17.5 7.5h575v-400zM1000 900h-200v200zM862 200h-162v-200h-100v200h-162l212 212zM480 0h-355q-10 0 -17.5 7.5t-7.5 17.5v55h380v-80zM1000 80v-55q0 -10 -7.5 -17.5t-17.5 -7.5h-155v80h180z" /> +<glyph unicode="" d="M1162 800h-162v-200h100l100 -100h-300v300h-162l212 212zM200 800h200q27 0 40 -2t29.5 -10.5t23.5 -30t7 -57.5h300v-100h-600l-200 -350v450h100q0 36 7 57.5t23.5 30t29.5 10.5t40 2zM800 400h240l-240 -400h-800l300 500h500v-100z" /> +<glyph unicode="" d="M650 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM1000 850v150q41 0 70.5 -29.5t29.5 -70.5v-800 q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-1 0 -20 4l246 246l-326 326v324q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM412 250l-212 -212v162h-200v100h200v162z" /> +<glyph unicode="" d="M450 1100h100q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-300q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h50v50q0 21 14.5 35.5t35.5 14.5zM800 850v150q41 0 70.5 -29.5t29.5 -70.5v-500 h-200v-300h200q0 -36 -7 -57.5t-23.5 -30t-29.5 -10.5t-40 -2h-600q-41 0 -70.5 29.5t-29.5 70.5v800q0 41 29.5 70.5t70.5 29.5v-150q0 -62 44 -106t106 -44h300q62 0 106 44t44 106zM1212 250l-212 -212v162h-200v100h200v162z" /> +<glyph unicode="" d="M658 1197l637 -1104q23 -38 7 -65.5t-60 -27.5h-1276q-44 0 -60 27.5t7 65.5l637 1104q22 39 54 39t54 -39zM704 800h-208q-20 0 -32 -14.5t-8 -34.5l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5zM500 300v-100h200 v100h-200z" /> +<glyph unicode="" d="M425 1100h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM825 800h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM25 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5zM425 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 500h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5 v150q0 10 7.5 17.5t17.5 7.5zM25 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM425 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5 t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM825 200h250q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-250q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M700 1200h100v-200h-100v-100h350q62 0 86.5 -39.5t-3.5 -94.5l-66 -132q-41 -83 -81 -134h-772q-40 51 -81 134l-66 132q-28 55 -3.5 94.5t86.5 39.5h350v100h-100v200h100v100h200v-100zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-12l137 -100 h-950l138 100h-13q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1300q40 0 68.5 -29.5t28.5 -70.5h-194q0 41 28.5 70.5t68.5 29.5zM443 1100h314q18 -37 18 -75q0 -8 -3 -25h328q41 0 44.5 -16.5t-30.5 -38.5l-175 -145h-678l-178 145q-34 22 -29 38.5t46 16.5h328q-3 17 -3 25q0 38 18 75zM250 700h700q21 0 35.5 -14.5 t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-150v-200l275 -200h-950l275 200v200h-150q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1181q75 0 128 -53t53 -128t-53 -128t-128 -53t-128 53t-53 128t53 128t128 53zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13 l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1300q47 0 92.5 -53.5t71 -123t25.5 -123.5q0 -78 -55.5 -133.5t-133.5 -55.5t-133.5 55.5t-55.5 133.5q0 62 34 143l144 -143l111 111l-163 163q34 26 63 26zM602 798h46q34 0 55.5 -28.5t21.5 -86.5q0 -76 39 -183h-324q39 107 39 183q0 58 21.5 86.5t56.5 28.5h45 zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1200l300 -161v-139h-300q0 -57 18.5 -108t50 -91.5t63 -72t70 -67.5t57.5 -61h-530q-60 83 -90.5 177.5t-30.5 178.5t33 164.5t87.5 139.5t126 96.5t145.5 41.5v-98zM250 400h700q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-13l138 -100h-950l137 100 h-12q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5zM50 100h1100q21 0 35.5 -14.5t14.5 -35.5v-50h-1200v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1300q41 0 70.5 -29.5t29.5 -70.5v-78q46 -26 73 -72t27 -100v-50h-400v50q0 54 27 100t73 72v78q0 41 29.5 70.5t70.5 29.5zM400 800h400q54 0 100 -27t72 -73h-172v-100h200v-100h-200v-100h200v-100h-200v-100h200q0 -83 -58.5 -141.5t-141.5 -58.5h-400 q-83 0 -141.5 58.5t-58.5 141.5v400q0 83 58.5 141.5t141.5 58.5z" /> +<glyph unicode="" d="M150 1100h900q21 0 35.5 -14.5t14.5 -35.5v-500q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v500q0 21 14.5 35.5t35.5 14.5zM125 400h950q10 0 17.5 -7.5t7.5 -17.5v-50q0 -10 -7.5 -17.5t-17.5 -7.5h-283l224 -224q13 -13 13 -31.5t-13 -32 t-31.5 -13.5t-31.5 13l-88 88h-524l-87 -88q-13 -13 -32 -13t-32 13.5t-13 32t13 31.5l224 224h-289q-10 0 -17.5 7.5t-7.5 17.5v50q0 10 7.5 17.5t17.5 7.5zM541 300l-100 -100h324l-100 100h-124z" /> +<glyph unicode="" d="M200 1100h800q83 0 141.5 -58.5t58.5 -141.5v-200h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100q0 41 -29.5 70.5t-70.5 29.5h-250q-41 0 -70.5 -29.5t-29.5 -70.5h-100v200q0 83 58.5 141.5t141.5 58.5zM100 600h1000q41 0 70.5 -29.5 t29.5 -70.5v-300h-1200v300q0 41 29.5 70.5t70.5 29.5zM300 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200zM1100 100v-50q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v50h200z" /> +<glyph unicode="" d="M480 1165l682 -683q31 -31 31 -75.5t-31 -75.5l-131 -131h-481l-517 518q-32 31 -32 75.5t32 75.5l295 296q31 31 75.5 31t76.5 -31zM108 794l342 -342l303 304l-341 341zM250 100h800q21 0 35.5 -14.5t14.5 -35.5v-50h-900v50q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M1057 647l-189 506q-8 19 -27.5 33t-40.5 14h-400q-21 0 -40.5 -14t-27.5 -33l-189 -506q-8 -19 1.5 -33t30.5 -14h625v-150q0 -21 14.5 -35.5t35.5 -14.5t35.5 14.5t14.5 35.5v150h125q21 0 30.5 14t1.5 33zM897 0h-595v50q0 21 14.5 35.5t35.5 14.5h50v50 q0 21 14.5 35.5t35.5 14.5h48v300h200v-300h47q21 0 35.5 -14.5t14.5 -35.5v-50h50q21 0 35.5 -14.5t14.5 -35.5v-50z" /> +<glyph unicode="" d="M900 800h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-375v591l-300 300v84q0 10 7.5 17.5t17.5 7.5h375v-400zM1200 900h-200v200zM400 600h300v-575q0 -10 -7.5 -17.5t-17.5 -7.5h-650q-10 0 -17.5 7.5t-7.5 17.5v950q0 10 7.5 17.5t17.5 7.5h375v-400zM700 700h-200v200z " /> +<glyph unicode="" d="M484 1095h195q75 0 146 -32.5t124 -86t89.5 -122.5t48.5 -142q18 -14 35 -20q31 -10 64.5 6.5t43.5 48.5q10 34 -15 71q-19 27 -9 43q5 8 12.5 11t19 -1t23.5 -16q41 -44 39 -105q-3 -63 -46 -106.5t-104 -43.5h-62q-7 -55 -35 -117t-56 -100l-39 -234q-3 -20 -20 -34.5 t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l12 70q-49 -14 -91 -14h-195q-24 0 -65 8l-11 -64q-3 -20 -20 -34.5t-38 -14.5h-100q-21 0 -33 14.5t-9 34.5l26 157q-84 74 -128 175l-159 53q-19 7 -33 26t-14 40v50q0 21 14.5 35.5t35.5 14.5h124q11 87 56 166l-111 95 q-16 14 -12.5 23.5t24.5 9.5h203q116 101 250 101zM675 1000h-250q-10 0 -17.5 -7.5t-7.5 -17.5v-50q0 -10 7.5 -17.5t17.5 -7.5h250q10 0 17.5 7.5t7.5 17.5v50q0 10 -7.5 17.5t-17.5 7.5z" /> +<glyph unicode="" d="M641 900l423 247q19 8 42 2.5t37 -21.5l32 -38q14 -15 12.5 -36t-17.5 -34l-139 -120h-390zM50 1100h106q67 0 103 -17t66 -71l102 -212h823q21 0 35.5 -14.5t14.5 -35.5v-50q0 -21 -14 -40t-33 -26l-737 -132q-23 -4 -40 6t-26 25q-42 67 -100 67h-300q-62 0 -106 44 t-44 106v200q0 62 44 106t106 44zM173 928h-80q-19 0 -28 -14t-9 -35v-56q0 -51 42 -51h134q16 0 21.5 8t5.5 24q0 11 -16 45t-27 51q-18 28 -43 28zM550 727q-32 0 -54.5 -22.5t-22.5 -54.5t22.5 -54.5t54.5 -22.5t54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5zM130 389 l152 130q18 19 34 24t31 -3.5t24.5 -17.5t25.5 -28q28 -35 50.5 -51t48.5 -13l63 5l48 -179q13 -61 -3.5 -97.5t-67.5 -79.5l-80 -69q-47 -40 -109 -35.5t-103 51.5l-130 151q-40 47 -35.5 109.5t51.5 102.5zM380 377l-102 -88q-31 -27 2 -65l37 -43q13 -15 27.5 -19.5 t31.5 6.5l61 53q19 16 14 49q-2 20 -12 56t-17 45q-11 12 -19 14t-23 -8z" /> +<glyph unicode="" d="M625 1200h150q10 0 17.5 -7.5t7.5 -17.5v-109q79 -33 131 -87.5t53 -128.5q1 -46 -15 -84.5t-39 -61t-46 -38t-39 -21.5l-17 -6q6 0 15 -1.5t35 -9t50 -17.5t53 -30t50 -45t35.5 -64t14.5 -84q0 -59 -11.5 -105.5t-28.5 -76.5t-44 -51t-49.5 -31.5t-54.5 -16t-49.5 -6.5 t-43.5 -1v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-100v-75q0 -10 -7.5 -17.5t-17.5 -7.5h-150q-10 0 -17.5 7.5t-7.5 17.5v75h-175q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5h75v600h-75q-10 0 -17.5 7.5t-7.5 17.5v150 q0 10 7.5 17.5t17.5 7.5h175v75q0 10 7.5 17.5t17.5 7.5h150q10 0 17.5 -7.5t7.5 -17.5v-75h100v75q0 10 7.5 17.5t17.5 7.5zM400 900v-200h263q28 0 48.5 10.5t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-263zM400 500v-200h363q28 0 48.5 10.5 t30 25t15 29t5.5 25.5l1 10q0 4 -0.5 11t-6 24t-15 30t-30 24t-48.5 11h-363z" /> +<glyph unicode="" d="M212 1198h780q86 0 147 -61t61 -147v-416q0 -51 -18 -142.5t-36 -157.5l-18 -66q-29 -87 -93.5 -146.5t-146.5 -59.5h-572q-82 0 -147 59t-93 147q-8 28 -20 73t-32 143.5t-20 149.5v416q0 86 61 147t147 61zM600 1045q-70 0 -132.5 -11.5t-105.5 -30.5t-78.5 -41.5 t-57 -45t-36 -41t-20.5 -30.5l-6 -12l156 -243h560l156 243q-2 5 -6 12.5t-20 29.5t-36.5 42t-57 44.5t-79 42t-105 29.5t-132.5 12zM762 703h-157l195 261z" /> +<glyph unicode="" d="M475 1300h150q103 0 189 -86t86 -189v-500q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" /> +<glyph unicode="" d="M475 1300h96q0 -150 89.5 -239.5t239.5 -89.5v-446q0 -41 -42 -83t-83 -42h-450q-41 0 -83 42t-42 83v500q0 103 86 189t189 86zM700 300v-225q0 -21 -27 -48t-48 -27h-150q-21 0 -48 27t-27 48v225h300z" /> +<glyph unicode="" d="M1294 767l-638 -283l-378 170l-78 -60v-224l100 -150v-199l-150 148l-150 -149v200l100 150v250q0 4 -0.5 10.5t0 9.5t1 8t3 8t6.5 6l47 40l-147 65l642 283zM1000 380l-350 -166l-350 166v147l350 -165l350 165v-147z" /> +<glyph unicode="" d="M250 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM650 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM1050 800q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" /> +<glyph unicode="" d="M550 1100q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 700q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44zM550 300q62 0 106 -44t44 -106t-44 -106t-106 -44t-106 44t-44 106t44 106t106 44z" /> +<glyph unicode="" d="M125 1100h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5zM125 700h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5 t17.5 7.5zM125 300h950q10 0 17.5 -7.5t7.5 -17.5v-150q0 -10 -7.5 -17.5t-17.5 -7.5h-950q-10 0 -17.5 7.5t-7.5 17.5v150q0 10 7.5 17.5t17.5 7.5z" /> +<glyph unicode="" d="M350 1200h500q162 0 256 -93.5t94 -256.5v-500q0 -165 -93.5 -257.5t-256.5 -92.5h-500q-165 0 -257.5 92.5t-92.5 257.5v500q0 165 92.5 257.5t257.5 92.5zM900 1000h-600q-41 0 -70.5 -29.5t-29.5 -70.5v-600q0 -41 29.5 -70.5t70.5 -29.5h600q41 0 70.5 29.5 t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5zM350 900h500q21 0 35.5 -14.5t14.5 -35.5v-300q0 -21 -14.5 -35.5t-35.5 -14.5h-500q-21 0 -35.5 14.5t-14.5 35.5v300q0 21 14.5 35.5t35.5 14.5zM400 800v-200h400v200h-400z" /> +<glyph unicode="" d="M150 1100h1000q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5t-35.5 -14.5h-50v-200h50q21 0 35.5 -14.5t14.5 -35.5t-14.5 -35.5 t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5h50v200h-50q-21 0 -35.5 14.5t-14.5 35.5t14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M650 1187q87 -67 118.5 -156t0 -178t-118.5 -155q-87 66 -118.5 155t0 178t118.5 156zM300 800q124 0 212 -88t88 -212q-124 0 -212 88t-88 212zM1000 800q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM300 500q124 0 212 -88t88 -212q-124 0 -212 88t-88 212z M1000 500q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM700 199v-144q0 -21 -14.5 -35.5t-35.5 -14.5t-35.5 14.5t-14.5 35.5v142q40 -4 43 -4q17 0 57 6z" /> +<glyph unicode="" d="M745 878l69 19q25 6 45 -12l298 -295q11 -11 15 -26.5t-2 -30.5q-5 -14 -18 -23.5t-28 -9.5h-8q1 0 1 -13q0 -29 -2 -56t-8.5 -62t-20 -63t-33 -53t-51 -39t-72.5 -14h-146q-184 0 -184 288q0 24 10 47q-20 4 -62 4t-63 -4q11 -24 11 -47q0 -288 -184 -288h-142 q-48 0 -84.5 21t-56 51t-32 71.5t-16 75t-3.5 68.5q0 13 2 13h-7q-15 0 -27.5 9.5t-18.5 23.5q-6 15 -2 30.5t15 25.5l298 296q20 18 46 11l76 -19q20 -5 30.5 -22.5t5.5 -37.5t-22.5 -31t-37.5 -5l-51 12l-182 -193h891l-182 193l-44 -12q-20 -5 -37.5 6t-22.5 31t6 37.5 t31 22.5z" /> +<glyph unicode="" d="M1200 900h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-200v-850q0 -22 25 -34.5t50 -13.5l25 -2v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v850h-200q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h1000v-300zM500 450h-25q0 15 -4 24.5t-9 14.5t-17 7.5t-20 3t-25 0.5h-100v-425q0 -11 12.5 -17.5t25.5 -7.5h12v-50h-200v50q50 0 50 25v425h-100q-17 0 -25 -0.5t-20 -3t-17 -7.5t-9 -14.5t-4 -24.5h-25v150h500v-150z" /> +<glyph unicode="" d="M1000 300v50q-25 0 -55 32q-14 14 -25 31t-16 27l-4 11l-289 747h-69l-300 -754q-18 -35 -39 -56q-9 -9 -24.5 -18.5t-26.5 -14.5l-11 -5v-50h273v50q-49 0 -78.5 21.5t-11.5 67.5l69 176h293l61 -166q13 -34 -3.5 -66.5t-55.5 -32.5v-50h312zM412 691l134 342l121 -342 h-255zM1100 150v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5z" /> +<glyph unicode="" d="M50 1200h1100q21 0 35.5 -14.5t14.5 -35.5v-1100q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-21 0 -35.5 14.5t-14.5 35.5v1100q0 21 14.5 35.5t35.5 14.5zM611 1118h-70q-13 0 -18 -12l-299 -753q-17 -32 -35 -51q-18 -18 -56 -34q-12 -5 -12 -18v-50q0 -8 5.5 -14t14.5 -6 h273q8 0 14 6t6 14v50q0 8 -6 14t-14 6q-55 0 -71 23q-10 14 0 39l63 163h266l57 -153q11 -31 -6 -55q-12 -17 -36 -17q-8 0 -14 -6t-6 -14v-50q0 -8 6 -14t14 -6h313q8 0 14 6t6 14v50q0 7 -5.5 13t-13.5 7q-17 0 -42 25q-25 27 -40 63h-1l-288 748q-5 12 -19 12zM639 611 h-197l103 264z" /> +<glyph unicode="" d="M1200 1100h-1200v100h1200v-100zM50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 1000h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM700 900v-300h300v300h-300z" /> +<glyph unicode="" d="M50 1200h400q21 0 35.5 -14.5t14.5 -35.5v-900q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v900q0 21 14.5 35.5t35.5 14.5zM650 700h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400 q0 21 14.5 35.5t35.5 14.5zM700 600v-300h300v300h-300zM1200 0h-1200v100h1200v-100z" /> +<glyph unicode="" d="M50 1000h400q21 0 35.5 -14.5t14.5 -35.5v-350h100v150q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h100v-100h-100v-150q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-100v-350q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5v800q0 21 14.5 35.5t35.5 14.5zM700 700v-300h300v300h-300z" /> +<glyph unicode="" d="M100 0h-100v1200h100v-1200zM250 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM300 1000v-300h300v300h-300zM250 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M600 1100h150q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-100h450q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h350v100h-150q-21 0 -35.5 14.5 t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5h150v100h100v-100zM400 1000v-300h300v300h-300z" /> +<glyph unicode="" d="M1200 0h-100v1200h100v-1200zM550 1100h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM600 1000v-300h300v300h-300zM50 500h900q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-900q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5z" /> +<glyph unicode="" d="M865 565l-494 -494q-23 -23 -41 -23q-14 0 -22 13.5t-8 38.5v1000q0 25 8 38.5t22 13.5q18 0 41 -23l494 -494q14 -14 14 -35t-14 -35z" /> +<glyph unicode="" d="M335 635l494 494q29 29 50 20.5t21 -49.5v-1000q0 -41 -21 -49.5t-50 20.5l-494 494q-14 14 -14 35t14 35z" /> +<glyph unicode="" d="M100 900h1000q41 0 49.5 -21t-20.5 -50l-494 -494q-14 -14 -35 -14t-35 14l-494 494q-29 29 -20.5 50t49.5 21z" /> +<glyph unicode="" d="M635 865l494 -494q29 -29 20.5 -50t-49.5 -21h-1000q-41 0 -49.5 21t20.5 50l494 494q14 14 35 14t35 -14z" /> +<glyph unicode="" d="M700 741v-182l-692 -323v221l413 193l-413 193v221zM1200 0h-800v200h800v-200z" /> +<glyph unicode="" d="M1200 900h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300zM0 700h50q0 21 4 37t9.5 26.5t18 17.5t22 11t28.5 5.5t31 2t37 0.5h100v-550q0 -22 -25 -34.5t-50 -13.5l-25 -2v-100h400v100q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v550h100q25 0 37 -0.5t31 -2 t28.5 -5.5t22 -11t18 -17.5t9.5 -26.5t4 -37h50v300h-800v-300z" /> +<glyph unicode="" d="M800 700h-50q0 21 -4 37t-9.5 26.5t-18 17.5t-22 11t-28.5 5.5t-31 2t-37 0.5h-100v-550q0 -22 25 -34.5t50 -14.5l25 -1v-100h-400v100q4 0 11 0.5t24 3t30 7t24 15t11 24.5v550h-100q-25 0 -37 -0.5t-31 -2t-28.5 -5.5t-22 -11t-18 -17.5t-9.5 -26.5t-4 -37h-50v300 h800v-300zM1100 200h-200v-100h200v-100h-300v300h200v100h-200v100h300v-300z" /> +<glyph unicode="" d="M701 1098h160q16 0 21 -11t-7 -23l-464 -464l464 -464q12 -12 7 -23t-21 -11h-160q-13 0 -23 9l-471 471q-7 8 -7 18t7 18l471 471q10 9 23 9z" /> +<glyph unicode="" d="M339 1098h160q13 0 23 -9l471 -471q7 -8 7 -18t-7 -18l-471 -471q-10 -9 -23 -9h-160q-16 0 -21 11t7 23l464 464l-464 464q-12 12 -7 23t21 11z" /> +<glyph unicode="" d="M1087 882q11 -5 11 -21v-160q0 -13 -9 -23l-471 -471q-8 -7 -18 -7t-18 7l-471 471q-9 10 -9 23v160q0 16 11 21t23 -7l464 -464l464 464q12 12 23 7z" /> +<glyph unicode="" d="M618 993l471 -471q9 -10 9 -23v-160q0 -16 -11 -21t-23 7l-464 464l-464 -464q-12 -12 -23 -7t-11 21v160q0 13 9 23l471 471q8 7 18 7t18 -7z" /> +<glyph unicode="" d="M1000 1200q0 -124 -88 -212t-212 -88q0 124 88 212t212 88zM450 1000h100q21 0 40 -14t26 -33l79 -194q5 1 16 3q34 6 54 9.5t60 7t65.5 1t61 -10t56.5 -23t42.5 -42t29 -64t5 -92t-19.5 -121.5q-1 -7 -3 -19.5t-11 -50t-20.5 -73t-32.5 -81.5t-46.5 -83t-64 -70 t-82.5 -50q-13 -5 -42 -5t-65.5 2.5t-47.5 2.5q-14 0 -49.5 -3.5t-63 -3.5t-43.5 7q-57 25 -104.5 78.5t-75 111.5t-46.5 112t-26 90l-7 35q-15 63 -18 115t4.5 88.5t26 64t39.5 43.5t52 25.5t58.5 13t62.5 2t59.5 -4.5t55.5 -8l-147 192q-12 18 -5.5 30t27.5 12z" /> +<glyph unicode="🔑" d="M250 1200h600q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-150v-500l-255 -178q-19 -9 -32 -1t-13 29v650h-150q-21 0 -35.5 14.5t-14.5 35.5v400q0 21 14.5 35.5t35.5 14.5zM400 1100v-100h300v100h-300z" /> +<glyph unicode="🚪" d="M250 1200h750q39 0 69.5 -40.5t30.5 -84.5v-933l-700 -117v950l600 125h-700v-1000h-100v1025q0 23 15.5 49t34.5 26zM500 525v-100l100 20v100z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf Binary files differnew file mode 100644 index 000000000..1413fc609 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.woff b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.woff Binary files differnew file mode 100644 index 000000000..9e612858f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.woff diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 Binary files differnew file mode 100644 index 000000000..64539b54c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/.jshintrc b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/.jshintrc new file mode 100644 index 000000000..0ea0495e9 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/.jshintrc @@ -0,0 +1,7 @@ +{ + "extends" : "../js/.jshintrc", + "asi" : false, + "browser" : false, + "es3" : false, + "node" : true +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-commonjs-generator.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-commonjs-generator.js new file mode 100644 index 000000000..b0642cd8f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-commonjs-generator.js @@ -0,0 +1,23 @@ +'use strict'; +var fs = require('fs'); +var path = require('path'); + +var COMMONJS_BANNER = '// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.\n'; + +module.exports = function generateCommonJSModule(grunt, srcFiles, destFilepath) { + var destDir = path.dirname(destFilepath); + + function srcPathToDestRequire(srcFilepath) { + var requirePath = path.relative(destDir, srcFilepath).replace(/\\/g, '/'); + return 'require(\'' + requirePath + '\')'; + } + + var moduleOutputJs = COMMONJS_BANNER + srcFiles.map(srcPathToDestRequire).join('\n'); + try { + fs.writeFileSync(destFilepath, moduleOutputJs); + } + catch (err) { + grunt.fail.warn(err); + } + grunt.log.writeln('File ' + destFilepath.cyan + ' created.'); +}; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-glyphicons-data-generator.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-glyphicons-data-generator.js new file mode 100644 index 000000000..339fd0ffe --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-glyphicons-data-generator.js @@ -0,0 +1,41 @@ +/*! + * Bootstrap Grunt task for Glyphicons data generation + * http://getbootstrap.com + * Copyright 2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +'use strict'; +var fs = require('fs'); + +module.exports = function generateGlyphiconsData(grunt) { + // Pass encoding, utf8, so `readFileSync` will return a string instead of a + // buffer + var glyphiconsFile = fs.readFileSync('less/glyphicons.less', 'utf8'); + var glyphiconsLines = glyphiconsFile.split('\n'); + + // Use any line that starts with ".glyphicon-" and capture the class name + var iconClassName = /^\.(glyphicon-[a-zA-Z0-9-]+)/; + var glyphiconsData = '# This file is generated via Grunt task. **Do not edit directly.**\n' + + '# See the \'build-glyphicons-data\' task in Gruntfile.js.\n\n'; + var glyphiconsYml = 'docs/_data/glyphicons.yml'; + for (var i = 0, len = glyphiconsLines.length; i < len; i++) { + var match = glyphiconsLines[i].match(iconClassName); + + if (match !== null) { + glyphiconsData += '- ' + match[1] + '\n'; + } + } + + // Create the `_data` directory if it doesn't already exist + if (!fs.existsSync('docs/_data')) { + fs.mkdirSync('docs/_data'); + } + + try { + fs.writeFileSync(glyphiconsYml, glyphiconsData); + } + catch (err) { + grunt.fail.warn(err); + } + grunt.log.writeln('File ' + glyphiconsYml.cyan + ' created.'); +}; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-lessdoc-parser.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-lessdoc-parser.js new file mode 100644 index 000000000..63ee65a0e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-lessdoc-parser.js @@ -0,0 +1,238 @@ +/*! + * Bootstrap Grunt task for parsing Less docstrings + * http://getbootstrap.com + * Copyright 2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +'use strict'; + +var Remarkable = require('remarkable'); + +function markdown2html(markdownString) { + var md = new Remarkable(); + + // the slice removes the <p>...</p> wrapper output by Markdown processor + return md.render(markdownString.trim()).slice(3, -5); +} + + +/* +Mini-language: + //== This is a normal heading, which starts a section. Sections group variables together. + //## Optional description for the heading + + //=== This is a subheading. + + //** Optional description for the following variable. You **can** use Markdown in descriptions to discuss `<html>` stuff. + @foo: #fff; + + //-- This is a heading for a section whose variables shouldn't be customizable + + All other lines are ignored completely. +*/ + + +var CUSTOMIZABLE_HEADING = /^[/]{2}={2}(.*)$/; +var UNCUSTOMIZABLE_HEADING = /^[/]{2}-{2}(.*)$/; +var SUBSECTION_HEADING = /^[/]{2}={3}(.*)$/; +var SECTION_DOCSTRING = /^[/]{2}#{2}(.+)$/; +var VAR_ASSIGNMENT = /^(@[a-zA-Z0-9_-]+):[ ]*([^ ;][^;]*);[ ]*$/; +var VAR_DOCSTRING = /^[/]{2}[*]{2}(.+)$/; + +function Section(heading, customizable) { + this.heading = heading.trim(); + this.id = this.heading.replace(/\s+/g, '-').toLowerCase(); + this.customizable = customizable; + this.docstring = null; + this.subsections = []; +} + +Section.prototype.addSubSection = function (subsection) { + this.subsections.push(subsection); +}; + +function SubSection(heading) { + this.heading = heading.trim(); + this.id = this.heading.replace(/\s+/g, '-').toLowerCase(); + this.variables = []; +} + +SubSection.prototype.addVar = function (variable) { + this.variables.push(variable); +}; + +function VarDocstring(markdownString) { + this.html = markdown2html(markdownString); +} + +function SectionDocstring(markdownString) { + this.html = markdown2html(markdownString); +} + +function Variable(name, defaultValue) { + this.name = name; + this.defaultValue = defaultValue; + this.docstring = null; +} + +function Tokenizer(fileContent) { + this._lines = fileContent.split('\n'); + this._next = undefined; +} + +Tokenizer.prototype.unshift = function (token) { + if (this._next !== undefined) { + throw new Error('Attempted to unshift twice!'); + } + this._next = token; +}; + +Tokenizer.prototype._shift = function () { + // returning null signals EOF + // returning undefined means the line was ignored + if (this._next !== undefined) { + var result = this._next; + this._next = undefined; + return result; + } + if (this._lines.length <= 0) { + return null; + } + var line = this._lines.shift(); + var match = null; + match = SUBSECTION_HEADING.exec(line); + if (match !== null) { + return new SubSection(match[1]); + } + match = CUSTOMIZABLE_HEADING.exec(line); + if (match !== null) { + return new Section(match[1], true); + } + match = UNCUSTOMIZABLE_HEADING.exec(line); + if (match !== null) { + return new Section(match[1], false); + } + match = SECTION_DOCSTRING.exec(line); + if (match !== null) { + return new SectionDocstring(match[1]); + } + match = VAR_DOCSTRING.exec(line); + if (match !== null) { + return new VarDocstring(match[1]); + } + var commentStart = line.lastIndexOf('//'); + var varLine = (commentStart === -1) ? line : line.slice(0, commentStart); + match = VAR_ASSIGNMENT.exec(varLine); + if (match !== null) { + return new Variable(match[1], match[2]); + } + return undefined; +}; + +Tokenizer.prototype.shift = function () { + while (true) { + var result = this._shift(); + if (result === undefined) { + continue; + } + return result; + } +}; + +function Parser(fileContent) { + this._tokenizer = new Tokenizer(fileContent); +} + +Parser.prototype.parseFile = function () { + var sections = []; + while (true) { + var section = this.parseSection(); + if (section === null) { + if (this._tokenizer.shift() !== null) { + throw new Error('Unexpected unparsed section of file remains!'); + } + return sections; + } + sections.push(section); + } +}; + +Parser.prototype.parseSection = function () { + var section = this._tokenizer.shift(); + if (section === null) { + return null; + } + if (!(section instanceof Section)) { + throw new Error('Expected section heading; got: ' + JSON.stringify(section)); + } + var docstring = this._tokenizer.shift(); + if (docstring instanceof SectionDocstring) { + section.docstring = docstring; + } + else { + this._tokenizer.unshift(docstring); + } + this.parseSubSections(section); + + return section; +}; + +Parser.prototype.parseSubSections = function (section) { + while (true) { + var subsection = this.parseSubSection(); + if (subsection === null) { + if (section.subsections.length === 0) { + // Presume an implicit initial subsection + subsection = new SubSection(''); + this.parseVars(subsection); + } + else { + break; + } + } + section.addSubSection(subsection); + } + + if (section.subsections.length === 1 && !(section.subsections[0].heading) && section.subsections[0].variables.length === 0) { + // Ignore lone empty implicit subsection + section.subsections = []; + } +}; + +Parser.prototype.parseSubSection = function () { + var subsection = this._tokenizer.shift(); + if (subsection instanceof SubSection) { + this.parseVars(subsection); + return subsection; + } + this._tokenizer.unshift(subsection); + return null; +}; + +Parser.prototype.parseVars = function (subsection) { + while (true) { + var variable = this.parseVar(); + if (variable === null) { + return; + } + subsection.addVar(variable); + } +}; + +Parser.prototype.parseVar = function () { + var docstring = this._tokenizer.shift(); + if (!(docstring instanceof VarDocstring)) { + this._tokenizer.unshift(docstring); + docstring = null; + } + var variable = this._tokenizer.shift(); + if (variable instanceof Variable) { + variable.docstring = docstring; + return variable; + } + this._tokenizer.unshift(variable); + return null; +}; + + +module.exports = Parser; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-raw-files-generator.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-raw-files-generator.js new file mode 100644 index 000000000..ec8c5314e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/bs-raw-files-generator.js @@ -0,0 +1,46 @@ +/*! + * Bootstrap Grunt task for generating raw-files.min.js for the Customizer + * http://getbootstrap.com + * Copyright 2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/* global btoa: true */ + +'use strict'; +var fs = require('fs'); +var btoa = require('btoa'); +var glob = require('glob'); + +function getFiles(type) { + var files = {}; + var recursive = (type === 'less'); + var globExpr = (recursive ? '/**/*' : '/*'); + glob.sync(type + globExpr) + .filter(function (path) { + return type === 'fonts' ? true : new RegExp('\\.' + type + '$').test(path); + }) + .forEach(function (fullPath) { + var relativePath = fullPath.replace(/^[^/]+\//, ''); + files[relativePath] = (type === 'fonts' ? btoa(fs.readFileSync(fullPath)) : fs.readFileSync(fullPath, 'utf8')); + }); + return 'var __' + type + ' = ' + JSON.stringify(files) + '\n'; +} + +module.exports = function generateRawFilesJs(grunt, banner) { + if (!banner) { + banner = ''; + } + var dirs = ['js', 'less', 'fonts']; + var files = banner + dirs.map(getFiles).reduce(function (combined, file) { + return combined + file; + }, ''); + var rawFilesJs = 'docs/assets/js/raw-files.min.js'; + try { + fs.writeFileSync(rawFilesJs, files); + } + catch (err) { + grunt.fail.warn(err); + } + grunt.log.writeln('File ' + rawFilesJs.cyan + ' created.'); +}; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/configBridge.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/configBridge.json new file mode 100644 index 000000000..78e7aae57 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/configBridge.json @@ -0,0 +1,45 @@ +{ + "paths": { + "customizerJs": [ + "../assets/js/vendor/autoprefixer.js", + "../assets/js/vendor/less.min.js", + "../assets/js/vendor/jszip.min.js", + "../assets/js/vendor/uglify.min.js", + "../assets/js/vendor/Blob.js", + "../assets/js/vendor/FileSaver.js", + "../assets/js/raw-files.min.js", + "../assets/js/src/customizer.js" + ], + "docsJs": [ + "../assets/js/vendor/holder.js", + "../assets/js/vendor/ZeroClipboard.min.js", + "../assets/js/src/application.js" + ] + }, + "config": { + "autoprefixerBrowsers": [ + "Android 2.3", + "Android >= 4", + "Chrome >= 20", + "Firefox >= 24", + "Explorer >= 8", + "iOS >= 6", + "Opera >= 12", + "Safari >= 6" + ], + "jqueryCheck": [ + "if (typeof jQuery === 'undefined') {", + " throw new Error('Bootstrap\\'s JavaScript requires jQuery')", + "}\n" + ], + "jqueryVersionCheck": [ + "+function ($) {", + " 'use strict';", + " var version = $.fn.jquery.split(' ')[0].split('.')", + " if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) {", + " throw new Error('Bootstrap\\'s JavaScript requires jQuery version 1.9.1 or higher')", + " }", + "}(jQuery);\n\n" + ] + } +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/sauce_browsers.yml b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/sauce_browsers.yml new file mode 100644 index 000000000..04e90928a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/grunt/sauce_browsers.yml @@ -0,0 +1,82 @@ +[ + # Docs: https://saucelabs.com/docs/platforms/webdriver + + { + browserName: "safari", + platform: "OS X 10.10" + }, + { + browserName: "chrome", + platform: "OS X 10.10" + }, + { + browserName: "firefox", + platform: "OS X 10.10" + }, + + # Mac Opera not currently supported by Sauce Labs + + { + browserName: "internet explorer", + version: "11", + platform: "Windows 8.1" + }, + { + browserName: "internet explorer", + version: "10", + platform: "Windows 8" + }, + { + browserName: "internet explorer", + version: "9", + platform: "Windows 7" + }, + { + browserName: "internet explorer", + version: "8", + platform: "Windows 7" + }, + + # { # Unofficial + # browserName: "internet explorer", + # version: "7", + # platform: "Windows XP" + # }, + + { + browserName: "chrome", + platform: "Windows 8.1" + }, + { + browserName: "firefox", + platform: "Windows 8.1" + }, + + # Win Opera 15+ not currently supported by Sauce Labs + + { + browserName: "iphone", + platform: "OS X 10.9", + version: "8.1" + }, + + # iOS Chrome not currently supported by Sauce Labs + + # Linux (unofficial) + { + browserName: "chrome", + platform: "Linux" + }, + { + browserName: "firefox", + platform: "Linux" + } + + # Android Chrome not currently supported by Sauce Labs + + # { # Android Browser (super-unofficial) + # browserName: "android", + # version: "4.0", + # platform: "Linux" + # } +] diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/.jscsrc b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/.jscsrc new file mode 100644 index 000000000..982676e15 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/.jscsrc @@ -0,0 +1,36 @@ +{ + "disallowEmptyBlocks": true, + "disallowKeywords": ["with"], + "disallowMixedSpacesAndTabs": true, + "disallowMultipleLineStrings": true, + "disallowMultipleVarDecl": true, + "disallowQuotedKeysInObjects": "allButReserved", + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforeBinaryOperators": [","], + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "disallowTrailingComma": true, + "disallowTrailingWhitespace": true, + "requireCamelCaseOrUpperCaseIdentifiers": true, + "requireCapitalizedConstructors": true, + "requireCommaBeforeLineBreak": true, + "requireDotNotation": true, + "requireLineFeedAtFileEnd": true, + "requireSpaceAfterBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", "<", ">=", "<="], + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], + "requireSpaceAfterLineComment": true, + "requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", "<", ">=", "<="], + "requireSpaceBetweenArguments": true, + "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningCurlyBrace": true, "beforeOpeningRoundBrace": true }, + "requireSpacesInConditionalExpression": true, + "requireSpacesInForStatement": true, + "requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true }, + "requireSpacesInFunctionExpression": { "beforeOpeningCurlyBrace": true }, + "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true }, + "requireSpacesInsideObjectBrackets": "allButNested", + "validateIndentation": 2, + "validateLineBreaks": "LF", + "validateQuoteMarks": "'" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/.jshintrc b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/.jshintrc new file mode 100644 index 000000000..a59e1d00a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/.jshintrc @@ -0,0 +1,15 @@ +{ + "asi" : true, + "browser" : true, + "eqeqeq" : false, + "eqnull" : true, + "es3" : true, + "expr" : true, + "jquery" : true, + "latedef" : true, + "laxbreak" : true, + "nonbsp" : true, + "strict" : true, + "undef" : true, + "unused" : true +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/affix.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/affix.js new file mode 100644 index 000000000..aaebc8c0f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/affix.js @@ -0,0 +1,162 @@ +/* ======================================================================== + * Bootstrap: affix.js v3.3.2 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = + this.unpin = + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.3.2' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && scrollTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var height = this.$element.height() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + var scrollHeight = $('body').height() + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) + + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - height - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/alert.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/alert.js new file mode 100644 index 000000000..e8212caf0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/alert.js @@ -0,0 +1,94 @@ +/* ======================================================================== + * Bootstrap: alert.js v3.3.2 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.3.2' + + Alert.TRANSITION_DURATION = 150 + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.closest('.alert') + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/button.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/button.js new file mode 100644 index 000000000..4d5690173 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/button.js @@ -0,0 +1,116 @@ +/* ======================================================================== + * Bootstrap: button.js v3.3.2 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.3.2' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state = state + 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked') && this.$element.hasClass('active')) changed = false + else $parent.find('.active').removeClass('active') + } + if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + } + + if (changed) this.$element.toggleClass('active') + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/carousel.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/carousel.js new file mode 100644 index 000000000..450e1812e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/carousel.js @@ -0,0 +1,237 @@ +/* ======================================================================== + * Bootstrap: carousel.js v3.3.2 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = + this.sliding = + this.interval = + this.$active = + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.3.2' + + Carousel.TRANSITION_DURATION = 600 + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + var clickHandler = function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/collapse.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/collapse.js new file mode 100644 index 000000000..2bc30e7ba --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/collapse.js @@ -0,0 +1,211 @@ +/* ======================================================================== + * Bootstrap: collapse.js v3.3.2 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') + this.transitioning = null + + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.3.2' + + Collapse.TRANSITION_DURATION = 350 + + Collapse.DEFAULTS = { + toggle: true, + trigger: '[data-toggle="collapse"]' + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && option == 'show') options.toggle = false + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var $this = $(this) + + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) + + Plugin.call($target, option) + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/dropdown.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/dropdown.js new file mode 100644 index 000000000..200e1c67b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/dropdown.js @@ -0,0 +1,161 @@ +/* ======================================================================== + * Bootstrap: dropdown.js v3.3.2 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.3.2' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) + } + + var relatedTarget = { relatedTarget: this } + $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this + .trigger('focus') + .attr('aria-expanded', 'true') + + $parent + .toggleClass('open') + .trigger('shown.bs.dropdown', relatedTarget) + } + + return false + } + + Dropdown.prototype.keydown = function (e) { + if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return + + var $this = $(this) + + e.preventDefault() + e.stopPropagation() + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + if ((!isActive && e.which != 27) || (isActive && e.which == 27)) { + if (e.which == 27) $parent.find(toggle).trigger('focus') + return $this.trigger('click') + } + + var desc = ' li:not(.divider):visible a' + var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc) + + if (!$items.length) return + + var index = $items.index(e.target) + + if (e.which == 38 && index > 0) index-- // up + if (e.which == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items.eq(index).trigger('focus') + } + + function clearMenus(e) { + if (e && e.which === 3) return + $(backdrop).remove() + $(toggle).each(function () { + var $this = $(this) + var $parent = getParent($this) + var relatedTarget = { relatedTarget: this } + + if (!$parent.hasClass('open')) return + + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') + $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) + }) + } + + function getParent($this) { + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = selector && $(selector) + + return $parent && $parent.length ? $parent : $this.parent() + } + + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.dropdown') + + if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.dropdown + + $.fn.dropdown = Plugin + $.fn.dropdown.Constructor = Dropdown + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old + return this + } + + + // APPLY TO STANDARD DROPDOWN ELEMENTS + // =================================== + + $(document) + .on('click.bs.dropdown.data-api', clearMenus) + .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) + .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) + .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/modal.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/modal.js new file mode 100644 index 000000000..e7704b8fc --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/modal.js @@ -0,0 +1,324 @@ +/* ======================================================================== + * Bootstrap: modal.js v3.3.2 + * http://getbootstrap.com/javascript/#modals + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$backdrop = + this.isShown = null + this.scrollbarWidth = 0 + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } + + Modal.VERSION = '3.3.2' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 + + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } + + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.checkScrollbar() + this.setScrollbar() + this.$body.addClass('modal-open') + + this.escape() + this.resize() + + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } + + that.$element + .show() + .scrollTop(0) + + if (that.options.backdrop) that.adjustBackdrop() + that.adjustDialog() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element + .addClass('in') + .attr('aria-hidden', false) + + that.enforceFocus() + + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) + + transition ? + that.$element.find('.modal-dialog') // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + that.$element.trigger('focus').trigger(e) + }) + } + + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() + + e = $.Event('hide.bs.modal') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.escape() + this.resize() + + $(document).off('focusin.bs.modal') + + this.$element + .removeClass('in') + .attr('aria-hidden', true) + .off('click.dismiss.bs.modal') + + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + this.hideModal() + } + + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) + } + + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keydown.dismiss.bs.modal') + } + } + + Modal.prototype.resize = function () { + if (this.isShown) { + $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) + } else { + $(window).off('resize.bs.modal') + } + } + + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetAdjustments() + that.resetScrollbar() + that.$element.trigger('hidden.bs.modal') + }) + } + + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') + .prependTo(this.$element) + .on('click.dismiss.bs.modal', $.proxy(function (e) { + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus.call(this.$element[0]) + : this.hide.call(this) + }, this)) + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + if (!callback) return + + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callbackRemove() + + } else if (callback) { + callback() + } + } + + // these following methods are used to handle overflowing modals + + Modal.prototype.handleUpdate = function () { + if (this.options.backdrop) this.adjustBackdrop() + this.adjustDialog() + } + + Modal.prototype.adjustBackdrop = function () { + this.$backdrop + .css('height', 0) + .css('height', this.$element[0].scrollHeight) + } + + Modal.prototype.adjustDialog = function () { + var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight + + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', + paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' + }) + } + + Modal.prototype.resetAdjustments = function () { + this.$element.css({ + paddingLeft: '', + paddingRight: '' + }) + } + + Modal.prototype.checkScrollbar = function () { + this.bodyIsOverflowing = document.body.scrollHeight > document.documentElement.clientHeight + this.scrollbarWidth = this.measureScrollbar() + } + + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) + } + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', '') + } + + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth + } + + + // MODAL PLUGIN DEFINITION + // ======================= + + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + + var old = $.fn.modal + + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal + + + // MODAL NO CONFLICT + // ================= + + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this + } + + + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/popover.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/popover.js new file mode 100644 index 000000000..be65b11a1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/popover.js @@ -0,0 +1,113 @@ +/* ======================================================================== + * Bootstrap: popover.js v3.3.2 + * http://getbootstrap.com/javascript/#popovers + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // POPOVER PUBLIC CLASS DEFINITION + // =============================== + + var Popover = function (element, options) { + this.init('popover', element, options) + } + + if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') + + Popover.VERSION = '3.3.2' + + Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { + placement: 'right', + trigger: 'click', + content: '', + template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' + }) + + + // NOTE: POPOVER EXTENDS tooltip.js + // ================================ + + Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) + + Popover.prototype.constructor = Popover + + Popover.prototype.getDefaults = function () { + return Popover.DEFAULTS + } + + Popover.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + var content = this.getContent() + + $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) + $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events + this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' + ](content) + + $tip.removeClass('fade top bottom left right in') + + // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do + // this manually by checking the contents. + if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() + } + + Popover.prototype.hasContent = function () { + return this.getTitle() || this.getContent() + } + + Popover.prototype.getContent = function () { + var $e = this.$element + var o = this.options + + return $e.attr('data-content') + || (typeof o.content == 'function' ? + o.content.call($e[0]) : + o.content) + } + + Popover.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.arrow')) + } + + Popover.prototype.tip = function () { + if (!this.$tip) this.$tip = $(this.options.template) + return this.$tip + } + + + // POPOVER PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.popover') + var options = typeof option == 'object' && option + + if (!data && option == 'destroy') return + if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.popover + + $.fn.popover = Plugin + $.fn.popover.Constructor = Popover + + + // POPOVER NO CONFLICT + // =================== + + $.fn.popover.noConflict = function () { + $.fn.popover = old + return this + } + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/scrollspy.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/scrollspy.js new file mode 100644 index 000000000..0987177fd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/scrollspy.js @@ -0,0 +1,175 @@ +/* ======================================================================== + * Bootstrap: scrollspy.js v3.3.2 + * http://getbootstrap.com/javascript/#scrollspy + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // SCROLLSPY CLASS DEFINITION + // ========================== + + function ScrollSpy(element, options) { + var process = $.proxy(this.process, this) + + this.$body = $('body') + this.$scrollElement = $(element).is('body') ? $(window) : $(element) + this.options = $.extend({}, ScrollSpy.DEFAULTS, options) + this.selector = (this.options.target || '') + ' .nav li > a' + this.offsets = [] + this.targets = [] + this.activeTarget = null + this.scrollHeight = 0 + + this.$scrollElement.on('scroll.bs.scrollspy', process) + this.refresh() + this.process() + } + + ScrollSpy.VERSION = '3.3.2' + + ScrollSpy.DEFAULTS = { + offset: 10 + } + + ScrollSpy.prototype.getScrollHeight = function () { + return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) + } + + ScrollSpy.prototype.refresh = function () { + var offsetMethod = 'offset' + var offsetBase = 0 + + if (!$.isWindow(this.$scrollElement[0])) { + offsetMethod = 'position' + offsetBase = this.$scrollElement.scrollTop() + } + + this.offsets = [] + this.targets = [] + this.scrollHeight = this.getScrollHeight() + + var self = this + + this.$body + .find(this.selector) + .map(function () { + var $el = $(this) + var href = $el.data('target') || $el.attr('href') + var $href = /^#./.test(href) && $(href) + + return ($href + && $href.length + && $href.is(':visible') + && [[$href[offsetMethod]().top + offsetBase, href]]) || null + }) + .sort(function (a, b) { return a[0] - b[0] }) + .each(function () { + self.offsets.push(this[0]) + self.targets.push(this[1]) + }) + } + + ScrollSpy.prototype.process = function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + var scrollHeight = this.getScrollHeight() + var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() + var offsets = this.offsets + var targets = this.targets + var activeTarget = this.activeTarget + var i + + if (this.scrollHeight != scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) + } + + if (activeTarget && scrollTop < offsets[0]) { + this.activeTarget = null + return this.clear() + } + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (!offsets[i + 1] || scrollTop <= offsets[i + 1]) + && this.activate(targets[i]) + } + } + + ScrollSpy.prototype.activate = function (target) { + this.activeTarget = target + + this.clear() + + var selector = this.selector + + '[data-target="' + target + '"],' + + this.selector + '[href="' + target + '"]' + + var active = $(selector) + .parents('li') + .addClass('active') + + if (active.parent('.dropdown-menu').length) { + active = active + .closest('li.dropdown') + .addClass('active') + } + + active.trigger('activate.bs.scrollspy') + } + + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + + + // SCROLLSPY PLUGIN DEFINITION + // =========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.scrollspy') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.scrollspy + + $.fn.scrollspy = Plugin + $.fn.scrollspy.Constructor = ScrollSpy + + + // SCROLLSPY NO CONFLICT + // ===================== + + $.fn.scrollspy.noConflict = function () { + $.fn.scrollspy = old + return this + } + + + // SCROLLSPY DATA-API + // ================== + + $(window).on('load.bs.scrollspy.data-api', function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + Plugin.call($spy, $spy.data()) + }) + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/tab.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/tab.js new file mode 100644 index 000000000..ddb7fc9c4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/tab.js @@ -0,0 +1,153 @@ +/* ======================================================================== + * Bootstrap: tab.js v3.3.2 + * http://getbootstrap.com/javascript/#tabs + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TAB CLASS DEFINITION + // ==================== + + var Tab = function (element) { + this.element = $(element) + } + + Tab.VERSION = '3.3.2' + + Tab.TRANSITION_DURATION = 150 + + Tab.prototype.show = function () { + var $this = this.element + var $ul = $this.closest('ul:not(.dropdown-menu)') + var selector = $this.data('target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + if ($this.parent('li').hasClass('active')) return + + var $previous = $ul.find('.active:last a') + var hideEvent = $.Event('hide.bs.tab', { + relatedTarget: $this[0] + }) + var showEvent = $.Event('show.bs.tab', { + relatedTarget: $previous[0] + }) + + $previous.trigger(hideEvent) + $this.trigger(showEvent) + + if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return + + var $target = $(selector) + + this.activate($this.closest('li'), $ul) + this.activate($target, $target.parent(), function () { + $previous.trigger({ + type: 'hidden.bs.tab', + relatedTarget: $this[0] + }) + $this.trigger({ + type: 'shown.bs.tab', + relatedTarget: $previous[0] + }) + }) + } + + Tab.prototype.activate = function (element, container, callback) { + var $active = container.find('> .active') + var transition = callback + && $.support.transition + && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length) + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', false) + + element + .addClass('active') + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if (element.parent('.dropdown-menu')) { + element + .closest('li.dropdown') + .addClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + } + + callback && callback() + } + + $active.length && transition ? + $active + .one('bsTransitionEnd', next) + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : + next() + + $active.removeClass('in') + } + + + // TAB PLUGIN DEFINITION + // ===================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tab') + + if (!data) $this.data('bs.tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tab + + $.fn.tab = Plugin + $.fn.tab.Constructor = Tab + + + // TAB NO CONFLICT + // =============== + + $.fn.tab.noConflict = function () { + $.fn.tab = old + return this + } + + + // TAB DATA-API + // ============ + + var clickHandler = function (e) { + e.preventDefault() + Plugin.call($(this), 'show') + } + + $(document) + .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) + .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/tooltip.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/tooltip.js new file mode 100644 index 000000000..1856df598 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/tooltip.js @@ -0,0 +1,472 @@ +/* ======================================================================== + * Bootstrap: tooltip.js v3.3.2 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TOOLTIP PUBLIC CLASS DEFINITION + // =============================== + + var Tooltip = function (element, options) { + this.type = + this.options = + this.enabled = + this.timeout = + this.hoverState = + this.$element = null + + this.init('tooltip', element, options) + } + + Tooltip.VERSION = '3.3.2' + + Tooltip.TRANSITION_DURATION = 150 + + Tooltip.DEFAULTS = { + animation: true, + placement: 'top', + selector: false, + template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + container: false, + viewport: { + selector: 'body', + padding: 0 + } + } + + Tooltip.prototype.init = function (type, element, options) { + this.enabled = true + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport) + + var triggers = this.options.trigger.split(' ') + + for (var i = triggers.length; i--;) { + var trigger = triggers[i] + + if (trigger == 'click') { + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) + } else if (trigger != 'manual') { + var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' + var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' + + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) + } + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + Tooltip.prototype.getDefaults = function () { + return Tooltip.DEFAULTS + } + + Tooltip.prototype.getOptions = function (options) { + options = $.extend({}, this.getDefaults(), this.$element.data(), options) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay, + hide: options.delay + } + } + + return options + } + + Tooltip.prototype.getDelegateOptions = function () { + var options = {} + var defaults = this.getDefaults() + + this._options && $.each(this._options, function (key, value) { + if (defaults[key] != value) options[key] = value + }) + + return options + } + + Tooltip.prototype.enter = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (self && self.$tip && self.$tip.is(':visible')) { + self.hoverState = 'in' + return + } + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + clearTimeout(self.timeout) + + self.hoverState = 'in' + + if (!self.options.delay || !self.options.delay.show) return self.show() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'in') self.show() + }, self.options.delay.show) + } + + Tooltip.prototype.leave = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + clearTimeout(self.timeout) + + self.hoverState = 'out' + + if (!self.options.delay || !self.options.delay.hide) return self.hide() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'out') self.hide() + }, self.options.delay.hide) + } + + Tooltip.prototype.show = function () { + var e = $.Event('show.bs.' + this.type) + + if (this.hasContent() && this.enabled) { + this.$element.trigger(e) + + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) + if (e.isDefaultPrevented() || !inDom) return + var that = this + + var $tip = this.tip() + + var tipId = this.getUID(this.type) + + this.setContent() + $tip.attr('id', tipId) + this.$element.attr('aria-describedby', tipId) + + if (this.options.animation) $tip.addClass('fade') + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + var autoToken = /\s?auto?\s?/i + var autoPlace = autoToken.test(placement) + if (autoPlace) placement = placement.replace(autoToken, '') || 'top' + + $tip + .detach() + .css({ top: 0, left: 0, display: 'block' }) + .addClass(placement) + .data('bs.' + this.type, this) + + this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) + + var pos = this.getPosition() + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (autoPlace) { + var orgPlacement = placement + var $container = this.options.container ? $(this.options.container) : this.$element.parent() + var containerDim = this.getPosition($container) + + placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' : + placement + + $tip + .removeClass(orgPlacement) + .addClass(placement) + } + + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) + + this.applyPlacement(calculatedOffset, placement) + + var complete = function () { + var prevHoverState = that.hoverState + that.$element.trigger('shown.bs.' + that.type) + that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) + } + + $.support.transition && this.$tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + } + } + + Tooltip.prototype.applyPlacement = function (offset, placement) { + var $tip = this.tip() + var width = $tip[0].offsetWidth + var height = $tip[0].offsetHeight + + // manually read margins because getBoundingClientRect includes difference + var marginTop = parseInt($tip.css('margin-top'), 10) + var marginLeft = parseInt($tip.css('margin-left'), 10) + + // we must check for NaN for ie 8/9 + if (isNaN(marginTop)) marginTop = 0 + if (isNaN(marginLeft)) marginLeft = 0 + + offset.top = offset.top + marginTop + offset.left = offset.left + marginLeft + + // $.fn.offset doesn't round pixel values + // so we use setOffset directly with our own function B-0 + $.offset.setOffset($tip[0], $.extend({ + using: function (props) { + $tip.css({ + top: Math.round(props.top), + left: Math.round(props.left) + }) + } + }, offset), 0) + + $tip.addClass('in') + + // check to see if placing tip in new offset caused the tip to resize itself + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (placement == 'top' && actualHeight != height) { + offset.top = offset.top + height - actualHeight + } + + var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) + + if (delta.left) offset.left += delta.left + else offset.top += delta.top + + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' + + $tip.offset(offset) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) + } + + Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) { + this.arrow() + .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isHorizontal ? 'top' : 'left', '') + } + + Tooltip.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + + $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) + $tip.removeClass('fade in top bottom left right') + } + + Tooltip.prototype.hide = function (callback) { + var that = this + var $tip = this.tip() + var e = $.Event('hide.bs.' + this.type) + + function complete() { + if (that.hoverState != 'in') $tip.detach() + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + callback && callback() + } + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + $tip.removeClass('in') + + $.support.transition && this.$tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + + this.hoverState = null + + return this + } + + Tooltip.prototype.fixTitle = function () { + var $e = this.$element + if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') + } + } + + Tooltip.prototype.hasContent = function () { + return this.getTitle() + } + + Tooltip.prototype.getPosition = function ($element) { + $element = $element || this.$element + + var el = $element[0] + var isBody = el.tagName == 'BODY' + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) + } + + Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { + return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : + /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + + } + + Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { + var delta = { top: 0, left: 0 } + if (!this.$viewport) return delta + + var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 + var viewportDimensions = this.getPosition(this.$viewport) + + if (/right|left/.test(placement)) { + var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll + var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight + if (topEdgeOffset < viewportDimensions.top) { // top overflow + delta.top = viewportDimensions.top - topEdgeOffset + } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow + delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset + } + } else { + var leftEdgeOffset = pos.left - viewportPadding + var rightEdgeOffset = pos.left + viewportPadding + actualWidth + if (leftEdgeOffset < viewportDimensions.left) { // left overflow + delta.left = viewportDimensions.left - leftEdgeOffset + } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow + delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset + } + } + + return delta + } + + Tooltip.prototype.getTitle = function () { + var title + var $e = this.$element + var o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + return title + } + + Tooltip.prototype.getUID = function (prefix) { + do prefix += ~~(Math.random() * 1000000) + while (document.getElementById(prefix)) + return prefix + } + + Tooltip.prototype.tip = function () { + return (this.$tip = this.$tip || $(this.options.template)) + } + + Tooltip.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) + } + + Tooltip.prototype.enable = function () { + this.enabled = true + } + + Tooltip.prototype.disable = function () { + this.enabled = false + } + + Tooltip.prototype.toggleEnabled = function () { + this.enabled = !this.enabled + } + + Tooltip.prototype.toggle = function (e) { + var self = this + if (e) { + self = $(e.currentTarget).data('bs.' + this.type) + if (!self) { + self = new this.constructor(e.currentTarget, this.getDelegateOptions()) + $(e.currentTarget).data('bs.' + this.type, self) + } + } + + self.tip().hasClass('in') ? self.leave(self) : self.enter(self) + } + + Tooltip.prototype.destroy = function () { + var that = this + clearTimeout(this.timeout) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + }) + } + + + // TOOLTIP PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + + if (!data && option == 'destroy') return + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tooltip + + $.fn.tooltip = Plugin + $.fn.tooltip.Constructor = Tooltip + + + // TOOLTIP NO CONFLICT + // =================== + + $.fn.tooltip.noConflict = function () { + $.fn.tooltip = old + return this + } + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/transition.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/transition.js new file mode 100644 index 000000000..ec9ab6824 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/js/transition.js @@ -0,0 +1,59 @@ +/* ======================================================================== + * Bootstrap: transition.js v3.3.2 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/.csscomb.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/.csscomb.json new file mode 100644 index 000000000..40695a478 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/.csscomb.json @@ -0,0 +1,304 @@ +{ + "always-semicolon": true, + "block-indent": 2, + "color-case": "lower", + "color-shorthand": true, + "element-case": "lower", + "eof-newline": true, + "leading-zero": false, + "remove-empty-rulesets": true, + "space-after-colon": 1, + "space-after-combinator": 1, + "space-before-selector-delimiter": 0, + "space-between-declarations": "\n", + "space-after-opening-brace": "\n", + "space-before-closing-brace": "\n", + "space-before-colon": 0, + "space-before-combinator": 1, + "space-before-opening-brace": 1, + "strip-spaces": true, + "unitless-zero": true, + "vendor-prefix-align": true, + "sort-order": [ + [ + "position", + "top", + "right", + "bottom", + "left", + "z-index", + "display", + "float", + "width", + "min-width", + "max-width", + "height", + "min-height", + "max-height", + "-webkit-box-sizing", + "-moz-box-sizing", + "box-sizing", + "-webkit-appearance", + "padding", + "padding-top", + "padding-right", + "padding-bottom", + "padding-left", + "margin", + "margin-top", + "margin-right", + "margin-bottom", + "margin-left", + "overflow", + "overflow-x", + "overflow-y", + "-webkit-overflow-scrolling", + "-ms-overflow-x", + "-ms-overflow-y", + "-ms-overflow-style", + "clip", + "clear", + "font", + "font-family", + "font-size", + "font-style", + "font-weight", + "font-variant", + "font-size-adjust", + "font-stretch", + "font-effect", + "font-emphasize", + "font-emphasize-position", + "font-emphasize-style", + "font-smooth", + "-webkit-hyphens", + "-moz-hyphens", + "hyphens", + "line-height", + "color", + "text-align", + "-webkit-text-align-last", + "-moz-text-align-last", + "-ms-text-align-last", + "text-align-last", + "text-emphasis", + "text-emphasis-color", + "text-emphasis-style", + "text-emphasis-position", + "text-decoration", + "text-indent", + "text-justify", + "text-outline", + "-ms-text-overflow", + "text-overflow", + "text-overflow-ellipsis", + "text-overflow-mode", + "text-shadow", + "text-transform", + "text-wrap", + "-webkit-text-size-adjust", + "-ms-text-size-adjust", + "letter-spacing", + "-ms-word-break", + "word-break", + "word-spacing", + "-ms-word-wrap", + "word-wrap", + "-moz-tab-size", + "-o-tab-size", + "tab-size", + "white-space", + "vertical-align", + "list-style", + "list-style-position", + "list-style-type", + "list-style-image", + "pointer-events", + "-ms-touch-action", + "touch-action", + "cursor", + "visibility", + "zoom", + "flex-direction", + "flex-order", + "flex-pack", + "flex-align", + "table-layout", + "empty-cells", + "caption-side", + "border-spacing", + "border-collapse", + "content", + "quotes", + "counter-reset", + "counter-increment", + "resize", + "-webkit-user-select", + "-moz-user-select", + "-ms-user-select", + "-o-user-select", + "user-select", + "nav-index", + "nav-up", + "nav-right", + "nav-down", + "nav-left", + "background", + "background-color", + "background-image", + "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", + "filter:progid:DXImageTransform.Microsoft.gradient", + "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", + "filter", + "background-repeat", + "background-attachment", + "background-position", + "background-position-x", + "background-position-y", + "-webkit-background-clip", + "-moz-background-clip", + "background-clip", + "background-origin", + "-webkit-background-size", + "-moz-background-size", + "-o-background-size", + "background-size", + "border", + "border-color", + "border-style", + "border-width", + "border-top", + "border-top-color", + "border-top-style", + "border-top-width", + "border-right", + "border-right-color", + "border-right-style", + "border-right-width", + "border-bottom", + "border-bottom-color", + "border-bottom-style", + "border-bottom-width", + "border-left", + "border-left-color", + "border-left-style", + "border-left-width", + "border-radius", + "border-top-left-radius", + "border-top-right-radius", + "border-bottom-right-radius", + "border-bottom-left-radius", + "-webkit-border-image", + "-moz-border-image", + "-o-border-image", + "border-image", + "-webkit-border-image-source", + "-moz-border-image-source", + "-o-border-image-source", + "border-image-source", + "-webkit-border-image-slice", + "-moz-border-image-slice", + "-o-border-image-slice", + "border-image-slice", + "-webkit-border-image-width", + "-moz-border-image-width", + "-o-border-image-width", + "border-image-width", + "-webkit-border-image-outset", + "-moz-border-image-outset", + "-o-border-image-outset", + "border-image-outset", + "-webkit-border-image-repeat", + "-moz-border-image-repeat", + "-o-border-image-repeat", + "border-image-repeat", + "outline", + "outline-width", + "outline-style", + "outline-color", + "outline-offset", + "-webkit-box-shadow", + "-moz-box-shadow", + "box-shadow", + "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", + "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", + "opacity", + "-ms-interpolation-mode", + "-webkit-transition", + "-moz-transition", + "-ms-transition", + "-o-transition", + "transition", + "-webkit-transition-delay", + "-moz-transition-delay", + "-ms-transition-delay", + "-o-transition-delay", + "transition-delay", + "-webkit-transition-timing-function", + "-moz-transition-timing-function", + "-ms-transition-timing-function", + "-o-transition-timing-function", + "transition-timing-function", + "-webkit-transition-duration", + "-moz-transition-duration", + "-ms-transition-duration", + "-o-transition-duration", + "transition-duration", + "-webkit-transition-property", + "-moz-transition-property", + "-ms-transition-property", + "-o-transition-property", + "transition-property", + "-webkit-transform", + "-moz-transform", + "-ms-transform", + "-o-transform", + "transform", + "-webkit-transform-origin", + "-moz-transform-origin", + "-ms-transform-origin", + "-o-transform-origin", + "transform-origin", + "-webkit-animation", + "-moz-animation", + "-ms-animation", + "-o-animation", + "animation", + "-webkit-animation-name", + "-moz-animation-name", + "-ms-animation-name", + "-o-animation-name", + "animation-name", + "-webkit-animation-duration", + "-moz-animation-duration", + "-ms-animation-duration", + "-o-animation-duration", + "animation-duration", + "-webkit-animation-play-state", + "-moz-animation-play-state", + "-ms-animation-play-state", + "-o-animation-play-state", + "animation-play-state", + "-webkit-animation-timing-function", + "-moz-animation-timing-function", + "-ms-animation-timing-function", + "-o-animation-timing-function", + "animation-timing-function", + "-webkit-animation-delay", + "-moz-animation-delay", + "-ms-animation-delay", + "-o-animation-delay", + "animation-delay", + "-webkit-animation-iteration-count", + "-moz-animation-iteration-count", + "-ms-animation-iteration-count", + "-o-animation-iteration-count", + "animation-iteration-count", + "-webkit-animation-direction", + "-moz-animation-direction", + "-ms-animation-direction", + "-o-animation-direction", + "animation-direction" + ] + ] +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/.csslintrc b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/.csslintrc new file mode 100644 index 000000000..005b86236 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/.csslintrc @@ -0,0 +1,19 @@ +{ + "adjoining-classes": false, + "box-sizing": false, + "box-model": false, + "compatible-vendor-prefixes": false, + "floats": false, + "font-sizes": false, + "gradients": false, + "important": false, + "known-properties": false, + "outline-none": false, + "qualified-headings": false, + "regex-selectors": false, + "shorthand": false, + "text-indent": false, + "unique-headings": false, + "universal-selector": false, + "unqualified-attributes": false +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/alerts.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/alerts.less new file mode 100644 index 000000000..5618bfcec --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/alerts.less @@ -0,0 +1,73 @@ +// +// Alerts +// -------------------------------------------------- + + +// Base styles +// ------------------------- + +.alert { + padding: @alert-padding; + margin-bottom: @line-height-computed; + border: 1px solid transparent; + border-radius: @alert-border-radius; + + // Headings for larger alerts + h4 { + margin-top: 0; + // Specified for the h4 to prevent conflicts of changing @headings-color + color: inherit; + } + + // Provide class for links that match alerts + .alert-link { + font-weight: @alert-link-font-weight; + } + + // Improve alignment and spacing of inner content + > p, + > ul { + margin-bottom: 0; + } + + > p + p { + margin-top: 5px; + } +} + +// Dismissible alerts +// +// Expand the right padding and account for the close button's positioning. + +.alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. +.alert-dismissible { + padding-right: (@alert-padding + 20); + + // Adjust close link position + .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; + } +} + +// Alternate styles +// +// Generate contextual modifier classes for colorizing the alert. + +.alert-success { + .alert-variant(@alert-success-bg; @alert-success-border; @alert-success-text); +} + +.alert-info { + .alert-variant(@alert-info-bg; @alert-info-border; @alert-info-text); +} + +.alert-warning { + .alert-variant(@alert-warning-bg; @alert-warning-border; @alert-warning-text); +} + +.alert-danger { + .alert-variant(@alert-danger-bg; @alert-danger-border; @alert-danger-text); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/badges.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/badges.less new file mode 100644 index 000000000..4a8040253 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/badges.less @@ -0,0 +1,65 @@ +// +// Badges +// -------------------------------------------------- + + +// Base class +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: @font-size-small; + font-weight: @badge-font-weight; + color: @badge-color; + line-height: @badge-line-height; + vertical-align: baseline; + white-space: nowrap; + text-align: center; + background-color: @badge-bg; + border-radius: @badge-border-radius; + + // Empty badges collapse automatically (not available in IE8) + &:empty { + display: none; + } + + // Quick fix for badges in buttons + .btn & { + position: relative; + top: -1px; + } + + .btn-xs & { + top: 0; + padding: 1px 5px; + } + + // Hover state, but only for links + a& { + &:hover, + &:focus { + color: @badge-link-hover-color; + text-decoration: none; + cursor: pointer; + } + } + + // Account for badges in navs + .list-group-item.active > &, + .nav-pills > .active > a > & { + color: @badge-active-color; + background-color: @badge-active-bg; + } + + .list-group-item > & { + float: right; + } + + .list-group-item > & + & { + margin-right: 5px; + } + + .nav-pills > li > a > & { + margin-left: 3px; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/bootstrap.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/bootstrap.less new file mode 100644 index 000000000..61b77474f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/bootstrap.less @@ -0,0 +1,50 @@ +// Core variables and mixins +@import "variables.less"; +@import "mixins.less"; + +// Reset and dependencies +@import "normalize.less"; +@import "print.less"; +@import "glyphicons.less"; + +// Core CSS +@import "scaffolding.less"; +@import "type.less"; +@import "code.less"; +@import "grid.less"; +@import "tables.less"; +@import "forms.less"; +@import "buttons.less"; + +// Components +@import "component-animations.less"; +@import "dropdowns.less"; +@import "button-groups.less"; +@import "input-groups.less"; +@import "navs.less"; +@import "navbar.less"; +@import "breadcrumbs.less"; +@import "pagination.less"; +@import "pager.less"; +@import "labels.less"; +@import "badges.less"; +@import "jumbotron.less"; +@import "thumbnails.less"; +@import "alerts.less"; +@import "progress-bars.less"; +@import "media.less"; +@import "list-group.less"; +@import "panels.less"; +@import "responsive-embed.less"; +@import "wells.less"; +@import "close.less"; + +// Components w/ JavaScript +@import "modals.less"; +@import "tooltip.less"; +@import "popovers.less"; +@import "carousel.less"; + +// Utility classes +@import "utilities.less"; +@import "responsive-utilities.less"; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/breadcrumbs.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/breadcrumbs.less new file mode 100644 index 000000000..cb01d503f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/breadcrumbs.less @@ -0,0 +1,26 @@ +// +// Breadcrumbs +// -------------------------------------------------- + + +.breadcrumb { + padding: @breadcrumb-padding-vertical @breadcrumb-padding-horizontal; + margin-bottom: @line-height-computed; + list-style: none; + background-color: @breadcrumb-bg; + border-radius: @border-radius-base; + + > li { + display: inline-block; + + + li:before { + content: "@{breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space + padding: 0 5px; + color: @breadcrumb-color; + } + } + + > .active { + color: @breadcrumb-active-color; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/button-groups.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/button-groups.less new file mode 100644 index 000000000..13c1800de --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/button-groups.less @@ -0,0 +1,243 @@ +// +// Button groups +// -------------------------------------------------- + +// Make the div behave like a button +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; // match .btn alignment given font-size hack above + > .btn { + position: relative; + float: left; + // Bring the "active" button to the front + &:hover, + &:focus, + &:active, + &.active { + z-index: 2; + } + } +} + +// Prevent double borders when buttons are next to each other +.btn-group { + .btn + .btn, + .btn + .btn-group, + .btn-group + .btn, + .btn-group + .btn-group { + margin-left: -1px; + } +} + +// Optional: Group multiple button groups together for a toolbar +.btn-toolbar { + margin-left: -5px; // Offset the first child's margin + &:extend(.clearfix all); + + .btn-group, + .input-group { + float: left; + } + > .btn, + > .btn-group, + > .input-group { + margin-left: 5px; + } +} + +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} + +// Set corners individual because sometimes a single button can be in a .btn-group and we need :first-child and :last-child to both match +.btn-group > .btn:first-child { + margin-left: 0; + &:not(:last-child):not(.dropdown-toggle) { + .border-right-radius(0); + } +} +// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + .border-left-radius(0); +} + +// Custom edits for including btn-groups within btn-groups (useful for including dropdown buttons within a btn-group) +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + .border-right-radius(0); + } +} +.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { + .border-left-radius(0); +} + +// On active and open, don't show outline +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} + + +// Sizing +// +// Remix the default button sizing classes into new ones for easier manipulation. + +.btn-group-xs > .btn { &:extend(.btn-xs); } +.btn-group-sm > .btn { &:extend(.btn-sm); } +.btn-group-lg > .btn { &:extend(.btn-lg); } + + +// Split button dropdowns +// ---------------------- + +// Give the line between buttons some depth +.btn-group > .btn + .dropdown-toggle { + padding-left: 8px; + padding-right: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-left: 12px; + padding-right: 12px; +} + +// The clickable button for toggling the menu +// Remove the gradient and set the same inset shadow as the :active state +.btn-group.open .dropdown-toggle { + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + + // Show no shadow for `.btn-link` since it has no other button styles. + &.btn-link { + .box-shadow(none); + } +} + + +// Reposition the caret +.btn .caret { + margin-left: 0; +} +// Carets in other button sizes +.btn-lg .caret { + border-width: @caret-width-large @caret-width-large 0; + border-bottom-width: 0; +} +// Upside down carets for .dropup +.dropup .btn-lg .caret { + border-width: 0 @caret-width-large @caret-width-large; +} + + +// Vertical button groups +// ---------------------- + +.btn-group-vertical { + > .btn, + > .btn-group, + > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; + } + + // Clear floats so dropdown menus can be properly placed + > .btn-group { + &:extend(.clearfix all); + > .btn { + float: none; + } + } + + > .btn + .btn, + > .btn + .btn-group, + > .btn-group + .btn, + > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; + } +} + +.btn-group-vertical > .btn { + &:not(:first-child):not(:last-child) { + border-radius: 0; + } + &:first-child:not(:last-child) { + border-top-right-radius: @border-radius-base; + .border-bottom-radius(0); + } + &:last-child:not(:first-child) { + border-bottom-left-radius: @border-radius-base; + .border-top-radius(0); + } +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) { + > .btn:last-child, + > .dropdown-toggle { + .border-bottom-radius(0); + } +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + .border-top-radius(0); +} + + +// Justified button groups +// ---------------------- + +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; + > .btn, + > .btn-group { + float: none; + display: table-cell; + width: 1%; + } + > .btn-group .btn { + width: 100%; + } + + > .btn-group .dropdown-menu { + left: auto; + } +} + + +// Checkbox and radio options +// +// In order to support the browser's form validation feedback, powered by the +// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use +// `display: none;` or `visibility: hidden;` as that also hides the popover. +// Simply visually hiding the inputs via `opacity` would leave them clickable in +// certain cases which is prevented by using `clip` and `pointer-events`. +// This way, we ensure a DOM element is visible to position the popover from. +// +// See https://github.com/twbs/bootstrap/pull/12794 and +// https://github.com/twbs/bootstrap/pull/14559 for more information. + +[data-toggle="buttons"] { + > .btn, + > .btn-group > .btn { + input[type="radio"], + input[type="checkbox"] { + position: absolute; + clip: rect(0,0,0,0); + pointer-events: none; + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/buttons.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/buttons.less new file mode 100644 index 000000000..5a746049e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/buttons.less @@ -0,0 +1,160 @@ +// +// Buttons +// -------------------------------------------------- + + +// Base styles +// -------------------------------------------------- + +.btn { + display: inline-block; + margin-bottom: 0; // For input.btn + font-weight: @btn-font-weight; + text-align: center; + vertical-align: middle; + touch-action: manipulation; + cursor: pointer; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + white-space: nowrap; + .button-size(@padding-base-vertical; @padding-base-horizontal; @font-size-base; @line-height-base; @border-radius-base); + .user-select(none); + + &, + &:active, + &.active { + &:focus, + &.focus { + .tab-focus(); + } + } + + &:hover, + &:focus, + &.focus { + color: @btn-default-color; + text-decoration: none; + } + + &:active, + &.active { + outline: 0; + background-image: none; + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + } + + &.disabled, + &[disabled], + fieldset[disabled] & { + cursor: @cursor-disabled; + pointer-events: none; // Future-proof disabling of clicks + .opacity(.65); + .box-shadow(none); + } +} + + +// Alternate buttons +// -------------------------------------------------- + +.btn-default { + .button-variant(@btn-default-color; @btn-default-bg; @btn-default-border); +} +.btn-primary { + .button-variant(@btn-primary-color; @btn-primary-bg; @btn-primary-border); +} +// Success appears as green +.btn-success { + .button-variant(@btn-success-color; @btn-success-bg; @btn-success-border); +} +// Info appears as blue-green +.btn-info { + .button-variant(@btn-info-color; @btn-info-bg; @btn-info-border); +} +// Warning appears as orange +.btn-warning { + .button-variant(@btn-warning-color; @btn-warning-bg; @btn-warning-border); +} +// Danger and error appear as red +.btn-danger { + .button-variant(@btn-danger-color; @btn-danger-bg; @btn-danger-border); +} + + +// Link buttons +// ------------------------- + +// Make a button look and behave like a link +.btn-link { + color: @link-color; + font-weight: normal; + border-radius: 0; + + &, + &:active, + &.active, + &[disabled], + fieldset[disabled] & { + background-color: transparent; + .box-shadow(none); + } + &, + &:hover, + &:focus, + &:active { + border-color: transparent; + } + &:hover, + &:focus { + color: @link-hover-color; + text-decoration: @link-hover-decoration; + background-color: transparent; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @btn-link-disabled-color; + text-decoration: none; + } + } +} + + +// Button Sizes +// -------------------------------------------------- + +.btn-lg { + // line-height: ensure even-numbered height of button next to large input + .button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large); +} +.btn-sm { + // line-height: ensure proper height of button next to small input + .button-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} +.btn-xs { + .button-size(@padding-xs-vertical; @padding-xs-horizontal; @font-size-small; @line-height-small; @border-radius-small); +} + + +// Block button +// -------------------------------------------------- + +.btn-block { + display: block; + width: 100%; +} + +// Vertically space out multiple block buttons +.btn-block + .btn-block { + margin-top: 5px; +} + +// Specificity overrides +input[type="submit"], +input[type="reset"], +input[type="button"] { + &.btn-block { + width: 100%; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/carousel.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/carousel.less new file mode 100644 index 000000000..4bbe946d3 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/carousel.less @@ -0,0 +1,269 @@ +// +// Carousel +// -------------------------------------------------- + + +// Wrapper for the slide container and indicators +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + overflow: hidden; + width: 100%; + + > .item { + display: none; + position: relative; + .transition(.6s ease-in-out left); + + // Account for jankitude on images + > img, + > a > img { + &:extend(.img-responsive); + line-height: 1; + } + + // WebKit CSS3 transforms for supported devices + @media all and (transform-3d), (-webkit-transform-3d) { + .transition-transform(~'0.6s ease-in-out'); + .backface-visibility(~'hidden'); + .perspective(1000); + + &.next, + &.active.right { + .translate3d(100%, 0, 0); + left: 0; + } + &.prev, + &.active.left { + .translate3d(-100%, 0, 0); + left: 0; + } + &.next.left, + &.prev.right, + &.active { + .translate3d(0, 0, 0); + left: 0; + } + } + } + + > .active, + > .next, + > .prev { + display: block; + } + + > .active { + left: 0; + } + + > .next, + > .prev { + position: absolute; + top: 0; + width: 100%; + } + + > .next { + left: 100%; + } + > .prev { + left: -100%; + } + > .next.left, + > .prev.right { + left: 0; + } + + > .active.left { + left: -100%; + } + > .active.right { + left: 100%; + } + +} + +// Left/right controls for nav +// --------------------------- + +.carousel-control { + position: absolute; + top: 0; + left: 0; + bottom: 0; + width: @carousel-control-width; + .opacity(@carousel-control-opacity); + font-size: @carousel-control-font-size; + color: @carousel-control-color; + text-align: center; + text-shadow: @carousel-text-shadow; + // We can't have this transition here because WebKit cancels the carousel + // animation if you trip this while in the middle of another animation. + + // Set gradients for backgrounds + &.left { + #gradient > .horizontal(@start-color: rgba(0,0,0,.5); @end-color: rgba(0,0,0,.0001)); + } + &.right { + left: auto; + right: 0; + #gradient > .horizontal(@start-color: rgba(0,0,0,.0001); @end-color: rgba(0,0,0,.5)); + } + + // Hover/focus state + &:hover, + &:focus { + outline: 0; + color: @carousel-control-color; + text-decoration: none; + .opacity(.9); + } + + // Toggles + .icon-prev, + .icon-next, + .glyphicon-chevron-left, + .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; + } + .icon-prev, + .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; + } + .icon-next, + .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; + } + .icon-prev, + .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + line-height: 1; + font-family: serif; + } + + + .icon-prev { + &:before { + content: '\2039';// SINGLE LEFT-POINTING ANGLE QUOTATION MARK (U+2039) + } + } + .icon-next { + &:before { + content: '\203a';// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (U+203A) + } + } +} + +// Optional indicator pips +// +// Add an unordered list with the following class and add a list item for each +// slide your carousel holds. + +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + margin-left: -30%; + padding-left: 0; + list-style: none; + text-align: center; + + li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + border: 1px solid @carousel-indicator-border-color; + border-radius: 10px; + cursor: pointer; + + // IE8-9 hack for event handling + // + // Internet Explorer 8-9 does not support clicks on elements without a set + // `background-color`. We cannot use `filter` since that's not viewed as a + // background color by the browser. Thus, a hack is needed. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Internet_Explorer + // + // For IE8, we set solid black as it doesn't support `rgba()`. For IE9, we + // set alpha transparency for the best results possible. + background-color: #000 \9; // IE8 + background-color: rgba(0,0,0,0); // IE9 + } + .active { + margin: 0; + width: 12px; + height: 12px; + background-color: @carousel-indicator-active-bg; + } +} + +// Optional captions +// ----------------------------- +// Hidden by default for smaller viewports +.carousel-caption { + position: absolute; + left: 15%; + right: 15%; + bottom: 20px; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: @carousel-caption-color; + text-align: center; + text-shadow: @carousel-text-shadow; + & .btn { + text-shadow: none; // No shadow for button elements in carousel-caption + } +} + + +// Scale up controls for tablets and up +@media screen and (min-width: @screen-sm-min) { + + // Scale up the controls a smidge + .carousel-control { + .glyphicon-chevron-left, + .glyphicon-chevron-right, + .icon-prev, + .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .glyphicon-chevron-left, + .icon-prev { + margin-left: -15px; + } + .glyphicon-chevron-right, + .icon-next { + margin-right: -15px; + } + } + + // Show and left align the captions + .carousel-caption { + left: 20%; + right: 20%; + padding-bottom: 30px; + } + + // Move up the indicators + .carousel-indicators { + bottom: 20px; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/close.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/close.less new file mode 100644 index 000000000..6d5bfe087 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/close.less @@ -0,0 +1,34 @@ +// +// Close icons +// -------------------------------------------------- + + +.close { + float: right; + font-size: (@font-size-base * 1.5); + font-weight: @close-font-weight; + line-height: 1; + color: @close-color; + text-shadow: @close-text-shadow; + .opacity(.2); + + &:hover, + &:focus { + color: @close-color; + text-decoration: none; + cursor: pointer; + .opacity(.5); + } + + // Additional properties for button version + // iOS requires the button element instead of an anchor tag. + // If you want the anchor version, it requires `href="#"`. + // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile + button& { + padding: 0; + cursor: pointer; + background: transparent; + border: 0; + -webkit-appearance: none; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/code.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/code.less new file mode 100644 index 000000000..a08b4d48c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/code.less @@ -0,0 +1,69 @@ +// +// Code (inline and block) +// -------------------------------------------------- + + +// Inline and block code styles +code, +kbd, +pre, +samp { + font-family: @font-family-monospace; +} + +// Inline code +code { + padding: 2px 4px; + font-size: 90%; + color: @code-color; + background-color: @code-bg; + border-radius: @border-radius-base; +} + +// User input typically entered via keyboard +kbd { + padding: 2px 4px; + font-size: 90%; + color: @kbd-color; + background-color: @kbd-bg; + border-radius: @border-radius-small; + box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); + + kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + box-shadow: none; + } +} + +// Blocks of code +pre { + display: block; + padding: ((@line-height-computed - 1) / 2); + margin: 0 0 (@line-height-computed / 2); + font-size: (@font-size-base - 1); // 14px to 13px + line-height: @line-height-base; + word-break: break-all; + word-wrap: break-word; + color: @pre-color; + background-color: @pre-bg; + border: 1px solid @pre-border-color; + border-radius: @border-radius-base; + + // Account for some code outputs that place code tags in pre tags + code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; + } +} + +// Enable scrollable blocks of code +.pre-scrollable { + max-height: @pre-scrollable-max-height; + overflow-y: scroll; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/component-animations.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/component-animations.less new file mode 100644 index 000000000..967715d98 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/component-animations.less @@ -0,0 +1,34 @@ +// +// Component animations +// -------------------------------------------------- + +// Heads up! +// +// We don't use the `.opacity()` mixin here since it causes a bug with text +// fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. + +.fade { + opacity: 0; + .transition(opacity .15s linear); + &.in { + opacity: 1; + } +} + +.collapse { + display: none; + visibility: hidden; + + &.in { display: block; visibility: visible; } + tr&.in { display: table-row; } + tbody&.in { display: table-row-group; } +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + .transition-property(~"height, visibility"); + .transition-duration(.35s); + .transition-timing-function(ease); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/dropdowns.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/dropdowns.less new file mode 100644 index 000000000..0cc46df5c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/dropdowns.less @@ -0,0 +1,214 @@ +// +// Dropdown menus +// -------------------------------------------------- + + +// Dropdown arrow/caret +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: @caret-width-base solid; + border-right: @caret-width-base solid transparent; + border-left: @caret-width-base solid transparent; +} + +// The dropdown wrapper (div) +.dropup, +.dropdown { + position: relative; +} + +// Prevent the focus on the dropdown toggle when closing dropdowns +.dropdown-toggle:focus { + outline: 0; +} + +// The dropdown menu (ul) +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: @zindex-dropdown; + display: none; // none by default, but block on "open" of the menu + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; // override default ul + list-style: none; + font-size: @font-size-base; + text-align: left; // Ensures proper alignment if parent has it changed (e.g., modal footer) + background-color: @dropdown-bg; + border: 1px solid @dropdown-fallback-border; // IE8 fallback + border: 1px solid @dropdown-border; + border-radius: @border-radius-base; + .box-shadow(0 6px 12px rgba(0,0,0,.175)); + background-clip: padding-box; + + // Aligns the dropdown menu to right + // + // Deprecated as of 3.1.0 in favor of `.dropdown-menu-[dir]` + &.pull-right { + right: 0; + left: auto; + } + + // Dividers (basically an hr) within the dropdown + .divider { + .nav-divider(@dropdown-divider-bg); + } + + // Links within the dropdown menu + > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: @line-height-base; + color: @dropdown-link-color; + white-space: nowrap; // prevent links from randomly breaking onto new lines + } +} + +// Hover/Focus state +.dropdown-menu > li > a { + &:hover, + &:focus { + text-decoration: none; + color: @dropdown-link-hover-color; + background-color: @dropdown-link-hover-bg; + } +} + +// Active state +.dropdown-menu > .active > a { + &, + &:hover, + &:focus { + color: @dropdown-link-active-color; + text-decoration: none; + outline: 0; + background-color: @dropdown-link-active-bg; + } +} + +// Disabled state +// +// Gray out text and ensure the hover/focus state remains gray + +.dropdown-menu > .disabled > a { + &, + &:hover, + &:focus { + color: @dropdown-link-disabled-color; + } + + // Nuke hover/focus effects + &:hover, + &:focus { + text-decoration: none; + background-color: transparent; + background-image: none; // Remove CSS gradient + .reset-filter(); + cursor: @cursor-disabled; + } +} + +// Open state for the dropdown +.open { + // Show the menu + > .dropdown-menu { + display: block; + } + + // Remove the outline when :focus is triggered + > a { + outline: 0; + } +} + +// Menu positioning +// +// Add extra class to `.dropdown-menu` to flip the alignment of the dropdown +// menu with the parent. +.dropdown-menu-right { + left: auto; // Reset the default from `.dropdown-menu` + right: 0; +} +// With v3, we enabled auto-flipping if you have a dropdown within a right +// aligned nav component. To enable the undoing of that, we provide an override +// to restore the default dropdown menu alignment. +// +// This is only for left-aligning a dropdown menu within a `.navbar-right` or +// `.pull-right` nav component. +.dropdown-menu-left { + left: 0; + right: auto; +} + +// Dropdown section headers +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: @font-size-small; + line-height: @line-height-base; + color: @dropdown-header-color; + white-space: nowrap; // as with > li > a +} + +// Backdrop to catch body clicks on mobile, etc. +.dropdown-backdrop { + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: (@zindex-dropdown - 10); +} + +// Right aligned dropdowns +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} + +// Allow for dropdowns to go bottom up (aka, dropup-menu) +// +// Just add .dropup after the standard .dropdown class and you're set, bro. +// TODO: abstract this so that the navbar fixed styles are not placed here? + +.dropup, +.navbar-fixed-bottom .dropdown { + // Reverse the caret + .caret { + border-top: 0; + border-bottom: @caret-width-base solid; + content: ""; + } + // Different positioning for bottom up menu + .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 2px; + } +} + + +// Component alignment +// +// Reiterate per navbar.less and the modified component alignment there. + +@media (min-width: @grid-float-breakpoint) { + .navbar-right { + .dropdown-menu { + .dropdown-menu-right(); + } + // Necessary for overrides of the default right aligned menu. + // Will remove come v4 in all likelihood. + .dropdown-menu-left { + .dropdown-menu-left(); + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/forms.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/forms.less new file mode 100644 index 000000000..3138c2c51 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/forms.less @@ -0,0 +1,566 @@ +// +// Forms +// -------------------------------------------------- + + +// Normalize non-controls +// +// Restyle and baseline non-control form elements. + +fieldset { + padding: 0; + margin: 0; + border: 0; + // Chrome and Firefox set a `min-width: min-content;` on fieldsets, + // so we reset that to ensure it behaves more like a standard block element. + // See https://github.com/twbs/bootstrap/issues/12359. + min-width: 0; +} + +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: @line-height-computed; + font-size: (@font-size-base * 1.5); + line-height: inherit; + color: @legend-color; + border: 0; + border-bottom: 1px solid @legend-border-color; +} + +label { + display: inline-block; + max-width: 100%; // Force IE8 to wrap long content (see https://github.com/twbs/bootstrap/issues/13141) + margin-bottom: 5px; + font-weight: bold; +} + + +// Normalize form controls +// +// While most of our form styles require extra classes, some basic normalization +// is required to ensure optimum display with or without those classes to better +// address browser inconsistencies. + +// Override content-box in Normalize (* isn't specific enough) +input[type="search"] { + .box-sizing(border-box); +} + +// Position radios and checkboxes better +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; // IE8-9 + line-height: normal; +} + +// Set the height of file controls to match text inputs +input[type="file"] { + display: block; +} + +// Make range inputs behave like textual form controls +input[type="range"] { + display: block; + width: 100%; +} + +// Make multiple select elements height not fixed +select[multiple], +select[size] { + height: auto; +} + +// Focus for file, radio, and checkbox +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + .tab-focus(); +} + +// Adjust output element +output { + display: block; + padding-top: (@padding-base-vertical + 1); + font-size: @font-size-base; + line-height: @line-height-base; + color: @input-color; +} + + +// Common form controls +// +// Shared size and type resets for form controls. Apply `.form-control` to any +// of the following form controls: +// +// select +// textarea +// input[type="text"] +// input[type="password"] +// input[type="datetime"] +// input[type="datetime-local"] +// input[type="date"] +// input[type="month"] +// input[type="time"] +// input[type="week"] +// input[type="number"] +// input[type="email"] +// input[type="url"] +// input[type="search"] +// input[type="tel"] +// input[type="color"] + +.form-control { + display: block; + width: 100%; + height: @input-height-base; // Make inputs at least the height of their button counterpart (base line-height + padding + border) + padding: @padding-base-vertical @padding-base-horizontal; + font-size: @font-size-base; + line-height: @line-height-base; + color: @input-color; + background-color: @input-bg; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid @input-border; + border-radius: @input-border-radius; // Note: This has no effect on <select>s in some browsers, due to the limited stylability of <select>s in CSS. + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); + .transition(~"border-color ease-in-out .15s, box-shadow ease-in-out .15s"); + + // Customize the `:focus` state to imitate native WebKit styles. + .form-control-focus(); + + // Placeholder + .placeholder(); + + // Disabled and read-only inputs + // + // HTML5 says that controls under a fieldset > legend:first-child won't be + // disabled if the fieldset is disabled. Due to implementation difficulty, we + // don't honor that edge case; we style them as disabled anyway. + &[disabled], + &[readonly], + fieldset[disabled] & { + cursor: @cursor-disabled; + background-color: @input-bg-disabled; + opacity: 1; // iOS fix for unreadable disabled content; see https://github.com/twbs/bootstrap/issues/11655 + } + + // Reset height for `textarea`s + textarea& { + height: auto; + } +} + + +// Search inputs in iOS +// +// This overrides the extra rounded corners on search inputs in iOS so that our +// `.form-control` class can properly style them. Note that this cannot simply +// be added to `.form-control` as it's not specific enough. For details, see +// https://github.com/twbs/bootstrap/issues/11586. + +input[type="search"] { + -webkit-appearance: none; +} + + +// Special styles for iOS temporal inputs +// +// In Mobile Safari, setting `display: block` on temporal inputs causes the +// text within the input to become vertically misaligned. As a workaround, we +// set a pixel line-height that matches the given height of the input, but only +// for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: @input-height-base; + + &.input-sm, + .input-group-sm & { + line-height: @input-height-small; + } + + &.input-lg, + .input-group-lg & { + line-height: @input-height-large; + } + } +} + + +// Form groups +// +// Designed to help with the organization and spacing of vertical forms. For +// horizontal forms, use the predefined grid classes. + +.form-group { + margin-bottom: 15px; +} + + +// Checkboxes and radios +// +// Indent the labels to position radios/checkboxes as hanging controls. + +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; + + label { + min-height: @line-height-computed; // Ensure the input doesn't jump when there is no text + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; + } +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-left: -20px; + margin-top: 4px \9; +} + +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; // Move up sibling radios or checkboxes for tighter spacing +} + +// Radios and checkboxes on same line +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + vertical-align: middle; + font-weight: normal; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; // space out consecutive inline controls +} + +// Apply same disabled cursor tweak as for inputs +// Some special care is needed because <label>s don't inherit their parent's `cursor`. +// +// Note: Neither radios nor checkboxes can be readonly. +input[type="radio"], +input[type="checkbox"] { + &[disabled], + &.disabled, + fieldset[disabled] & { + cursor: @cursor-disabled; + } +} +// These classes are used directly on <label>s +.radio-inline, +.checkbox-inline { + &.disabled, + fieldset[disabled] & { + cursor: @cursor-disabled; + } +} +// These classes are used on elements with <label> descendants +.radio, +.checkbox { + &.disabled, + fieldset[disabled] & { + label { + cursor: @cursor-disabled; + } + } +} + + +// Static form control text +// +// Apply class to a `p` element to make any string of text align with labels in +// a horizontal form layout. + +.form-control-static { + // Size it appropriately next to real form controls + padding-top: (@padding-base-vertical + 1); + padding-bottom: (@padding-base-vertical + 1); + // Remove default margin from `p` + margin-bottom: 0; + + &.input-lg, + &.input-sm { + padding-left: 0; + padding-right: 0; + } +} + + +// Form control sizing +// +// Build on `.form-control` with modifier classes to decrease or increase the +// height and font-size of form controls. +// +// The `.form-group-* form-control` variations are sadly duplicated to avoid the +// issue documented in https://github.com/twbs/bootstrap/issues/15074. + +.input-sm { + .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small); +} +.form-group-sm { + .form-control { + .input-size(@input-height-small; @padding-small-vertical; @padding-small-horizontal; @font-size-small; @line-height-small; @input-border-radius-small); + } + .form-control-static { + height: @input-height-small; + padding: @padding-small-vertical @padding-small-horizontal; + font-size: @font-size-small; + line-height: @line-height-small; + } +} + +.input-lg { + .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large); +} +.form-group-lg { + .form-control { + .input-size(@input-height-large; @padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @input-border-radius-large); + } + .form-control-static { + height: @input-height-large; + padding: @padding-large-vertical @padding-large-horizontal; + font-size: @font-size-large; + line-height: @line-height-large; + } +} + + +// Form control feedback states +// +// Apply contextual and semantic states to individual form controls. + +.has-feedback { + // Enable absolute positioning + position: relative; + + // Ensure icons don't overlap text + .form-control { + padding-right: (@input-height-base * 1.25); + } +} +// Feedback icon (requires .glyphicon classes) +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; // Ensure icon is above input groups + display: block; + width: @input-height-base; + height: @input-height-base; + line-height: @input-height-base; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback { + width: @input-height-large; + height: @input-height-large; + line-height: @input-height-large; +} +.input-sm + .form-control-feedback { + width: @input-height-small; + height: @input-height-small; + line-height: @input-height-small; +} + +// Feedback states +.has-success { + .form-control-validation(@state-success-text; @state-success-text; @state-success-bg); +} +.has-warning { + .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg); +} +.has-error { + .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg); +} + +// Reposition feedback icon if input has visible label above +.has-feedback label { + + & ~ .form-control-feedback { + top: (@line-height-computed + 5); // Height of the `label` and its margin + } + &.sr-only ~ .form-control-feedback { + top: 0; + } +} + + +// Help text +// +// Apply to any element you wish to create light text for placement immediately +// below a form control. Use for general help, formatting, or instructional text. + +.help-block { + display: block; // account for any element using help-block + margin-top: 5px; + margin-bottom: 10px; + color: lighten(@text-color, 25%); // lighten the text some for contrast +} + + +// Inline forms +// +// Make forms appear inline(-block) by adding the `.form-inline` class. Inline +// forms begin stacked on extra small (mobile) devices and then go inline when +// viewports reach <768px. +// +// Requires wrapping inputs and labels with `.form-group` for proper display of +// default HTML form controls and our custom form controls (e.g., input groups). +// +// Heads up! This is mixin-ed into `.navbar-form` in navbars.less. + +.form-inline { + + // Kick in the inline + @media (min-width: @screen-sm-min) { + // Inline-block all the things for "inline" + .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + + // In navbar-form, allow folks to *not* use `.form-group` + .form-control { + display: inline-block; + width: auto; // Prevent labels from stacking above inputs in `.form-group` + vertical-align: middle; + } + + // Make static controls behave like regular ones + .form-control-static { + display: inline-block; + } + + .input-group { + display: inline-table; + vertical-align: middle; + + .input-group-addon, + .input-group-btn, + .form-control { + width: auto; + } + } + + // Input groups need that 100% width though + .input-group > .form-control { + width: 100%; + } + + .control-label { + margin-bottom: 0; + vertical-align: middle; + } + + // Remove default margin on radios/checkboxes that were used for stacking, and + // then undo the floating of radios and checkboxes to match. + .radio, + .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + + label { + padding-left: 0; + } + } + .radio input[type="radio"], + .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + + // Re-override the feedback icon. + .has-feedback .form-control-feedback { + top: 0; + } + } +} + + +// Horizontal forms +// +// Horizontal forms are built on grid classes and allow you to create forms with +// labels on the left and inputs on the right. + +.form-horizontal { + + // Consistent vertical alignment of radios and checkboxes + // + // Labels also get some reset styles, but that is scoped to a media query below. + .radio, + .checkbox, + .radio-inline, + .checkbox-inline { + margin-top: 0; + margin-bottom: 0; + padding-top: (@padding-base-vertical + 1); // Default padding plus a border + } + // Account for padding we're adding to ensure the alignment and of help text + // and other content below items + .radio, + .checkbox { + min-height: (@line-height-computed + (@padding-base-vertical + 1)); + } + + // Make form groups behave like rows + .form-group { + .make-row(); + } + + // Reset spacing and right align labels, but scope to media queries so that + // labels on narrow viewports stack the same as a default form example. + @media (min-width: @screen-sm-min) { + .control-label { + text-align: right; + margin-bottom: 0; + padding-top: (@padding-base-vertical + 1); // Default padding plus a border + } + } + + // Validation states + // + // Reposition the icon because it's now within a grid column and columns have + // `position: relative;` on them. Also accounts for the grid gutter padding. + .has-feedback .form-control-feedback { + right: (@grid-gutter-width / 2); + } + + // Form group sizes + // + // Quick utility class for applying `.input-lg` and `.input-sm` styles to the + // inputs and labels within a `.form-group`. + .form-group-lg { + @media (min-width: @screen-sm-min) { + .control-label { + padding-top: ((@padding-large-vertical * @line-height-large) + 1); + } + } + } + .form-group-sm { + @media (min-width: @screen-sm-min) { + .control-label { + padding-top: (@padding-small-vertical + 1); + } + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/glyphicons.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/glyphicons.less new file mode 100644 index 000000000..8b25f89a5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/glyphicons.less @@ -0,0 +1,301 @@ +// +// Glyphicons for Bootstrap +// +// Since icons are fonts, they can be placed anywhere text is placed and are +// thus automatically sized to match the surrounding child. To use, create an +// inline element with the appropriate classes, like so: +// +// <a href="#"><span class="glyphicon glyphicon-star"></span> Star</a> + +// Import the fonts +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('@{icon-font-path}@{icon-font-name}.eot'); + src: url('@{icon-font-path}@{icon-font-name}.eot?#iefix') format('embedded-opentype'), + url('@{icon-font-path}@{icon-font-name}.woff2') format('woff2'), + url('@{icon-font-path}@{icon-font-name}.woff') format('woff'), + url('@{icon-font-path}@{icon-font-name}.ttf') format('truetype'), + url('@{icon-font-path}@{icon-font-name}.svg#@{icon-font-svg-id}') format('svg'); +} + +// Catchall baseclass +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Individual icons +.glyphicon-asterisk { &:before { content: "\2a"; } } +.glyphicon-plus { &:before { content: "\2b"; } } +.glyphicon-euro, +.glyphicon-eur { &:before { content: "\20ac"; } } +.glyphicon-minus { &:before { content: "\2212"; } } +.glyphicon-cloud { &:before { content: "\2601"; } } +.glyphicon-envelope { &:before { content: "\2709"; } } +.glyphicon-pencil { &:before { content: "\270f"; } } +.glyphicon-glass { &:before { content: "\e001"; } } +.glyphicon-music { &:before { content: "\e002"; } } +.glyphicon-search { &:before { content: "\e003"; } } +.glyphicon-heart { &:before { content: "\e005"; } } +.glyphicon-star { &:before { content: "\e006"; } } +.glyphicon-star-empty { &:before { content: "\e007"; } } +.glyphicon-user { &:before { content: "\e008"; } } +.glyphicon-film { &:before { content: "\e009"; } } +.glyphicon-th-large { &:before { content: "\e010"; } } +.glyphicon-th { &:before { content: "\e011"; } } +.glyphicon-th-list { &:before { content: "\e012"; } } +.glyphicon-ok { &:before { content: "\e013"; } } +.glyphicon-remove { &:before { content: "\e014"; } } +.glyphicon-zoom-in { &:before { content: "\e015"; } } +.glyphicon-zoom-out { &:before { content: "\e016"; } } +.glyphicon-off { &:before { content: "\e017"; } } +.glyphicon-signal { &:before { content: "\e018"; } } +.glyphicon-cog { &:before { content: "\e019"; } } +.glyphicon-trash { &:before { content: "\e020"; } } +.glyphicon-home { &:before { content: "\e021"; } } +.glyphicon-file { &:before { content: "\e022"; } } +.glyphicon-time { &:before { content: "\e023"; } } +.glyphicon-road { &:before { content: "\e024"; } } +.glyphicon-download-alt { &:before { content: "\e025"; } } +.glyphicon-download { &:before { content: "\e026"; } } +.glyphicon-upload { &:before { content: "\e027"; } } +.glyphicon-inbox { &:before { content: "\e028"; } } +.glyphicon-play-circle { &:before { content: "\e029"; } } +.glyphicon-repeat { &:before { content: "\e030"; } } +.glyphicon-refresh { &:before { content: "\e031"; } } +.glyphicon-list-alt { &:before { content: "\e032"; } } +.glyphicon-lock { &:before { content: "\e033"; } } +.glyphicon-flag { &:before { content: "\e034"; } } +.glyphicon-headphones { &:before { content: "\e035"; } } +.glyphicon-volume-off { &:before { content: "\e036"; } } +.glyphicon-volume-down { &:before { content: "\e037"; } } +.glyphicon-volume-up { &:before { content: "\e038"; } } +.glyphicon-qrcode { &:before { content: "\e039"; } } +.glyphicon-barcode { &:before { content: "\e040"; } } +.glyphicon-tag { &:before { content: "\e041"; } } +.glyphicon-tags { &:before { content: "\e042"; } } +.glyphicon-book { &:before { content: "\e043"; } } +.glyphicon-bookmark { &:before { content: "\e044"; } } +.glyphicon-print { &:before { content: "\e045"; } } +.glyphicon-camera { &:before { content: "\e046"; } } +.glyphicon-font { &:before { content: "\e047"; } } +.glyphicon-bold { &:before { content: "\e048"; } } +.glyphicon-italic { &:before { content: "\e049"; } } +.glyphicon-text-height { &:before { content: "\e050"; } } +.glyphicon-text-width { &:before { content: "\e051"; } } +.glyphicon-align-left { &:before { content: "\e052"; } } +.glyphicon-align-center { &:before { content: "\e053"; } } +.glyphicon-align-right { &:before { content: "\e054"; } } +.glyphicon-align-justify { &:before { content: "\e055"; } } +.glyphicon-list { &:before { content: "\e056"; } } +.glyphicon-indent-left { &:before { content: "\e057"; } } +.glyphicon-indent-right { &:before { content: "\e058"; } } +.glyphicon-facetime-video { &:before { content: "\e059"; } } +.glyphicon-picture { &:before { content: "\e060"; } } +.glyphicon-map-marker { &:before { content: "\e062"; } } +.glyphicon-adjust { &:before { content: "\e063"; } } +.glyphicon-tint { &:before { content: "\e064"; } } +.glyphicon-edit { &:before { content: "\e065"; } } +.glyphicon-share { &:before { content: "\e066"; } } +.glyphicon-check { &:before { content: "\e067"; } } +.glyphicon-move { &:before { content: "\e068"; } } +.glyphicon-step-backward { &:before { content: "\e069"; } } +.glyphicon-fast-backward { &:before { content: "\e070"; } } +.glyphicon-backward { &:before { content: "\e071"; } } +.glyphicon-play { &:before { content: "\e072"; } } +.glyphicon-pause { &:before { content: "\e073"; } } +.glyphicon-stop { &:before { content: "\e074"; } } +.glyphicon-forward { &:before { content: "\e075"; } } +.glyphicon-fast-forward { &:before { content: "\e076"; } } +.glyphicon-step-forward { &:before { content: "\e077"; } } +.glyphicon-eject { &:before { content: "\e078"; } } +.glyphicon-chevron-left { &:before { content: "\e079"; } } +.glyphicon-chevron-right { &:before { content: "\e080"; } } +.glyphicon-plus-sign { &:before { content: "\e081"; } } +.glyphicon-minus-sign { &:before { content: "\e082"; } } +.glyphicon-remove-sign { &:before { content: "\e083"; } } +.glyphicon-ok-sign { &:before { content: "\e084"; } } +.glyphicon-question-sign { &:before { content: "\e085"; } } +.glyphicon-info-sign { &:before { content: "\e086"; } } +.glyphicon-screenshot { &:before { content: "\e087"; } } +.glyphicon-remove-circle { &:before { content: "\e088"; } } +.glyphicon-ok-circle { &:before { content: "\e089"; } } +.glyphicon-ban-circle { &:before { content: "\e090"; } } +.glyphicon-arrow-left { &:before { content: "\e091"; } } +.glyphicon-arrow-right { &:before { content: "\e092"; } } +.glyphicon-arrow-up { &:before { content: "\e093"; } } +.glyphicon-arrow-down { &:before { content: "\e094"; } } +.glyphicon-share-alt { &:before { content: "\e095"; } } +.glyphicon-resize-full { &:before { content: "\e096"; } } +.glyphicon-resize-small { &:before { content: "\e097"; } } +.glyphicon-exclamation-sign { &:before { content: "\e101"; } } +.glyphicon-gift { &:before { content: "\e102"; } } +.glyphicon-leaf { &:before { content: "\e103"; } } +.glyphicon-fire { &:before { content: "\e104"; } } +.glyphicon-eye-open { &:before { content: "\e105"; } } +.glyphicon-eye-close { &:before { content: "\e106"; } } +.glyphicon-warning-sign { &:before { content: "\e107"; } } +.glyphicon-plane { &:before { content: "\e108"; } } +.glyphicon-calendar { &:before { content: "\e109"; } } +.glyphicon-random { &:before { content: "\e110"; } } +.glyphicon-comment { &:before { content: "\e111"; } } +.glyphicon-magnet { &:before { content: "\e112"; } } +.glyphicon-chevron-up { &:before { content: "\e113"; } } +.glyphicon-chevron-down { &:before { content: "\e114"; } } +.glyphicon-retweet { &:before { content: "\e115"; } } +.glyphicon-shopping-cart { &:before { content: "\e116"; } } +.glyphicon-folder-close { &:before { content: "\e117"; } } +.glyphicon-folder-open { &:before { content: "\e118"; } } +.glyphicon-resize-vertical { &:before { content: "\e119"; } } +.glyphicon-resize-horizontal { &:before { content: "\e120"; } } +.glyphicon-hdd { &:before { content: "\e121"; } } +.glyphicon-bullhorn { &:before { content: "\e122"; } } +.glyphicon-bell { &:before { content: "\e123"; } } +.glyphicon-certificate { &:before { content: "\e124"; } } +.glyphicon-thumbs-up { &:before { content: "\e125"; } } +.glyphicon-thumbs-down { &:before { content: "\e126"; } } +.glyphicon-hand-right { &:before { content: "\e127"; } } +.glyphicon-hand-left { &:before { content: "\e128"; } } +.glyphicon-hand-up { &:before { content: "\e129"; } } +.glyphicon-hand-down { &:before { content: "\e130"; } } +.glyphicon-circle-arrow-right { &:before { content: "\e131"; } } +.glyphicon-circle-arrow-left { &:before { content: "\e132"; } } +.glyphicon-circle-arrow-up { &:before { content: "\e133"; } } +.glyphicon-circle-arrow-down { &:before { content: "\e134"; } } +.glyphicon-globe { &:before { content: "\e135"; } } +.glyphicon-wrench { &:before { content: "\e136"; } } +.glyphicon-tasks { &:before { content: "\e137"; } } +.glyphicon-filter { &:before { content: "\e138"; } } +.glyphicon-briefcase { &:before { content: "\e139"; } } +.glyphicon-fullscreen { &:before { content: "\e140"; } } +.glyphicon-dashboard { &:before { content: "\e141"; } } +.glyphicon-paperclip { &:before { content: "\e142"; } } +.glyphicon-heart-empty { &:before { content: "\e143"; } } +.glyphicon-link { &:before { content: "\e144"; } } +.glyphicon-phone { &:before { content: "\e145"; } } +.glyphicon-pushpin { &:before { content: "\e146"; } } +.glyphicon-usd { &:before { content: "\e148"; } } +.glyphicon-gbp { &:before { content: "\e149"; } } +.glyphicon-sort { &:before { content: "\e150"; } } +.glyphicon-sort-by-alphabet { &:before { content: "\e151"; } } +.glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } } +.glyphicon-sort-by-order { &:before { content: "\e153"; } } +.glyphicon-sort-by-order-alt { &:before { content: "\e154"; } } +.glyphicon-sort-by-attributes { &:before { content: "\e155"; } } +.glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } } +.glyphicon-unchecked { &:before { content: "\e157"; } } +.glyphicon-expand { &:before { content: "\e158"; } } +.glyphicon-collapse-down { &:before { content: "\e159"; } } +.glyphicon-collapse-up { &:before { content: "\e160"; } } +.glyphicon-log-in { &:before { content: "\e161"; } } +.glyphicon-flash { &:before { content: "\e162"; } } +.glyphicon-log-out { &:before { content: "\e163"; } } +.glyphicon-new-window { &:before { content: "\e164"; } } +.glyphicon-record { &:before { content: "\e165"; } } +.glyphicon-save { &:before { content: "\e166"; } } +.glyphicon-open { &:before { content: "\e167"; } } +.glyphicon-saved { &:before { content: "\e168"; } } +.glyphicon-import { &:before { content: "\e169"; } } +.glyphicon-export { &:before { content: "\e170"; } } +.glyphicon-send { &:before { content: "\e171"; } } +.glyphicon-floppy-disk { &:before { content: "\e172"; } } +.glyphicon-floppy-saved { &:before { content: "\e173"; } } +.glyphicon-floppy-remove { &:before { content: "\e174"; } } +.glyphicon-floppy-save { &:before { content: "\e175"; } } +.glyphicon-floppy-open { &:before { content: "\e176"; } } +.glyphicon-credit-card { &:before { content: "\e177"; } } +.glyphicon-transfer { &:before { content: "\e178"; } } +.glyphicon-cutlery { &:before { content: "\e179"; } } +.glyphicon-header { &:before { content: "\e180"; } } +.glyphicon-compressed { &:before { content: "\e181"; } } +.glyphicon-earphone { &:before { content: "\e182"; } } +.glyphicon-phone-alt { &:before { content: "\e183"; } } +.glyphicon-tower { &:before { content: "\e184"; } } +.glyphicon-stats { &:before { content: "\e185"; } } +.glyphicon-sd-video { &:before { content: "\e186"; } } +.glyphicon-hd-video { &:before { content: "\e187"; } } +.glyphicon-subtitles { &:before { content: "\e188"; } } +.glyphicon-sound-stereo { &:before { content: "\e189"; } } +.glyphicon-sound-dolby { &:before { content: "\e190"; } } +.glyphicon-sound-5-1 { &:before { content: "\e191"; } } +.glyphicon-sound-6-1 { &:before { content: "\e192"; } } +.glyphicon-sound-7-1 { &:before { content: "\e193"; } } +.glyphicon-copyright-mark { &:before { content: "\e194"; } } +.glyphicon-registration-mark { &:before { content: "\e195"; } } +.glyphicon-cloud-download { &:before { content: "\e197"; } } +.glyphicon-cloud-upload { &:before { content: "\e198"; } } +.glyphicon-tree-conifer { &:before { content: "\e199"; } } +.glyphicon-tree-deciduous { &:before { content: "\e200"; } } +.glyphicon-cd { &:before { content: "\e201"; } } +.glyphicon-save-file { &:before { content: "\e202"; } } +.glyphicon-open-file { &:before { content: "\e203"; } } +.glyphicon-level-up { &:before { content: "\e204"; } } +.glyphicon-copy { &:before { content: "\e205"; } } +.glyphicon-paste { &:before { content: "\e206"; } } +// The following 2 Glyphicons are omitted for the time being because +// they currently use Unicode codepoints that are outside the +// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle +// non-BMP codepoints in CSS string escapes, and thus can't display these two icons. +// Notably, the bug affects some older versions of the Android Browser. +// More info: https://github.com/twbs/bootstrap/issues/10106 +// .glyphicon-door { &:before { content: "\1f6aa"; } } +// .glyphicon-key { &:before { content: "\1f511"; } } +.glyphicon-alert { &:before { content: "\e209"; } } +.glyphicon-equalizer { &:before { content: "\e210"; } } +.glyphicon-king { &:before { content: "\e211"; } } +.glyphicon-queen { &:before { content: "\e212"; } } +.glyphicon-pawn { &:before { content: "\e213"; } } +.glyphicon-bishop { &:before { content: "\e214"; } } +.glyphicon-knight { &:before { content: "\e215"; } } +.glyphicon-baby-formula { &:before { content: "\e216"; } } +.glyphicon-tent { &:before { content: "\26fa"; } } +.glyphicon-blackboard { &:before { content: "\e218"; } } +.glyphicon-bed { &:before { content: "\e219"; } } +.glyphicon-apple { &:before { content: "\f8ff"; } } +.glyphicon-erase { &:before { content: "\e221"; } } +.glyphicon-hourglass { &:before { content: "\231b"; } } +.glyphicon-lamp { &:before { content: "\e223"; } } +.glyphicon-duplicate { &:before { content: "\e224"; } } +.glyphicon-piggy-bank { &:before { content: "\e225"; } } +.glyphicon-scissors { &:before { content: "\e226"; } } +.glyphicon-bitcoin { &:before { content: "\e227"; } } +.glyphicon-yen { &:before { content: "\00a5"; } } +.glyphicon-ruble { &:before { content: "\20bd"; } } +.glyphicon-scale { &:before { content: "\e230"; } } +.glyphicon-ice-lolly { &:before { content: "\e231"; } } +.glyphicon-ice-lolly-tasted { &:before { content: "\e232"; } } +.glyphicon-education { &:before { content: "\e233"; } } +.glyphicon-option-horizontal { &:before { content: "\e234"; } } +.glyphicon-option-vertical { &:before { content: "\e235"; } } +.glyphicon-menu-hamburger { &:before { content: "\e236"; } } +.glyphicon-modal-window { &:before { content: "\e237"; } } +.glyphicon-oil { &:before { content: "\e238"; } } +.glyphicon-grain { &:before { content: "\e239"; } } +.glyphicon-sunglasses { &:before { content: "\e240"; } } +.glyphicon-text-size { &:before { content: "\e241"; } } +.glyphicon-text-color { &:before { content: "\e242"; } } +.glyphicon-text-background { &:before { content: "\e243"; } } +.glyphicon-object-align-top { &:before { content: "\e244"; } } +.glyphicon-object-align-bottom { &:before { content: "\e245"; } } +.glyphicon-object-align-horizontal{ &:before { content: "\e246"; } } +.glyphicon-object-align-left { &:before { content: "\e247"; } } +.glyphicon-object-align-vertical { &:before { content: "\e248"; } } +.glyphicon-object-align-right { &:before { content: "\e249"; } } +.glyphicon-triangle-right { &:before { content: "\e250"; } } +.glyphicon-triangle-left { &:before { content: "\e251"; } } +.glyphicon-triangle-bottom { &:before { content: "\e252"; } } +.glyphicon-triangle-top { &:before { content: "\e253"; } } +.glyphicon-console { &:before { content: "\e254"; } } +.glyphicon-superscript { &:before { content: "\e255"; } } +.glyphicon-subscript { &:before { content: "\e256"; } } +.glyphicon-menu-left { &:before { content: "\e257"; } } +.glyphicon-menu-right { &:before { content: "\e258"; } } +.glyphicon-menu-down { &:before { content: "\e259"; } } +.glyphicon-menu-up { &:before { content: "\e260"; } } diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/grid.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/grid.less new file mode 100644 index 000000000..e100655b7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/grid.less @@ -0,0 +1,84 @@ +// +// Grid system +// -------------------------------------------------- + + +// Container widths +// +// Set the container width, and override it for fixed navbars in media queries. + +.container { + .container-fixed(); + + @media (min-width: @screen-sm-min) { + width: @container-sm; + } + @media (min-width: @screen-md-min) { + width: @container-md; + } + @media (min-width: @screen-lg-min) { + width: @container-lg; + } +} + + +// Fluid container +// +// Utilizes the mixin meant for fixed width containers, but without any defined +// width for fluid, full width layouts. + +.container-fluid { + .container-fixed(); +} + + +// Row +// +// Rows contain and clear the floats of your columns. + +.row { + .make-row(); +} + + +// Columns +// +// Common styles for small and large grid columns + +.make-grid-columns(); + + +// Extra small grid +// +// Columns, offsets, pushes, and pulls for extra small devices like +// smartphones. + +.make-grid(xs); + + +// Small grid +// +// Columns, offsets, pushes, and pulls for the small device range, from phones +// to tablets. + +@media (min-width: @screen-sm-min) { + .make-grid(sm); +} + + +// Medium grid +// +// Columns, offsets, pushes, and pulls for the desktop device range. + +@media (min-width: @screen-md-min) { + .make-grid(md); +} + + +// Large grid +// +// Columns, offsets, pushes, and pulls for the large desktop device range. + +@media (min-width: @screen-lg-min) { + .make-grid(lg); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/input-groups.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/input-groups.less new file mode 100644 index 000000000..a8712f25b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/input-groups.less @@ -0,0 +1,166 @@ +// +// Input groups +// -------------------------------------------------- + +// Base styles +// ------------------------- +.input-group { + position: relative; // For dropdowns + display: table; + border-collapse: separate; // prevent input groups from inheriting border styles from table cells when placed within a table + + // Undo padding and float of grid classes + &[class*="col-"] { + float: none; + padding-left: 0; + padding-right: 0; + } + + .form-control { + // Ensure that the input is always above the *appended* addon button for + // proper border colors. + position: relative; + z-index: 2; + + // IE9 fubars the placeholder attribute in text inputs and the arrows on + // select elements in input groups. To fix it, we float the input. Details: + // https://github.com/twbs/bootstrap/issues/11561#issuecomment-28936855 + float: left; + + width: 100%; + margin-bottom: 0; + } +} + +// Sizing options +// +// Remix the default form control sizing classes into new ones for easier +// manipulation. + +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + .input-lg(); +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + .input-sm(); +} + + +// Display as table-cell +// ------------------------- +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; + + &:not(:first-child):not(:last-child) { + border-radius: 0; + } +} +// Addon and addon wrapper for buttons +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; // Match the inputs +} + +// Text input groups +// ------------------------- +.input-group-addon { + padding: @padding-base-vertical @padding-base-horizontal; + font-size: @font-size-base; + font-weight: normal; + line-height: 1; + color: @input-color; + text-align: center; + background-color: @input-group-addon-bg; + border: 1px solid @input-group-addon-border-color; + border-radius: @border-radius-base; + + // Sizing + &.input-sm { + padding: @padding-small-vertical @padding-small-horizontal; + font-size: @font-size-small; + border-radius: @border-radius-small; + } + &.input-lg { + padding: @padding-large-vertical @padding-large-horizontal; + font-size: @font-size-large; + border-radius: @border-radius-large; + } + + // Nuke default margins from checkboxes and radios to vertically center within. + input[type="radio"], + input[type="checkbox"] { + margin-top: 0; + } +} + +// Reset rounded corners +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + .border-right-radius(0); +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + .border-left-radius(0); +} +.input-group-addon:last-child { + border-left: 0; +} + +// Button input groups +// ------------------------- +.input-group-btn { + position: relative; + // Jankily prevent input button groups from wrapping with `white-space` and + // `font-size` in combination with `inline-block` on buttons. + font-size: 0; + white-space: nowrap; + + // Negative margin for spacing, position for bringing hovered/focused/actived + // element above the siblings. + > .btn { + position: relative; + + .btn { + margin-left: -1px; + } + // Bring the "active" button to the front + &:hover, + &:focus, + &:active { + z-index: 2; + } + } + + // Negative margin to only have a 1px border between the two + &:first-child { + > .btn, + > .btn-group { + margin-right: -1px; + } + } + &:last-child { + > .btn, + > .btn-group { + margin-left: -1px; + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/jumbotron.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/jumbotron.less new file mode 100644 index 000000000..d6410238a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/jumbotron.less @@ -0,0 +1,50 @@ +// +// Jumbotron +// -------------------------------------------------- + + +.jumbotron { + padding: @jumbotron-padding (@jumbotron-padding / 2); + margin-bottom: @jumbotron-padding; + color: @jumbotron-color; + background-color: @jumbotron-bg; + + h1, + .h1 { + color: @jumbotron-heading-color; + } + + p { + margin-bottom: (@jumbotron-padding / 2); + font-size: @jumbotron-font-size; + font-weight: 200; + } + + > hr { + border-top-color: darken(@jumbotron-bg, 10%); + } + + .container &, + .container-fluid & { + border-radius: @border-radius-large; // Only round corners at higher resolutions if contained in a container + } + + .container { + max-width: 100%; + } + + @media screen and (min-width: @screen-sm-min) { + padding: (@jumbotron-padding * 1.6) 0; + + .container &, + .container-fluid & { + padding-left: (@jumbotron-padding * 2); + padding-right: (@jumbotron-padding * 2); + } + + h1, + .h1 { + font-size: (@font-size-base * 4.5); + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/labels.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/labels.less new file mode 100644 index 000000000..9a5a27006 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/labels.less @@ -0,0 +1,64 @@ +// +// Labels +// -------------------------------------------------- + +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: @label-color; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; + + // Add hover effects, but only for links + a& { + &:hover, + &:focus { + color: @label-link-hover-color; + text-decoration: none; + cursor: pointer; + } + } + + // Empty labels collapse automatically (not available in IE8) + &:empty { + display: none; + } + + // Quick fix for labels in buttons + .btn & { + position: relative; + top: -1px; + } +} + +// Colors +// Contextual variations (linked labels get darker on :hover) + +.label-default { + .label-variant(@label-default-bg); +} + +.label-primary { + .label-variant(@label-primary-bg); +} + +.label-success { + .label-variant(@label-success-bg); +} + +.label-info { + .label-variant(@label-info-bg); +} + +.label-warning { + .label-variant(@label-warning-bg); +} + +.label-danger { + .label-variant(@label-danger-bg); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/list-group.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/list-group.less new file mode 100644 index 000000000..1462ce16b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/list-group.less @@ -0,0 +1,124 @@ +// +// List groups +// -------------------------------------------------- + + +// Base class +// +// Easily usable on <ul>, <ol>, or <div>. + +.list-group { + // No need to set list-style: none; since .list-group-item is block level + margin-bottom: 20px; + padding-left: 0; // reset padding because ul and ol +} + + +// Individual list items +// +// Use on `li`s or `div`s within the `.list-group` parent. + +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + // Place the border on the list items and negative margin up for better styling + margin-bottom: -1px; + background-color: @list-group-bg; + border: 1px solid @list-group-border; + + // Round the first and last items + &:first-child { + .border-top-radius(@list-group-border-radius); + } + &:last-child { + margin-bottom: 0; + .border-bottom-radius(@list-group-border-radius); + } +} + + +// Linked list items +// +// Use anchor elements instead of `li`s or `div`s to create linked list items. +// Includes an extra `.active` modifier class for showing selected items. + +a.list-group-item { + color: @list-group-link-color; + + .list-group-item-heading { + color: @list-group-link-heading-color; + } + + // Hover state + &:hover, + &:focus { + text-decoration: none; + color: @list-group-link-hover-color; + background-color: @list-group-hover-bg; + } +} + +.list-group-item { + // Disabled state + &.disabled, + &.disabled:hover, + &.disabled:focus { + background-color: @list-group-disabled-bg; + color: @list-group-disabled-color; + cursor: @cursor-disabled; + + // Force color to inherit for custom content + .list-group-item-heading { + color: inherit; + } + .list-group-item-text { + color: @list-group-disabled-text-color; + } + } + + // Active class on item itself, not parent + &.active, + &.active:hover, + &.active:focus { + z-index: 2; // Place active items above their siblings for proper border styling + color: @list-group-active-color; + background-color: @list-group-active-bg; + border-color: @list-group-active-border; + + // Force color to inherit for custom content + .list-group-item-heading, + .list-group-item-heading > small, + .list-group-item-heading > .small { + color: inherit; + } + .list-group-item-text { + color: @list-group-active-text-color; + } + } +} + + +// Contextual variants +// +// Add modifier classes to change text and background color on individual items. +// Organizationally, this must come after the `:hover` states. + +.list-group-item-variant(success; @state-success-bg; @state-success-text); +.list-group-item-variant(info; @state-info-bg; @state-info-text); +.list-group-item-variant(warning; @state-warning-bg; @state-warning-text); +.list-group-item-variant(danger; @state-danger-bg; @state-danger-text); + + +// Custom content options +// +// Extra classes for creating well-formatted content within `.list-group-item`s. + +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/media.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/media.less new file mode 100644 index 000000000..a6bf78192 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/media.less @@ -0,0 +1,61 @@ +.media { + // Proper spacing between instances of .media + margin-top: 15px; + + &:first-child { + margin-top: 0; + } +} + +.media, +.media-body { + zoom: 1; + overflow: hidden; +} + +.media-body { + width: 10000px; +} + +.media-object { + display: block; +} + +.media-right, +.media > .pull-right { + padding-left: 10px; +} + +.media-left, +.media > .pull-left { + padding-right: 10px; +} + +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} + +.media-middle { + vertical-align: middle; +} + +.media-bottom { + vertical-align: bottom; +} + +// Reset margins on headings for tighter default spacing +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} + +// Media list variation +// +// Undo default ul/ol styles +.media-list { + padding-left: 0; + list-style: none; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins.less new file mode 100644 index 000000000..af4408fc2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins.less @@ -0,0 +1,39 @@ +// Mixins +// -------------------------------------------------- + +// Utilities +@import "mixins/hide-text.less"; +@import "mixins/opacity.less"; +@import "mixins/image.less"; +@import "mixins/labels.less"; +@import "mixins/reset-filter.less"; +@import "mixins/resize.less"; +@import "mixins/responsive-visibility.less"; +@import "mixins/size.less"; +@import "mixins/tab-focus.less"; +@import "mixins/text-emphasis.less"; +@import "mixins/text-overflow.less"; +@import "mixins/vendor-prefixes.less"; + +// Components +@import "mixins/alerts.less"; +@import "mixins/buttons.less"; +@import "mixins/panels.less"; +@import "mixins/pagination.less"; +@import "mixins/list-group.less"; +@import "mixins/nav-divider.less"; +@import "mixins/forms.less"; +@import "mixins/progress-bar.less"; +@import "mixins/table-row.less"; + +// Skins +@import "mixins/background-variant.less"; +@import "mixins/border-radius.less"; +@import "mixins/gradients.less"; + +// Layout +@import "mixins/clearfix.less"; +@import "mixins/center-block.less"; +@import "mixins/nav-vertical-align.less"; +@import "mixins/grid-framework.less"; +@import "mixins/grid.less"; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/alerts.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/alerts.less new file mode 100644 index 000000000..396196f43 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/alerts.less @@ -0,0 +1,14 @@ +// Alerts + +.alert-variant(@background; @border; @text-color) { + background-color: @background; + border-color: @border; + color: @text-color; + + hr { + border-top-color: darken(@border, 5%); + } + .alert-link { + color: darken(@text-color, 10%); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/background-variant.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/background-variant.less new file mode 100644 index 000000000..556e490d4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/background-variant.less @@ -0,0 +1,8 @@ +// Contextual backgrounds + +.bg-variant(@color) { + background-color: @color; + a&:hover { + background-color: darken(@color, 10%); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/border-radius.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/border-radius.less new file mode 100644 index 000000000..ca05dbf45 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/border-radius.less @@ -0,0 +1,18 @@ +// Single side border-radius + +.border-top-radius(@radius) { + border-top-right-radius: @radius; + border-top-left-radius: @radius; +} +.border-right-radius(@radius) { + border-bottom-right-radius: @radius; + border-top-right-radius: @radius; +} +.border-bottom-radius(@radius) { + border-bottom-right-radius: @radius; + border-bottom-left-radius: @radius; +} +.border-left-radius(@radius) { + border-bottom-left-radius: @radius; + border-top-left-radius: @radius; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/buttons.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/buttons.less new file mode 100644 index 000000000..92d8a056c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/buttons.less @@ -0,0 +1,52 @@ +// Button variants +// +// Easily pump out default styles, as well as :hover, :focus, :active, +// and disabled options for all buttons + +.button-variant(@color; @background; @border) { + color: @color; + background-color: @background; + border-color: @border; + + &:hover, + &:focus, + &.focus, + &:active, + &.active, + .open > .dropdown-toggle& { + color: @color; + background-color: darken(@background, 10%); + border-color: darken(@border, 12%); + } + &:active, + &.active, + .open > .dropdown-toggle& { + background-image: none; + } + &.disabled, + &[disabled], + fieldset[disabled] & { + &, + &:hover, + &:focus, + &.focus, + &:active, + &.active { + background-color: @background; + border-color: @border; + } + } + + .badge { + color: @background; + background-color: @color; + } +} + +// Button sizes +.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { + padding: @padding-vertical @padding-horizontal; + font-size: @font-size; + line-height: @line-height; + border-radius: @border-radius; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/center-block.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/center-block.less new file mode 100644 index 000000000..d18d6de9e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/center-block.less @@ -0,0 +1,7 @@ +// Center-align a block level element + +.center-block() { + display: block; + margin-left: auto; + margin-right: auto; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/clearfix.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/clearfix.less new file mode 100644 index 000000000..3f7a3820c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/clearfix.less @@ -0,0 +1,22 @@ +// Clearfix +// +// For modern browsers +// 1. The space content is one way to avoid an Opera bug when the +// contenteditable attribute is included anywhere else in the document. +// Otherwise it causes space to appear at the top and bottom of elements +// that are clearfixed. +// 2. The use of `table` rather than `block` is only necessary if using +// `:before` to contain the top-margins of child elements. +// +// Source: http://nicolasgallagher.com/micro-clearfix-hack/ + +.clearfix() { + &:before, + &:after { + content: " "; // 1 + display: table; // 2 + } + &:after { + clear: both; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/forms.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/forms.less new file mode 100644 index 000000000..6f55ed967 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/forms.less @@ -0,0 +1,85 @@ +// Form validation states +// +// Used in forms.less to generate the form validation CSS for warnings, errors, +// and successes. + +.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) { + // Color the label and help text + .help-block, + .control-label, + .radio, + .checkbox, + .radio-inline, + .checkbox-inline, + &.radio label, + &.checkbox label, + &.radio-inline label, + &.checkbox-inline label { + color: @text-color; + } + // Set the border and box shadow on specific inputs to match + .form-control { + border-color: @border-color; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work + &:focus { + border-color: darken(@border-color, 10%); + @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%); + .box-shadow(@shadow); + } + } + // Set validation states also for addons + .input-group-addon { + color: @text-color; + border-color: @border-color; + background-color: @background-color; + } + // Optional feedback icon + .form-control-feedback { + color: @text-color; + } +} + + +// Form control focus state +// +// Generate a customized focus state and for any input with the specified color, +// which defaults to the `@input-border-focus` variable. +// +// We highly encourage you to not customize the default value, but instead use +// this to tweak colors on an as-needed basis. This aesthetic change is based on +// WebKit's default styles, but applicable to a wider range of browsers. Its +// usability and accessibility should be taken into account with any change. +// +// Example usage: change the default blue border and shadow to white for better +// contrast against a dark gray background. +.form-control-focus(@color: @input-border-focus) { + @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); + &:focus { + border-color: @color; + outline: 0; + .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); + } +} + +// Form control sizing +// +// Relative text size, padding, and border-radii changes for form controls. For +// horizontal sizing, wrap controls in the predefined grid classes. `<select>` +// element gets special love because it's special, and that's a fact! +.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) { + height: @input-height; + padding: @padding-vertical @padding-horizontal; + font-size: @font-size; + line-height: @line-height; + border-radius: @border-radius; + + select& { + height: @input-height; + line-height: @input-height; + } + + textarea&, + select[multiple]& { + height: auto; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/gradients.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/gradients.less new file mode 100644 index 000000000..0b88a89cc --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/gradients.less @@ -0,0 +1,59 @@ +// Gradients + +#gradient { + + // Horizontal gradient, from left to right + // + // Creates two color stops, start and end, by specifying a color and position for each color stop. + // Color stops are not available in IE9 and below. + .horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) { + background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+ + background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12 + background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down + } + + // Vertical gradient, from top to bottom + // + // Creates two color stops, start and end, by specifying a color and position for each color stop. + // Color stops are not available in IE9 and below. + .vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) { + background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+ + background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12 + background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ + background-repeat: repeat-x; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down + } + + .directional(@start-color: #555; @end-color: #333; @deg: 45deg) { + background-repeat: repeat-x; + background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+ + background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12 + background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+ + } + .horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) { + background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color); + background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color); + background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback + } + .vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) { + background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color); + background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color); + background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color); + background-repeat: no-repeat; + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@start-color),argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback + } + .radial(@inner-color: #555; @outer-color: #333) { + background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color); + background-image: radial-gradient(circle, @inner-color, @outer-color); + background-repeat: no-repeat; + } + .striped(@color: rgba(255,255,255,.15); @angle: 45deg) { + background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent); + background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/grid-framework.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/grid-framework.less new file mode 100644 index 000000000..f3b3929d6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/grid-framework.less @@ -0,0 +1,91 @@ +// Framework grid generation +// +// Used only by Bootstrap to generate the correct number of grid classes given +// any value of `@grid-columns`. + +.make-grid-columns() { + // Common styles for all sizes of grid columns, widths 1-12 + .col(@index) { // initial + @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}"; + .col((@index + 1), @item); + } + .col(@index, @list) when (@index =< @grid-columns) { // general; "=<" isn't a typo + @item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}"; + .col((@index + 1), ~"@{list}, @{item}"); + } + .col(@index, @list) when (@index > @grid-columns) { // terminal + @{list} { + position: relative; + // Prevent columns from collapsing when empty + min-height: 1px; + // Inner gutter via padding + padding-left: (@grid-gutter-width / 2); + padding-right: (@grid-gutter-width / 2); + } + } + .col(1); // kickstart it +} + +.float-grid-columns(@class) { + .col(@index) { // initial + @item: ~".col-@{class}-@{index}"; + .col((@index + 1), @item); + } + .col(@index, @list) when (@index =< @grid-columns) { // general + @item: ~".col-@{class}-@{index}"; + .col((@index + 1), ~"@{list}, @{item}"); + } + .col(@index, @list) when (@index > @grid-columns) { // terminal + @{list} { + float: left; + } + } + .col(1); // kickstart it +} + +.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) { + .col-@{class}-@{index} { + width: percentage((@index / @grid-columns)); + } +} +.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) { + .col-@{class}-push-@{index} { + left: percentage((@index / @grid-columns)); + } +} +.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) { + .col-@{class}-push-0 { + left: auto; + } +} +.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) { + .col-@{class}-pull-@{index} { + right: percentage((@index / @grid-columns)); + } +} +.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) { + .col-@{class}-pull-0 { + right: auto; + } +} +.calc-grid-column(@index, @class, @type) when (@type = offset) { + .col-@{class}-offset-@{index} { + margin-left: percentage((@index / @grid-columns)); + } +} + +// Basic looping in LESS +.loop-grid-columns(@index, @class, @type) when (@index >= 0) { + .calc-grid-column(@index, @class, @type); + // next iteration + .loop-grid-columns((@index - 1), @class, @type); +} + +// Create grid for specific class +.make-grid(@class) { + .float-grid-columns(@class); + .loop-grid-columns(@grid-columns, @class, width); + .loop-grid-columns(@grid-columns, @class, pull); + .loop-grid-columns(@grid-columns, @class, push); + .loop-grid-columns(@grid-columns, @class, offset); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/grid.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/grid.less new file mode 100644 index 000000000..cae5eaff9 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/grid.less @@ -0,0 +1,122 @@ +// Grid system +// +// Generate semantic grid columns with these mixins. + +// Centered container element +.container-fixed(@gutter: @grid-gutter-width) { + margin-right: auto; + margin-left: auto; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + &:extend(.clearfix all); +} + +// Creates a wrapper for a series of columns +.make-row(@gutter: @grid-gutter-width) { + margin-left: (@gutter / -2); + margin-right: (@gutter / -2); + &:extend(.clearfix all); +} + +// Generate the extra small columns +.make-xs-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + float: left; + width: percentage((@columns / @grid-columns)); + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); +} +.make-xs-column-offset(@columns) { + margin-left: percentage((@columns / @grid-columns)); +} +.make-xs-column-push(@columns) { + left: percentage((@columns / @grid-columns)); +} +.make-xs-column-pull(@columns) { + right: percentage((@columns / @grid-columns)); +} + +// Generate the small columns +.make-sm-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + + @media (min-width: @screen-sm-min) { + float: left; + width: percentage((@columns / @grid-columns)); + } +} +.make-sm-column-offset(@columns) { + @media (min-width: @screen-sm-min) { + margin-left: percentage((@columns / @grid-columns)); + } +} +.make-sm-column-push(@columns) { + @media (min-width: @screen-sm-min) { + left: percentage((@columns / @grid-columns)); + } +} +.make-sm-column-pull(@columns) { + @media (min-width: @screen-sm-min) { + right: percentage((@columns / @grid-columns)); + } +} + +// Generate the medium columns +.make-md-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + + @media (min-width: @screen-md-min) { + float: left; + width: percentage((@columns / @grid-columns)); + } +} +.make-md-column-offset(@columns) { + @media (min-width: @screen-md-min) { + margin-left: percentage((@columns / @grid-columns)); + } +} +.make-md-column-push(@columns) { + @media (min-width: @screen-md-min) { + left: percentage((@columns / @grid-columns)); + } +} +.make-md-column-pull(@columns) { + @media (min-width: @screen-md-min) { + right: percentage((@columns / @grid-columns)); + } +} + +// Generate the large columns +.make-lg-column(@columns; @gutter: @grid-gutter-width) { + position: relative; + min-height: 1px; + padding-left: (@gutter / 2); + padding-right: (@gutter / 2); + + @media (min-width: @screen-lg-min) { + float: left; + width: percentage((@columns / @grid-columns)); + } +} +.make-lg-column-offset(@columns) { + @media (min-width: @screen-lg-min) { + margin-left: percentage((@columns / @grid-columns)); + } +} +.make-lg-column-push(@columns) { + @media (min-width: @screen-lg-min) { + left: percentage((@columns / @grid-columns)); + } +} +.make-lg-column-pull(@columns) { + @media (min-width: @screen-lg-min) { + right: percentage((@columns / @grid-columns)); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/hide-text.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/hide-text.less new file mode 100644 index 000000000..c2315e572 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/hide-text.less @@ -0,0 +1,21 @@ +// CSS image replacement +// +// Heads up! v3 launched with with only `.hide-text()`, but per our pattern for +// mixins being reused as classes with the same name, this doesn't hold up. As +// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. +// +// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 + +// Deprecated as of v3.0.1 (will be removed in v4) +.hide-text() { + font: ~"0/0" a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +// New mixin to use as of v3.0.1 +.text-hide() { + .hide-text(); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/image.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/image.less new file mode 100644 index 000000000..f233cb3e1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/image.less @@ -0,0 +1,33 @@ +// Image Mixins +// - Responsive image +// - Retina image + + +// Responsive image +// +// Keep images from scaling beyond the width of their parents. +.img-responsive(@display: block) { + display: @display; + max-width: 100%; // Part 1: Set a maximum relative to the parent + height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching +} + + +// Retina image +// +// Short retina mixin for setting background-image and -size. Note that the +// spelling of `min--moz-device-pixel-ratio` is intentional. +.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) { + background-image: url("@{file-1x}"); + + @media + only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and ( min--moz-device-pixel-ratio: 2), + only screen and ( -o-min-device-pixel-ratio: 2/1), + only screen and ( min-device-pixel-ratio: 2), + only screen and ( min-resolution: 192dpi), + only screen and ( min-resolution: 2dppx) { + background-image: url("@{file-2x}"); + background-size: @width-1x @height-1x; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/labels.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/labels.less new file mode 100644 index 000000000..9f7a67ee3 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/labels.less @@ -0,0 +1,12 @@ +// Labels + +.label-variant(@color) { + background-color: @color; + + &[href] { + &:hover, + &:focus { + background-color: darken(@color, 10%); + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/list-group.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/list-group.less new file mode 100644 index 000000000..8b5b065cb --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/list-group.less @@ -0,0 +1,29 @@ +// List Groups + +.list-group-item-variant(@state; @background; @color) { + .list-group-item-@{state} { + color: @color; + background-color: @background; + + a& { + color: @color; + + .list-group-item-heading { + color: inherit; + } + + &:hover, + &:focus { + color: @color; + background-color: darken(@background, 5%); + } + &.active, + &.active:hover, + &.active:focus { + color: #fff; + background-color: @color; + border-color: @color; + } + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/nav-divider.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/nav-divider.less new file mode 100644 index 000000000..feb1e9ed0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/nav-divider.less @@ -0,0 +1,10 @@ +// Horizontal dividers +// +// Dividers (basically an hr) within dropdowns and nav lists + +.nav-divider(@color: #e5e5e5) { + height: 1px; + margin: ((@line-height-computed / 2) - 1) 0; + overflow: hidden; + background-color: @color; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/nav-vertical-align.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/nav-vertical-align.less new file mode 100644 index 000000000..d458c7861 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/nav-vertical-align.less @@ -0,0 +1,9 @@ +// Navbar vertical align +// +// Vertically center elements in the navbar. +// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin. + +.navbar-vertical-align(@element-height) { + margin-top: ((@navbar-height - @element-height) / 2); + margin-bottom: ((@navbar-height - @element-height) / 2); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/opacity.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/opacity.less new file mode 100644 index 000000000..33ed25ce6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/opacity.less @@ -0,0 +1,8 @@ +// Opacity + +.opacity(@opacity) { + opacity: @opacity; + // IE8 filter + @opacity-ie: (@opacity * 100); + filter: ~"alpha(opacity=@{opacity-ie})"; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/pagination.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/pagination.less new file mode 100644 index 000000000..7deb505d2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/pagination.less @@ -0,0 +1,23 @@ +// Pagination + +.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @border-radius) { + > li { + > a, + > span { + padding: @padding-vertical @padding-horizontal; + font-size: @font-size; + } + &:first-child { + > a, + > span { + .border-left-radius(@border-radius); + } + } + &:last-child { + > a, + > span { + .border-right-radius(@border-radius); + } + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/panels.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/panels.less new file mode 100644 index 000000000..49ee10d4a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/panels.less @@ -0,0 +1,24 @@ +// Panels + +.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) { + border-color: @border; + + & > .panel-heading { + color: @heading-text-color; + background-color: @heading-bg-color; + border-color: @heading-border; + + + .panel-collapse > .panel-body { + border-top-color: @border; + } + .badge { + color: @heading-bg-color; + background-color: @heading-text-color; + } + } + & > .panel-footer { + + .panel-collapse > .panel-body { + border-bottom-color: @border; + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/progress-bar.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/progress-bar.less new file mode 100644 index 000000000..f07996a34 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/progress-bar.less @@ -0,0 +1,10 @@ +// Progress bars + +.progress-bar-variant(@color) { + background-color: @color; + + // Deprecated parent class requirement as of v3.2.0 + .progress-striped & { + #gradient > .striped(); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/reset-filter.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/reset-filter.less new file mode 100644 index 000000000..68cdb5e18 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/reset-filter.less @@ -0,0 +1,8 @@ +// Reset filters for IE +// +// When you need to remove a gradient background, do not forget to use this to reset +// the IE filter for IE9 and below. + +.reset-filter() { + filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)")); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/resize.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/resize.less new file mode 100644 index 000000000..3acd3afdb --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/resize.less @@ -0,0 +1,6 @@ +// Resize anything + +.resizable(@direction) { + resize: @direction; // Options: horizontal, vertical, both + overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/responsive-visibility.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/responsive-visibility.less new file mode 100644 index 000000000..f7951c3d7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/responsive-visibility.less @@ -0,0 +1,15 @@ +// Responsive utilities + +// +// More easily include all the states for responsive-utilities.less. +.responsive-visibility() { + display: block !important; + table& { display: table; } + tr& { display: table-row !important; } + th&, + td& { display: table-cell !important; } +} + +.responsive-invisibility() { + display: none !important; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/size.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/size.less new file mode 100644 index 000000000..a8be65089 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/size.less @@ -0,0 +1,10 @@ +// Sizing shortcuts + +.size(@width; @height) { + width: @width; + height: @height; +} + +.square(@size) { + .size(@size; @size); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/tab-focus.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/tab-focus.less new file mode 100644 index 000000000..1f1f05ab0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/tab-focus.less @@ -0,0 +1,9 @@ +// WebKit-style focus + +.tab-focus() { + // Default + outline: thin dotted; + // WebKit + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/table-row.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/table-row.less new file mode 100644 index 000000000..0f287f1a8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/table-row.less @@ -0,0 +1,28 @@ +// Tables + +.table-row-variant(@state; @background) { + // Exact selectors below required to override `.table-striped` and prevent + // inheritance to nested tables. + .table > thead > tr, + .table > tbody > tr, + .table > tfoot > tr { + > td.@{state}, + > th.@{state}, + &.@{state} > td, + &.@{state} > th { + background-color: @background; + } + } + + // Hover states for `.table-hover` + // Note: this is not available for cells or rows within `thead` or `tfoot`. + .table-hover > tbody > tr { + > td.@{state}:hover, + > th.@{state}:hover, + &.@{state}:hover > td, + &:hover > .@{state}, + &.@{state}:hover > th { + background-color: darken(@background, 5%); + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/text-emphasis.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/text-emphasis.less new file mode 100644 index 000000000..0868ef9f2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/text-emphasis.less @@ -0,0 +1,8 @@ +// Typography + +.text-emphasis-variant(@color) { + color: @color; + a&:hover { + color: darken(@color, 10%); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/text-overflow.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/text-overflow.less new file mode 100644 index 000000000..c11ad2fb7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/text-overflow.less @@ -0,0 +1,8 @@ +// Text overflow +// Requires inline-block or block for proper styling + +.text-overflow() { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/vendor-prefixes.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/vendor-prefixes.less new file mode 100644 index 000000000..afd3331c3 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/mixins/vendor-prefixes.less @@ -0,0 +1,227 @@ +// Vendor Prefixes +// +// All vendor mixins are deprecated as of v3.2.0 due to the introduction of +// Autoprefixer in our Gruntfile. They will be removed in v4. + +// - Animations +// - Backface visibility +// - Box shadow +// - Box sizing +// - Content columns +// - Hyphens +// - Placeholder text +// - Transformations +// - Transitions +// - User Select + + +// Animations +.animation(@animation) { + -webkit-animation: @animation; + -o-animation: @animation; + animation: @animation; +} +.animation-name(@name) { + -webkit-animation-name: @name; + animation-name: @name; +} +.animation-duration(@duration) { + -webkit-animation-duration: @duration; + animation-duration: @duration; +} +.animation-timing-function(@timing-function) { + -webkit-animation-timing-function: @timing-function; + animation-timing-function: @timing-function; +} +.animation-delay(@delay) { + -webkit-animation-delay: @delay; + animation-delay: @delay; +} +.animation-iteration-count(@iteration-count) { + -webkit-animation-iteration-count: @iteration-count; + animation-iteration-count: @iteration-count; +} +.animation-direction(@direction) { + -webkit-animation-direction: @direction; + animation-direction: @direction; +} +.animation-fill-mode(@fill-mode) { + -webkit-animation-fill-mode: @fill-mode; + animation-fill-mode: @fill-mode; +} + +// Backface visibility +// Prevent browsers from flickering when using CSS 3D transforms. +// Default value is `visible`, but can be changed to `hidden` + +.backface-visibility(@visibility){ + -webkit-backface-visibility: @visibility; + -moz-backface-visibility: @visibility; + backface-visibility: @visibility; +} + +// Drop shadows +// +// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's +// supported browsers that have box shadow capabilities now support it. + +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1 + box-shadow: @shadow; +} + +// Box sizing +.box-sizing(@boxmodel) { + -webkit-box-sizing: @boxmodel; + -moz-box-sizing: @boxmodel; + box-sizing: @boxmodel; +} + +// CSS3 Content Columns +.content-columns(@column-count; @column-gap: @grid-gutter-width) { + -webkit-column-count: @column-count; + -moz-column-count: @column-count; + column-count: @column-count; + -webkit-column-gap: @column-gap; + -moz-column-gap: @column-gap; + column-gap: @column-gap; +} + +// Optional hyphenation +.hyphens(@mode: auto) { + word-wrap: break-word; + -webkit-hyphens: @mode; + -moz-hyphens: @mode; + -ms-hyphens: @mode; // IE10+ + -o-hyphens: @mode; + hyphens: @mode; +} + +// Placeholder text +.placeholder(@color: @input-color-placeholder) { + // Firefox + &::-moz-placeholder { + color: @color; + opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526 + } + &:-ms-input-placeholder { color: @color; } // Internet Explorer 10+ + &::-webkit-input-placeholder { color: @color; } // Safari and Chrome +} + +// Transformations +.scale(@ratio) { + -webkit-transform: scale(@ratio); + -ms-transform: scale(@ratio); // IE9 only + -o-transform: scale(@ratio); + transform: scale(@ratio); +} +.scale(@ratioX; @ratioY) { + -webkit-transform: scale(@ratioX, @ratioY); + -ms-transform: scale(@ratioX, @ratioY); // IE9 only + -o-transform: scale(@ratioX, @ratioY); + transform: scale(@ratioX, @ratioY); +} +.scaleX(@ratio) { + -webkit-transform: scaleX(@ratio); + -ms-transform: scaleX(@ratio); // IE9 only + -o-transform: scaleX(@ratio); + transform: scaleX(@ratio); +} +.scaleY(@ratio) { + -webkit-transform: scaleY(@ratio); + -ms-transform: scaleY(@ratio); // IE9 only + -o-transform: scaleY(@ratio); + transform: scaleY(@ratio); +} +.skew(@x; @y) { + -webkit-transform: skewX(@x) skewY(@y); + -ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+ + -o-transform: skewX(@x) skewY(@y); + transform: skewX(@x) skewY(@y); +} +.translate(@x; @y) { + -webkit-transform: translate(@x, @y); + -ms-transform: translate(@x, @y); // IE9 only + -o-transform: translate(@x, @y); + transform: translate(@x, @y); +} +.translate3d(@x; @y; @z) { + -webkit-transform: translate3d(@x, @y, @z); + transform: translate3d(@x, @y, @z); +} +.rotate(@degrees) { + -webkit-transform: rotate(@degrees); + -ms-transform: rotate(@degrees); // IE9 only + -o-transform: rotate(@degrees); + transform: rotate(@degrees); +} +.rotateX(@degrees) { + -webkit-transform: rotateX(@degrees); + -ms-transform: rotateX(@degrees); // IE9 only + -o-transform: rotateX(@degrees); + transform: rotateX(@degrees); +} +.rotateY(@degrees) { + -webkit-transform: rotateY(@degrees); + -ms-transform: rotateY(@degrees); // IE9 only + -o-transform: rotateY(@degrees); + transform: rotateY(@degrees); +} +.perspective(@perspective) { + -webkit-perspective: @perspective; + -moz-perspective: @perspective; + perspective: @perspective; +} +.perspective-origin(@perspective) { + -webkit-perspective-origin: @perspective; + -moz-perspective-origin: @perspective; + perspective-origin: @perspective; +} +.transform-origin(@origin) { + -webkit-transform-origin: @origin; + -moz-transform-origin: @origin; + -ms-transform-origin: @origin; // IE9 only + transform-origin: @origin; +} + + +// Transitions + +.transition(@transition) { + -webkit-transition: @transition; + -o-transition: @transition; + transition: @transition; +} +.transition-property(@transition-property) { + -webkit-transition-property: @transition-property; + transition-property: @transition-property; +} +.transition-delay(@transition-delay) { + -webkit-transition-delay: @transition-delay; + transition-delay: @transition-delay; +} +.transition-duration(@transition-duration) { + -webkit-transition-duration: @transition-duration; + transition-duration: @transition-duration; +} +.transition-timing-function(@timing-function) { + -webkit-transition-timing-function: @timing-function; + transition-timing-function: @timing-function; +} +.transition-transform(@transition) { + -webkit-transition: -webkit-transform @transition; + -moz-transition: -moz-transform @transition; + -o-transition: -o-transform @transition; + transition: transform @transition; +} + + +// User select +// For selecting text on the page + +.user-select(@select) { + -webkit-user-select: @select; + -moz-user-select: @select; + -ms-user-select: @select; // IE10+ + user-select: @select; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/modals.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/modals.less new file mode 100644 index 000000000..032a497d6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/modals.less @@ -0,0 +1,148 @@ +// +// Modals +// -------------------------------------------------- + +// .modal-open - body class for killing the scroll +// .modal - container to scroll within +// .modal-dialog - positioning shell for the actual modal +// .modal-content - actual modal w/ bg and corners and shit + +// Kill the scroll on the body +.modal-open { + overflow: hidden; +} + +// Container that the modal scrolls within +.modal { + display: none; + overflow: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: @zindex-modal; + -webkit-overflow-scrolling: touch; + + // Prevent Chrome on Windows from adding a focus outline. For details, see + // https://github.com/twbs/bootstrap/pull/10951. + outline: 0; + + // When fading in the modal, animate it to slide down + &.fade .modal-dialog { + .translate(0, -25%); + .transition-transform(~"0.3s ease-out"); + } + &.in .modal-dialog { .translate(0, 0) } +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +// Shell div to position the modal with bottom padding +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} + +// Actual modal +.modal-content { + position: relative; + background-color: @modal-content-bg; + border: 1px solid @modal-content-fallback-border-color; //old browsers fallback (ie8 etc) + border: 1px solid @modal-content-border-color; + border-radius: @border-radius-large; + .box-shadow(0 3px 9px rgba(0,0,0,.5)); + background-clip: padding-box; + // Remove focus outline from opened modal + outline: 0; +} + +// Modal background +.modal-backdrop { + position: absolute; + top: 0; + right: 0; + left: 0; + background-color: @modal-backdrop-bg; + // Fade for backdrop + &.fade { .opacity(0); } + &.in { .opacity(@modal-backdrop-opacity); } +} + +// Modal header +// Top section of the modal w/ title and dismiss +.modal-header { + padding: @modal-title-padding; + border-bottom: 1px solid @modal-header-border-color; + min-height: (@modal-title-padding + @modal-title-line-height); +} +// Close icon +.modal-header .close { + margin-top: -2px; +} + +// Title text within header +.modal-title { + margin: 0; + line-height: @modal-title-line-height; +} + +// Modal body +// Where all modal content resides (sibling of .modal-header and .modal-footer) +.modal-body { + position: relative; + padding: @modal-inner-padding; +} + +// Footer (for actions) +.modal-footer { + padding: @modal-inner-padding; + text-align: right; // right align buttons + border-top: 1px solid @modal-footer-border-color; + &:extend(.clearfix all); // clear it in case folks use .pull-* classes on buttons + + // Properly space out buttons + .btn + .btn { + margin-left: 5px; + margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs + } + // but override that for button groups + .btn-group .btn + .btn { + margin-left: -1px; + } + // and override it for block buttons as well + .btn-block + .btn-block { + margin-left: 0; + } +} + +// Measure scrollbar width for padding body during modal show/hide +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +// Scale up the modal +@media (min-width: @screen-sm-min) { + // Automatically set modal's width for larger viewports + .modal-dialog { + width: @modal-md; + margin: 30px auto; + } + .modal-content { + .box-shadow(0 5px 15px rgba(0,0,0,.5)); + } + + // Modal sizes + .modal-sm { width: @modal-sm; } +} + +@media (min-width: @screen-md-min) { + .modal-lg { width: @modal-lg; } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/navbar.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/navbar.less new file mode 100644 index 000000000..0c835030e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/navbar.less @@ -0,0 +1,661 @@ +// +// Navbars +// -------------------------------------------------- + + +// Wrapper and base class +// +// Provide a static navbar from which we expand to create full-width, fixed, and +// other navbar variations. + +.navbar { + position: relative; + min-height: @navbar-height; // Ensure a navbar always shows (e.g., without a .navbar-brand in collapsed mode) + margin-bottom: @navbar-margin-bottom; + border: 1px solid transparent; + + // Prevent floats from breaking the navbar + &:extend(.clearfix all); + + @media (min-width: @grid-float-breakpoint) { + border-radius: @navbar-border-radius; + } +} + + +// Navbar heading +// +// Groups `.navbar-brand` and `.navbar-toggle` into a single component for easy +// styling of responsive aspects. + +.navbar-header { + &:extend(.clearfix all); + + @media (min-width: @grid-float-breakpoint) { + float: left; + } +} + + +// Navbar collapse (body) +// +// Group your navbar content into this for easy collapsing and expanding across +// various device sizes. By default, this content is collapsed when <768px, but +// will expand past that for a horizontal display. +// +// To start (on mobile devices) the navbar links, forms, and buttons are stacked +// vertically and include a `max-height` to overflow in case you have too much +// content for the user's viewport. + +.navbar-collapse { + overflow-x: visible; + padding-right: @navbar-padding-horizontal; + padding-left: @navbar-padding-horizontal; + border-top: 1px solid transparent; + box-shadow: inset 0 1px 0 rgba(255,255,255,.1); + &:extend(.clearfix all); + -webkit-overflow-scrolling: touch; + + &.in { + overflow-y: auto; + } + + @media (min-width: @grid-float-breakpoint) { + width: auto; + border-top: 0; + box-shadow: none; + + &.collapse { + display: block !important; + visibility: visible !important; + height: auto !important; + padding-bottom: 0; // Override default setting + overflow: visible !important; + } + + &.in { + overflow-y: visible; + } + + // Undo the collapse side padding for navbars with containers to ensure + // alignment of right-aligned contents. + .navbar-fixed-top &, + .navbar-static-top &, + .navbar-fixed-bottom & { + padding-left: 0; + padding-right: 0; + } + } +} + +.navbar-fixed-top, +.navbar-fixed-bottom { + .navbar-collapse { + max-height: @navbar-collapse-max-height; + + @media (max-device-width: @screen-xs-min) and (orientation: landscape) { + max-height: 200px; + } + } +} + + +// Both navbar header and collapse +// +// When a container is present, change the behavior of the header and collapse. + +.container, +.container-fluid { + > .navbar-header, + > .navbar-collapse { + margin-right: -@navbar-padding-horizontal; + margin-left: -@navbar-padding-horizontal; + + @media (min-width: @grid-float-breakpoint) { + margin-right: 0; + margin-left: 0; + } + } +} + + +// +// Navbar alignment options +// +// Display the navbar across the entirety of the page or fixed it to the top or +// bottom of the page. + +// Static top (unfixed, but 100% wide) navbar +.navbar-static-top { + z-index: @zindex-navbar; + border-width: 0 0 1px; + + @media (min-width: @grid-float-breakpoint) { + border-radius: 0; + } +} + +// Fix the top/bottom navbars when screen real estate supports it +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: @zindex-navbar-fixed; + + // Undo the rounded corners + @media (min-width: @grid-float-breakpoint) { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; // override .navbar defaults + border-width: 1px 0 0; +} + + +// Brand/project name + +.navbar-brand { + float: left; + padding: @navbar-padding-vertical @navbar-padding-horizontal; + font-size: @font-size-large; + line-height: @line-height-computed; + height: @navbar-height; + + &:hover, + &:focus { + text-decoration: none; + } + + > img { + display: block; + } + + @media (min-width: @grid-float-breakpoint) { + .navbar > .container &, + .navbar > .container-fluid & { + margin-left: -@navbar-padding-horizontal; + } + } +} + + +// Navbar toggle +// +// Custom button for toggling the `.navbar-collapse`, powered by the collapse +// JavaScript plugin. + +.navbar-toggle { + position: relative; + float: right; + margin-right: @navbar-padding-horizontal; + padding: 9px 10px; + .navbar-vertical-align(34px); + background-color: transparent; + background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 + border: 1px solid transparent; + border-radius: @border-radius-base; + + // We remove the `outline` here, but later compensate by attaching `:hover` + // styles to `:focus`. + &:focus { + outline: 0; + } + + // Bars + .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; + } + .icon-bar + .icon-bar { + margin-top: 4px; + } + + @media (min-width: @grid-float-breakpoint) { + display: none; + } +} + + +// Navbar nav links +// +// Builds on top of the `.nav` components with its own modifier class to make +// the nav the full height of the horizontal nav (above 768px). + +.navbar-nav { + margin: (@navbar-padding-vertical / 2) -@navbar-padding-horizontal; + + > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: @line-height-computed; + } + + @media (max-width: @grid-float-breakpoint-max) { + // Dropdowns get custom display when collapsed + .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + box-shadow: none; + > li > a, + .dropdown-header { + padding: 5px 15px 5px 25px; + } + > li > a { + line-height: @line-height-computed; + &:hover, + &:focus { + background-image: none; + } + } + } + } + + // Uncollapse the nav + @media (min-width: @grid-float-breakpoint) { + float: left; + margin: 0; + + > li { + float: left; + > a { + padding-top: @navbar-padding-vertical; + padding-bottom: @navbar-padding-vertical; + } + } + } +} + + +// Navbar form +// +// Extension of the `.form-inline` with some extra flavor for optimum display in +// our navbars. + +.navbar-form { + margin-left: -@navbar-padding-horizontal; + margin-right: -@navbar-padding-horizontal; + padding: 10px @navbar-padding-horizontal; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + @shadow: inset 0 1px 0 rgba(255,255,255,.1), 0 1px 0 rgba(255,255,255,.1); + .box-shadow(@shadow); + + // Mixin behavior for optimum display + .form-inline(); + + .form-group { + @media (max-width: @grid-float-breakpoint-max) { + margin-bottom: 5px; + + &:last-child { + margin-bottom: 0; + } + } + } + + // Vertically center in expanded, horizontal navbar + .navbar-vertical-align(@input-height-base); + + // Undo 100% width for pull classes + @media (min-width: @grid-float-breakpoint) { + width: auto; + border: 0; + margin-left: 0; + margin-right: 0; + padding-top: 0; + padding-bottom: 0; + .box-shadow(none); + } +} + + +// Dropdown menus + +// Menu position and menu carets +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + .border-top-radius(0); +} +// Menu position and menu caret support for dropups via extra dropup class +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + margin-bottom: 0; + .border-top-radius(@navbar-border-radius); + .border-bottom-radius(0); +} + + +// Buttons in navbars +// +// Vertically center a button within a navbar (when *not* in a form). + +.navbar-btn { + .navbar-vertical-align(@input-height-base); + + &.btn-sm { + .navbar-vertical-align(@input-height-small); + } + &.btn-xs { + .navbar-vertical-align(22); + } +} + + +// Text in navbars +// +// Add a class to make any element properly align itself vertically within the navbars. + +.navbar-text { + .navbar-vertical-align(@line-height-computed); + + @media (min-width: @grid-float-breakpoint) { + float: left; + margin-left: @navbar-padding-horizontal; + margin-right: @navbar-padding-horizontal; + } +} + + +// Component alignment +// +// Repurpose the pull utilities as their own navbar utilities to avoid specificity +// issues with parents and chaining. Only do this when the navbar is uncollapsed +// though so that navbar contents properly stack and align in mobile. +// +// Declared after the navbar components to ensure more specificity on the margins. + +@media (min-width: @grid-float-breakpoint) { + .navbar-left { .pull-left(); } + .navbar-right { + .pull-right(); + margin-right: -@navbar-padding-horizontal; + + ~ .navbar-right { + margin-right: 0; + } + } +} + + +// Alternate navbars +// -------------------------------------------------- + +// Default navbar +.navbar-default { + background-color: @navbar-default-bg; + border-color: @navbar-default-border; + + .navbar-brand { + color: @navbar-default-brand-color; + &:hover, + &:focus { + color: @navbar-default-brand-hover-color; + background-color: @navbar-default-brand-hover-bg; + } + } + + .navbar-text { + color: @navbar-default-color; + } + + .navbar-nav { + > li > a { + color: @navbar-default-link-color; + + &:hover, + &:focus { + color: @navbar-default-link-hover-color; + background-color: @navbar-default-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-active-color; + background-color: @navbar-default-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-disabled-color; + background-color: @navbar-default-link-disabled-bg; + } + } + } + + .navbar-toggle { + border-color: @navbar-default-toggle-border-color; + &:hover, + &:focus { + background-color: @navbar-default-toggle-hover-bg; + } + .icon-bar { + background-color: @navbar-default-toggle-icon-bar-bg; + } + } + + .navbar-collapse, + .navbar-form { + border-color: @navbar-default-border; + } + + // Dropdown menu items + .navbar-nav { + // Remove background color from open dropdown + > .open > a { + &, + &:hover, + &:focus { + background-color: @navbar-default-link-active-bg; + color: @navbar-default-link-active-color; + } + } + + @media (max-width: @grid-float-breakpoint-max) { + // Dropdowns get custom display when collapsed + .open .dropdown-menu { + > li > a { + color: @navbar-default-link-color; + &:hover, + &:focus { + color: @navbar-default-link-hover-color; + background-color: @navbar-default-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-active-color; + background-color: @navbar-default-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-default-link-disabled-color; + background-color: @navbar-default-link-disabled-bg; + } + } + } + } + } + + + // Links in navbars + // + // Add a class to ensure links outside the navbar nav are colored correctly. + + .navbar-link { + color: @navbar-default-link-color; + &:hover { + color: @navbar-default-link-hover-color; + } + } + + .btn-link { + color: @navbar-default-link-color; + &:hover, + &:focus { + color: @navbar-default-link-hover-color; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @navbar-default-link-disabled-color; + } + } + } +} + +// Inverse navbar + +.navbar-inverse { + background-color: @navbar-inverse-bg; + border-color: @navbar-inverse-border; + + .navbar-brand { + color: @navbar-inverse-brand-color; + &:hover, + &:focus { + color: @navbar-inverse-brand-hover-color; + background-color: @navbar-inverse-brand-hover-bg; + } + } + + .navbar-text { + color: @navbar-inverse-color; + } + + .navbar-nav { + > li > a { + color: @navbar-inverse-link-color; + + &:hover, + &:focus { + color: @navbar-inverse-link-hover-color; + background-color: @navbar-inverse-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-active-color; + background-color: @navbar-inverse-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-disabled-color; + background-color: @navbar-inverse-link-disabled-bg; + } + } + } + + // Darken the responsive nav toggle + .navbar-toggle { + border-color: @navbar-inverse-toggle-border-color; + &:hover, + &:focus { + background-color: @navbar-inverse-toggle-hover-bg; + } + .icon-bar { + background-color: @navbar-inverse-toggle-icon-bar-bg; + } + } + + .navbar-collapse, + .navbar-form { + border-color: darken(@navbar-inverse-bg, 7%); + } + + // Dropdowns + .navbar-nav { + > .open > a { + &, + &:hover, + &:focus { + background-color: @navbar-inverse-link-active-bg; + color: @navbar-inverse-link-active-color; + } + } + + @media (max-width: @grid-float-breakpoint-max) { + // Dropdowns get custom display + .open .dropdown-menu { + > .dropdown-header { + border-color: @navbar-inverse-border; + } + .divider { + background-color: @navbar-inverse-border; + } + > li > a { + color: @navbar-inverse-link-color; + &:hover, + &:focus { + color: @navbar-inverse-link-hover-color; + background-color: @navbar-inverse-link-hover-bg; + } + } + > .active > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-active-color; + background-color: @navbar-inverse-link-active-bg; + } + } + > .disabled > a { + &, + &:hover, + &:focus { + color: @navbar-inverse-link-disabled-color; + background-color: @navbar-inverse-link-disabled-bg; + } + } + } + } + } + + .navbar-link { + color: @navbar-inverse-link-color; + &:hover { + color: @navbar-inverse-link-hover-color; + } + } + + .btn-link { + color: @navbar-inverse-link-color; + &:hover, + &:focus { + color: @navbar-inverse-link-hover-color; + } + &[disabled], + fieldset[disabled] & { + &:hover, + &:focus { + color: @navbar-inverse-link-disabled-color; + } + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/navs.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/navs.less new file mode 100644 index 000000000..f26fec7a5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/navs.less @@ -0,0 +1,244 @@ +// +// Navs +// -------------------------------------------------- + + +// Base class +// -------------------------------------------------- + +.nav { + margin-bottom: 0; + padding-left: 0; // Override default ul/ol + list-style: none; + &:extend(.clearfix all); + + > li { + position: relative; + display: block; + + > a { + position: relative; + display: block; + padding: @nav-link-padding; + &:hover, + &:focus { + text-decoration: none; + background-color: @nav-link-hover-bg; + } + } + + // Disabled state sets text to gray and nukes hover/tab effects + &.disabled > a { + color: @nav-disabled-link-color; + + &:hover, + &:focus { + color: @nav-disabled-link-hover-color; + text-decoration: none; + background-color: transparent; + cursor: @cursor-disabled; + } + } + } + + // Open dropdowns + .open > a { + &, + &:hover, + &:focus { + background-color: @nav-link-hover-bg; + border-color: @link-color; + } + } + + // Nav dividers (deprecated with v3.0.1) + // + // This should have been removed in v3 with the dropping of `.nav-list`, but + // we missed it. We don't currently support this anywhere, but in the interest + // of maintaining backward compatibility in case you use it, it's deprecated. + .nav-divider { + .nav-divider(); + } + + // Prevent IE8 from misplacing imgs + // + // See https://github.com/h5bp/html5-boilerplate/issues/984#issuecomment-3985989 + > li > a > img { + max-width: none; + } +} + + +// Tabs +// ------------------------- + +// Give the tabs something to sit on +.nav-tabs { + border-bottom: 1px solid @nav-tabs-border-color; + > li { + float: left; + // Make the list-items overlay the bottom border + margin-bottom: -1px; + + // Actual tabs (as links) + > a { + margin-right: 2px; + line-height: @line-height-base; + border: 1px solid transparent; + border-radius: @border-radius-base @border-radius-base 0 0; + &:hover { + border-color: @nav-tabs-link-hover-border-color @nav-tabs-link-hover-border-color @nav-tabs-border-color; + } + } + + // Active state, and its :hover to override normal :hover + &.active > a { + &, + &:hover, + &:focus { + color: @nav-tabs-active-link-hover-color; + background-color: @nav-tabs-active-link-hover-bg; + border: 1px solid @nav-tabs-active-link-hover-border-color; + border-bottom-color: transparent; + cursor: default; + } + } + } + // pulling this in mainly for less shorthand + &.nav-justified { + .nav-justified(); + .nav-tabs-justified(); + } +} + + +// Pills +// ------------------------- +.nav-pills { + > li { + float: left; + + // Links rendered as pills + > a { + border-radius: @nav-pills-border-radius; + } + + li { + margin-left: 2px; + } + + // Active state + &.active > a { + &, + &:hover, + &:focus { + color: @nav-pills-active-link-hover-color; + background-color: @nav-pills-active-link-hover-bg; + } + } + } +} + + +// Stacked pills +.nav-stacked { + > li { + float: none; + + li { + margin-top: 2px; + margin-left: 0; // no need for this gap between nav items + } + } +} + + +// Nav variations +// -------------------------------------------------- + +// Justified nav links +// ------------------------- + +.nav-justified { + width: 100%; + + > li { + float: none; + > a { + text-align: center; + margin-bottom: 5px; + } + } + + > .dropdown .dropdown-menu { + top: auto; + left: auto; + } + + @media (min-width: @screen-sm-min) { + > li { + display: table-cell; + width: 1%; + > a { + margin-bottom: 0; + } + } + } +} + +// Move borders to anchors instead of bottom of list +// +// Mixin for adding on top the shared `.nav-justified` styles for our tabs +.nav-tabs-justified { + border-bottom: 0; + + > li > a { + // Override margin from .nav-tabs + margin-right: 0; + border-radius: @border-radius-base; + } + + > .active > a, + > .active > a:hover, + > .active > a:focus { + border: 1px solid @nav-tabs-justified-link-border-color; + } + + @media (min-width: @screen-sm-min) { + > li > a { + border-bottom: 1px solid @nav-tabs-justified-link-border-color; + border-radius: @border-radius-base @border-radius-base 0 0; + } + > .active > a, + > .active > a:hover, + > .active > a:focus { + border-bottom-color: @nav-tabs-justified-active-link-border-color; + } + } +} + + +// Tabbable tabs +// ------------------------- + +// Hide tabbable panes to start, show them when `.active` +.tab-content { + > .tab-pane { + display: none; + visibility: hidden; + } + > .active { + display: block; + visibility: visible; + } +} + + +// Dropdowns +// ------------------------- + +// Specific dropdowns +.nav-tabs .dropdown-menu { + // make dropdown border overlap tab border + margin-top: -1px; + // Remove the top rounded corners here since there is a hard edge above the menu + .border-top-radius(0); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/normalize.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/normalize.less new file mode 100644 index 000000000..62a085a48 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/normalize.less @@ -0,0 +1,427 @@ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ + +// +// 1. Set default font family to sans-serif. +// 2. Prevent iOS text size adjust after orientation change, without disabling +// user zoom. +// + +html { + font-family: sans-serif; // 1 + -ms-text-size-adjust: 100%; // 2 + -webkit-text-size-adjust: 100%; // 2 +} + +// +// Remove default margin. +// + +body { + margin: 0; +} + +// HTML5 display definitions +// ========================================================================== + +// +// Correct `block` display not defined for any HTML5 element in IE 8/9. +// Correct `block` display not defined for `details` or `summary` in IE 10/11 +// and Firefox. +// Correct `block` display not defined for `main` in IE 11. +// + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +// +// 1. Correct `inline-block` display not defined in IE 8/9. +// 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. +// + +audio, +canvas, +progress, +video { + display: inline-block; // 1 + vertical-align: baseline; // 2 +} + +// +// Prevent modern browsers from displaying `audio` without controls. +// Remove excess height in iOS 5 devices. +// + +audio:not([controls]) { + display: none; + height: 0; +} + +// +// Address `[hidden]` styling not present in IE 8/9/10. +// Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. +// + +[hidden], +template { + display: none; +} + +// Links +// ========================================================================== + +// +// Remove the gray background color from active links in IE 10. +// + +a { + background-color: transparent; +} + +// +// Improve readability when focused and also mouse hovered in all browsers. +// + +a:active, +a:hover { + outline: 0; +} + +// Text-level semantics +// ========================================================================== + +// +// Address styling not present in IE 8/9/10/11, Safari, and Chrome. +// + +abbr[title] { + border-bottom: 1px dotted; +} + +// +// Address style set to `bolder` in Firefox 4+, Safari, and Chrome. +// + +b, +strong { + font-weight: bold; +} + +// +// Address styling not present in Safari and Chrome. +// + +dfn { + font-style: italic; +} + +// +// Address variable `h1` font-size and margin within `section` and `article` +// contexts in Firefox 4+, Safari, and Chrome. +// + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +// +// Address styling not present in IE 8/9. +// + +mark { + background: #ff0; + color: #000; +} + +// +// Address inconsistent and variable font size in all browsers. +// + +small { + font-size: 80%; +} + +// +// Prevent `sub` and `sup` affecting `line-height` in all browsers. +// + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +// Embedded content +// ========================================================================== + +// +// Remove border when inside `a` element in IE 8/9/10. +// + +img { + border: 0; +} + +// +// Correct overflow not hidden in IE 9/10/11. +// + +svg:not(:root) { + overflow: hidden; +} + +// Grouping content +// ========================================================================== + +// +// Address margin not present in IE 8/9 and Safari. +// + +figure { + margin: 1em 40px; +} + +// +// Address differences between Firefox and other browsers. +// + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +// +// Contain overflow in all browsers. +// + +pre { + overflow: auto; +} + +// +// Address odd `em`-unit font size rendering in all browsers. +// + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +// Forms +// ========================================================================== + +// +// Known limitation: by default, Chrome and Safari on OS X allow very limited +// styling of `select`, unless a `border` property is set. +// + +// +// 1. Correct color not being inherited. +// Known issue: affects color of disabled elements. +// 2. Correct font properties not being inherited. +// 3. Address margins set differently in Firefox 4+, Safari, and Chrome. +// + +button, +input, +optgroup, +select, +textarea { + color: inherit; // 1 + font: inherit; // 2 + margin: 0; // 3 +} + +// +// Address `overflow` set to `hidden` in IE 8/9/10/11. +// + +button { + overflow: visible; +} + +// +// Address inconsistent `text-transform` inheritance for `button` and `select`. +// All other form control elements do not inherit `text-transform` values. +// Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. +// Correct `select` style inheritance in Firefox. +// + +button, +select { + text-transform: none; +} + +// +// 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` +// and `video` controls. +// 2. Correct inability to style clickable `input` types in iOS. +// 3. Improve usability and consistency of cursor style between image-type +// `input` and others. +// + +button, +html input[type="button"], // 1 +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; // 2 + cursor: pointer; // 3 +} + +// +// Re-set default cursor for disabled elements. +// + +button[disabled], +html input[disabled] { + cursor: default; +} + +// +// Remove inner padding and border in Firefox 4+. +// + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +// +// Address Firefox 4+ setting `line-height` on `input` using `!important` in +// the UA stylesheet. +// + +input { + line-height: normal; +} + +// +// It's recommended that you don't attempt to style these elements. +// Firefox's implementation doesn't respect box-sizing, padding, or width. +// +// 1. Address box sizing set to `content-box` in IE 8/9/10. +// 2. Remove excess padding in IE 8/9/10. +// + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; // 1 + padding: 0; // 2 +} + +// +// Fix the cursor style for Chrome's increment/decrement buttons. For certain +// `font-size` values of the `input`, it causes the cursor style of the +// decrement button to change from `default` to `text`. +// + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +// +// 1. Address `appearance` set to `searchfield` in Safari and Chrome. +// 2. Address `box-sizing` set to `border-box` in Safari and Chrome +// (include `-moz` to future-proof). +// + +input[type="search"] { + -webkit-appearance: textfield; // 1 + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; // 2 + box-sizing: content-box; +} + +// +// Remove inner padding and search cancel button in Safari and Chrome on OS X. +// Safari (but not Chrome) clips the cancel button when the search input has +// padding (and `textfield` appearance). +// + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +// +// Define consistent border, margin, and padding. +// + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +// +// 1. Correct `color` not being inherited in IE 8/9/10/11. +// 2. Remove padding so people aren't caught out if they zero out fieldsets. +// + +legend { + border: 0; // 1 + padding: 0; // 2 +} + +// +// Remove default vertical scrollbar in IE 8/9/10/11. +// + +textarea { + overflow: auto; +} + +// +// Don't inherit the `font-weight` (applied by a rule above). +// NOTE: the default cannot safely be changed in Chrome and Safari on OS X. +// + +optgroup { + font-weight: bold; +} + +// Tables +// ========================================================================== + +// +// Remove most spacing between table cells. +// + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/pager.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/pager.less new file mode 100644 index 000000000..41abaaadc --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/pager.less @@ -0,0 +1,54 @@ +// +// Pager pagination +// -------------------------------------------------- + + +.pager { + padding-left: 0; + margin: @line-height-computed 0; + list-style: none; + text-align: center; + &:extend(.clearfix all); + li { + display: inline; + > a, + > span { + display: inline-block; + padding: 5px 14px; + background-color: @pager-bg; + border: 1px solid @pager-border; + border-radius: @pager-border-radius; + } + + > a:hover, + > a:focus { + text-decoration: none; + background-color: @pager-hover-bg; + } + } + + .next { + > a, + > span { + float: right; + } + } + + .previous { + > a, + > span { + float: left; + } + } + + .disabled { + > a, + > a:hover, + > a:focus, + > span { + color: @pager-disabled-color; + background-color: @pager-bg; + cursor: @cursor-disabled; + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/pagination.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/pagination.less new file mode 100644 index 000000000..38c4c3d34 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/pagination.less @@ -0,0 +1,88 @@ +// +// Pagination (multiple pages) +// -------------------------------------------------- +.pagination { + display: inline-block; + padding-left: 0; + margin: @line-height-computed 0; + border-radius: @border-radius-base; + + > li { + display: inline; // Remove list-style and block-level defaults + > a, + > span { + position: relative; + float: left; // Collapse white-space + padding: @padding-base-vertical @padding-base-horizontal; + line-height: @line-height-base; + text-decoration: none; + color: @pagination-color; + background-color: @pagination-bg; + border: 1px solid @pagination-border; + margin-left: -1px; + } + &:first-child { + > a, + > span { + margin-left: 0; + .border-left-radius(@border-radius-base); + } + } + &:last-child { + > a, + > span { + .border-right-radius(@border-radius-base); + } + } + } + + > li > a, + > li > span { + &:hover, + &:focus { + color: @pagination-hover-color; + background-color: @pagination-hover-bg; + border-color: @pagination-hover-border; + } + } + + > .active > a, + > .active > span { + &, + &:hover, + &:focus { + z-index: 2; + color: @pagination-active-color; + background-color: @pagination-active-bg; + border-color: @pagination-active-border; + cursor: default; + } + } + + > .disabled { + > span, + > span:hover, + > span:focus, + > a, + > a:hover, + > a:focus { + color: @pagination-disabled-color; + background-color: @pagination-disabled-bg; + border-color: @pagination-disabled-border; + cursor: @cursor-disabled; + } + } +} + +// Sizing +// -------------------------------------------------- + +// Large +.pagination-lg { + .pagination-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @border-radius-large); +} + +// Small +.pagination-sm { + .pagination-size(@padding-small-vertical; @padding-small-horizontal; @font-size-small; @border-radius-small); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/panels.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/panels.less new file mode 100644 index 000000000..04eb2b39c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/panels.less @@ -0,0 +1,265 @@ +// +// Panels +// -------------------------------------------------- + + +// Base class +.panel { + margin-bottom: @line-height-computed; + background-color: @panel-bg; + border: 1px solid transparent; + border-radius: @panel-border-radius; + .box-shadow(0 1px 1px rgba(0,0,0,.05)); +} + +// Panel contents +.panel-body { + padding: @panel-body-padding; + &:extend(.clearfix all); +} + +// Optional heading +.panel-heading { + padding: @panel-heading-padding; + border-bottom: 1px solid transparent; + .border-top-radius((@panel-border-radius - 1)); + + > .dropdown .dropdown-toggle { + color: inherit; + } +} + +// Within heading, strip any `h*` tag of its default margins for spacing. +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: ceil((@font-size-base * 1.125)); + color: inherit; + + > a, + > small, + > .small, + > small > a, + > .small > a { + color: inherit; + } +} + +// Optional footer (stays gray in every modifier class) +.panel-footer { + padding: @panel-footer-padding; + background-color: @panel-footer-bg; + border-top: 1px solid @panel-inner-border; + .border-bottom-radius((@panel-border-radius - 1)); +} + + +// List groups in panels +// +// By default, space out list group content from panel headings to account for +// any kind of custom content between the two. + +.panel { + > .list-group, + > .panel-collapse > .list-group { + margin-bottom: 0; + + .list-group-item { + border-width: 1px 0; + border-radius: 0; + } + + // Add border top radius for first one + &:first-child { + .list-group-item:first-child { + border-top: 0; + .border-top-radius((@panel-border-radius - 1)); + } + } + // Add border bottom radius for last one + &:last-child { + .list-group-item:last-child { + border-bottom: 0; + .border-bottom-radius((@panel-border-radius - 1)); + } + } + } +} +// Collapse space between when there's no additional content. +.panel-heading + .list-group { + .list-group-item:first-child { + border-top-width: 0; + } +} +.list-group + .panel-footer { + border-top-width: 0; +} + +// Tables in panels +// +// Place a non-bordered `.table` within a panel (not within a `.panel-body`) and +// watch it go full width. + +.panel { + > .table, + > .table-responsive > .table, + > .panel-collapse > .table { + margin-bottom: 0; + + caption { + padding-left: @panel-body-padding; + padding-right: @panel-body-padding; + } + } + // Add border top radius for first one + > .table:first-child, + > .table-responsive:first-child > .table:first-child { + .border-top-radius((@panel-border-radius - 1)); + + > thead:first-child, + > tbody:first-child { + > tr:first-child { + border-top-left-radius: (@panel-border-radius - 1); + border-top-right-radius: (@panel-border-radius - 1); + + td:first-child, + th:first-child { + border-top-left-radius: (@panel-border-radius - 1); + } + td:last-child, + th:last-child { + border-top-right-radius: (@panel-border-radius - 1); + } + } + } + } + // Add border bottom radius for last one + > .table:last-child, + > .table-responsive:last-child > .table:last-child { + .border-bottom-radius((@panel-border-radius - 1)); + + > tbody:last-child, + > tfoot:last-child { + > tr:last-child { + border-bottom-left-radius: (@panel-border-radius - 1); + border-bottom-right-radius: (@panel-border-radius - 1); + + td:first-child, + th:first-child { + border-bottom-left-radius: (@panel-border-radius - 1); + } + td:last-child, + th:last-child { + border-bottom-right-radius: (@panel-border-radius - 1); + } + } + } + } + > .panel-body + .table, + > .panel-body + .table-responsive, + > .table + .panel-body, + > .table-responsive + .panel-body { + border-top: 1px solid @table-border-color; + } + > .table > tbody:first-child > tr:first-child th, + > .table > tbody:first-child > tr:first-child td { + border-top: 0; + } + > .table-bordered, + > .table-responsive > .table-bordered { + border: 0; + > thead, + > tbody, + > tfoot { + > tr { + > th:first-child, + > td:first-child { + border-left: 0; + } + > th:last-child, + > td:last-child { + border-right: 0; + } + } + } + > thead, + > tbody { + > tr:first-child { + > td, + > th { + border-bottom: 0; + } + } + } + > tbody, + > tfoot { + > tr:last-child { + > td, + > th { + border-bottom: 0; + } + } + } + } + > .table-responsive { + border: 0; + margin-bottom: 0; + } +} + + +// Collapsable panels (aka, accordion) +// +// Wrap a series of panels in `.panel-group` to turn them into an accordion with +// the help of our collapse JavaScript plugin. + +.panel-group { + margin-bottom: @line-height-computed; + + // Tighten up margin so it's only between panels + .panel { + margin-bottom: 0; + border-radius: @panel-border-radius; + + + .panel { + margin-top: 5px; + } + } + + .panel-heading { + border-bottom: 0; + + + .panel-collapse > .panel-body, + + .panel-collapse > .list-group { + border-top: 1px solid @panel-inner-border; + } + } + + .panel-footer { + border-top: 0; + + .panel-collapse .panel-body { + border-bottom: 1px solid @panel-inner-border; + } + } +} + + +// Contextual variations +.panel-default { + .panel-variant(@panel-default-border; @panel-default-text; @panel-default-heading-bg; @panel-default-border); +} +.panel-primary { + .panel-variant(@panel-primary-border; @panel-primary-text; @panel-primary-heading-bg; @panel-primary-border); +} +.panel-success { + .panel-variant(@panel-success-border; @panel-success-text; @panel-success-heading-bg; @panel-success-border); +} +.panel-info { + .panel-variant(@panel-info-border; @panel-info-text; @panel-info-heading-bg; @panel-info-border); +} +.panel-warning { + .panel-variant(@panel-warning-border; @panel-warning-text; @panel-warning-heading-bg; @panel-warning-border); +} +.panel-danger { + .panel-variant(@panel-danger-border; @panel-danger-text; @panel-danger-heading-bg; @panel-danger-border); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/popovers.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/popovers.less new file mode 100644 index 000000000..2748eead1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/popovers.less @@ -0,0 +1,135 @@ +// +// Popovers +// -------------------------------------------------- + + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: @zindex-popover; + display: none; + max-width: @popover-max-width; + padding: 1px; + // Reset font and text properties given new insertion method + font-family: @font-family-base; + font-size: @font-size-base; + font-weight: normal; + line-height: @line-height-base; + text-align: left; + background-color: @popover-bg; + background-clip: padding-box; + border: 1px solid @popover-fallback-border-color; + border: 1px solid @popover-border-color; + border-radius: @border-radius-large; + .box-shadow(0 5px 10px rgba(0,0,0,.2)); + + // Overrides for proper insertion + white-space: normal; + + // Offset the popover to account for the popover arrow + &.top { margin-top: -@popover-arrow-width; } + &.right { margin-left: @popover-arrow-width; } + &.bottom { margin-top: @popover-arrow-width; } + &.left { margin-left: -@popover-arrow-width; } +} + +.popover-title { + margin: 0; // reset heading margin + padding: 8px 14px; + font-size: @font-size-base; + background-color: @popover-title-bg; + border-bottom: 1px solid darken(@popover-title-bg, 5%); + border-radius: (@border-radius-large - 1) (@border-radius-large - 1) 0 0; +} + +.popover-content { + padding: 9px 14px; +} + +// Arrows +// +// .arrow is outer, .arrow:after is inner + +.popover > .arrow { + &, + &:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; + } +} +.popover > .arrow { + border-width: @popover-arrow-outer-width; +} +.popover > .arrow:after { + border-width: @popover-arrow-width; + content: ""; +} + +.popover { + &.top > .arrow { + left: 50%; + margin-left: -@popover-arrow-outer-width; + border-bottom-width: 0; + border-top-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-top-color: @popover-arrow-outer-color; + bottom: -@popover-arrow-outer-width; + &:after { + content: " "; + bottom: 1px; + margin-left: -@popover-arrow-width; + border-bottom-width: 0; + border-top-color: @popover-arrow-color; + } + } + &.right > .arrow { + top: 50%; + left: -@popover-arrow-outer-width; + margin-top: -@popover-arrow-outer-width; + border-left-width: 0; + border-right-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-right-color: @popover-arrow-outer-color; + &:after { + content: " "; + left: 1px; + bottom: -@popover-arrow-width; + border-left-width: 0; + border-right-color: @popover-arrow-color; + } + } + &.bottom > .arrow { + left: 50%; + margin-left: -@popover-arrow-outer-width; + border-top-width: 0; + border-bottom-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-bottom-color: @popover-arrow-outer-color; + top: -@popover-arrow-outer-width; + &:after { + content: " "; + top: 1px; + margin-left: -@popover-arrow-width; + border-top-width: 0; + border-bottom-color: @popover-arrow-color; + } + } + + &.left > .arrow { + top: 50%; + right: -@popover-arrow-outer-width; + margin-top: -@popover-arrow-outer-width; + border-right-width: 0; + border-left-color: @popover-arrow-outer-fallback-color; // IE8 fallback + border-left-color: @popover-arrow-outer-color; + &:after { + content: " "; + right: 1px; + border-right-width: 0; + border-left-color: @popover-arrow-color; + bottom: -@popover-arrow-width; + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/print.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/print.less new file mode 100644 index 000000000..94ca58f12 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/print.less @@ -0,0 +1,107 @@ +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ + +// ========================================================================== +// Print styles. +// Inlined to avoid the additional HTTP request: h5bp.com/r +// ========================================================================== + +@media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; // Black prints faster: h5bp.com/s + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links that are fragment identifiers, + // or use the `javascript:` pseudo protocol + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + + // Bootstrap specific changes start + // + // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 + // Once fixed, we can just straight up remove this. + select { + background: #fff !important; + } + + // Bootstrap components + .navbar { + display: none; + } + .btn, + .dropup > .btn { + > .caret { + border-top-color: #000 !important; + } + } + .label { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + + td, + th { + background-color: #fff !important; + } + } + .table-bordered { + th, + td { + border: 1px solid #ddd !important; + } + } + + // Bootstrap specific changes end +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/progress-bars.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/progress-bars.less new file mode 100644 index 000000000..8868a1fee --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/progress-bars.less @@ -0,0 +1,87 @@ +// +// Progress bars +// -------------------------------------------------- + + +// Bar animations +// ------------------------- + +// WebKit +@-webkit-keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + +// Spec and IE10+ +@keyframes progress-bar-stripes { + from { background-position: 40px 0; } + to { background-position: 0 0; } +} + + +// Bar itself +// ------------------------- + +// Outer container +.progress { + overflow: hidden; + height: @line-height-computed; + margin-bottom: @line-height-computed; + background-color: @progress-bg; + border-radius: @progress-border-radius; + .box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); +} + +// Bar of progress +.progress-bar { + float: left; + width: 0%; + height: 100%; + font-size: @font-size-small; + line-height: @line-height-computed; + color: @progress-bar-color; + text-align: center; + background-color: @progress-bar-bg; + .box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); + .transition(width .6s ease); +} + +// Striped bars +// +// `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the +// `.progress-bar-striped` class, which you just add to an existing +// `.progress-bar`. +.progress-striped .progress-bar, +.progress-bar-striped { + #gradient > .striped(); + background-size: 40px 40px; +} + +// Call animation for the active one +// +// `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the +// `.progress-bar.active` approach. +.progress.active .progress-bar, +.progress-bar.active { + .animation(progress-bar-stripes 2s linear infinite); +} + + +// Variations +// ------------------------- + +.progress-bar-success { + .progress-bar-variant(@progress-bar-success-bg); +} + +.progress-bar-info { + .progress-bar-variant(@progress-bar-info-bg); +} + +.progress-bar-warning { + .progress-bar-variant(@progress-bar-warning-bg); +} + +.progress-bar-danger { + .progress-bar-variant(@progress-bar-danger-bg); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/responsive-embed.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/responsive-embed.less new file mode 100644 index 000000000..c1fa8f848 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/responsive-embed.less @@ -0,0 +1,35 @@ +// Embeds responsive +// +// Credit: Nicolas Gallagher and SUIT CSS. + +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; + + .embed-responsive-item, + iframe, + embed, + object, + video { + position: absolute; + top: 0; + left: 0; + bottom: 0; + height: 100%; + width: 100%; + border: 0; + } + + // Modifier class for 16:9 aspect ratio + &.embed-responsive-16by9 { + padding-bottom: 56.25%; + } + + // Modifier class for 4:3 aspect ratio + &.embed-responsive-4by3 { + padding-bottom: 75%; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/responsive-utilities.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/responsive-utilities.less new file mode 100644 index 000000000..b1db31d7b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/responsive-utilities.less @@ -0,0 +1,194 @@ +// +// Responsive: Utility classes +// -------------------------------------------------- + + +// IE10 in Windows (Phone) 8 +// +// Support for responsive views via media queries is kind of borked in IE10, for +// Surface/desktop in split view and for Windows Phone 8. This particular fix +// must be accompanied by a snippet of JavaScript to sniff the user agent and +// apply some conditional CSS to *only* the Surface/desktop Windows 8. Look at +// our Getting Started page for more information on this bug. +// +// For more information, see the following: +// +// Issue: https://github.com/twbs/bootstrap/issues/10497 +// Docs: http://getbootstrap.com/getting-started/#support-ie10-width +// Source: http://timkadlec.com/2013/01/windows-phone-8-and-device-width/ +// Source: http://timkadlec.com/2012/10/ie10-snap-mode-and-responsive-design/ + +@-ms-viewport { + width: device-width; +} + + +// Visibility utilities +// Note: Deprecated .visible-xs, .visible-sm, .visible-md, and .visible-lg as of v3.2.0 +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + .responsive-invisibility(); +} + +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} + +.visible-xs { + @media (max-width: @screen-xs-max) { + .responsive-visibility(); + } +} +.visible-xs-block { + @media (max-width: @screen-xs-max) { + display: block !important; + } +} +.visible-xs-inline { + @media (max-width: @screen-xs-max) { + display: inline !important; + } +} +.visible-xs-inline-block { + @media (max-width: @screen-xs-max) { + display: inline-block !important; + } +} + +.visible-sm { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + .responsive-visibility(); + } +} +.visible-sm-block { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + display: block !important; + } +} +.visible-sm-inline { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + display: inline !important; + } +} +.visible-sm-inline-block { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + display: inline-block !important; + } +} + +.visible-md { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + .responsive-visibility(); + } +} +.visible-md-block { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + display: block !important; + } +} +.visible-md-inline { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + display: inline !important; + } +} +.visible-md-inline-block { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + display: inline-block !important; + } +} + +.visible-lg { + @media (min-width: @screen-lg-min) { + .responsive-visibility(); + } +} +.visible-lg-block { + @media (min-width: @screen-lg-min) { + display: block !important; + } +} +.visible-lg-inline { + @media (min-width: @screen-lg-min) { + display: inline !important; + } +} +.visible-lg-inline-block { + @media (min-width: @screen-lg-min) { + display: inline-block !important; + } +} + +.hidden-xs { + @media (max-width: @screen-xs-max) { + .responsive-invisibility(); + } +} +.hidden-sm { + @media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { + .responsive-invisibility(); + } +} +.hidden-md { + @media (min-width: @screen-md-min) and (max-width: @screen-md-max) { + .responsive-invisibility(); + } +} +.hidden-lg { + @media (min-width: @screen-lg-min) { + .responsive-invisibility(); + } +} + + +// Print utilities +// +// Media queries are placed on the inside to be mixin-friendly. + +// Note: Deprecated .visible-print as of v3.2.0 +.visible-print { + .responsive-invisibility(); + + @media print { + .responsive-visibility(); + } +} +.visible-print-block { + display: none !important; + + @media print { + display: block !important; + } +} +.visible-print-inline { + display: none !important; + + @media print { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; + + @media print { + display: inline-block !important; + } +} + +.hidden-print { + @media print { + .responsive-invisibility(); + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/scaffolding.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/scaffolding.less new file mode 100644 index 000000000..2a40fbcbe --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/scaffolding.less @@ -0,0 +1,150 @@ +// +// Scaffolding +// -------------------------------------------------- + + +// Reset the box-sizing +// +// Heads up! This reset may cause conflicts with some third-party widgets. +// For recommendations on resolving such conflicts, see +// http://getbootstrap.com/getting-started/#third-box-sizing +* { + .box-sizing(border-box); +} +*:before, +*:after { + .box-sizing(border-box); +} + + +// Body reset + +html { + font-size: 10px; + -webkit-tap-highlight-color: rgba(0,0,0,0); +} + +body { + font-family: @font-family-base; + font-size: @font-size-base; + line-height: @line-height-base; + color: @text-color; + background-color: @body-bg; +} + +// Reset fonts for relevant elements +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + + +// Links + +a { + color: @link-color; + text-decoration: none; + + &:hover, + &:focus { + color: @link-hover-color; + text-decoration: @link-hover-decoration; + } + + &:focus { + .tab-focus(); + } +} + + +// Figures +// +// We reset this here because previously Normalize had no `figure` margins. This +// ensures we don't break anyone's use of the element. + +figure { + margin: 0; +} + + +// Images + +img { + vertical-align: middle; +} + +// Responsive images (ensure images don't scale beyond their parents) +.img-responsive { + .img-responsive(); +} + +// Rounded corners +.img-rounded { + border-radius: @border-radius-large; +} + +// Image thumbnails +// +// Heads up! This is mixin-ed into thumbnails.less for `.thumbnail`. +.img-thumbnail { + padding: @thumbnail-padding; + line-height: @line-height-base; + background-color: @thumbnail-bg; + border: 1px solid @thumbnail-border; + border-radius: @thumbnail-border-radius; + .transition(all .2s ease-in-out); + + // Keep them at most 100% wide + .img-responsive(inline-block); +} + +// Perfect circle +.img-circle { + border-radius: 50%; // set radius in percents +} + + +// Horizontal rules + +hr { + margin-top: @line-height-computed; + margin-bottom: @line-height-computed; + border: 0; + border-top: 1px solid @hr-border; +} + + +// Only display content to screen readers +// +// See: http://a11yproject.com/posts/how-to-hide-content/ + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0,0,0,0); + border: 0; +} + +// Use in conjunction with .sr-only to only display content when it's focused. +// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 +// Credit: HTML5 Boilerplate + +.sr-only-focusable { + &:active, + &:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/tables.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/tables.less new file mode 100644 index 000000000..2242c0368 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/tables.less @@ -0,0 +1,234 @@ +// +// Tables +// -------------------------------------------------- + + +table { + background-color: @table-bg; +} +caption { + padding-top: @table-cell-padding; + padding-bottom: @table-cell-padding; + color: @text-muted; + text-align: left; +} +th { + text-align: left; +} + + +// Baseline styles + +.table { + width: 100%; + max-width: 100%; + margin-bottom: @line-height-computed; + // Cells + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + padding: @table-cell-padding; + line-height: @line-height-base; + vertical-align: top; + border-top: 1px solid @table-border-color; + } + } + } + // Bottom align for column headings + > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid @table-border-color; + } + // Remove top border from thead by default + > caption + thead, + > colgroup + thead, + > thead:first-child { + > tr:first-child { + > th, + > td { + border-top: 0; + } + } + } + // Account for multiple tbody instances + > tbody + tbody { + border-top: 2px solid @table-border-color; + } + + // Nesting + .table { + background-color: @body-bg; + } +} + + +// Condensed table w/ half padding + +.table-condensed { + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + padding: @table-condensed-cell-padding; + } + } + } +} + + +// Bordered version +// +// Add borders all around the table and between all the columns. + +.table-bordered { + border: 1px solid @table-border-color; + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + border: 1px solid @table-border-color; + } + } + } + > thead > tr { + > th, + > td { + border-bottom-width: 2px; + } + } +} + + +// Zebra-striping +// +// Default zebra-stripe styles (alternating gray and transparent backgrounds) + +.table-striped { + > tbody > tr:nth-of-type(odd) { + background-color: @table-bg-accent; + } +} + + +// Hover effect +// +// Placed here since it has to come after the potential zebra striping + +.table-hover { + > tbody > tr:hover { + background-color: @table-bg-hover; + } +} + + +// Table cell sizing +// +// Reset default table behavior + +table col[class*="col-"] { + position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623) + float: none; + display: table-column; +} +table { + td, + th { + &[class*="col-"] { + position: static; // Prevent border hiding in Firefox and IE9-11 (see https://github.com/twbs/bootstrap/issues/11623) + float: none; + display: table-cell; + } + } +} + + +// Table backgrounds +// +// Exact selectors below required to override `.table-striped` and prevent +// inheritance to nested tables. + +// Generate the contextual variants +.table-row-variant(active; @table-bg-active); +.table-row-variant(success; @state-success-bg); +.table-row-variant(info; @state-info-bg); +.table-row-variant(warning; @state-warning-bg); +.table-row-variant(danger; @state-danger-bg); + + +// Responsive tables +// +// Wrap your tables in `.table-responsive` and we'll make them mobile friendly +// by enabling horizontal scrolling. Only applies <768px. Everything above that +// will display normally. + +.table-responsive { + overflow-x: auto; + min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837) + + @media screen and (max-width: @screen-xs-max) { + width: 100%; + margin-bottom: (@line-height-computed * 0.75); + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid @table-border-color; + + // Tighten up spacing + > .table { + margin-bottom: 0; + + // Ensure the content doesn't wrap + > thead, + > tbody, + > tfoot { + > tr { + > th, + > td { + white-space: nowrap; + } + } + } + } + + // Special overrides for the bordered tables + > .table-bordered { + border: 0; + + // Nuke the appropriate borders so that the parent can handle them + > thead, + > tbody, + > tfoot { + > tr { + > th:first-child, + > td:first-child { + border-left: 0; + } + > th:last-child, + > td:last-child { + border-right: 0; + } + } + } + + // Only nuke the last row's bottom-border in `tbody` and `tfoot` since + // chances are there will be only one `tr` in a `thead` and that would + // remove the border altogether. + > tbody, + > tfoot { + > tr:last-child { + > th, + > td { + border-bottom: 0; + } + } + } + + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/theme.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/theme.less new file mode 100644 index 000000000..6f2eb6212 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/theme.less @@ -0,0 +1,273 @@ + +// +// Load core variables and mixins +// -------------------------------------------------- + +@import "variables.less"; +@import "mixins.less"; + + +// +// Buttons +// -------------------------------------------------- + +// Common styles +.btn-default, +.btn-primary, +.btn-success, +.btn-info, +.btn-warning, +.btn-danger { + text-shadow: 0 -1px 0 rgba(0,0,0,.2); + @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075); + .box-shadow(@shadow); + + // Reset the shadow + &:active, + &.active { + .box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); + } + + .badge { + text-shadow: none; + } +} + +// Mixin for generating new styles +.btn-styles(@btn-color: #555) { + #gradient > .vertical(@start-color: @btn-color; @end-color: darken(@btn-color, 12%)); + .reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners; see https://github.com/twbs/bootstrap/issues/10620 + background-repeat: repeat-x; + border-color: darken(@btn-color, 14%); + + &:hover, + &:focus { + background-color: darken(@btn-color, 12%); + background-position: 0 -15px; + } + + &:active, + &.active { + background-color: darken(@btn-color, 12%); + border-color: darken(@btn-color, 14%); + } + + &.disabled, + &:disabled, + &[disabled] { + background-color: darken(@btn-color, 12%); + background-image: none; + } +} + +// Common styles +.btn { + // Remove the gradient for the pressed/active state + &:active, + &.active { + background-image: none; + } +} + +// Apply the mixin to the buttons +.btn-default { .btn-styles(@btn-default-bg); text-shadow: 0 1px 0 #fff; border-color: #ccc; } +.btn-primary { .btn-styles(@btn-primary-bg); } +.btn-success { .btn-styles(@btn-success-bg); } +.btn-info { .btn-styles(@btn-info-bg); } +.btn-warning { .btn-styles(@btn-warning-bg); } +.btn-danger { .btn-styles(@btn-danger-bg); } + + +// +// Images +// -------------------------------------------------- + +.thumbnail, +.img-thumbnail { + .box-shadow(0 1px 2px rgba(0,0,0,.075)); +} + + +// +// Dropdowns +// -------------------------------------------------- + +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + #gradient > .vertical(@start-color: @dropdown-link-hover-bg; @end-color: darken(@dropdown-link-hover-bg, 5%)); + background-color: darken(@dropdown-link-hover-bg, 5%); +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%)); + background-color: darken(@dropdown-link-active-bg, 5%); +} + + +// +// Navbar +// -------------------------------------------------- + +// Default navbar +.navbar-default { + #gradient > .vertical(@start-color: lighten(@navbar-default-bg, 10%); @end-color: @navbar-default-bg); + .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered + border-radius: @navbar-border-radius; + @shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075); + .box-shadow(@shadow); + + .navbar-nav > .open > a, + .navbar-nav > .active > a { + #gradient > .vertical(@start-color: darken(@navbar-default-link-active-bg, 5%); @end-color: darken(@navbar-default-link-active-bg, 2%)); + .box-shadow(inset 0 3px 9px rgba(0,0,0,.075)); + } +} +.navbar-brand, +.navbar-nav > li > a { + text-shadow: 0 1px 0 rgba(255,255,255,.25); +} + +// Inverted navbar +.navbar-inverse { + #gradient > .vertical(@start-color: lighten(@navbar-inverse-bg, 10%); @end-color: @navbar-inverse-bg); + .reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered; see https://github.com/twbs/bootstrap/issues/10257 + + .navbar-nav > .open > a, + .navbar-nav > .active > a { + #gradient > .vertical(@start-color: @navbar-inverse-link-active-bg; @end-color: lighten(@navbar-inverse-link-active-bg, 2.5%)); + .box-shadow(inset 0 3px 9px rgba(0,0,0,.25)); + } + + .navbar-brand, + .navbar-nav > li > a { + text-shadow: 0 -1px 0 rgba(0,0,0,.25); + } +} + +// Undo rounded corners in static and fixed navbars +.navbar-static-top, +.navbar-fixed-top, +.navbar-fixed-bottom { + border-radius: 0; +} + +// Fix active state of dropdown items in collapsed mode +@media (max-width: @grid-float-breakpoint-max) { + .navbar .navbar-nav .open .dropdown-menu > .active > a { + &, + &:hover, + &:focus { + color: #fff; + #gradient > .vertical(@start-color: @dropdown-link-active-bg; @end-color: darken(@dropdown-link-active-bg, 5%)); + } + } +} + + +// +// Alerts +// -------------------------------------------------- + +// Common styles +.alert { + text-shadow: 0 1px 0 rgba(255,255,255,.2); + @shadow: inset 0 1px 0 rgba(255,255,255,.25), 0 1px 2px rgba(0,0,0,.05); + .box-shadow(@shadow); +} + +// Mixin for generating new styles +.alert-styles(@color) { + #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 7.5%)); + border-color: darken(@color, 15%); +} + +// Apply the mixin to the alerts +.alert-success { .alert-styles(@alert-success-bg); } +.alert-info { .alert-styles(@alert-info-bg); } +.alert-warning { .alert-styles(@alert-warning-bg); } +.alert-danger { .alert-styles(@alert-danger-bg); } + + +// +// Progress bars +// -------------------------------------------------- + +// Give the progress background some depth +.progress { + #gradient > .vertical(@start-color: darken(@progress-bg, 4%); @end-color: @progress-bg) +} + +// Mixin for generating new styles +.progress-bar-styles(@color) { + #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 10%)); +} + +// Apply the mixin to the progress bars +.progress-bar { .progress-bar-styles(@progress-bar-bg); } +.progress-bar-success { .progress-bar-styles(@progress-bar-success-bg); } +.progress-bar-info { .progress-bar-styles(@progress-bar-info-bg); } +.progress-bar-warning { .progress-bar-styles(@progress-bar-warning-bg); } +.progress-bar-danger { .progress-bar-styles(@progress-bar-danger-bg); } + +// Reset the striped class because our mixins don't do multiple gradients and +// the above custom styles override the new `.progress-bar-striped` in v3.2.0. +.progress-bar-striped { + #gradient > .striped(); +} + + +// +// List groups +// -------------------------------------------------- + +.list-group { + border-radius: @border-radius-base; + .box-shadow(0 1px 2px rgba(0,0,0,.075)); +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + text-shadow: 0 -1px 0 darken(@list-group-active-bg, 10%); + #gradient > .vertical(@start-color: @list-group-active-bg; @end-color: darken(@list-group-active-bg, 7.5%)); + border-color: darken(@list-group-active-border, 7.5%); + + .badge { + text-shadow: none; + } +} + + +// +// Panels +// -------------------------------------------------- + +// Common styles +.panel { + .box-shadow(0 1px 2px rgba(0,0,0,.05)); +} + +// Mixin for generating new styles +.panel-heading-styles(@color) { + #gradient > .vertical(@start-color: @color; @end-color: darken(@color, 5%)); +} + +// Apply the mixin to the panel headings only +.panel-default > .panel-heading { .panel-heading-styles(@panel-default-heading-bg); } +.panel-primary > .panel-heading { .panel-heading-styles(@panel-primary-heading-bg); } +.panel-success > .panel-heading { .panel-heading-styles(@panel-success-heading-bg); } +.panel-info > .panel-heading { .panel-heading-styles(@panel-info-heading-bg); } +.panel-warning > .panel-heading { .panel-heading-styles(@panel-warning-heading-bg); } +.panel-danger > .panel-heading { .panel-heading-styles(@panel-danger-heading-bg); } + + +// +// Wells +// -------------------------------------------------- + +.well { + #gradient > .vertical(@start-color: darken(@well-bg, 5%); @end-color: @well-bg); + border-color: darken(@well-bg, 10%); + @shadow: inset 0 1px 3px rgba(0,0,0,.05), 0 1px 0 rgba(255,255,255,.1); + .box-shadow(@shadow); +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/thumbnails.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/thumbnails.less new file mode 100644 index 000000000..0713e67d0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/thumbnails.less @@ -0,0 +1,36 @@ +// +// Thumbnails +// -------------------------------------------------- + + +// Mixin and adjust the regular image class +.thumbnail { + display: block; + padding: @thumbnail-padding; + margin-bottom: @line-height-computed; + line-height: @line-height-base; + background-color: @thumbnail-bg; + border: 1px solid @thumbnail-border; + border-radius: @thumbnail-border-radius; + .transition(border .2s ease-in-out); + + > img, + a > img { + &:extend(.img-responsive); + margin-left: auto; + margin-right: auto; + } + + // Add a hover state for linked versions only + a&:hover, + a&:focus, + a&.active { + border-color: @link-color; + } + + // Image captions + .caption { + padding: @thumbnail-caption-padding; + color: @thumbnail-caption-color; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/tooltip.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/tooltip.less new file mode 100644 index 000000000..3ebfdcdff --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/tooltip.less @@ -0,0 +1,103 @@ +// +// Tooltips +// -------------------------------------------------- + + +// Base class +.tooltip { + position: absolute; + z-index: @zindex-tooltip; + display: block; + visibility: visible; + // Reset font and text properties given new insertion method + font-family: @font-family-base; + font-size: @font-size-small; + font-weight: normal; + line-height: 1.4; + .opacity(0); + + &.in { .opacity(@tooltip-opacity); } + &.top { margin-top: -3px; padding: @tooltip-arrow-width 0; } + &.right { margin-left: 3px; padding: 0 @tooltip-arrow-width; } + &.bottom { margin-top: 3px; padding: @tooltip-arrow-width 0; } + &.left { margin-left: -3px; padding: 0 @tooltip-arrow-width; } +} + +// Wrapper for the tooltip content +.tooltip-inner { + max-width: @tooltip-max-width; + padding: 3px 8px; + color: @tooltip-color; + text-align: center; + text-decoration: none; + background-color: @tooltip-bg; + border-radius: @border-radius-base; +} + +// Arrows +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +// Note: Deprecated .top-left, .top-right, .bottom-left, and .bottom-right as of v3.3.1 +.tooltip { + &.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width 0; + border-top-color: @tooltip-arrow-color; + } + &.top-left .tooltip-arrow { + bottom: 0; + right: @tooltip-arrow-width; + margin-bottom: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width 0; + border-top-color: @tooltip-arrow-color; + } + &.top-right .tooltip-arrow { + bottom: 0; + left: @tooltip-arrow-width; + margin-bottom: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width 0; + border-top-color: @tooltip-arrow-color; + } + &.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; + border-right-color: @tooltip-arrow-color; + } + &.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -@tooltip-arrow-width; + border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; + border-left-color: @tooltip-arrow-color; + } + &.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -@tooltip-arrow-width; + border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; + border-bottom-color: @tooltip-arrow-color; + } + &.bottom-left .tooltip-arrow { + top: 0; + right: @tooltip-arrow-width; + margin-top: -@tooltip-arrow-width; + border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; + border-bottom-color: @tooltip-arrow-color; + } + &.bottom-right .tooltip-arrow { + top: 0; + left: @tooltip-arrow-width; + margin-top: -@tooltip-arrow-width; + border-width: 0 @tooltip-arrow-width @tooltip-arrow-width; + border-bottom-color: @tooltip-arrow-color; + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/type.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/type.less new file mode 100644 index 000000000..3ec976eef --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/type.less @@ -0,0 +1,302 @@ +// +// Typography +// -------------------------------------------------- + + +// Headings +// ------------------------- + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + font-family: @headings-font-family; + font-weight: @headings-font-weight; + line-height: @headings-line-height; + color: @headings-color; + + small, + .small { + font-weight: normal; + line-height: 1; + color: @headings-small-color; + } +} + +h1, .h1, +h2, .h2, +h3, .h3 { + margin-top: @line-height-computed; + margin-bottom: (@line-height-computed / 2); + + small, + .small { + font-size: 65%; + } +} +h4, .h4, +h5, .h5, +h6, .h6 { + margin-top: (@line-height-computed / 2); + margin-bottom: (@line-height-computed / 2); + + small, + .small { + font-size: 75%; + } +} + +h1, .h1 { font-size: @font-size-h1; } +h2, .h2 { font-size: @font-size-h2; } +h3, .h3 { font-size: @font-size-h3; } +h4, .h4 { font-size: @font-size-h4; } +h5, .h5 { font-size: @font-size-h5; } +h6, .h6 { font-size: @font-size-h6; } + + +// Body text +// ------------------------- + +p { + margin: 0 0 (@line-height-computed / 2); +} + +.lead { + margin-bottom: @line-height-computed; + font-size: floor((@font-size-base * 1.15)); + font-weight: 300; + line-height: 1.4; + + @media (min-width: @screen-sm-min) { + font-size: (@font-size-base * 1.5); + } +} + + +// Emphasis & misc +// ------------------------- + +// Ex: (12px small font / 14px base font) * 100% = about 85% +small, +.small { + font-size: floor((100% * @font-size-small / @font-size-base)); +} + +mark, +.mark { + background-color: @state-warning-bg; + padding: .2em; +} + +// Alignment +.text-left { text-align: left; } +.text-right { text-align: right; } +.text-center { text-align: center; } +.text-justify { text-align: justify; } +.text-nowrap { white-space: nowrap; } + +// Transformation +.text-lowercase { text-transform: lowercase; } +.text-uppercase { text-transform: uppercase; } +.text-capitalize { text-transform: capitalize; } + +// Contextual colors +.text-muted { + color: @text-muted; +} +.text-primary { + .text-emphasis-variant(@brand-primary); +} +.text-success { + .text-emphasis-variant(@state-success-text); +} +.text-info { + .text-emphasis-variant(@state-info-text); +} +.text-warning { + .text-emphasis-variant(@state-warning-text); +} +.text-danger { + .text-emphasis-variant(@state-danger-text); +} + +// Contextual backgrounds +// For now we'll leave these alongside the text classes until v4 when we can +// safely shift things around (per SemVer rules). +.bg-primary { + // Given the contrast here, this is the only class to have its color inverted + // automatically. + color: #fff; + .bg-variant(@brand-primary); +} +.bg-success { + .bg-variant(@state-success-bg); +} +.bg-info { + .bg-variant(@state-info-bg); +} +.bg-warning { + .bg-variant(@state-warning-bg); +} +.bg-danger { + .bg-variant(@state-danger-bg); +} + + +// Page header +// ------------------------- + +.page-header { + padding-bottom: ((@line-height-computed / 2) - 1); + margin: (@line-height-computed * 2) 0 @line-height-computed; + border-bottom: 1px solid @page-header-border-color; +} + + +// Lists +// ------------------------- + +// Unordered and Ordered lists +ul, +ol { + margin-top: 0; + margin-bottom: (@line-height-computed / 2); + ul, + ol { + margin-bottom: 0; + } +} + +// List options + +// Unstyled keeps list items block level, just removes default browser padding and list-style +.list-unstyled { + padding-left: 0; + list-style: none; +} + +// Inline turns list items into inline-block +.list-inline { + .list-unstyled(); + margin-left: -5px; + + > li { + display: inline-block; + padding-left: 5px; + padding-right: 5px; + } +} + +// Description Lists +dl { + margin-top: 0; // Remove browser default + margin-bottom: @line-height-computed; +} +dt, +dd { + line-height: @line-height-base; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; // Undo browser default +} + +// Horizontal description lists +// +// Defaults to being stacked without any of the below styles applied, until the +// grid breakpoint is reached (default of ~768px). + +.dl-horizontal { + dd { + &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present + } + + @media (min-width: @grid-float-breakpoint) { + dt { + float: left; + width: (@dl-horizontal-offset - 20); + clear: left; + text-align: right; + .text-overflow(); + } + dd { + margin-left: @dl-horizontal-offset; + } + } +} + + +// Misc +// ------------------------- + +// Abbreviations and acronyms +abbr[title], +// Add data-* attribute to help out our tooltip plugin, per https://github.com/twbs/bootstrap/issues/5257 +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted @abbr-border-color; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +// Blockquotes +blockquote { + padding: (@line-height-computed / 2) @line-height-computed; + margin: 0 0 @line-height-computed; + font-size: @blockquote-font-size; + border-left: 5px solid @blockquote-border-color; + + p, + ul, + ol { + &:last-child { + margin-bottom: 0; + } + } + + // Note: Deprecated small and .small as of v3.1.0 + // Context: https://github.com/twbs/bootstrap/issues/11660 + footer, + small, + .small { + display: block; + font-size: 80%; // back to default font-size + line-height: @line-height-base; + color: @blockquote-small-color; + + &:before { + content: '\2014 \00A0'; // em dash, nbsp + } + } +} + +// Opposite alignment of blockquote +// +// Heads up: `blockquote.pull-right` has been deprecated as of v3.1.0. +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + border-right: 5px solid @blockquote-border-color; + border-left: 0; + text-align: right; + + // Account for citation + footer, + small, + .small { + &:before { content: ''; } + &:after { + content: '\00A0 \2014'; // nbsp, em dash + } + } +} + +// Addresses +address { + margin-bottom: @line-height-computed; + font-style: normal; + line-height: @line-height-base; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/utilities.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/utilities.less new file mode 100644 index 000000000..a26031214 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/utilities.less @@ -0,0 +1,56 @@ +// +// Utility classes +// -------------------------------------------------- + + +// Floats +// ------------------------- + +.clearfix { + .clearfix(); +} +.center-block { + .center-block(); +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} + + +// Toggling content +// ------------------------- + +// Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + .text-hide(); +} + + +// Hide from screenreaders and browsers +// +// Credit: HTML5 Boilerplate + +.hidden { + display: none !important; + visibility: hidden !important; +} + + +// For Affix plugin +// ------------------------- + +.affix { + position: fixed; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/variables.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/variables.less new file mode 100644 index 000000000..002be6afb --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/variables.less @@ -0,0 +1,857 @@ +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +@gray-base: #000; +@gray-darker: lighten(@gray-base, 13.5%); // #222 +@gray-dark: lighten(@gray-base, 20%); // #333 +@gray: lighten(@gray-base, 33.5%); // #555 +@gray-light: lighten(@gray-base, 46.7%); // #777 +@gray-lighter: lighten(@gray-base, 93.5%); // #eee + +@brand-primary: darken(#428bca, 6.5%); // #337ab7 +@brand-success: #5cb85c; +@brand-info: #5bc0de; +@brand-warning: #f0ad4e; +@brand-danger: #d9534f; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for `<body>`. +@body-bg: #fff; +//** Global text color on `<body>`. +@text-color: @gray-dark; + +//** Global textual link color. +@link-color: @brand-primary; +//** Link hover color set via `darken()` function. +@link-hover-color: darken(@link-color, 15%); +//** Link hover decoration. +@link-hover-decoration: underline; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; +@font-family-serif: Georgia, "Times New Roman", Times, serif; +//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. +@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; +@font-family-base: @font-family-sans-serif; + +@font-size-base: 14px; +@font-size-large: ceil((@font-size-base * 1.25)); // ~18px +@font-size-small: ceil((@font-size-base * 0.85)); // ~12px + +@font-size-h1: floor((@font-size-base * 2.6)); // ~36px +@font-size-h2: floor((@font-size-base * 2.15)); // ~30px +@font-size-h3: ceil((@font-size-base * 1.7)); // ~24px +@font-size-h4: ceil((@font-size-base * 1.25)); // ~18px +@font-size-h5: @font-size-base; +@font-size-h6: ceil((@font-size-base * 0.85)); // ~12px + +//** Unit-less `line-height` for use in components like buttons. +@line-height-base: 1.428571429; // 20/14 +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc. +@line-height-computed: floor((@font-size-base * @line-height-base)); // ~20px + +//** By default, this inherits from the `<body>`. +@headings-font-family: inherit; +@headings-font-weight: 500; +@headings-line-height: 1.1; +@headings-color: inherit; + + +//== Iconography +// +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower. + +//** Load fonts from this directory. +@icon-font-path: "../fonts/"; +//** File name for all font files. +@icon-font-name: "glyphicons-halflings-regular"; +//** Element ID within SVG icon file. +@icon-font-svg-id: "glyphicons_halflingsregular"; + + +//== Components +// +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). + +@padding-base-vertical: 6px; +@padding-base-horizontal: 12px; + +@padding-large-vertical: 10px; +@padding-large-horizontal: 16px; + +@padding-small-vertical: 5px; +@padding-small-horizontal: 10px; + +@padding-xs-vertical: 1px; +@padding-xs-horizontal: 5px; + +@line-height-large: 1.3333333; // extra decimals for Win 8.1 Chrome +@line-height-small: 1.5; + +@border-radius-base: 4px; +@border-radius-large: 6px; +@border-radius-small: 3px; + +//** Global color for active items (e.g., navs or dropdowns). +@component-active-color: #fff; +//** Global background color for active items (e.g., navs or dropdowns). +@component-active-bg: @brand-primary; + +//** Width of the `border` for generating carets that indicator dropdowns. +@caret-width-base: 4px; +//** Carets increase slightly in size for larger components. +@caret-width-large: 5px; + + +//== Tables +// +//## Customizes the `.table` component with basic values, each used across all table variations. + +//** Padding for `<th>`s and `<td>`s. +@table-cell-padding: 8px; +//** Padding for cells in `.table-condensed`. +@table-condensed-cell-padding: 5px; + +//** Default background color used for all tables. +@table-bg: transparent; +//** Background color used for `.table-striped`. +@table-bg-accent: #f9f9f9; +//** Background color used for `.table-hover`. +@table-bg-hover: #f5f5f5; +@table-bg-active: @table-bg-hover; + +//** Border color for table and cell borders. +@table-border-color: #ddd; + + +//== Buttons +// +//## For each of Bootstrap's buttons, define text, background and border color. + +@btn-font-weight: normal; + +@btn-default-color: #333; +@btn-default-bg: #fff; +@btn-default-border: #ccc; + +@btn-primary-color: #fff; +@btn-primary-bg: @brand-primary; +@btn-primary-border: darken(@btn-primary-bg, 5%); + +@btn-success-color: #fff; +@btn-success-bg: @brand-success; +@btn-success-border: darken(@btn-success-bg, 5%); + +@btn-info-color: #fff; +@btn-info-bg: @brand-info; +@btn-info-border: darken(@btn-info-bg, 5%); + +@btn-warning-color: #fff; +@btn-warning-bg: @brand-warning; +@btn-warning-border: darken(@btn-warning-bg, 5%); + +@btn-danger-color: #fff; +@btn-danger-bg: @brand-danger; +@btn-danger-border: darken(@btn-danger-bg, 5%); + +@btn-link-disabled-color: @gray-light; + + +//== Forms +// +//## + +//** `<input>` background color +@input-bg: #fff; +//** `<input disabled>` background color +@input-bg-disabled: @gray-lighter; + +//** Text color for `<input>`s +@input-color: @gray; +//** `<input>` border color +@input-border: #ccc; + +// TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4 +//** Default `.form-control` border radius +// This has no effect on `<select>`s in some browsers, due to the limited stylability of `<select>`s in CSS. +@input-border-radius: @border-radius-base; +//** Large `.form-control` border radius +@input-border-radius-large: @border-radius-large; +//** Small `.form-control` border radius +@input-border-radius-small: @border-radius-small; + +//** Border color for inputs on focus +@input-border-focus: #66afe9; + +//** Placeholder text color +@input-color-placeholder: #999; + +//** Default `.form-control` height +@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2); +//** Large `.form-control` height +@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2); +//** Small `.form-control` height +@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2); + +@legend-color: @gray-dark; +@legend-border-color: #e5e5e5; + +//** Background color for textual input addons +@input-group-addon-bg: @gray-lighter; +//** Border color for textual input addons +@input-group-addon-border-color: @input-border; + +//** Disabled cursor for form controls and buttons. +@cursor-disabled: not-allowed; + + +//== Dropdowns +// +//## Dropdown menu container and contents. + +//** Background for the dropdown menu. +@dropdown-bg: #fff; +//** Dropdown menu `border-color`. +@dropdown-border: rgba(0,0,0,.15); +//** Dropdown menu `border-color` **for IE8**. +@dropdown-fallback-border: #ccc; +//** Divider color for between dropdown items. +@dropdown-divider-bg: #e5e5e5; + +//** Dropdown link text color. +@dropdown-link-color: @gray-dark; +//** Hover color for dropdown links. +@dropdown-link-hover-color: darken(@gray-dark, 5%); +//** Hover background for dropdown links. +@dropdown-link-hover-bg: #f5f5f5; + +//** Active dropdown menu item text color. +@dropdown-link-active-color: @component-active-color; +//** Active dropdown menu item background color. +@dropdown-link-active-bg: @component-active-bg; + +//** Disabled dropdown menu item background color. +@dropdown-link-disabled-color: @gray-light; + +//** Text color for headers within dropdown menus. +@dropdown-header-color: @gray-light; + +//** Deprecated `@dropdown-caret-color` as of v3.1.0 +@dropdown-caret-color: #000; + + +//-- Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. +// +// Note: These variables are not generated into the Customizer. + +@zindex-navbar: 1000; +@zindex-dropdown: 1000; +@zindex-popover: 1060; +@zindex-tooltip: 1070; +@zindex-navbar-fixed: 1030; +@zindex-modal: 1040; + + +//== Media queries breakpoints +// +//## Define the breakpoints at which your layout will change, adapting to different screen sizes. + +// Extra small screen / phone +//** Deprecated `@screen-xs` as of v3.0.1 +@screen-xs: 480px; +//** Deprecated `@screen-xs-min` as of v3.2.0 +@screen-xs-min: @screen-xs; +//** Deprecated `@screen-phone` as of v3.0.1 +@screen-phone: @screen-xs-min; + +// Small screen / tablet +//** Deprecated `@screen-sm` as of v3.0.1 +@screen-sm: 768px; +@screen-sm-min: @screen-sm; +//** Deprecated `@screen-tablet` as of v3.0.1 +@screen-tablet: @screen-sm-min; + +// Medium screen / desktop +//** Deprecated `@screen-md` as of v3.0.1 +@screen-md: 992px; +@screen-md-min: @screen-md; +//** Deprecated `@screen-desktop` as of v3.0.1 +@screen-desktop: @screen-md-min; + +// Large screen / wide desktop +//** Deprecated `@screen-lg` as of v3.0.1 +@screen-lg: 1200px; +@screen-lg-min: @screen-lg; +//** Deprecated `@screen-lg-desktop` as of v3.0.1 +@screen-lg-desktop: @screen-lg-min; + +// So media queries don't overlap when required, provide a maximum +@screen-xs-max: (@screen-sm-min - 1); +@screen-sm-max: (@screen-md-min - 1); +@screen-md-max: (@screen-lg-min - 1); + + +//== Grid system +// +//## Define your custom responsive grid. + +//** Number of columns in the grid. +@grid-columns: 12; +//** Padding between columns. Gets divided in half for the left and right. +@grid-gutter-width: 30px; +// Navbar collapse +//** Point at which the navbar becomes uncollapsed. +@grid-float-breakpoint: @screen-sm-min; +//** Point at which the navbar begins collapsing. +@grid-float-breakpoint-max: (@grid-float-breakpoint - 1); + + +//== Container sizes +// +//## Define the maximum width of `.container` for different screen sizes. + +// Small screen / tablet +@container-tablet: (720px + @grid-gutter-width); +//** For `@screen-sm-min` and up. +@container-sm: @container-tablet; + +// Medium screen / desktop +@container-desktop: (940px + @grid-gutter-width); +//** For `@screen-md-min` and up. +@container-md: @container-desktop; + +// Large screen / wide desktop +@container-large-desktop: (1140px + @grid-gutter-width); +//** For `@screen-lg-min` and up. +@container-lg: @container-large-desktop; + + +//== Navbar +// +//## + +// Basics of a navbar +@navbar-height: 50px; +@navbar-margin-bottom: @line-height-computed; +@navbar-border-radius: @border-radius-base; +@navbar-padding-horizontal: floor((@grid-gutter-width / 2)); +@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2); +@navbar-collapse-max-height: 340px; + +@navbar-default-color: #777; +@navbar-default-bg: #f8f8f8; +@navbar-default-border: darken(@navbar-default-bg, 6.5%); + +// Navbar links +@navbar-default-link-color: #777; +@navbar-default-link-hover-color: #333; +@navbar-default-link-hover-bg: transparent; +@navbar-default-link-active-color: #555; +@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%); +@navbar-default-link-disabled-color: #ccc; +@navbar-default-link-disabled-bg: transparent; + +// Navbar brand label +@navbar-default-brand-color: @navbar-default-link-color; +@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%); +@navbar-default-brand-hover-bg: transparent; + +// Navbar toggle +@navbar-default-toggle-hover-bg: #ddd; +@navbar-default-toggle-icon-bar-bg: #888; +@navbar-default-toggle-border-color: #ddd; + + +// Inverted navbar +// Reset inverted navbar basics +@navbar-inverse-color: lighten(@gray-light, 15%); +@navbar-inverse-bg: #222; +@navbar-inverse-border: darken(@navbar-inverse-bg, 10%); + +// Inverted navbar links +@navbar-inverse-link-color: lighten(@gray-light, 15%); +@navbar-inverse-link-hover-color: #fff; +@navbar-inverse-link-hover-bg: transparent; +@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color; +@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%); +@navbar-inverse-link-disabled-color: #444; +@navbar-inverse-link-disabled-bg: transparent; + +// Inverted navbar brand label +@navbar-inverse-brand-color: @navbar-inverse-link-color; +@navbar-inverse-brand-hover-color: #fff; +@navbar-inverse-brand-hover-bg: transparent; + +// Inverted navbar toggle +@navbar-inverse-toggle-hover-bg: #333; +@navbar-inverse-toggle-icon-bar-bg: #fff; +@navbar-inverse-toggle-border-color: #333; + + +//== Navs +// +//## + +//=== Shared nav styles +@nav-link-padding: 10px 15px; +@nav-link-hover-bg: @gray-lighter; + +@nav-disabled-link-color: @gray-light; +@nav-disabled-link-hover-color: @gray-light; + +//== Tabs +@nav-tabs-border-color: #ddd; + +@nav-tabs-link-hover-border-color: @gray-lighter; + +@nav-tabs-active-link-hover-bg: @body-bg; +@nav-tabs-active-link-hover-color: @gray; +@nav-tabs-active-link-hover-border-color: #ddd; + +@nav-tabs-justified-link-border-color: #ddd; +@nav-tabs-justified-active-link-border-color: @body-bg; + +//== Pills +@nav-pills-border-radius: @border-radius-base; +@nav-pills-active-link-hover-bg: @component-active-bg; +@nav-pills-active-link-hover-color: @component-active-color; + + +//== Pagination +// +//## + +@pagination-color: @link-color; +@pagination-bg: #fff; +@pagination-border: #ddd; + +@pagination-hover-color: @link-hover-color; +@pagination-hover-bg: @gray-lighter; +@pagination-hover-border: #ddd; + +@pagination-active-color: #fff; +@pagination-active-bg: @brand-primary; +@pagination-active-border: @brand-primary; + +@pagination-disabled-color: @gray-light; +@pagination-disabled-bg: #fff; +@pagination-disabled-border: #ddd; + + +//== Pager +// +//## + +@pager-bg: @pagination-bg; +@pager-border: @pagination-border; +@pager-border-radius: 15px; + +@pager-hover-bg: @pagination-hover-bg; + +@pager-active-bg: @pagination-active-bg; +@pager-active-color: @pagination-active-color; + +@pager-disabled-color: @pagination-disabled-color; + + +//== Jumbotron +// +//## + +@jumbotron-padding: 30px; +@jumbotron-color: inherit; +@jumbotron-bg: @gray-lighter; +@jumbotron-heading-color: inherit; +@jumbotron-font-size: ceil((@font-size-base * 1.5)); + + +//== Form states and alerts +// +//## Define colors for form feedback states and, by default, alerts. + +@state-success-text: #3c763d; +@state-success-bg: #dff0d8; +@state-success-border: darken(spin(@state-success-bg, -10), 5%); + +@state-info-text: #31708f; +@state-info-bg: #d9edf7; +@state-info-border: darken(spin(@state-info-bg, -10), 7%); + +@state-warning-text: #8a6d3b; +@state-warning-bg: #fcf8e3; +@state-warning-border: darken(spin(@state-warning-bg, -10), 5%); + +@state-danger-text: #a94442; +@state-danger-bg: #f2dede; +@state-danger-border: darken(spin(@state-danger-bg, -10), 5%); + + +//== Tooltips +// +//## + +//** Tooltip max width +@tooltip-max-width: 200px; +//** Tooltip text color +@tooltip-color: #fff; +//** Tooltip background color +@tooltip-bg: #000; +@tooltip-opacity: .9; + +//** Tooltip arrow width +@tooltip-arrow-width: 5px; +//** Tooltip arrow color +@tooltip-arrow-color: @tooltip-bg; + + +//== Popovers +// +//## + +//** Popover body background color +@popover-bg: #fff; +//** Popover maximum width +@popover-max-width: 276px; +//** Popover border color +@popover-border-color: rgba(0,0,0,.2); +//** Popover fallback border color +@popover-fallback-border-color: #ccc; + +//** Popover title background color +@popover-title-bg: darken(@popover-bg, 3%); + +//** Popover arrow width +@popover-arrow-width: 10px; +//** Popover arrow color +@popover-arrow-color: @popover-bg; + +//** Popover outer arrow width +@popover-arrow-outer-width: (@popover-arrow-width + 1); +//** Popover outer arrow color +@popover-arrow-outer-color: fadein(@popover-border-color, 5%); +//** Popover outer arrow fallback color +@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%); + + +//== Labels +// +//## + +//** Default label background color +@label-default-bg: @gray-light; +//** Primary label background color +@label-primary-bg: @brand-primary; +//** Success label background color +@label-success-bg: @brand-success; +//** Info label background color +@label-info-bg: @brand-info; +//** Warning label background color +@label-warning-bg: @brand-warning; +//** Danger label background color +@label-danger-bg: @brand-danger; + +//** Default label text color +@label-color: #fff; +//** Default text color of a linked label +@label-link-hover-color: #fff; + + +//== Modals +// +//## + +//** Padding applied to the modal body +@modal-inner-padding: 15px; + +//** Padding applied to the modal title +@modal-title-padding: 15px; +//** Modal title line-height +@modal-title-line-height: @line-height-base; + +//** Background color of modal content area +@modal-content-bg: #fff; +//** Modal content border color +@modal-content-border-color: rgba(0,0,0,.2); +//** Modal content border color **for IE8** +@modal-content-fallback-border-color: #999; + +//** Modal backdrop background color +@modal-backdrop-bg: #000; +//** Modal backdrop opacity +@modal-backdrop-opacity: .5; +//** Modal header border color +@modal-header-border-color: #e5e5e5; +//** Modal footer border color +@modal-footer-border-color: @modal-header-border-color; + +@modal-lg: 900px; +@modal-md: 600px; +@modal-sm: 300px; + + +//== Alerts +// +//## Define alert colors, border radius, and padding. + +@alert-padding: 15px; +@alert-border-radius: @border-radius-base; +@alert-link-font-weight: bold; + +@alert-success-bg: @state-success-bg; +@alert-success-text: @state-success-text; +@alert-success-border: @state-success-border; + +@alert-info-bg: @state-info-bg; +@alert-info-text: @state-info-text; +@alert-info-border: @state-info-border; + +@alert-warning-bg: @state-warning-bg; +@alert-warning-text: @state-warning-text; +@alert-warning-border: @state-warning-border; + +@alert-danger-bg: @state-danger-bg; +@alert-danger-text: @state-danger-text; +@alert-danger-border: @state-danger-border; + + +//== Progress bars +// +//## + +//** Background color of the whole progress component +@progress-bg: #f5f5f5; +//** Progress bar text color +@progress-bar-color: #fff; +//** Variable for setting rounded corners on progress bar. +@progress-border-radius: @border-radius-base; + +//** Default progress bar color +@progress-bar-bg: @brand-primary; +//** Success progress bar color +@progress-bar-success-bg: @brand-success; +//** Warning progress bar color +@progress-bar-warning-bg: @brand-warning; +//** Danger progress bar color +@progress-bar-danger-bg: @brand-danger; +//** Info progress bar color +@progress-bar-info-bg: @brand-info; + + +//== List group +// +//## + +//** Background color on `.list-group-item` +@list-group-bg: #fff; +//** `.list-group-item` border color +@list-group-border: #ddd; +//** List group border radius +@list-group-border-radius: @border-radius-base; + +//** Background color of single list items on hover +@list-group-hover-bg: #f5f5f5; +//** Text color of active list items +@list-group-active-color: @component-active-color; +//** Background color of active list items +@list-group-active-bg: @component-active-bg; +//** Border color of active list elements +@list-group-active-border: @list-group-active-bg; +//** Text color for content within active list items +@list-group-active-text-color: lighten(@list-group-active-bg, 40%); + +//** Text color of disabled list items +@list-group-disabled-color: @gray-light; +//** Background color of disabled list items +@list-group-disabled-bg: @gray-lighter; +//** Text color for content within disabled list items +@list-group-disabled-text-color: @list-group-disabled-color; + +@list-group-link-color: #555; +@list-group-link-hover-color: @list-group-link-color; +@list-group-link-heading-color: #333; + + +//== Panels +// +//## + +@panel-bg: #fff; +@panel-body-padding: 15px; +@panel-heading-padding: 10px 15px; +@panel-footer-padding: @panel-heading-padding; +@panel-border-radius: @border-radius-base; + +//** Border color for elements within panels +@panel-inner-border: #ddd; +@panel-footer-bg: #f5f5f5; + +@panel-default-text: @gray-dark; +@panel-default-border: #ddd; +@panel-default-heading-bg: #f5f5f5; + +@panel-primary-text: #fff; +@panel-primary-border: @brand-primary; +@panel-primary-heading-bg: @brand-primary; + +@panel-success-text: @state-success-text; +@panel-success-border: @state-success-border; +@panel-success-heading-bg: @state-success-bg; + +@panel-info-text: @state-info-text; +@panel-info-border: @state-info-border; +@panel-info-heading-bg: @state-info-bg; + +@panel-warning-text: @state-warning-text; +@panel-warning-border: @state-warning-border; +@panel-warning-heading-bg: @state-warning-bg; + +@panel-danger-text: @state-danger-text; +@panel-danger-border: @state-danger-border; +@panel-danger-heading-bg: @state-danger-bg; + + +//== Thumbnails +// +//## + +//** Padding around the thumbnail image +@thumbnail-padding: 4px; +//** Thumbnail background color +@thumbnail-bg: @body-bg; +//** Thumbnail border color +@thumbnail-border: #ddd; +//** Thumbnail border radius +@thumbnail-border-radius: @border-radius-base; + +//** Custom text color for thumbnail captions +@thumbnail-caption-color: @text-color; +//** Padding around the thumbnail caption +@thumbnail-caption-padding: 9px; + + +//== Wells +// +//## + +@well-bg: #f5f5f5; +@well-border: darken(@well-bg, 7%); + + +//== Badges +// +//## + +@badge-color: #fff; +//** Linked badge text color on hover +@badge-link-hover-color: #fff; +@badge-bg: @gray-light; + +//** Badge text color in active nav link +@badge-active-color: @link-color; +//** Badge background color in active nav link +@badge-active-bg: #fff; + +@badge-font-weight: bold; +@badge-line-height: 1; +@badge-border-radius: 10px; + + +//== Breadcrumbs +// +//## + +@breadcrumb-padding-vertical: 8px; +@breadcrumb-padding-horizontal: 15px; +//** Breadcrumb background color +@breadcrumb-bg: #f5f5f5; +//** Breadcrumb text color +@breadcrumb-color: #ccc; +//** Text color of current page in the breadcrumb +@breadcrumb-active-color: @gray-light; +//** Textual separator for between breadcrumb elements +@breadcrumb-separator: "/"; + + +//== Carousel +// +//## + +@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6); + +@carousel-control-color: #fff; +@carousel-control-width: 15%; +@carousel-control-opacity: .5; +@carousel-control-font-size: 20px; + +@carousel-indicator-active-bg: #fff; +@carousel-indicator-border-color: #fff; + +@carousel-caption-color: #fff; + + +//== Close +// +//## + +@close-font-weight: bold; +@close-color: #000; +@close-text-shadow: 0 1px 0 #fff; + + +//== Code +// +//## + +@code-color: #c7254e; +@code-bg: #f9f2f4; + +@kbd-color: #fff; +@kbd-bg: #333; + +@pre-bg: #f5f5f5; +@pre-color: @gray-dark; +@pre-border-color: #ccc; +@pre-scrollable-max-height: 340px; + + +//== Type +// +//## + +//** Horizontal offset for forms and lists. +@component-offset-horizontal: 180px; +//** Text muted color +@text-muted: @gray-light; +//** Abbreviations and acronyms border color +@abbr-border-color: @gray-light; +//** Headings small color +@headings-small-color: @gray-light; +//** Blockquote small color +@blockquote-small-color: @gray-light; +//** Blockquote font size +@blockquote-font-size: (@font-size-base * 1.25); +//** Blockquote border color +@blockquote-border-color: @gray-lighter; +//** Page header border color +@page-header-border-color: @gray-lighter; +//** Width of horizontal description list titles +@dl-horizontal-offset: @component-offset-horizontal; +//** Horizontal line color. +@hr-border: @gray-lighter; diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/wells.less b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/wells.less new file mode 100644 index 000000000..15d072b0c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/less/wells.less @@ -0,0 +1,29 @@ +// +// Wells +// -------------------------------------------------- + + +// Base class +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: @well-bg; + border: 1px solid @well-border; + border-radius: @border-radius-base; + .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); + blockquote { + border-color: #ddd; + border-color: rgba(0,0,0,.15); + } +} + +// Sizes +.well-lg { + padding: 24px; + border-radius: @border-radius-large; +} +.well-sm { + padding: 9px; + border-radius: @border-radius-small; +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/package.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/package.json new file mode 100644 index 000000000..de9a095a6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/bootstrap/package.json @@ -0,0 +1,83 @@ +{ + "name": "bootstrap", + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "version": "3.3.2", + "keywords": [ + "css", + "less", + "mobile-first", + "responsive", + "front-end", + "framework", + "web" + ], + "homepage": "http://getbootstrap.com", + "author": "Twitter, Inc.", + "scripts": { + "test": "grunt test" + }, + "style": "dist/css/bootstrap.css", + "less": "less/bootstrap.less", + "main": "./dist/js/npm", + "repository": { + "type": "git", + "url": "https://github.com/twbs/bootstrap.git" + }, + "bugs": { + "url": "https://github.com/twbs/bootstrap/issues" + }, + "license": { + "type": "MIT", + "url": "https://github.com/twbs/bootstrap/blob/master/LICENSE" + }, + "devDependencies": { + "btoa": "~1.1.2", + "glob": "~4.3.5", + "grunt": "~0.4.5", + "grunt-autoprefixer": "~2.1.0", + "grunt-banner": "~0.3.1", + "grunt-contrib-clean": "~0.6.0", + "grunt-contrib-compress": "~0.13.0", + "grunt-contrib-concat": "~0.5.0", + "grunt-contrib-connect": "~0.9.0", + "grunt-contrib-copy": "~0.7.0", + "grunt-contrib-csslint": "~0.3.1", + "grunt-contrib-cssmin": "~0.11.0", + "grunt-contrib-jade": "~0.14.0", + "grunt-contrib-jshint": "~0.10.0", + "grunt-contrib-less": "~1.0.0", + "grunt-contrib-qunit": "~0.5.2", + "grunt-contrib-uglify": "~0.7.0", + "grunt-contrib-watch": "~0.6.1", + "grunt-csscomb": "~3.0.0", + "grunt-exec": "~0.4.6", + "grunt-html-validation": "~0.1.18", + "grunt-jekyll": "~0.4.2", + "grunt-jscs": "~1.2.0", + "grunt-saucelabs": "~8.4.1", + "grunt-sed": "~0.1.1", + "load-grunt-tasks": "~2.0.0", + "npm-shrinkwrap": "^200.0.0", + "remarkable": "~1.6.0", + "time-grunt": "~1.0.0" + }, + "engines": { + "node": "~0.10.1" + }, + "jspm": { + "main": "js/bootstrap", + "directories": { + "example": "examples", + "lib": "dist" + }, + "shim": { + "js/bootstrap": { + "imports": "jquery", + "exports": "$" + } + }, + "buildConfig": { + "uglify": true + } + } +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/.bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/.bower.json new file mode 100644 index 000000000..28bf959a8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/.bower.json @@ -0,0 +1,25 @@ +{ + "name": "jquery", + "main": "dist/jquery.js", + "license": "MIT", + "ignore": [ + "package.json" + ], + "keywords": [ + "jquery", + "javascript", + "browser", + "library" + ], + "homepage": "https://github.com/jquery/jquery-dist", + "version": "3.2.1", + "_release": "3.2.1", + "_resolution": { + "type": "version", + "tag": "3.2.1", + "commit": "77d2a51d0520d2ee44173afdf4e40a9201f5964e" + }, + "_source": "https://github.com/jquery/jquery-dist.git", + "_target": ">= 1.9.1", + "_originalSource": "jquery" +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/AUTHORS.txt b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/AUTHORS.txt new file mode 100644 index 000000000..c32c25f9c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/AUTHORS.txt @@ -0,0 +1,301 @@ +Authors ordered by first contribution. + +John Resig <jeresig@gmail.com> +Gilles van den Hoven <gilles0181@gmail.com> +Michael Geary <mike@geary.com> +Stefan Petre <stefan.petre@gmail.com> +Yehuda Katz <wycats@gmail.com> +Corey Jewett <cj@syntheticplayground.com> +Klaus Hartl <klaus.hartl@gmail.com> +Franck Marcia <franck.marcia@gmail.com> +Jörn Zaefferer <joern.zaefferer@gmail.com> +Paul Bakaus <paul.bakaus@gmail.com> +Brandon Aaron <brandon.aaron@gmail.com> +Mike Alsup <malsup@gmail.com> +Dave Methvin <dave.methvin@gmail.com> +Ed Engelhardt <edengelhardt@gmail.com> +Sean Catchpole <littlecooldude@gmail.com> +Paul Mclanahan <pmclanahan@gmail.com> +David Serduke <davidserduke@gmail.com> +Richard D. Worth <rdworth@gmail.com> +Scott González <scott.gonzalez@gmail.com> +Ariel Flesler <aflesler@gmail.com> +Jon Evans <jon@springyweb.com> +TJ Holowaychuk <tj@vision-media.ca> +Michael Bensoussan <mickey@seesmic.com> +Robert Katić <robert.katic@gmail.com> +Louis-Rémi Babé <lrbabe@gmail.com> +Earle Castledine <mrspeaker@gmail.com> +Damian Janowski <damian.janowski@gmail.com> +Rich Dougherty <rich@rd.gen.nz> +Kim Dalsgaard <kim@kimdalsgaard.com> +Andrea Giammarchi <andrea.giammarchi@gmail.com> +Mark Gibson <jollytoad@gmail.com> +Karl Swedberg <kswedberg@gmail.com> +Justin Meyer <justinbmeyer@gmail.com> +Ben Alman <cowboy@rj3.net> +James Padolsey <cla@padolsey.net> +David Petersen <public@petersendidit.com> +Batiste Bieler <batiste.bieler@gmail.com> +Alexander Farkas <info@corrupt-system.de> +Rick Waldron <waldron.rick@gmail.com> +Filipe Fortes <filipe@fortes.com> +Neeraj Singh <neerajdotname@gmail.com> +Paul Irish <paul.irish@gmail.com> +Iraê Carvalho <irae@irae.pro.br> +Matt Curry <matt@pseudocoder.com> +Michael Monteleone <michael@michaelmonteleone.net> +Noah Sloan <noah.sloan@gmail.com> +Tom Viner <github@viner.tv> +Douglas Neiner <doug@dougneiner.com> +Adam J. Sontag <ajpiano@ajpiano.com> +Dave Reed <dareed@microsoft.com> +Ralph Whitbeck <ralph.whitbeck@gmail.com> +Carl Fürstenberg <azatoth@gmail.com> +Jacob Wright <jacwright@gmail.com> +J. Ryan Stinnett <jryans@gmail.com> +unknown <Igen005@.upcorp.ad.uprr.com> +temp01 <temp01irc@gmail.com> +Heungsub Lee <h@subl.ee> +Colin Snover <github.com@zetafleet.com> +Ryan W Tenney <ryan@10e.us> +Pinhook <contact@pinhooklabs.com> +Ron Otten <r.j.g.otten@gmail.com> +Jephte Clain <Jephte.Clain@univ-reunion.fr> +Anton Matzneller <obhvsbypqghgc@gmail.com> +Alex Sexton <AlexSexton@gmail.com> +Dan Heberden <danheberden@gmail.com> +Henri Wiechers <hwiechers@gmail.com> +Russell Holbrook <russell.holbrook@patch.com> +Julian Aubourg <aubourg.julian@gmail.com> +Gianni Alessandro Chiappetta <gianni@runlevel6.org> +Scott Jehl <scottjehl@gmail.com> +James Burke <jrburke@gmail.com> +Jonas Pfenniger <jonas@pfenniger.name> +Xavi Ramirez <xavi.rmz@gmail.com> +Jared Grippe <jared@deadlyicon.com> +Sylvester Keil <sylvester@keil.or.at> +Brandon Sterne <bsterne@mozilla.com> +Mathias Bynens <mathias@qiwi.be> +Timmy Willison <4timmywil@gmail.com> +Corey Frang <gnarf37@gmail.com> +Digitalxero <digitalxero> +Anton Kovalyov <anton@kovalyov.net> +David Murdoch <david@davidmurdoch.com> +Josh Varner <josh.varner@gmail.com> +Charles McNulty <cmcnulty@kznf.com> +Jordan Boesch <jboesch26@gmail.com> +Jess Thrysoee <jess@thrysoee.dk> +Michael Murray <m@murz.net> +Lee Carpenter <elcarpie@gmail.com> +Alexis Abril <me@alexisabril.com> +Rob Morgan <robbym@gmail.com> +John Firebaugh <john_firebaugh@bigfix.com> +Sam Bisbee <sam@sbisbee.com> +Gilmore Davidson <gilmoreorless@gmail.com> +Brian Brennan <me@brianlovesthings.com> +Xavier Montillet <xavierm02.net@gmail.com> +Daniel Pihlstrom <sciolist.se@gmail.com> +Sahab Yazdani <sahab.yazdani+github@gmail.com> +avaly <github-com@agachi.name> +Scott Hughes <hi@scott-hughes.me> +Mike Sherov <mike.sherov@gmail.com> +Greg Hazel <ghazel@gmail.com> +Schalk Neethling <schalk@ossreleasefeed.com> +Denis Knauf <Denis.Knauf@gmail.com> +Timo Tijhof <krinklemail@gmail.com> +Steen Nielsen <swinedk@gmail.com> +Anton Ryzhov <anton@ryzhov.me> +Shi Chuan <shichuanr@gmail.com> +Berker Peksag <berker.peksag@gmail.com> +Toby Brain <tobyb@freshview.com> +Matt Mueller <mattmuelle@gmail.com> +Justin <drakefjustin@gmail.com> +Daniel Herman <daniel.c.herman@gmail.com> +Oleg Gaidarenko <markelog@gmail.com> +Richard Gibson <richard.gibson@gmail.com> +Rafaël Blais Masson <rafbmasson@gmail.com> +cmc3cn <59194618@qq.com> +Joe Presbrey <presbrey@gmail.com> +Sindre Sorhus <sindresorhus@gmail.com> +Arne de Bree <arne@bukkie.nl> +Vladislav Zarakovsky <vlad.zar@gmail.com> +Andrew E Monat <amonat@gmail.com> +Oskari <admin@o-programs.com> +Joao Henrique de Andrade Bruni <joaohbruni@yahoo.com.br> +tsinha <tsinha@Anthonys-MacBook-Pro.local> +Matt Farmer <matt@frmr.me> +Trey Hunner <treyhunner@gmail.com> +Jason Moon <jmoon@socialcast.com> +Jeffery To <jeffery.to@gmail.com> +Kris Borchers <kris.borchers@gmail.com> +Vladimir Zhuravlev <private.face@gmail.com> +Jacob Thornton <jacobthornton@gmail.com> +Chad Killingsworth <chadkillingsworth@missouristate.edu> +Nowres Rafid <nowres.rafed@gmail.com> +David Benjamin <davidben@mit.edu> +Uri Gilad <antishok@gmail.com> +Chris Faulkner <thefaulkner@gmail.com> +Elijah Manor <elijah.manor@gmail.com> +Daniel Chatfield <chatfielddaniel@gmail.com> +Nikita Govorov <nikita.govorov@gmail.com> +Wesley Walser <waw325@gmail.com> +Mike Pennisi <mike@mikepennisi.com> +Markus Staab <markus.staab@redaxo.de> +Dave Riddle <david@joyvuu.com> +Callum Macrae <callum@lynxphp.com> +Benjamin Truyman <bentruyman@gmail.com> +James Huston <james@jameshuston.net> +Erick Ruiz de Chávez <erickrdch@gmail.com> +David Bonner <dbonner@cogolabs.com> +Akintayo Akinwunmi <aakinwunmi@judge.com> +MORGAN <morgan@morgangraphics.com> +Ismail Khair <ismail.khair@gmail.com> +Carl Danley <carldanley@gmail.com> +Mike Petrovich <michael.c.petrovich@gmail.com> +Greg Lavallee <greglavallee@wapolabs.com> +Daniel Gálvez <dgalvez@editablething.com> +Sai Lung Wong <sai.wong@huffingtonpost.com> +Tom H Fuertes <TomFuertes@gmail.com> +Roland Eckl <eckl.roland@googlemail.com> +Jay Merrifield <fracmak@gmail.com> +Allen J Schmidt Jr <cobrasoft@gmail.com> +Jonathan Sampson <jjdsampson@gmail.com> +Marcel Greter <marcel.greter@ocbnet.ch> +Matthias Jäggli <matthias.jaeggli@gmail.com> +David Fox <dfoxinator@gmail.com> +Yiming He <yiminghe@gmail.com> +Devin Cooper <cooper.semantics@gmail.com> +Paul Ramos <paul.b.ramos@gmail.com> +Rod Vagg <rod@vagg.org> +Bennett Sorbo <bsorbo@gmail.com> +Sebastian Burkhard <sebi.burkhard@gmail.com> +Zachary Adam Kaplan <razic@viralkitty.com> +nanto_vi <nanto@moon.email.ne.jp> +nanto <nanto@moon.email.ne.jp> +Danil Somsikov <danilasomsikov@gmail.com> +Ryunosuke SATO <tricknotes.rs@gmail.com> +Jean Boussier <jean.boussier@gmail.com> +Adam Coulombe <me@adam.co> +Andrew Plummer <plummer.andrew@gmail.com> +Mark Raddatz <mraddatz@gmail.com> +Isaac Z. Schlueter <i@izs.me> +Karl Sieburg <ksieburg@yahoo.com> +Pascal Borreli <pascal@borreli.com> +Nguyen Phuc Lam <ruado1987@gmail.com> +Dmitry Gusev <dmitry.gusev@gmail.com> +Michał Gołębiowski <m.goleb@gmail.com> +Li Xudong <istonelee@gmail.com> +Steven Benner <admin@stevenbenner.com> +Tom H Fuertes <tomfuertes@gmail.com> +Renato Oliveira dos Santos <ros3@cin.ufpe.br> +ros3cin <ros3@cin.ufpe.br> +Jason Bedard <jason+jquery@jbedard.ca> +Kyle Robinson Young <kyle@dontkry.com> +Chris Talkington <chris@talkingtontech.com> +Eddie Monge <eddie@eddiemonge.com> +Terry Jones <terry@jon.es> +Jason Merino <jasonmerino@gmail.com> +Jeremy Dunck <jdunck@gmail.com> +Chris Price <price.c@gmail.com> +Guy Bedford <guybedford@gmail.com> +Amey Sakhadeo <me@ameyms.com> +Mike Sidorov <mikes.ekb@gmail.com> +Anthony Ryan <anthonyryan1@gmail.com> +Dominik D. Geyer <dominik.geyer@gmail.com> +George Kats <katsgeorgeek@gmail.com> +Lihan Li <frankieteardrop@gmail.com> +Ronny Springer <springer.ronny@gmail.com> +Chris Antaki <ChrisAntaki@gmail.com> +Marian Sollmann <marian.sollmann@cargomedia.ch> +njhamann <njhamann@gmail.com> +Ilya Kantor <iliakan@gmail.com> +David Hong <d.hong@me.com> +John Paul <john@johnkpaul.com> +Jakob Stoeck <jakob@pokermania.de> +Christopher Jones <chris@cjqed.com> +Forbes Lindesay <forbes@lindesay.co.uk> +S. Andrew Sheppard <andrew@wq.io> +Leonardo Balter <leonardo.balter@gmail.com> +Roman Reiß <me@silverwind.io> +Benjy Cui <benjytrys@gmail.com> +Rodrigo Rosenfeld Rosas <rr.rosas@gmail.com> +John Hoven <hovenj@gmail.com> +Philip Jägenstedt <philip@foolip.org> +Christian Kosmowski <ksmwsk@gmail.com> +Liang Peng <poppinlp@gmail.com> +TJ VanToll <tj.vantoll@gmail.com> +Senya Pugach <upisfree@outlook.com> +Aurelio De Rosa <aurelioderosa@gmail.com> +Nazar Mokrynskyi <nazar@mokrynskyi.com> +Amit Merchant <bullredeyes@gmail.com> +Jason Bedard <jason+github@jbedard.ca> +Arthur Verschaeve <contact@arthurverschaeve.be> +Dan Hart <danhart@notonthehighstreet.com> +Bin Xin <rhyzix@gmail.com> +David Corbacho <davidcorbacho@gmail.com> +Veaceslav Grimalschi <grimalschi@yandex.ru> +Daniel Husar <dano.husar@gmail.com> +Frederic Hemberger <mail@frederic-hemberger.de> +Ben Toews <mastahyeti@gmail.com> +Aditya Raghavan <araghavan3@gmail.com> +Victor Homyakov <vkhomyackov@gmail.com> +Shivaji Varma <contact@shivajivarma.com> +Nicolas HENRY <icewil@gmail.com> +Anne-Gaelle Colom <coloma@westminster.ac.uk> +George Mauer <gmauer@gmail.com> +Leonardo Braga <leonardo.braga@gmail.com> +Stephen Edgar <stephen@netweb.com.au> +Thomas Tortorini <thomastortorini@gmail.com> +Winston Howes <winstonhowes@gmail.com> +Jon Hester <jon.d.hester@gmail.com> +Alexander O'Mara <me@alexomara.com> +Bastian Buchholz <buchholz.bastian@googlemail.com> +Arthur Stolyar <nekr.fabula@gmail.com> +Calvin Metcalf <calvin.metcalf@gmail.com> +Mu Haibao <mhbseal@163.com> +Richard McDaniel <rm0026@uah.edu> +Chris Rebert <github@rebertia.com> +Gabriel Schulhof <gabriel.schulhof@intel.com> +Gilad Peleg <giladp007@gmail.com> +Martin Naumann <martin@geekonaut.de> +Marek Lewandowski <m.lewandowski@cksource.com> +Bruno Pérel <brunoperel@gmail.com> +Reed Loden <reed@reedloden.com> +Daniel Nill <daniellnill@gmail.com> +Yongwoo Jeon <yongwoo.jeon@navercorp.com> +Sean Henderson <seanh.za@gmail.com> +Richard Kraaijenhagen <stdin+git@riichard.com> +Connor Atherton <c.liam.atherton@gmail.com> +Gary Ye <garysye@gmail.com> +Christian Grete <webmaster@christiangrete.com> +Liza Ramo <liza.h.ramo@gmail.com> +Julian Alexander Murillo <julian.alexander.murillo@gmail.com> +Joelle Fleurantin <joasqueeniebee@gmail.com> +Jae Sung Park <alberto.park@gmail.com> +Jun Sun <klsforever@gmail.com> +Josh Soref <apache@soref.com> +Henry Wong <henryw4k@gmail.com> +Jon Dufresne <jon.dufresne@gmail.com> +Martijn W. van der Lee <martijn@vanderlee.com> +Devin Wilson <dwilson6.github@gmail.com> +Steve Mao <maochenyan@gmail.com> +Zack Hall <zackhall@outlook.com> +Bernhard M. Wiedemann <jquerybmw@lsmod.de> +Todor Prikumov <tono_pr@abv.bg> +Jha Naman <createnaman@gmail.com> +William Robinet <william.robinet@conostix.com> +Alexander Lisianoi <all3fox@gmail.com> +Vitaliy Terziev <vitaliyterziev@gmail.com> +Joe Trumbull <trumbull.j@gmail.com> +Alexander K <xpyro@ya.ru> +Damian Senn <jquery@topaxi.codes> +Ralin Chimev <ralin.chimev@gmail.com> +Felipe Sateler <fsateler@gmail.com> +Christophe Tafani-Dereeper <christophetd@hotmail.fr> +Manoj Kumar <nithmanoj@gmail.com> +David Broder-Rodgers <broder93@gmail.com> +Alex Louden <alex@louden.com> +Alex Padilla <alexonezero@outlook.com> +南漂一卒 <shiy007@qq.com> +karan-96 <karanbatra96@gmail.com> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/LICENSE.txt b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/LICENSE.txt new file mode 100644 index 000000000..e4e5e00ef --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/LICENSE.txt @@ -0,0 +1,36 @@ +Copyright JS Foundation and other contributors, https://js.foundation/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/jquery + +The following license applies to all parts of this software except as +documented below: + +==== + +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. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/README.md b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/README.md new file mode 100644 index 000000000..09a4273e8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/README.md @@ -0,0 +1,67 @@ +# jQuery + +> jQuery is a fast, small, and feature-rich JavaScript library. + +For information on how to get started and how to use jQuery, please see [jQuery's documentation](http://api.jquery.com/). +For source files and issues, please visit the [jQuery repo](https://github.com/jquery/jquery). + +If upgrading, please see the [blog post for 3.2.1](https://blog.jquery.com/2017/03/20/jquery-3-2-1-now-available/). This includes notable differences from the previous version and a more readable changelog. + +## Including jQuery + +Below are some of the most common ways to include jQuery. + +### Browser + +#### Script tag + +```html +<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> +``` + +#### Babel + +[Babel](http://babeljs.io/) is a next generation JavaScript compiler. One of the features is the ability to use ES6/ES2015 modules now, even though browsers do not yet support this feature natively. + +```js +import $ from "jquery"; +``` + +#### Browserify/Webpack + +There are several ways to use [Browserify](http://browserify.org/) and [Webpack](https://webpack.github.io/). For more information on using these tools, please refer to the corresponding project's documention. In the script, including jQuery will usually look like this... + +```js +var $ = require("jquery"); +``` + +#### AMD (Asynchronous Module Definition) + +AMD is a module format built for the browser. For more information, we recommend [require.js' documentation](http://requirejs.org/docs/whyamd.html). + +```js +define(["jquery"], function($) { + +}); +``` + +### Node + +To include jQuery in [Node](nodejs.org), first install with npm. + +```sh +npm install jquery +``` + +For jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by tools such as [jsdom](https://github.com/tmpvar/jsdom). This can be useful for testing purposes. + +```js +require("jsdom").env("", function(err, window) { + if (err) { + console.error(err); + return; + } + + var $ = require("jquery")(window); +}); +``` diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/bower.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/bower.json new file mode 100644 index 000000000..95798d5ad --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/bower.json @@ -0,0 +1,14 @@ +{ + "name": "jquery", + "main": "dist/jquery.js", + "license": "MIT", + "ignore": [ + "package.json" + ], + "keywords": [ + "jquery", + "javascript", + "browser", + "library" + ] +}
\ No newline at end of file diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/external/sizzle/LICENSE.txt b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/external/sizzle/LICENSE.txt new file mode 100644 index 000000000..dd7ce9402 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/external/sizzle/LICENSE.txt @@ -0,0 +1,36 @@ +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/jquery/sizzle + +The following license applies to all parts of this software except as +documented below: + +==== + +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. + +==== + +All files located in the node_modules and external directories are +externally maintained libraries used by this software which have their +own licenses; we recommend you read them, as their terms may differ from +the terms above. diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/.eslintrc.json b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/.eslintrc.json new file mode 100644 index 000000000..3a4a3d257 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "root": true, + + "extends": "../.eslintrc-browser.json" +} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax.js new file mode 100644 index 000000000..36f707d7d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax.js @@ -0,0 +1,855 @@ +define( [ + "./core", + "./var/document", + "./var/rnothtmlwhite", + "./ajax/var/location", + "./ajax/var/nonce", + "./ajax/var/rquery", + + "./core/init", + "./ajax/parseXML", + "./event/trigger", + "./deferred", + "./serialize" // jQuery.param +], function( jQuery, document, rnothtmlwhite, location, nonce, rquery ) { + +"use strict"; + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( jQuery.isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 13 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available, append data to url + if ( s.data ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/jsonp.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/jsonp.js new file mode 100644 index 000000000..8c406e722 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/jsonp.js @@ -0,0 +1,102 @@ +define( [ + "../core", + "./var/nonce", + "./var/rquery", + "../ajax" +], function( jQuery, nonce, rquery ) { + +"use strict"; + +var oldCallbacks = [], + rjsonp = /(=)\?(?=&|$)|\?\?/; + +// Default jsonp settings +jQuery.ajaxSetup( { + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +} ); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? + "url" : + typeof s.data === "string" && + ( s.contentType || "" ) + .indexOf( "application/x-www-form-urlencoded" ) === 0 && + rjsonp.test( s.data ) && "data" + ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + + // Insert callback into url or form data + if ( jsonProp ) { + s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); + } else if ( s.jsonp !== false ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters[ "script json" ] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // Force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + overwritten = window[ callbackName ]; + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always( function() { + + // If previous value didn't exist - remove it + if ( overwritten === undefined ) { + jQuery( window ).removeProp( callbackName ); + + // Otherwise restore preexisting value + } else { + window[ callbackName ] = overwritten; + } + + // Save back as free + if ( s[ callbackName ] ) { + + // Make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // Save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + } ); + + // Delegate to script + return "script"; + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/load.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/load.js new file mode 100644 index 000000000..3ce3a5aae --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/load.js @@ -0,0 +1,76 @@ +define( [ + "../core", + "../core/stripAndCollapse", + "../core/parseHTML", + "../ajax", + "../traversing", + "../manipulation", + "../selector" +], function( jQuery, stripAndCollapse ) { + +"use strict"; + +/** + * Load a url into a page + */ +jQuery.fn.load = function( url, params, callback ) { + var selector, type, response, + self = this, + off = url.indexOf( " " ); + + if ( off > -1 ) { + selector = stripAndCollapse( url.slice( off ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax( { + url: url, + + // If "type" variable is undefined, then "GET" method will be used. + // Make value of this field explicit since + // user can override it through ajaxSetup method + type: type || "GET", + dataType: "html", + data: params + } ).done( function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + // If the request succeeds, this function gets "data", "status", "jqXHR" + // but they are ignored because response was set above. + // If it fails, this function gets "jqXHR", "status", "error" + } ).always( callback && function( jqXHR, status ) { + self.each( function() { + callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); + } ); + } ); + } + + return this; +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/parseXML.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/parseXML.js new file mode 100644 index 000000000..acf7ab259 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/parseXML.js @@ -0,0 +1,30 @@ +define( [ + "../core" +], function( jQuery ) { + +"use strict"; + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + +return jQuery.parseXML; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/script.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/script.js new file mode 100644 index 000000000..6e0d21e99 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/script.js @@ -0,0 +1,77 @@ +define( [ + "../core", + "../var/document", + "../ajax" +], function( jQuery, document ) { + +"use strict"; + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "<script>" ).prop( { + charset: s.scriptCharset, + src: s.url + } ).on( + "load error", + callback = function( evt ) { + script.remove(); + callback = null; + if ( evt ) { + complete( evt.type === "error" ? 404 : 200, evt.type ); + } + } + ); + + // Use native DOM manipulation to avoid our domManip AJAX trickery + document.head.appendChild( script[ 0 ] ); + }, + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/location.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/location.js new file mode 100644 index 000000000..4171d18c3 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/location.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return window.location; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/nonce.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/nonce.js new file mode 100644 index 000000000..6e23708a8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/nonce.js @@ -0,0 +1,7 @@ +define( [ + "../../core" +], function( jQuery ) { + "use strict"; + + return jQuery.now(); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/rquery.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/rquery.js new file mode 100644 index 000000000..06fc37439 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/var/rquery.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return ( /\?/ ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/xhr.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/xhr.js new file mode 100644 index 000000000..33dafb01d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/ajax/xhr.js @@ -0,0 +1,169 @@ +define( [ + "../core", + "../var/support", + "../ajax" +], function( jQuery, support ) { + +"use strict"; + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes.js new file mode 100644 index 000000000..2d801e563 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes.js @@ -0,0 +1,13 @@ +define( [ + "./core", + "./attributes/attr", + "./attributes/prop", + "./attributes/classes", + "./attributes/val" +], function( jQuery ) { + +"use strict"; + +// Return jQuery for attributes-only inclusion +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/attr.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/attr.js new file mode 100644 index 000000000..6b5cbd2c4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/attr.js @@ -0,0 +1,141 @@ +define( [ + "../core", + "../core/access", + "../core/nodeName", + "./support", + "../var/rnothtmlwhite", + "../selector" +], function( jQuery, access, nodeName, support, rnothtmlwhite ) { + +"use strict"; + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/classes.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/classes.js new file mode 100644 index 000000000..23b4cd6af --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/classes.js @@ -0,0 +1,174 @@ +define( [ + "../core", + "../core/stripAndCollapse", + "../var/rnothtmlwhite", + "../data/var/dataPriv", + "../core/init" +], function( jQuery, stripAndCollapse, rnothtmlwhite, dataPriv ) { + +"use strict"; + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( typeof value === "string" && value ) { + classes = value.match( rnothtmlwhite ) || []; + + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( jQuery.isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + if ( typeof value === "string" && value ) { + classes = value.match( rnothtmlwhite ) || []; + + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value; + + if ( typeof stateVal === "boolean" && type === "string" ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( jQuery.isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( type === "string" ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = value.match( rnothtmlwhite ) || []; + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/prop.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/prop.js new file mode 100644 index 000000000..49ac244df --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/prop.js @@ -0,0 +1,143 @@ +define( [ + "../core", + "../core/access", + "./support", + "../selector" +], function( jQuery, access, support ) { + +"use strict"; + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/support.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/support.js new file mode 100644 index 000000000..af60e9694 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/support.js @@ -0,0 +1,33 @@ +define( [ + "../var/document", + "../var/support" +], function( document, support ) { + +"use strict"; + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + +return support; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/val.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/val.js new file mode 100644 index 000000000..d764ec6d1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/attributes/val.js @@ -0,0 +1,190 @@ +define( [ + "../core", + "../core/stripAndCollapse", + "./support", + "../core/nodeName", + + "../core/init" +], function( jQuery, stripAndCollapse, support, nodeName ) { + +"use strict"; + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, isFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/callbacks.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/callbacks.js new file mode 100644 index 000000000..8831e531a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/callbacks.js @@ -0,0 +1,234 @@ +define( [ + "./core", + "./var/rnothtmlwhite" +], function( jQuery, rnothtmlwhite ) { + +"use strict"; + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( jQuery.isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core.js new file mode 100644 index 000000000..0e95274cf --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core.js @@ -0,0 +1,476 @@ +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + +define( [ + "./var/arr", + "./var/document", + "./var/getProto", + "./var/slice", + "./var/concat", + "./var/push", + "./var/indexOf", + "./var/class2type", + "./var/toString", + "./var/hasOwn", + "./var/fnToString", + "./var/ObjectFunctionString", + "./var/support", + "./core/DOMEval" +], function( arr, document, getProto, slice, concat, push, indexOf, + class2type, toString, hasOwn, fnToString, ObjectFunctionString, + support, DOMEval ) { + +"use strict"; + +var + version = "3.2.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && Array.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); + }, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE <=9 - 11, Edge 12 - 13 + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/DOMEval.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/DOMEval.js new file mode 100644 index 000000000..c49c12e5e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/DOMEval.js @@ -0,0 +1,16 @@ +define( [ + "../var/document" +], function( document ) { + "use strict"; + + function DOMEval( code, doc ) { + doc = doc || document; + + var script = doc.createElement( "script" ); + + script.text = code; + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + return DOMEval; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/access.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/access.js new file mode 100644 index 000000000..86cdbc7e6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/access.js @@ -0,0 +1,70 @@ +define( [ + "../core" +], function( jQuery ) { + +"use strict"; + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + +return access; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/init.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/init.js new file mode 100644 index 000000000..8841264de --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/init.js @@ -0,0 +1,128 @@ +// Initialize a jQuery object +define( [ + "../core", + "../var/document", + "./var/rsingleTag", + + "../traversing/findFilter" +], function( jQuery, document, rsingleTag ) { + +"use strict"; + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + +return init; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/nodeName.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/nodeName.js new file mode 100644 index 000000000..8a5f5f036 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/nodeName.js @@ -0,0 +1,13 @@ +define( function() { + +"use strict"; + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; + +return nodeName; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/parseHTML.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/parseHTML.js new file mode 100644 index 000000000..21ff6bfa7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/parseHTML.js @@ -0,0 +1,65 @@ +define( [ + "../core", + "../var/document", + "./var/rsingleTag", + "../manipulation/buildFragment", + + // This is the only module that needs core/support + "./support" +], function( jQuery, document, rsingleTag, buildFragment, support ) { + +"use strict"; + +// Argument "data" should be string of html +// context (optional): If specified, the fragment will be created in this context, +// defaults to document +// keepScripts (optional): If true, will include scripts passed in the html string +jQuery.parseHTML = function( data, context, keepScripts ) { + if ( typeof data !== "string" ) { + return []; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + + var base, parsed, scripts; + + if ( !context ) { + + // Stop scripts or inline event handlers from being executed immediately + // by using document.implementation + if ( support.createHTMLDocument ) { + context = document.implementation.createHTMLDocument( "" ); + + // Set the base href for the created document + // so any parsed elements with URLs + // are based on the document's URL (gh-2965) + base = context.createElement( "base" ); + base.href = document.location.href; + context.head.appendChild( base ); + } else { + context = document; + } + } + + parsed = rsingleTag.exec( data ); + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[ 1 ] ) ]; + } + + parsed = buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); +}; + +return jQuery.parseHTML; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/ready-no-deferred.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/ready-no-deferred.js new file mode 100644 index 000000000..a7722ae9a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/ready-no-deferred.js @@ -0,0 +1,96 @@ +define( [ + "../core", + "../var/document" +], function( jQuery, document ) { + +"use strict"; + +var readyCallbacks = [], + whenReady = function( fn ) { + readyCallbacks.push( fn ); + }, + executeReady = function( fn ) { + + // Prevent errors from freezing future callback execution (gh-1823) + // Not backwards-compatible as this does not execute sync + window.setTimeout( function() { + fn.call( document, jQuery ); + } ); + }; + +jQuery.fn.ready = function( fn ) { + whenReady( fn ); + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + whenReady = function( fn ) { + readyCallbacks.push( fn ); + + while ( readyCallbacks.length ) { + fn = readyCallbacks.shift(); + if ( jQuery.isFunction( fn ) ) { + executeReady( fn ); + } + } + }; + + whenReady(); + } +} ); + +// Make jQuery.ready Promise consumable (gh-1778) +jQuery.ready.then = jQuery.fn.ready; + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE9-10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/ready.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/ready.js new file mode 100644 index 000000000..794feeec0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/ready.js @@ -0,0 +1,86 @@ +define( [ + "../core", + "../var/document", + "../core/readyException", + "../deferred" +], function( jQuery, document ) { + +"use strict"; + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/readyException.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/readyException.js new file mode 100644 index 000000000..72bdd90b5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/readyException.js @@ -0,0 +1,13 @@ +define( [ + "../core" +], function( jQuery ) { + +"use strict"; + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/stripAndCollapse.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/stripAndCollapse.js new file mode 100644 index 000000000..ccad6602e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/stripAndCollapse.js @@ -0,0 +1,14 @@ +define( [ + "../var/rnothtmlwhite" +], function( rnothtmlwhite ) { + "use strict"; + + // Strip and collapse whitespace according to HTML spec + // https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + return stripAndCollapse; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/support.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/support.js new file mode 100644 index 000000000..13ae02f08 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/support.js @@ -0,0 +1,20 @@ +define( [ + "../var/document", + "../var/support" +], function( document, support ) { + +"use strict"; + +// Support: Safari 8 only +// In Safari 8 documents created via document.implementation.createHTMLDocument +// collapse sibling forms: the second one becomes a child of the first one. +// Because of that, this security measure has to be disabled in Safari 8. +// https://bugs.webkit.org/show_bug.cgi?id=137337 +support.createHTMLDocument = ( function() { + var body = document.implementation.createHTMLDocument( "" ).body; + body.innerHTML = "<form></form><form></form>"; + return body.childNodes.length === 2; +} )(); + +return support; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/var/rsingleTag.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/var/rsingleTag.js new file mode 100644 index 000000000..4d6e8a0ac --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/core/var/rsingleTag.js @@ -0,0 +1,6 @@ +define( function() { + "use strict"; + + // Match a standalone tag + return ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css.js new file mode 100644 index 000000000..48a83404b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css.js @@ -0,0 +1,438 @@ +define( [ + "./core", + "./var/pnum", + "./core/access", + "./css/var/rmargin", + "./var/document", + "./var/rcssNum", + "./css/var/rnumnonpx", + "./css/var/cssExpand", + "./css/var/getStyles", + "./css/var/swap", + "./css/curCSS", + "./css/adjustCSS", + "./css/addGetHookIf", + "./css/support", + + "./core/init", + "./core/ready", + "./selector" // contains +], function( jQuery, pnum, access, rmargin, document, rcssNum, rnumnonpx, cssExpand, + getStyles, swap, curCSS, adjustCSS, addGetHookIf, support ) { + +"use strict"; + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in emptyStyle ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a property mapped along what jQuery.cssProps suggests or to +// a vendor prefixed property. +function finalPropName( name ) { + var ret = jQuery.cssProps[ name ]; + if ( !ret ) { + ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; + } + return ret; +} + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i, + val = 0; + + // If we already have the right measurement, avoid augmentation + if ( extra === ( isBorderBox ? "border" : "content" ) ) { + i = 4; + + // Otherwise initialize for horizontal or vertical properties + } else { + i = name === "width" ? 1 : 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // At this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + + // At this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // At this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with computed style + var valueIsBorderBox, + styles = getStyles( elem ), + val = curCSS( elem, name, styles ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test( val ) ) { + return val; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && + ( support.boxSizingReliable() || val === elem.style[ name ] ); + + // Fall back to offsetWidth/Height when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + if ( val === "auto" ) { + val = elem[ "offset" + name[ 0 ].toUpperCase() + name.slice( 1 ) ]; + } + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + + // Use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + "float": "cssFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = jQuery.camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + } ) : + getWidthOrHeight( elem, name, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = extra && getStyles( elem ), + subtract = extra && augmentWidthOrHeight( + elem, + name, + extra, + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + styles + ); + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ name ] = value; + value = jQuery.css( elem, name ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/addGetHookIf.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/addGetHookIf.js new file mode 100644 index 000000000..e4bb49a67 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/addGetHookIf.js @@ -0,0 +1,26 @@ +define( function() { + +"use strict"; + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + +return addGetHookIf; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/adjustCSS.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/adjustCSS.js new file mode 100644 index 000000000..ded3a1666 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/adjustCSS.js @@ -0,0 +1,71 @@ +define( [ + "../core", + "../var/rcssNum" +], function( jQuery, rcssNum ) { + +"use strict"; + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + +return adjustCSS; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/curCSS.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/curCSS.js new file mode 100644 index 000000000..01ccad3ae --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/curCSS.js @@ -0,0 +1,65 @@ +define( [ + "../core", + "./var/rnumnonpx", + "./var/rmargin", + "./var/getStyles", + "./support", + "../selector" // Get jQuery.contains +], function( jQuery, rnumnonpx, rmargin, getStyles, support ) { + +"use strict"; + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelMarginRight() && rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + +return curCSS; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/hiddenVisibleSelectors.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/hiddenVisibleSelectors.js new file mode 100644 index 000000000..d7a9339dd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/hiddenVisibleSelectors.js @@ -0,0 +1,15 @@ +define( [ + "../core", + "../selector" +], function( jQuery ) { + +"use strict"; + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/showHide.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/showHide.js new file mode 100644 index 000000000..3eeafef11 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/showHide.js @@ -0,0 +1,105 @@ +define( [ + "../core", + "../data/var/dataPriv", + "../css/var/isHiddenWithinTree" +], function( jQuery, dataPriv, isHiddenWithinTree ) { + +"use strict"; + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); + +return showHide; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/support.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/support.js new file mode 100644 index 000000000..883d0e53e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/support.js @@ -0,0 +1,89 @@ +define( [ + "../core", + "../var/document", + "../var/documentElement", + "../var/support" +], function( jQuery, document, documentElement, support ) { + +"use strict"; + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + div.style.cssText = + "box-sizing:border-box;" + + "position:relative;display:block;" + + "margin:auto;border:1px;padding:1px;" + + "top:1%;width:50%"; + div.innerHTML = ""; + documentElement.appendChild( container ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = divStyle.marginLeft === "2px"; + boxSizingReliableVal = divStyle.width === "4px"; + + // Support: Android 4.0 - 4.3 only + // Some styles come back with percentage values, even though they shouldn't + div.style.marginRight = "50%"; + pixelMarginRightVal = divStyle.marginRight === "4px"; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" + + "padding:0;margin-top:1px;position:absolute"; + container.appendChild( div ); + + jQuery.extend( support, { + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelMarginRight: function() { + computeStyleTests(); + return pixelMarginRightVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + } + } ); +} )(); + +return support; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/cssExpand.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/cssExpand.js new file mode 100644 index 000000000..dd2007c3f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/cssExpand.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return [ "Top", "Right", "Bottom", "Left" ]; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/getStyles.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/getStyles.js new file mode 100644 index 000000000..0b893acf0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/getStyles.js @@ -0,0 +1,17 @@ +define( function() { + "use strict"; + + return function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/isHiddenWithinTree.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/isHiddenWithinTree.js new file mode 100644 index 000000000..3cfb93e16 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/isHiddenWithinTree.js @@ -0,0 +1,34 @@ +define( [ + "../../core", + "../../selector" + + // css is assumed +], function( jQuery ) { + "use strict"; + + // isHiddenWithinTree reports if an element has a non-"none" display style (inline and/or + // through the CSS cascade), which is useful in deciding whether or not to make it visible. + // It differs from the :hidden selector (jQuery.expr.pseudos.hidden) in two important ways: + // * A hidden ancestor does not force an element to be classified as hidden. + // * Being disconnected from the document does not force an element to be classified as hidden. + // These differences improve the behavior of .toggle() et al. when applied to elements that are + // detached or contained within hidden ancestors (gh-2404, gh-2863). + return function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains( elem.ownerDocument, elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/rmargin.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/rmargin.js new file mode 100644 index 000000000..0fbfbd853 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/rmargin.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return ( /^margin/ ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/rnumnonpx.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/rnumnonpx.js new file mode 100644 index 000000000..056cda7ad --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/rnumnonpx.js @@ -0,0 +1,7 @@ +define( [ + "../../var/pnum" +], function( pnum ) { + "use strict"; + + return new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/swap.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/swap.js new file mode 100644 index 000000000..1a9556bad --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/css/var/swap.js @@ -0,0 +1,26 @@ +define( function() { + +"use strict"; + +// A method for quickly swapping in/out CSS properties to get correct calculations. +return function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data.js new file mode 100644 index 000000000..087ce4eb7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data.js @@ -0,0 +1,179 @@ +define( [ + "./core", + "./core/access", + "./data/var/dataPriv", + "./data/var/dataUser" +], function( jQuery, access, dataPriv, dataUser ) { + +"use strict"; + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/Data.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/Data.js new file mode 100644 index 000000000..8c856c039 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/Data.js @@ -0,0 +1,161 @@ +define( [ + "../core", + "../var/rnothtmlwhite", + "./var/acceptData" +], function( jQuery, rnothtmlwhite, acceptData ) { + +"use strict"; + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ jQuery.camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ jQuery.camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( jQuery.camelCase ); + } else { + key = jQuery.camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; + +return Data; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/acceptData.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/acceptData.js new file mode 100644 index 000000000..e00f7538b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/acceptData.js @@ -0,0 +1,19 @@ +define( function() { + +"use strict"; + +/** + * Determines whether an object can have data + */ +return function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/dataPriv.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/dataPriv.js new file mode 100644 index 000000000..72713c88c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/dataPriv.js @@ -0,0 +1,7 @@ +define( [ + "../Data" +], function( Data ) { + "use strict"; + + return new Data(); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/dataUser.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/dataUser.js new file mode 100644 index 000000000..72713c88c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/data/var/dataUser.js @@ -0,0 +1,7 @@ +define( [ + "../Data" +], function( Data ) { + "use strict"; + + return new Data(); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deferred.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deferred.js new file mode 100644 index 000000000..7e2ced25b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deferred.js @@ -0,0 +1,391 @@ +define( [ + "./core", + "./var/slice", + "./callbacks" +], function( jQuery, slice ) { + +"use strict"; + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && jQuery.isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( jQuery.isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + jQuery.isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + jQuery.isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + jQuery.isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deferred/exceptionHook.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deferred/exceptionHook.js new file mode 100644 index 000000000..6dbdc8520 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deferred/exceptionHook.js @@ -0,0 +1,21 @@ +define( [ + "../core", + "../deferred" +], function( jQuery ) { + +"use strict"; + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deprecated.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deprecated.js new file mode 100644 index 000000000..9589ec872 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/deprecated.js @@ -0,0 +1,40 @@ +define( [ + "./core", + "./core/nodeName" +], function( jQuery, nodeName ) { + +"use strict"; + +jQuery.fn.extend( { + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? + this.off( selector, "**" ) : + this.off( types, selector || "**", fn ); + } +} ); + +jQuery.holdReady = function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } +}; +jQuery.isArray = Array.isArray; +jQuery.parseJSON = JSON.parse; +jQuery.nodeName = nodeName; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/dimensions.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/dimensions.js new file mode 100644 index 000000000..46e7b1c46 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/dimensions.js @@ -0,0 +1,56 @@ +define( [ + "./core", + "./core/access", + "./css" +], function( jQuery, access ) { + +"use strict"; + +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, + function( defaultExtra, funcName ) { + + // Margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + + // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) + return funcName.indexOf( "outer" ) === 0 ? + elem[ "inner" + name ] : + elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], + // whichever is greatest + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable ); + }; + } ); +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects.js new file mode 100644 index 000000000..879b36135 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects.js @@ -0,0 +1,699 @@ +define( [ + "./core", + "./var/document", + "./var/rcssNum", + "./var/rnothtmlwhite", + "./css/var/cssExpand", + "./css/var/isHiddenWithinTree", + "./css/var/swap", + "./css/adjustCSS", + "./data/var/dataPriv", + "./css/showHide", + + "./core/init", + "./queue", + "./deferred", + "./traversing", + "./manipulation", + "./css", + "./effects/Tween" +], function( jQuery, document, rcssNum, rnothtmlwhite, cssExpand, isHiddenWithinTree, swap, + adjustCSS, dataPriv, showHide ) { + +"use strict"; + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = jQuery.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 13 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( jQuery.isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + jQuery.proxy( result.stop, result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects/Tween.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects/Tween.js new file mode 100644 index 000000000..43eb8fa0b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects/Tween.js @@ -0,0 +1,123 @@ +define( [ + "../core", + "../css" +], function( jQuery ) { + +"use strict"; + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && + ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || + jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects/animatedSelector.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects/animatedSelector.js new file mode 100644 index 000000000..24c1bfba2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/effects/animatedSelector.js @@ -0,0 +1,15 @@ +define( [ + "../core", + "../selector", + "../effects" +], function( jQuery ) { + +"use strict"; + +jQuery.expr.pseudos.animated = function( elem ) { + return jQuery.grep( jQuery.timers, function( fn ) { + return elem === fn.elem; + } ).length; +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event.js new file mode 100644 index 000000000..071deb645 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event.js @@ -0,0 +1,746 @@ +define( [ + "./core", + "./var/document", + "./var/documentElement", + "./var/rnothtmlwhite", + "./var/slice", + "./data/var/dataPriv", + "./core/nodeName", + + "./core/init", + "./selector" +], function( jQuery, document, documentElement, rnothtmlwhite, slice, dataPriv, nodeName ) { + +"use strict"; + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 only +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG <use> instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: jQuery.isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/ajax.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/ajax.js new file mode 100644 index 000000000..500b36cdd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/ajax.js @@ -0,0 +1,22 @@ +define( [ + "../core", + "../event" +], function( jQuery ) { + +"use strict"; + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ + "ajaxStart", + "ajaxStop", + "ajaxComplete", + "ajaxError", + "ajaxSuccess", + "ajaxSend" +], function( i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/alias.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/alias.js new file mode 100644 index 000000000..863c94ad2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/alias.js @@ -0,0 +1,29 @@ +define( [ + "../core", + + "../event", + "./trigger" +], function( jQuery ) { + +"use strict"; + +jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup contextmenu" ).split( " " ), + function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +} ); + +jQuery.fn.extend( { + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/focusin.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/focusin.js new file mode 100644 index 000000000..7faef2981 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/focusin.js @@ -0,0 +1,55 @@ +define( [ + "../core", + "../data/var/dataPriv", + "./support", + + "../event", + "./trigger" +], function( jQuery, dataPriv, support ) { + +"use strict"; + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/support.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/support.js new file mode 100644 index 000000000..e3db9ad83 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/support.js @@ -0,0 +1,11 @@ +define( [ + "../var/support" +], function( support ) { + +"use strict"; + +support.focusin = "onfocusin" in window; + +return support; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/trigger.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/trigger.js new file mode 100644 index 000000000..ef391370f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/event/trigger.js @@ -0,0 +1,185 @@ +define( [ + "../core", + "../var/document", + "../data/var/dataPriv", + "../data/var/acceptData", + "../var/hasOwn", + + "../event" +], function( jQuery, document, dataPriv, acceptData, hasOwn ) { + +"use strict"; + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/exports/amd.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/exports/amd.js new file mode 100644 index 000000000..cbb1ef580 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/exports/amd.js @@ -0,0 +1,26 @@ +define( [ + "../core" +], function( jQuery ) { + +"use strict"; + +// Register as a named AMD module, since jQuery can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase jquery is used because AMD module names are +// derived from file names, and jQuery is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of jQuery, it will work. + +// Note that for maximum portability, libraries that are not jQuery should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. jQuery is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + +if ( typeof define === "function" && define.amd ) { + define( "jquery", [], function() { + return jQuery; + } ); +} + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/exports/global.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/exports/global.js new file mode 100644 index 000000000..460b56e47 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/exports/global.js @@ -0,0 +1,34 @@ +define( [ + "../core" +], function( jQuery, noGlobal ) { + +"use strict"; + +var + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$; + +jQuery.noConflict = function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; +}; + +// Expose jQuery and $ identifiers, even in AMD +// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) +// and CommonJS for browser emulators (#13566) +if ( !noGlobal ) { + window.jQuery = window.$ = jQuery; +} + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/jquery.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/jquery.js new file mode 100644 index 000000000..52fc87dbd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/jquery.js @@ -0,0 +1,41 @@ +define( [ + "./core", + "./selector", + "./traversing", + "./callbacks", + "./deferred", + "./deferred/exceptionHook", + "./core/ready", + "./data", + "./queue", + "./queue/delay", + "./attributes", + "./event", + "./event/alias", + "./event/focusin", + "./manipulation", + "./manipulation/_evalUrl", + "./wrap", + "./css", + "./css/hiddenVisibleSelectors", + "./serialize", + "./ajax", + "./ajax/xhr", + "./ajax/script", + "./ajax/jsonp", + "./ajax/load", + "./event/ajax", + "./effects", + "./effects/animatedSelector", + "./offset", + "./dimensions", + "./deprecated", + "./exports/amd", + "./exports/global" +], function( jQuery ) { + +"use strict"; + +return jQuery; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation.js new file mode 100644 index 000000000..cd225a6db --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation.js @@ -0,0 +1,488 @@ +define( [ + "./core", + "./var/concat", + "./var/push", + "./core/access", + "./manipulation/var/rcheckableType", + "./manipulation/var/rtagName", + "./manipulation/var/rscriptType", + "./manipulation/wrapMap", + "./manipulation/getAll", + "./manipulation/setGlobalEval", + "./manipulation/buildFragment", + "./manipulation/support", + + "./data/var/dataPriv", + "./data/var/dataUser", + "./data/var/acceptData", + "./core/DOMEval", + "./core/nodeName", + + "./core/init", + "./traversing", + "./selector", + "./event" +], function( jQuery, concat, push, access, + rcheckableType, rtagName, rscriptType, + wrapMap, getAll, setGlobalEval, buildFragment, support, + dataPriv, dataUser, acceptData, DOMEval, nodeName ) { + +"use strict"; + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /<script|<style|<link/i, + + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( ">tbody", elem )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1></$2>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/_evalUrl.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/_evalUrl.js new file mode 100644 index 000000000..f9ec7027c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/_evalUrl.js @@ -0,0 +1,23 @@ +define( [ + "../ajax" +], function( jQuery ) { + +"use strict"; + +jQuery._evalUrl = function( url ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + "throws": true + } ); +}; + +return jQuery._evalUrl; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/buildFragment.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/buildFragment.js new file mode 100644 index 000000000..bcb508531 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/buildFragment.js @@ -0,0 +1,104 @@ +define( [ + "../core", + "./var/rtagName", + "./var/rscriptType", + "./wrapMap", + "./getAll", + "./setGlobalEval" +], function( jQuery, rtagName, rscriptType, wrapMap, getAll, setGlobalEval ) { + +"use strict"; + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + +return buildFragment; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/getAll.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/getAll.js new file mode 100644 index 000000000..fede6c78a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/getAll.js @@ -0,0 +1,32 @@ +define( [ + "../core", + "../core/nodeName" +], function( jQuery, nodeName ) { + +"use strict"; + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + +return getAll; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/setGlobalEval.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/setGlobalEval.js new file mode 100644 index 000000000..cf95240a4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/setGlobalEval.js @@ -0,0 +1,22 @@ +define( [ + "../data/var/dataPriv" +], function( dataPriv ) { + +"use strict"; + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + +return setGlobalEval; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/support.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/support.js new file mode 100644 index 000000000..4a5d9af4c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/support.js @@ -0,0 +1,35 @@ +define( [ + "../var/document", + "../var/support" +], function( document, support ) { + +"use strict"; + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); + +return support; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rcheckableType.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rcheckableType.js new file mode 100644 index 000000000..25bbcb418 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rcheckableType.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return ( /^(?:checkbox|radio)$/i ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rscriptType.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rscriptType.js new file mode 100644 index 000000000..7237c8a31 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rscriptType.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return ( /^$|\/(?:java|ecma)script/i ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rtagName.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rtagName.js new file mode 100644 index 000000000..d565dd3de --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/var/rtagName.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/wrapMap.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/wrapMap.js new file mode 100644 index 000000000..1f446f7d7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/manipulation/wrapMap.js @@ -0,0 +1,29 @@ +define( function() { + +"use strict"; + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "<select multiple='multiple'>", "</select>" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting <tbody> or other required elements. + thead: [ 1, "<table>", "</table>" ], + col: [ 2, "<table><colgroup>", "</colgroup></table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +return wrapMap; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/offset.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/offset.js new file mode 100644 index 000000000..c1ab85787 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/offset.js @@ -0,0 +1,229 @@ +define( [ + "./core", + "./core/access", + "./var/document", + "./var/documentElement", + "./css/var/rnumnonpx", + "./css/curCSS", + "./css/addGetHookIf", + "./css/support", + "./core/nodeName", + + "./core/init", + "./css", + "./selector" // contains +], function( jQuery, access, document, documentElement, rnumnonpx, + curCSS, addGetHookIf, support, nodeName ) { + +"use strict"; + +jQuery.offset = { + setOffset: function( elem, options, i ) { + var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, + position = jQuery.css( elem, "position" ), + curElem = jQuery( elem ), + props = {}; + + // Set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + curOffset = curElem.offset(); + curCSSTop = jQuery.css( elem, "top" ); + curCSSLeft = jQuery.css( elem, "left" ); + calculatePosition = ( position === "absolute" || position === "fixed" ) && + ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; + + // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + + // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) + options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + + } else { + curElem.css( props ); + } + } +}; + +jQuery.fn.extend( { + offset: function( options ) { + + // Preserve chaining for setter + if ( arguments.length ) { + return options === undefined ? + this : + this.each( function( i ) { + jQuery.offset.setOffset( this, options, i ); + } ); + } + + var doc, docElem, rect, win, + elem = this[ 0 ]; + + if ( !elem ) { + return; + } + + // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if ( !elem.getClientRects().length ) { + return { top: 0, left: 0 }; + } + + rect = elem.getBoundingClientRect(); + + doc = elem.ownerDocument; + docElem = doc.documentElement; + win = doc.defaultView; + + return { + top: rect.top + win.pageYOffset - docElem.clientTop, + left: rect.left + win.pageXOffset - docElem.clientLeft + }; + }, + + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, + elem = this[ 0 ], + parentOffset = { top: 0, left: 0 }; + + // Fixed elements are offset from window (parentOffset = {top:0, left: 0}, + // because it is its only offset parent + if ( jQuery.css( elem, "position" ) === "fixed" ) { + + // Assume getBoundingClientRect is there when computed position is fixed + offset = elem.getBoundingClientRect(); + + } else { + + // Get *real* offsetParent + offsetParent = this.offsetParent(); + + // Get correct offsets + offset = this.offset(); + if ( !nodeName( offsetParent[ 0 ], "html" ) ) { + parentOffset = offsetParent.offset(); + } + + // Add offsetParent borders + parentOffset = { + top: parentOffset.top + jQuery.css( offsetParent[ 0 ], "borderTopWidth", true ), + left: parentOffset.left + jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true ) + }; + } + + // Subtract parent offsets and element margins + return { + top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), + left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) + }; + }, + + // This method will return documentElement in the following cases: + // 1) For the element inside the iframe without offsetParent, this method will return + // documentElement of the parent window + // 2) For the hidden or detached element + // 3) For body or html element, i.e. in case of the html node - it will return itself + // + // but those exceptions were never presented as a real life use-cases + // and might be considered as more preferable results. + // + // This logic, however, is not guaranteed and can change at any point in the future + offsetParent: function() { + return this.map( function() { + var offsetParent = this.offsetParent; + + while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || documentElement; + } ); + } +} ); + +// Create scrollLeft and scrollTop methods +jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { + var top = "pageYOffset" === prop; + + jQuery.fn[ method ] = function( val ) { + return access( this, function( elem, method, val ) { + + // Coalesce documents and windows + var win; + if ( jQuery.isWindow( elem ) ) { + win = elem; + } else if ( elem.nodeType === 9 ) { + win = elem.defaultView; + } + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : win.pageXOffset, + top ? val : win.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length ); + }; +} ); + +// Support: Safari <=7 - 9.1, Chrome <=37 - 49 +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 +// getComputedStyle returns percent when specified for top/left/bottom/right; +// rather than make the css module depend on the offset module, just check for it here +jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, + function( elem, computed ) { + if ( computed ) { + computed = curCSS( elem, prop ); + + // If curCSS returns percentage, fallback to offset + return rnumnonpx.test( computed ) ? + jQuery( elem ).position()[ prop ] + "px" : + computed; + } + } + ); +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/queue.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/queue.js new file mode 100644 index 000000000..fbbbeab71 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/queue.js @@ -0,0 +1,145 @@ +define( [ + "./core", + "./data/var/dataPriv", + "./deferred", + "./callbacks" +], function( jQuery, dataPriv ) { + +"use strict"; + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/queue/delay.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/queue/delay.js new file mode 100644 index 000000000..d471eedc5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/queue/delay.js @@ -0,0 +1,24 @@ +define( [ + "../core", + "../queue", + "../effects" // Delay is optional because of this dependency +], function( jQuery ) { + +"use strict"; + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + +return jQuery.fn.delay; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector-native.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector-native.js new file mode 100644 index 000000000..da837a004 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector-native.js @@ -0,0 +1,237 @@ +define( [ + "./core", + "./var/document", + "./var/documentElement", + "./var/hasOwn", + "./var/indexOf" +], function( jQuery, document, documentElement, hasOwn, indexOf ) { + +"use strict"; + +/* + * Optional (non-Sizzle) selector module for custom builds. + * + * Note that this DOES NOT SUPPORT many documented jQuery + * features in exchange for its smaller size: + * + * Attribute not equal selector + * Positional selectors (:first; :eq(n); :odd; etc.) + * Type selectors (:input; :checkbox; :button; etc.) + * State-based selectors (:animated; :visible; :hidden; etc.) + * :has(selector) + * :not(complex selector) + * custom selectors via Sizzle extensions + * Leading combinators (e.g., $collection.find("> *")) + * Reliable functionality on XML fragments + * Requiring all parts of a selector to match elements under context + * (e.g., $div.find("div > *") now matches children of $div) + * Matching against non-elements + * Reliable sorting of disconnected nodes + * querySelectorAll bug fixes (e.g., unreliable :focus on WebKit) + * + * If any of these are unacceptable tradeoffs, either use Sizzle or + * customize this stub for the project's specific needs. + */ + +var hasDuplicate, sortInput, + sortStable = jQuery.expando.split( "" ).sort( sortOrder ).join( "" ) === jQuery.expando, + matches = documentElement.matches || + documentElement.webkitMatchesSelector || + documentElement.mozMatchesSelector || + documentElement.oMatchesSelector || + documentElement.msMatchesSelector, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }; + +function sortOrder( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === document && + jQuery.contains( document, a ) ) { + return -1; + } + if ( b === document || b.ownerDocument === document && + jQuery.contains( document, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; +} + +function uniqueSort( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + hasDuplicate = false; + sortInput = !sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +} + +function escape( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +} + +jQuery.extend( { + uniqueSort: uniqueSort, + unique: uniqueSort, + escapeSelector: escape, + find: function( selector, context, results, seed ) { + var elem, nodeType, + i = 0; + + results = results || []; + context = context || document; + + // Same basic safeguard as Sizzle + if ( !selector || typeof selector !== "string" ) { + return results; + } + + // Early return if context is not an element or document + if ( ( nodeType = context.nodeType ) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( seed ) { + while ( ( elem = seed[ i++ ] ) ) { + if ( jQuery.find.matchesSelector( elem, selector ) ) { + results.push( elem ); + } + } + } else { + jQuery.merge( results, context.querySelectorAll( selector ) ); + } + + return results; + }, + text: function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += jQuery.text( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + return elem.textContent; + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; + }, + contains: function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains( bup ) ); + }, + isXMLDoc: function( elem ) { + + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && ( elem.ownerDocument || elem ).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; + }, + expr: { + attrHandle: {}, + match: { + bool: new RegExp( "^(?:checked|selected|async|autofocus|autoplay|controls|defer" + + "|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped)$", "i" ), + needsContext: /^[\x20\t\r\n\f]*[>+~]/ + } + } +} ); + +jQuery.extend( jQuery.find, { + matches: function( expr, elements ) { + return jQuery.find( expr, null, null, elements ); + }, + matchesSelector: function( elem, expr ) { + return matches.call( elem, expr ); + }, + attr: function( elem, name ) { + var fn = jQuery.expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + value = fn && hasOwn.call( jQuery.expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, jQuery.isXMLDoc( elem ) ) : + undefined; + return value !== undefined ? value : elem.getAttribute( name ); + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector-sizzle.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector-sizzle.js new file mode 100644 index 000000000..ff7bc70ee --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector-sizzle.js @@ -0,0 +1,19 @@ +define( [ + "./core", + "../external/sizzle/dist/sizzle" +], function( jQuery, Sizzle ) { + +"use strict"; + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector.js new file mode 100644 index 000000000..2e0c17e15 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/selector.js @@ -0,0 +1,3 @@ +define( [ "./selector-sizzle" ], function() { + "use strict"; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/serialize.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/serialize.js new file mode 100644 index 000000000..2e28ce1e0 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/serialize.js @@ -0,0 +1,130 @@ +define( [ + "./core", + "./manipulation/var/rcheckableType", + "./core/init", + "./traversing", // filter + "./attributes/prop" +], function( jQuery, rcheckableType ) { + +"use strict"; + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = jQuery.isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing.js new file mode 100644 index 000000000..64c7252b9 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing.js @@ -0,0 +1,191 @@ +define( [ + "./core", + "./var/indexOf", + "./traversing/var/dir", + "./traversing/var/siblings", + "./traversing/var/rneedsContext", + "./core/nodeName", + + "./core/init", + "./traversing/findFilter", + "./selector" +], function( jQuery, indexOf, dir, siblings, rneedsContext, nodeName ) { + +"use strict"; + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( nodeName( elem, "iframe" ) ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/findFilter.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/findFilter.js new file mode 100644 index 000000000..3bd036a18 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/findFilter.js @@ -0,0 +1,106 @@ +define( [ + "../core", + "../var/indexOf", + "./var/rneedsContext", + "../selector" +], function( jQuery, indexOf, rneedsContext ) { + +"use strict"; + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Simple selector that can be filtered directly, removing non-Elements + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + // Complex selector, compare the two sets, removing non-Elements + qualifier = jQuery.filter( qualifier, elements ); + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not && elem.nodeType === 1; + } ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/dir.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/dir.js new file mode 100644 index 000000000..366a823d6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/dir.js @@ -0,0 +1,22 @@ +define( [ + "../../core" +], function( jQuery ) { + +"use strict"; + +return function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/rneedsContext.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/rneedsContext.js new file mode 100644 index 000000000..d0663cee8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/rneedsContext.js @@ -0,0 +1,8 @@ +define( [ + "../../core", + "../../selector" +], function( jQuery ) { + "use strict"; + + return jQuery.expr.match.needsContext; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/siblings.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/siblings.js new file mode 100644 index 000000000..952629d0c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/traversing/var/siblings.js @@ -0,0 +1,17 @@ +define( function() { + +"use strict"; + +return function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/ObjectFunctionString.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/ObjectFunctionString.js new file mode 100644 index 000000000..f9e850fd8 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/ObjectFunctionString.js @@ -0,0 +1,7 @@ +define( [ + "./fnToString" +], function( fnToString ) { + "use strict"; + + return fnToString.call( Object ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/arr.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/arr.js new file mode 100644 index 000000000..84713d838 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/arr.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return []; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/class2type.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/class2type.js new file mode 100644 index 000000000..4365d46a2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/class2type.js @@ -0,0 +1,6 @@ +define( function() { + "use strict"; + + // [[Class]] -> type pairs + return {}; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/concat.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/concat.js new file mode 100644 index 000000000..e47c19d75 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/concat.js @@ -0,0 +1,7 @@ +define( [ + "./arr" +], function( arr ) { + "use strict"; + + return arr.concat; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/document.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/document.js new file mode 100644 index 000000000..dd3939df4 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/document.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return window.document; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/documentElement.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/documentElement.js new file mode 100644 index 000000000..0e3f8b48c --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/documentElement.js @@ -0,0 +1,7 @@ +define( [ + "./document" +], function( document ) { + "use strict"; + + return document.documentElement; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/fnToString.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/fnToString.js new file mode 100644 index 000000000..18c43ff30 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/fnToString.js @@ -0,0 +1,7 @@ +define( [ + "./hasOwn" +], function( hasOwn ) { + "use strict"; + + return hasOwn.toString; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/getProto.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/getProto.js new file mode 100644 index 000000000..965fab8fb --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/getProto.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return Object.getPrototypeOf; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/hasOwn.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/hasOwn.js new file mode 100644 index 000000000..44ab6807d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/hasOwn.js @@ -0,0 +1,7 @@ +define( [ + "./class2type" +], function( class2type ) { + "use strict"; + + return class2type.hasOwnProperty; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/indexOf.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/indexOf.js new file mode 100644 index 000000000..8320b98e5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/indexOf.js @@ -0,0 +1,7 @@ +define( [ + "./arr" +], function( arr ) { + "use strict"; + + return arr.indexOf; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/pnum.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/pnum.js new file mode 100644 index 000000000..6f06d73b1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/pnum.js @@ -0,0 +1,5 @@ +define( function() { + "use strict"; + + return ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/push.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/push.js new file mode 100644 index 000000000..94656209a --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/push.js @@ -0,0 +1,7 @@ +define( [ + "./arr" +], function( arr ) { + "use strict"; + + return arr.push; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/rcssNum.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/rcssNum.js new file mode 100644 index 000000000..4214b14aa --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/rcssNum.js @@ -0,0 +1,9 @@ +define( [ + "../var/pnum" +], function( pnum ) { + +"use strict"; + +return new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/rnothtmlwhite.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/rnothtmlwhite.js new file mode 100644 index 000000000..30604db4f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/rnothtmlwhite.js @@ -0,0 +1,8 @@ +define( function() { + "use strict"; + + // Only count HTML whitespace + // Other whitespace should count in values + // https://html.spec.whatwg.org/multipage/infrastructure.html#space-character + return ( /[^\x20\t\r\n\f]+/g ); +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/slice.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/slice.js new file mode 100644 index 000000000..915f837be --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/slice.js @@ -0,0 +1,7 @@ +define( [ + "./arr" +], function( arr ) { + "use strict"; + + return arr.slice; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/support.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/support.js new file mode 100644 index 000000000..094d0aece --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/support.js @@ -0,0 +1,6 @@ +define( function() { + "use strict"; + + // All support tests are defined in their respective modules. + return {}; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/toString.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/toString.js new file mode 100644 index 000000000..ff4ecdc72 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/var/toString.js @@ -0,0 +1,7 @@ +define( [ + "./class2type" +], function( class2type ) { + "use strict"; + + return class2type.toString; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/wrap.js b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/wrap.js new file mode 100644 index 000000000..88b9bb56d --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/assets/lib/jquery/src/wrap.js @@ -0,0 +1,77 @@ +define( [ + "./core", + "./core/init", + "./manipulation", // clone + "./traversing" // parent, contents +], function( jQuery ) { + +"use strict"; + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( jQuery.isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( isFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + +return jQuery; +} ); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/about/about.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/about/about.html new file mode 100644 index 000000000..65860a8cc --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/about/about.html @@ -0,0 +1,32 @@ +<h1>TestAPI Documentation</h1> + +<p>TestAPI is a source of tools for test results collection of OPNFV clouds.</p> +<p>To learn more about TestAPI, visit the links below.</p> + +<ol> + <li> + <a href="https://wiki.opnfv.org/pages/viewpage.action?pageId=2926452#testapi" + target="_blank" + </a> + <strong>About TestAPI</strong> + </li> + <li> + <a href="https://wiki.opnfv.org/pages/viewpage.action?pageId=6825128#how-to-declare-test-project-in-testapi" + target="_blank" + </a> + <strong>How do I declare project in TestAPI</strong> + </li> + <li> + <a href="https://#how-to-declare-test-case-in-testapi" + target="_blank" + </a> + <strong>How do I declare test case in TestAPI</strong> + </li> + <li> + <a href="https://wiki.opnfv.org/pages/viewpage.action?pageId=6825133#how-to-push-result-to-testapi" + target="_blank" + </a> + <strong>How do I push my results to TestAPI</strong> + </li> +</ol> + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/auth-failure/authFailureController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/auth-failure/authFailureController.js new file mode 100644 index 000000000..29d1d70fa --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/auth-failure/authFailureController.js @@ -0,0 +1,33 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('AuthFailureController', AuthFailureController); + + AuthFailureController.$inject = ['$location', '$state', 'raiseAlert']; + /** + * TestAPI Auth Failure Controller + * This controller handles messages from TestAPI API if user auth fails. + */ + function AuthFailureController($location, $state, raiseAlert) { + var ctrl = this; + ctrl.message = $location.search().message; + raiseAlert('danger', 'Authentication Failure:', ctrl.message); + $state.go('home'); + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelines.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelines.html new file mode 100644 index 000000000..1dd39ff17 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelines.html @@ -0,0 +1,80 @@ +<h3>OpenStack Powered™ Guidelines</h3> + +<!-- Guideline Filters --> +<div class="row"> + <div class="col-md-3"> + <strong>Version:</strong> + <!-- Slicing the version file name here gets rid of the '.json' file extension --> + <select ng-model="ctrl.version" + ng-change="ctrl.update()" + class="form-control" + ng-options="versionFile.slice(0,-5) for versionFile in ctrl.versionList"> + </select> + </div> + <div class="col-md-4"> + <strong>Target Program:</strong> + <span class="program-about"><a target="_blank" href="http://www.openstack.org/brand/interop/">About</a></span> + <select ng-model="ctrl.target" class="form-control" ng-change="ctrl.updateTargetCapabilities()"> + <option value="platform">OpenStack Powered Platform</option> + <option value="compute">OpenStack Powered Compute</option> + <option value="object">OpenStack Powered Object Storage</option> + </select> + </div> +</div> + +<br /> +<div ng-if="ctrl.guidelines"> + <strong>Guideline Status:</strong> + {{ctrl.guidelines.status | capitalize}} +</div> + +<div ng-show="ctrl.guidelines"> + <strong>Corresponding OpenStack Releases:</strong> + <ul class="list-inline"> + <li ng-repeat="release in ctrl.guidelines.releases"> + {{release | capitalize}} + </li> + </ul> +</div> + +<strong>Capability Status:</strong> +<div class="checkbox"> + <label> + <input type="checkbox" ng-model="ctrl.status.required"> + <span class="required">Required</span> + </label> + <label> + <input type="checkbox" ng-model="ctrl.status.advisory"> + <span class="advisory">Advisory</span> + </label> + <label> + <input type="checkbox" ng-model="ctrl.status.deprecated"> + <span class="deprecated">Deprecated</span> + </label> + <label> + <input type="checkbox" ng-model="ctrl.status.removed"> + <span class="removed">Removed</span> + </label> + <a class="test-list-dl pull-right" + title="Get a test list for capabilities matching selected statuses." + ng-click="ctrl.openTestListModal()"> + + Test List <span class="glyphicon glyphicon-file"></span> + </a> +</div> +<!-- End Capability Filters --> + +<p><small>Tests marked with <span class="glyphicon glyphicon-flag text-warning"></span> are tests flagged by Interop Working Group.</small></p> + +<!-- Loading animation divs --> +<div cg-busy="{promise:ctrl.versionsRequest,message:'Loading versions'}"></div> +<div cg-busy="{promise:ctrl.capsRequest,message:'Loading capabilities'}"></div> + +<!-- Get the version-specific template --> +<div ng-include src="ctrl.detailsTemplate"></div> + +<div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{ctrl.error}} +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelinesController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelinesController.js new file mode 100644 index 000000000..a6f4258a2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/guidelinesController.js @@ -0,0 +1,322 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('GuidelinesController', GuidelinesController); + + GuidelinesController.$inject = ['$http', '$uibModal', 'testapiApiUrl']; + + /** + * TestAPI Guidelines Controller + * This controller is for the '/guidelines' page where a user can browse + * through tests belonging to Interop WG defined capabilities. + */ + function GuidelinesController($http, $uibModal, testapiApiUrl) { + var ctrl = this; + + ctrl.getVersionList = getVersionList; + ctrl.update = update; + ctrl.updateTargetCapabilities = updateTargetCapabilities; + ctrl.filterStatus = filterStatus; + ctrl.getObjectLength = getObjectLength; + ctrl.openTestListModal = openTestListModal; + + /** The target OpenStack marketing program to show capabilities for. */ + ctrl.target = 'platform'; + + /** The various possible capability statuses. */ + ctrl.status = { + required: true, + advisory: false, + deprecated: false, + removed: false + }; + + /** + * The template to load for displaying capability details. + */ + ctrl.detailsTemplate = 'components/guidelines/partials/' + + 'guidelineDetails.html'; + + /** + * Retrieve an array of available guideline files from the TestAPI + * API server, sort this array reverse-alphabetically, and store it in + * a scoped variable. The scope's selected version is initialized to + * the latest (i.e. first) version here as well. After a successful API + * call, the function to update the capabilities is called. + * Sample API return array: ["2015.03.json", "2015.04.json"] + */ + function getVersionList() { + var content_url = testapiApiUrl + '/guidelines'; + ctrl.versionsRequest = + $http.get(content_url).success(function (data) { + ctrl.versionList = data.sort().reverse(); + // Default to the first approved guideline which is expected + // to be at index 1. + ctrl.version = ctrl.versionList[1]; + ctrl.update(); + }).error(function (error) { + ctrl.showError = true; + ctrl.error = 'Error retrieving version list: ' + + angular.toJson(error); + }); + } + + /** + * This will contact the TestAPI API server to retrieve the JSON + * content of the guideline file corresponding to the selected + * version. + */ + function update() { + var content_url = testapiApiUrl + '/guidelines/' + ctrl.version; + ctrl.capsRequest = + $http.get(content_url).success(function (data) { + ctrl.guidelines = data; + ctrl.updateTargetCapabilities(); + }).error(function (error) { + ctrl.showError = true; + ctrl.guidelines = null; + ctrl.error = 'Error retrieving guideline content: ' + + angular.toJson(error); + }); + } + + /** + * This will update the scope's 'targetCapabilities' object with + * capabilities belonging to the selected OpenStack marketing program + * (programs typically correspond to 'components' in the Interop WG + * schema). Each capability will have its status mapped to it. + */ + function updateTargetCapabilities() { + ctrl.targetCapabilities = {}; + var components = ctrl.guidelines.components; + var targetCaps = ctrl.targetCapabilities; + + // The 'platform' target is comprised of multiple components, so + // we need to get the capabilities belonging to each of its + // components. + if (ctrl.target === 'platform') { + var platform_components = ctrl.guidelines.platform.required; + + // This will contain status priority values, where lower + // values mean higher priorities. + var statusMap = { + required: 1, + advisory: 2, + deprecated: 3, + removed: 4 + }; + + // For each component required for the platform program. + angular.forEach(platform_components, function (component) { + // Get each capability list belonging to each status. + angular.forEach(components[component], + function (caps, status) { + // For each capability. + angular.forEach(caps, function(cap) { + // If the capability has already been added. + if (cap in targetCaps) { + // If the status priority value is less + // than the saved priority value, update + // the value. + if (statusMap[status] < + statusMap[targetCaps[cap]]) { + targetCaps[cap] = status; + } + } + else { + targetCaps[cap] = status; + } + }); + }); + }); + } + else { + angular.forEach(components[ctrl.target], + function (caps, status) { + angular.forEach(caps, function(cap) { + targetCaps[cap] = status; + }); + }); + } + } + + /** + * This filter will check if a capability's status corresponds + * to a status that is checked/selected in the UI. This filter + * is meant to be used with the ng-repeat directive. + * @param {Object} capability + * @returns {Boolean} True if capability's status is selected + */ + function filterStatus(capability) { + var caps = ctrl.targetCapabilities; + return (ctrl.status.required && + caps[capability.id] === 'required') || + (ctrl.status.advisory && + caps[capability.id] === 'advisory') || + (ctrl.status.deprecated && + caps[capability.id] === 'deprecated') || + (ctrl.status.removed && + caps[capability.id] === 'removed'); + } + + /** + * This function will get the length of an Object/dict based on + * the number of keys it has. + * @param {Object} object + * @returns {Number} length of object + */ + function getObjectLength(object) { + return Object.keys(object).length; + } + + /** + * This will open the modal that will show a list of all tests + * belonging to capabilities with the selected status(es). + */ + function openTestListModal() { + $uibModal.open({ + templateUrl: '/components/guidelines/partials' + + '/testListModal.html', + backdrop: true, + windowClass: 'modal', + animation: true, + controller: 'TestListModalController as modal', + size: 'lg', + resolve: { + version: function () { + return ctrl.version.slice(0, -5); + }, + target: function () { + return ctrl.target; + }, + status: function () { + return ctrl.status; + } + } + }); + } + + ctrl.getVersionList(); + } + + angular + .module('testapiApp') + .controller('TestListModalController', TestListModalController); + + TestListModalController.$inject = [ + '$uibModalInstance', '$http', 'version', + 'target', 'status', 'testapiApiUrl' + ]; + + /** + * Test List Modal Controller + * This controller is for the modal that appears if a user wants to see the + * test list corresponding to Interop WG capabilities with the selected + * statuses. + */ + function TestListModalController($uibModalInstance, $http, version, + target, status, testapiApiUrl) { + + var ctrl = this; + + ctrl.version = version; + ctrl.target = target; + ctrl.status = status; + ctrl.close = close; + ctrl.updateTestListString = updateTestListString; + + ctrl.aliases = true; + ctrl.flagged = false; + + // Check if the API URL is absolute or relative. + if (testapiApiUrl.indexOf('http') > -1) { + ctrl.url = testapiApiUrl; + } + else { + ctrl.url = location.protocol + '//' + location.host + + testapiApiUrl; + } + + /** + * This function will close/dismiss the modal. + */ + function close() { + $uibModalInstance.dismiss('exit'); + } + + /** + * This function will return a list of statuses based on which ones + * are selected. + */ + function getStatusList() { + var statusList = []; + angular.forEach(ctrl.status, function(value, key) { + if (value) { + statusList.push(key); + } + }); + return statusList; + } + + /** + * This will get the list of tests from the API and update the + * controller's test list string variable. + */ + function updateTestListString() { + var statuses = getStatusList(); + if (!statuses.length) { + ctrl.error = 'No tests matching selected criteria.'; + return; + } + ctrl.testListUrl = [ + ctrl.url, '/guidelines/', ctrl.version, '/tests?', + 'target=', ctrl.target, '&', + 'type=', statuses.join(','), '&', + 'alias=', ctrl.aliases.toString(), '&', + 'flag=', ctrl.flagged.toString() + ].join(''); + ctrl.testListRequest = + $http.get(ctrl.testListUrl). + then(function successCallback(response) { + ctrl.error = null; + ctrl.testListString = response.data; + if (!ctrl.testListString) { + ctrl.testListCount = 0; + } + else { + ctrl.testListCount = + ctrl.testListString.split('\n').length; + } + }, function errorCallback(response) { + ctrl.testListString = null; + ctrl.testListCount = null; + if (angular.isObject(response.data) && + response.data.message) { + ctrl.error = 'Error retrieving test list: ' + + response.data.message; + } + else { + ctrl.error = 'Unknown error retrieving test list.'; + } + }); + } + + updateTestListString(); + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/guidelineDetails.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/guidelineDetails.html new file mode 100644 index 000000000..f020c9a09 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/guidelineDetails.html @@ -0,0 +1,50 @@ +<!-- +HTML for guidelines page for all OpenStack Powered (TM) guideline schemas +This expects the JSON data of the guidelines file to be stored in scope +variable 'guidelines'. +--> + +<ol ng-show="ctrl.guidelines" class="capabilities"> + <li class="capability-list-item" ng-repeat="capability in ctrl.guidelines.capabilities | arrayConverter | filter:ctrl.filterStatus | orderBy:'id'"> + <span class="capability-name">{{capability.id}}</span><br /> + <em>{{capability.description}}</em><br /> + Status: <span class="{{ctrl.targetCapabilities[capability.id]}}">{{ctrl.targetCapabilities[capability.id]}}</span><br /> + <span ng-if="capability.project">Project: {{capability.project | capitalize}}<br /></span> + <a ng-click="showAchievements = !showAchievements">Achievements ({{capability.achievements.length}})</a><br /> + <ol uib-collapse="!showAchievements" class="list-inline"> + <li ng-repeat="achievement in capability.achievements"> + {{achievement}} + </li> + </ol> + + <a ng-click="showTests = !showTests">Tests ({{ctrl.getObjectLength(capability.tests)}})</a> + <ul uib-collapse="!showTests"> + <li ng-if="ctrl.guidelines.schema === '1.2'" ng-repeat="test in capability.tests"> + <span ng-class="{'glyphicon glyphicon-flag text-warning': capability.flagged.indexOf(test) > -1}"></span> + {{test}} + </li> + <li ng-if="ctrl.guidelines.schema > '1.2'" ng-repeat="(testName, testDetails) in capability.tests"> + <span ng-class="{'glyphicon glyphicon-flag text-warning': testDetails.flagged}" title="{{testDetails.flagged.reason}}"></span> + {{testName}} + <div class="test-detail" ng-if="testDetails.aliases"> + <strong>Aliases:</strong> + <ul><li ng-repeat="alias in testDetails.aliases">{{alias}}</li></ul> + </div> + </li> + </ul> + </li> +</ol> + +<div ng-show="ctrl.guidelines" class="criteria"> + <hr> + <h4><a ng-click="showCriteria = !showCriteria">Criteria</a></h4> + <div uib-collapse="showCriteria"> + <ul> + <li ng-repeat="(key, criterion) in ctrl.guidelines.criteria"> + <span class="criterion-name">{{criterion.name}}</span><br /> + <em>{{criterion.Description}}</em><br /> + Weight: {{criterion.weight}} + </li> + </ul> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/testListModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/testListModal.html new file mode 100644 index 000000000..5b1d698d5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/guidelines/partials/testListModal.html @@ -0,0 +1,46 @@ +<div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" aria-hidden="true" ng-click="modal.close()">×</button> + <h4>Test List ({{modal.testListCount}})</h4> + <p>Use this test list with <a title="testapi-client" target="_blank"href="https://github.com/openstack/testapi-client">testapi-client</a> + to run only tests in the {{modal.version}} OpenStack Powered™ guideline from capabilities with the following statuses: + </p> + <ul class="list-inline"> + <li class="required" ng-if="modal.status.required"> Required</li> + <li class="advisory" ng-if="modal.status.advisory"> Advisory</li> + <li class="deprecated" ng-if="modal.status.deprecated"> Deprecated</li> + <li class="removed" ng-if="modal.status.removed"> Removed</li> + </ul> + <div class="checkbox checkbox-test-list"> + <label><input type="checkbox" ng-model="modal.aliases" ng-change="modal.updateTestListString()">Aliases</label> + <span class="glyphicon glyphicon-info-sign info-hover" aria-hidden="true" + title="Include test aliases as tests may have been renamed over time. It does not hurt to include these."></span> + + <label><input type="checkbox" ng-model="modal.flagged" ng-change="modal.updateTestListString()">Flagged</label> + <span class="glyphicon glyphicon-info-sign info-hover" aria-hidden="true" + title="Include flagged tests."> + </span> + </div> + <p ng-hide="modal.error"> Alternatively, get the test list directly from the API on your CLI:</p> + <code ng-hide="modal.error">wget "{{modal.testListUrl}}" -O {{modal.version}}-test-list.txt</code> + </div> + <div class="modal-body tests-modal-content"> + <div cg-busy="{promise:modal.testListRequest,message:'Loading'}"></div> + <div ng-show="modal.error" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{modal.error}} + </div> + <div class="form-group"> + <textarea class="form-control" rows="16" id="tests" wrap="off">{{modal.testListString}}</textarea> + </div> + </div> + <div class="modal-footer"> + <a target="_blank" href="{{modal.testListUrl}}" download="{{modal.version + '-test-list.txt'}}"> + <button class="btn btn-primary" ng-if="modal.testListCount > 0" type="button"> + Download + </button> + </a> + <button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/home/home.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/home/home.html new file mode 100644 index 000000000..04f64d52b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/home/home.html @@ -0,0 +1,23 @@ +<div class="jumbotron openstack-intro"> + <div class="pull-right right openstack-intro__logo"> + <img src="swagger/testapi-ui/assets/img/opnfv-logo.png" alt="OPNFV"> + </div> + <div class="pull-left left openstack-intro__content"> + <h1>Results Collection</h1> + <p>TestAPI is a source of tools for OPNFV test results collection</p> + </div> + <div class="clearfix"></div> +</div> + +<div class="row"> + <div class="col-lg-6"> + <h4>What is TestAPI?</h4> + <ul> + <li>Toolset for testing interoperability between OPNFV test projects.</li> + <li>Database backed website supporting collection and publication of + community test results for OPNFV.</li> + <li>User interface to display individual test run results.</li> + </ul> + </div> +</div> + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/logout/logout.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/logout/logout.html new file mode 100644 index 000000000..38a5c3698 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/logout/logout.html @@ -0,0 +1 @@ +<div cg-busy="{promise:ctrl.redirectWait,message:'Logging you out...'}"></div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/logout/logoutController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/logout/logoutController.js new file mode 100644 index 000000000..1b6d78c63 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/logout/logoutController.js @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('LogoutController', LogoutController); + + LogoutController.$inject = [ + '$location', '$window', '$timeout' + ]; + + /** + * TestAPI Logout Controller + * This controller handles logging out. In order to fully logout, the + * openstackid_session cookie must also be removed. The way to do that + * is to have the user's browser make a request to the openstackid logout + * page. We do this by placing the logout link as the src for an html + * image. After some time, the user is redirected home. + */ + function LogoutController($location, $window, $timeout) { + var ctrl = this; + + ctrl.openid_logout_url = $location.search().openid_logout; + var img = new Image(0, 0); + img.src = ctrl.openid_logout_url; + ctrl.redirectWait = $timeout(function() { + $window.location.href = '/'; + }, 500); + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/importPubKeyModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/importPubKeyModal.html new file mode 100644 index 000000000..0f55c27fd --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/importPubKeyModal.html @@ -0,0 +1,27 @@ +<div class="modal-header"> + <h4>Import Public Key</h4> + <p>Instructions for adding a public key and signature can be found + <a href="https://github.com/openstack/refstack/blob/master/doc/source/uploading_private_results.rst#generate-ssh-keys-locally" + target="_blank" + title="How to generate and upload SSH key and signature with testapi-client">here. + </a> + </p> +</div> +<div class="modal-body container-fluid"> + <div class="row"> + <div class="col-md-2">Public Key</div> + <div class="col-md-9 pull-right"> + <textarea type="text" rows="10" cols="42" ng-model="modal.raw_key" required></textarea> + </div> + </div> + <div class="row"> + <div class="col-md-2">Signature</div> + <div class="col-md-9 pull-right"> + <textarea type="text" rows="10" cols="42" ng-model="modal.self_signature" required></textarea> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-warning btn-sm" ng-click="modal.cancel()">Cancel</button> + <button type="button" class="btn btn-default btn-sm" ng-click="modal.importPubKey()">Import Public Key</button> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profile.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profile.html new file mode 100644 index 000000000..dc97c41e2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profile.html @@ -0,0 +1,37 @@ +<h3>User profile</h3> +<div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div> +<div> + <table class="table table-striped table-hover"> + <tbody> + <tr> <td>User name</td> <td>{{auth.currentUser.fullname}}</td> </tr> + <tr> <td>User OpenId</td> <td>{{auth.currentUser.openid}}</td> </tr> + <tr> <td>Email</td> <td>{{auth.currentUser.email}}</td> </tr> + </tbody> + </table> +</div> +<div ng-show="ctrl.pubkeys"> + <div class="container-fluid"> + <div class="row"> + <div class="col-md-4"> + <h4>User Public Keys</h4> + </div> + <div class="col-md-2 pull-right"> + <button type="button" class="btn btn-default btn-sm" ng-click="ctrl.openImportPubKeyModal()"> + <span class="glyphicon glyphicon-plus"></span> Import Public Key + </button> + </div> + </div> + </div> + + <div> + <table class="table table-striped table-hover"> + <tbody> + <tr ng-repeat="pubKey in ctrl.pubkeys" ng-click="ctrl.openShowPubKeyModal(pubKey)"> + <td>{{pubKey.format}}</td> + <td>{{pubKey.shortKey}}</td> + <td>{{pubKey.comment}}</td> + </tr> + </tbody> + </table> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profileController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profileController.js new file mode 100644 index 000000000..0660e19f6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/profileController.js @@ -0,0 +1,219 @@ +/* + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .factory('PubKeys', PubKeys); + + PubKeys.$inject = ['$resource', 'testapiApiUrl']; + + /** + * This is a provider for the user's uploaded public keys. + */ + function PubKeys($resource, testapiApiUrl) { + return $resource(testapiApiUrl + '/profile/pubkeys/:id', null, null); + } + + angular + .module('testapiApp') + .controller('ProfileController', ProfileController); + + ProfileController.$inject = [ + '$scope', '$http', 'testapiApiUrl', 'PubKeys', + '$uibModal', 'raiseAlert', '$state' + ]; + + /** + * TestAPI Profile Controller + * This controller handles user's profile page, where a user can view + * account-specific information. + */ + function ProfileController($scope, $http, testapiApiUrl, + PubKeys, $uibModal, raiseAlert, $state) { + + var ctrl = this; + + ctrl.updatePubKeys = updatePubKeys; + ctrl.openImportPubKeyModal = openImportPubKeyModal; + ctrl.openShowPubKeyModal = openShowPubKeyModal; + + // Must be authenticated to view this page. + if (!$scope.auth.isAuthenticated) { + $state.go('home'); + } + + /** + * This function will fetch all the user's public keys from the + * server and store them in an array. + */ + function updatePubKeys() { + var keys = PubKeys.query(function() { + ctrl.pubkeys = []; + angular.forEach(keys, function (key) { + ctrl.pubkeys.push({ + 'resource': key, + 'format': key.format, + 'shortKey': [ + key.pubkey.slice(0, 10), + '.', + key.pubkey.slice(-10) + ].join('.'), + 'pubkey': key.pubkey, + 'comment': key.comment + }); + }); + }); + } + + /** + * This function will open the modal that will give the user a form + * for importing a public key. + */ + function openImportPubKeyModal() { + $uibModal.open({ + templateUrl: '/components/profile/importPubKeyModal.html', + backdrop: true, + windowClass: 'modal', + controller: 'ImportPubKeyModalController as modal' + }).result.finally(function() { + ctrl.updatePubKeys(); + }); + } + + /** + * This function will open the modal that will give the full + * information regarding a specific public key. + * @param {Object} pubKey resource + */ + function openShowPubKeyModal(pubKey) { + $uibModal.open({ + templateUrl: '/components/profile/showPubKeyModal.html', + backdrop: true, + windowClass: 'modal', + controller: 'ShowPubKeyModalController as modal', + resolve: { + pubKey: function() { + return pubKey; + } + } + }).result.finally(function() { + ctrl.updatePubKeys(); + }); + } + + ctrl.authRequest = $scope.auth.doSignCheck().then(ctrl.updatePubKeys); + } + + angular + .module('testapiApp') + .controller('ImportPubKeyModalController', ImportPubKeyModalController); + + ImportPubKeyModalController.$inject = [ + '$uibModalInstance', 'PubKeys', 'raiseAlert' + ]; + + /** + * Import Pub Key Modal Controller + * This controller is for the modal that appears if a user wants to import + * a public key. + */ + function ImportPubKeyModalController($uibModalInstance, + PubKeys, raiseAlert) { + + var ctrl = this; + + ctrl.importPubKey = importPubKey; + ctrl.cancel = cancel; + + /** + * This function will save a new public key resource to the API server. + */ + function importPubKey() { + var newPubKey = new PubKeys( + {raw_key: ctrl.raw_key, self_signature: ctrl.self_signature} + ); + newPubKey.$save( + function(newPubKey_) { + raiseAlert('success', '', 'Public key saved successfully'); + $uibModalInstance.close(newPubKey_); + }, + function(httpResp) { + raiseAlert('danger', + httpResp.statusText, httpResp.data.title); + ctrl.cancel(); + } + ); + } + + /** + * This function will dismiss the modal. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } + + angular + .module('testapiApp') + .controller('ShowPubKeyModalController', ShowPubKeyModalController); + + ShowPubKeyModalController.$inject = [ + '$uibModalInstance', 'raiseAlert', 'pubKey' + ]; + + /** + * Show Pub Key Modal Controller + * This controller is for the modal that appears if a user wants to see the + * full details of one of their public keys. + */ + function ShowPubKeyModalController($uibModalInstance, raiseAlert, pubKey) { + var ctrl = this; + + ctrl.deletePubKey = deletePubKey; + ctrl.cancel = cancel; + + ctrl.pubKey = pubKey.resource; + ctrl.rawKey = [pubKey.format, pubKey.pubkey, pubKey.comment].join('\n'); + + /** + * This function will delete a public key resource. + */ + function deletePubKey() { + ctrl.pubKey.$remove( + {id: ctrl.pubKey.id}, + function() { + raiseAlert('success', + '', 'Public key deleted successfully'); + $uibModalInstance.close(ctrl.pubKey.id); + }, + function(httpResp) { + raiseAlert('danger', + httpResp.statusText, httpResp.data.title); + ctrl.cancel(); + } + ); + } + + /** + * This method will dismiss the modal. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/showPubKeyModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/showPubKeyModal.html new file mode 100644 index 000000000..5f63a5ef6 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/profile/showPubKeyModal.html @@ -0,0 +1,11 @@ +<div class="modal-header"> + <h4>Public Key</h4> +</div> +<div class="modal-body container-fluid"> + <textarea type="text" rows="10" cols="67" readonly="readonly">{{modal.rawKey}}</textarea> + <div class="modal-footer"> + <button class="btn btn-warning" ng-click="modal.cancel()">Cancel</button> + <button type="button" class="btn btn-danger btn-sm" ng-click="modal.deletePubKey() " + confirm="Are you sure you want to delete this public key? You will lose management access to any test results signed with this key.">Delete</button> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/editTestModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/editTestModal.html new file mode 100644 index 000000000..583c9b92b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/editTestModal.html @@ -0,0 +1,65 @@ +<div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" aria-hidden="true" ng-click="modal.close()">×</button> + <h4>Edit Test Run Metadata</h4> + <p>Make changes to your test metadata.</p> + </div> + <div class="modal-body"> + <div class="form-group"> + <strong>Publicly Shared:</strong> + <select ng-model="modal.metaCopy.shared" + class="form-control"> + <option value="true">Yes</option> + <option value="">No</option> + </select> + <br /> + <strong>Associated Guideline:</strong> + <select ng-model="modal.metaCopy.guideline" + ng-options="o as o.slice(0, -5) for o in modal.versionList" + class="form-control"> + <option value="">None</option> + </select> + <br /> + <strong>Associated Target Program:</strong> + <select ng-model="modal.metaCopy.target" + class="form-control"> + <option value="">None</option> + <option value="platform">OpenStack Powered Platform</option> + <option value="compute">OpenStack Powered Compute</option> + <option value="object">OpenStack Powered Object Storage</option> + </select> + <hr> + <strong>Associated Product:</strong> + <select ng-options="product as product.name for product in modal.products | arrayConverter | orderBy: 'name' track by product.id" + ng-model="modal.selectedProduct" + ng-change="modal.getProductVersions()" + class="form-control"> + <option value="">-- No Product --</option> + </select> + + <span ng-if="modal.productVersions.length"> + <strong>Product Version:</strong> + <select ng-options="version as version.version for version in modal.productVersions | orderBy: 'version' track by version.id" + ng-model="modal.selectedVersion" + class="form-control"> + </select> + + </span> + + </div> + <div ng-show="modal.showError" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{modal.error}} + </div> + <div ng-show="modal.showSuccess" class="alert alert-success" role="success"> + <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> + <span class="sr-only">Success:</span> + Changes saved successfully. + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" type="button" ng-click="modal.saveChanges()">Save Changes</button> + <button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/fullTestListModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/fullTestListModal.html new file mode 100644 index 000000000..6db198b02 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/fullTestListModal.html @@ -0,0 +1,13 @@ +<div class="modal-content"> + <div class="modal-header"> + <h4>All Passed Tests ({{modal.tests.length}})</h4> + </div> + <div class="modal-body tests-modal-content"> + <div class="form-group"> + <textarea class="form-control" rows="20" id="tests" wrap="off">{{modal.getTestListString()}}</textarea> + </div> + </div> + <div class="modal-footer"> + <button class="btn btn-primary" type="button" ng-click="modal.close()">Close</button> + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/reportDetails.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/reportDetails.html new file mode 100644 index 000000000..517e569c7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/partials/reportDetails.html @@ -0,0 +1,87 @@ +<!-- +HTML for each accordion group that separates the status types on the results +report page. +--> + +<uib-accordion-group is-open="isOpen" is-disabled="ctrl.caps[status].caps.length == 0"> + <uib-accordion-heading> + {{status | capitalize}} + <small> + (<strong>Total:</strong> {{ctrl.caps[status].caps.length}} capabilities, {{ctrl.caps[status].count}} tests) + <span ng-if="ctrl.testStatus !== 'total'"> + (<strong>{{ctrl.testStatus | capitalize}}:</strong> {{ctrl.getStatusTestCount(status)}} tests) + </span> + </small> + <i class="pull-right glyphicon" + ng-class="{'glyphicon-chevron-down': isOpen, 'glyphicon-chevron-right': !isOpen}"> + </i> + </uib-accordion-heading> + <ol class="capabilities"> + <li ng-repeat="capability in ctrl.caps[status].caps | orderBy:'id'" + ng-if="ctrl.isCapabilityShown(capability)"> + + <a ng-click="showTests = !showTests" + title="{{ctrl.guidelineData.capabilities[capability.id].description}}"> + {{capability.id}} + </a> + <span ng-class="{'text-success': ctrl.testStatus === 'passed', + 'text-danger': ctrl.testStatus === 'not passed', + 'text-warning': ctrl.testStatus === 'flagged'}" + ng-if="ctrl.testStatus !== 'total'"> + [{{ctrl.getCapabilityTestCount(capability)}}] + </span> + <span ng-class="{'text-success': (capability.passedTests.length > 0 && + capability.notPassedTests.length == 0), + 'text-danger': (capability.passedTests.length == 0 && + capability.notPassedTests.length > 0), + 'text-warning': (capability.passedTests.length > 0 && + capability.notPassedTests.length > 0)}" + ng-if="ctrl.testStatus === 'total'"> + [{{capability.passedTests.length}}/{{capability.passedTests.length + + capability.notPassedTests.length}}] + </span> + + <ul class="list-unstyled" uib-collapse="!showTests"> + <!-- Start passed test list --> + <li ng-repeat="test in capability.passedTests | orderBy:'toString()'" + ng-if="ctrl.isTestShown(test, capability)"> + + <span class="glyphicon glyphicon-ok text-success" + aria-hidden="true"> + </span> + <span ng-class="{'glyphicon glyphicon-flag text-warning': + ctrl.isTestFlagged(test, ctrl.guidelineData.capabilities[capability.id])}" + title="{{ctrl.getFlaggedReason(test, ctrl.guidelineData.capabilities[capability.id])}}"> + </span> + {{test}} + <span ng-if="ctrl.guidelineData.capabilities[capability.id].tests[test].aliases"> — + <a ng-click="showAliases = !showAliases">[Aliases]</a> + <div class="test-detail-report" ng-if="ctrl.guidelineData.capabilities[capability.id].tests[test].aliases && showAliases"> + <ul><li ng-repeat="alias in ctrl.guidelineData.capabilities[capability.id].tests[test].aliases">{{alias}}</li></ul> + </div> + </span> + </li> + <!-- End passed test list --> + + <!-- Start not passed test list --> + <li ng-repeat="test in capability.notPassedTests | orderBy:'toString()'" + ng-if="ctrl.isTestShown(test, capability)"> + + <span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span> + <span ng-class="{'glyphicon glyphicon-flag text-warning': + ctrl.isTestFlagged(test, ctrl.guidelineData.capabilities[capability.id])}" + title="{{ctrl.getFlaggedReason(test, ctrl.guidelineData.capabilities[capability.id])}}"> + </span> + {{test}} + <span ng-if="ctrl.guidelineData.capabilities[capability.id].tests[test].aliases"> — + <a ng-click="showAliases = !showAliases">[Aliases]</a> + <div class="test-detail-report" ng-if="ctrl.guidelineData.capabilities[capability.id].tests[test].aliases && showAliases"> + <ul><li ng-repeat="alias in ctrl.guidelineData.capabilities[capability.id].tests[test].aliases">{{alias}}</li></ul> + </div> + </span> + </li> + <!-- End not passed test list --> + </ul> + </li> + </ol> +</uib-accordion-group> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/resultsReport.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/resultsReport.html new file mode 100644 index 000000000..5527121ba --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/resultsReport.html @@ -0,0 +1,185 @@ +<h3>Test Run Results</h3> + +<div ng-show="ctrl.resultsData" class="container-fluid"> + <div class="row"> + <div class="pull-left"> + <div class="test-report"> + <strong>Test ID:</strong> {{ctrl.testId}}<br /> + <div ng-if="ctrl.isResultAdmin()"><strong>Cloud ID:</strong> {{ctrl.resultsData.cpid}}<br /></div> + <strong>Upload Date:</strong> {{ctrl.resultsData.created_at}} UTC<br /> + <strong>Duration:</strong> {{ctrl.resultsData.duration_seconds}} seconds<br /> + <strong>Total Number of Passed Tests:</strong> + <a title="See all passed tests" ng-click="ctrl.openFullTestListModal()"> + {{ctrl.resultsData.results.length}} + </a> + </div> + <hr> + <div ng-show="ctrl.isResultAdmin()"> + <strong>Publicly Shared:</strong> + <span ng-if="ctrl.resultsData.meta.shared">Yes</span> + <span ng-if="!ctrl.resultsData.meta.shared">No</span> + <br /> + </div> + <div ng-show="ctrl.resultsData.product_version"> + <strong>Product:</strong> + {{ctrl.resultsData.product_version.product_info.name}} + <span ng-if="ctrl.resultsData.product_version.version"> + ({{ctrl.resultsData.product_version.version}}) + </span><br /> + </div> + <div ng-show="ctrl.resultsData.meta.guideline"> + <strong>Associated Guideline:</strong> + {{ctrl.resultsData.meta.guideline.slice(0, -5)}} + </div> + <div ng-show="ctrl.resultsData.meta.target"> + <strong>Associated Target Program:</strong> + {{ctrl.targetMappings[ctrl.resultsData.meta.target]}} + </div> + <div ng-show="ctrl.resultsData.verification_status"> + <strong>Verified:</strong> + <span class="yes">YES</span> + </div> + <hr> + </div> + + <div class="pull-right"> + <div ng-show="ctrl.isResultAdmin() && !ctrl.resultsData.verification_status"> + <button class="btn btn-info" ng-click="ctrl.openEditTestModal()">Edit</button> + <button type="button" class="btn btn-danger" ng-click="ctrl.deleteTestRun()" confirm="Are you sure you want to delete these test run results?">Delete</button> + </div> + <div ng-show="ctrl.resultsData.user_role === 'foundation'"> + <hr> + <div class="checkbox checkbox-verified"> + <label><input type="checkbox" + ng-model="ctrl.isVerified" + ng-change="ctrl.updateVerificationStatus()" + ng-true-value="1" + ng-false-value="0"> + <strong>Verified</strong> + </label> + </div> + </div> + </div> + </div> +</div> + +<div ng-show="ctrl.resultsData"> + <p>See how these results stack up against Interop Working Group capabilities and OpenStack + <a target="_blank" href="http://www.openstack.org/brand/interop/">target marketing programs.</a> + </p> + + <!-- User Options --> + <div class="row"> + <div class="col-md-3"> + <strong>Guideline Version:</strong> + <!-- Slicing the version file name here gets rid of the '.json' file extension --> + <select ng-model="ctrl.version" + ng-change="ctrl.updateGuidelines()" + class="form-control" + ng-options="versionFile.slice(0,-5) for versionFile in ctrl.versionList"> + </select> + </div> + <div class="col-md-4"> + <strong>Target Program:</strong> + <select ng-model="ctrl.target" class="form-control" ng-change="ctrl.buildCapabilitiesObject()"> + <option value="platform">OpenStack Powered Platform</option> + <option value="compute">OpenStack Powered Compute</option> + <option value="object">OpenStack Powered Object Storage</option> + </select> + </div> + </div> + <!-- End User Options --> + + <br /> + <div ng-if="ctrl.guidelineData"> + <strong>Guideline Status:</strong> + {{ctrl.guidelineData.status | capitalize}} + </div> + + <strong>Corresponding OpenStack Releases:</strong> + <ul class="list-inline"> + <li ng-repeat="release in ctrl.guidelineData.releases"> + {{release | capitalize}} + </li> + </ul> + <hr > + + <div ng-show="ctrl.guidelineData"> + <strong>Status:</strong> + <p>This cloud passes <strong>{{ctrl.requiredPassPercent | number:1}}% </strong> + ({{ctrl.caps.required.passedCount}}/{{ctrl.caps.required.count}}) + of the tests in the <strong>{{ctrl.version.slice(0, -5)}}</strong> <em>required</em> capabilities for the + <strong>{{ctrl.targetMappings[target]}}</strong> program. <br /> + Excluding flagged tests, this cloud passes + <strong>{{ctrl.nonFlagRequiredPassPercent | number:1}}%</strong> + ({{ctrl.nonFlagPassCount}}/{{ctrl.totalNonFlagCount}}) + of the <em>required</em> tests. + </p> + + <p>Compliance with <strong>{{ctrl.version.slice(0, -5)}}</strong>: + <strong> + <span ng-if="ctrl.nonFlagPassCount === ctrl.totalNonFlagCount" class="yes">YES</span> + <span ng-if="ctrl.nonFlagPassCount !== ctrl.totalNonFlagCount" class="no">NO</span> + </strong> + </p> + + <hr> + <h4>Capability Overview</h4> + + Test Filters:<br /> + <div class="btn-group button-margin" data-toggle="buttons"> + <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'total'}"> + <input type="radio" ng-model="ctrl.testStatus" value="total"> + <span class="text-primary">All</span> + </label> + <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'passed'}"> + <input type="radio" ng-model="ctrl.testStatus" value="passed"> + <span class="text-success">Passed</span> + </label> + <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'not passed'}"> + <input type="radio" ng-model="ctrl.testStatus" value="not passed"> + <span class="text-danger">Not Passed</span> + </label> + <label class="btn btn-default" ng-class="{'active': ctrl.testStatus === 'flagged'}"> + <input type="radio" ng-model="ctrl.testStatus" value="flagged"> + <span class="text-warning">Flagged</span> + </label> + </div> + + <uib-accordion close-others=false> + <!-- The ng-repeat is used to pass in a local variable to the template. --> + <ng-include + ng-repeat="status in ['required']" + src="ctrl.detailsTemplate" + onload="isOpen = true"> + </ng-include> + <br /> + <ng-include + ng-repeat="status in ['advisory']" + src="ctrl.detailsTemplate"> + </ng-include> + <br /> + <ng-include + ng-repeat="status in ['deprecated']" + src="ctrl.detailsTemplate"> + </ng-include> + <br /> + <ng-include + ng-repeat="status in ['removed']" + src="ctrl.detailsTemplate"> + </ng-include> + </uib-accordion> + </div> +</div> + +<div class="loading"> + <div cg-busy="{promise:versionsRequest,message:'Loading versions'}"></div> + <div cg-busy="{promise:capsRequest,message:'Loading capabilities'}"></div> + <div cg-busy="{promise:resultsRequest,message:'Loading results'}"></div> +</div> + +<div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{ctrl.error}} +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/resultsReportController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/resultsReportController.js new file mode 100644 index 000000000..591ad402b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results-report/resultsReportController.js @@ -0,0 +1,869 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ResultsReportController', ResultsReportController); + + ResultsReportController.$inject = [ + '$http', '$stateParams', '$window', + '$uibModal', 'testapiApiUrl', 'raiseAlert' + ]; + + /** + * TestAPI Results Report Controller + * This controller is for the '/results/<test run ID>' page where a user can + * view details for a specific test run. + */ + function ResultsReportController($http, $stateParams, $window, + $uibModal, testapiApiUrl, raiseAlert) { + + var ctrl = this; + + ctrl.getVersionList = getVersionList; + ctrl.getResults = getResults; + ctrl.isResultAdmin = isResultAdmin; + ctrl.isShared = isShared; + ctrl.shareTestRun = shareTestRun; + ctrl.deleteTestRun = deleteTestRun; + ctrl.updateVerificationStatus = updateVerificationStatus; + ctrl.updateGuidelines = updateGuidelines; + ctrl.getTargetCapabilities = getTargetCapabilities; + ctrl.buildCapabilityV1_2 = buildCapabilityV1_2; + ctrl.buildCapabilityV1_3 = buildCapabilityV1_3; + ctrl.buildCapabilitiesObject = buildCapabilitiesObject; + ctrl.isTestFlagged = isTestFlagged; + ctrl.getFlaggedReason = getFlaggedReason; + ctrl.isCapabilityShown = isCapabilityShown; + ctrl.isTestShown = isTestShown; + ctrl.getCapabilityTestCount = getCapabilityTestCount; + ctrl.getStatusTestCount = getStatusTestCount; + ctrl.openFullTestListModal = openFullTestListModal; + ctrl.openEditTestModal = openEditTestModal; + + /** The testID extracted from the URL route. */ + ctrl.testId = $stateParams.testID; + + /** The target OpenStack marketing program to compare against. */ + ctrl.target = 'platform'; + + /** Mappings of Interop WG components to marketing program names. */ + ctrl.targetMappings = { + 'platform': 'Openstack Powered Platform', + 'compute': 'OpenStack Powered Compute', + 'object': 'OpenStack Powered Object Storage' + }; + + /** The schema version of the currently selected guideline data. */ + ctrl.schemaVersion = null; + + /** The selected test status used for test filtering. */ + ctrl.testStatus = 'total'; + + /** The HTML template that all accordian groups will use. */ + ctrl.detailsTemplate = 'components/results-report/partials/' + + 'reportDetails.html'; + + /** + * Retrieve an array of available guideline files from the TestAPI + * API server, sort this array reverse-alphabetically, and store it in + * a scoped variable. The scope's selected version is initialized to + * the latest (i.e. first) version here as well. After a successful API + * call, the function to update the capabilities is called. + * Sample API return array: ["2015.03.json", "2015.04.json"] + */ + function getVersionList() { + var content_url = testapiApiUrl + '/guidelines'; + ctrl.versionsRequest = + $http.get(content_url).success(function (data) { + ctrl.versionList = data.sort().reverse(); + if (!ctrl.version) { + // Default to the first approved guideline which is + // expected to be at index 1. + ctrl.version = ctrl.versionList[1]; + } + ctrl.updateGuidelines(); + }).error(function (error) { + ctrl.showError = true; + ctrl.error = 'Error retrieving version list: ' + + angular.toJson(error); + }); + } + + /** + * Retrieve results from the TestAPI API server based on the test + * run id in the URL. This function is the first function that will + * be called from the controller. Upon successful retrieval of results, + * the function that gets the version list will be called. + */ + function getResults() { + var content_url = testapiApiUrl + '/results/' + ctrl.testId; + ctrl.resultsRequest = + $http.get(content_url).success(function (data) { + ctrl.resultsData = data; + ctrl.version = ctrl.resultsData.meta.guideline; + ctrl.isVerified = ctrl.resultsData.verification_status; + if (ctrl.resultsData.meta.target) { + ctrl.target = ctrl.resultsData.meta.target; + } + getVersionList(); + }).error(function (error) { + ctrl.showError = true; + ctrl.resultsData = null; + ctrl.error = 'Error retrieving results from server: ' + + angular.toJson(error); + }); + } + + /** + * This tells you whether the current user has administrative + * privileges for the test result. + * @returns {Boolean} true if the user has admin privileges. + */ + function isResultAdmin() { + return Boolean(ctrl.resultsData && + (ctrl.resultsData.user_role === 'owner' || + ctrl.resultsData.user_role === 'foundation')); + } + /** + * This tells you whether the current results are shared with the + * community or not. + * @returns {Boolean} true if the results are shared + */ + function isShared() { + return Boolean(ctrl.resultsData && + 'shared' in ctrl.resultsData.meta); + } + + /** + * This will send an API request in order to share or unshare the + * current results based on the passed in shareState. + * @param {Boolean} shareState - Whether to share or unshare results. + */ + function shareTestRun(shareState) { + var content_url = [ + testapiApiUrl, '/results/', ctrl.testId, '/meta/shared' + ].join(''); + if (shareState) { + ctrl.shareRequest = + $http.post(content_url, 'true').success(function () { + ctrl.resultsData.meta.shared = 'true'; + raiseAlert('success', '', 'Test run shared!'); + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } else { + ctrl.shareRequest = + $http.delete(content_url).success(function () { + delete ctrl.resultsData.meta.shared; + raiseAlert('success', '', 'Test run unshared!'); + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + } + + /** + * This will send a request to the API to delete the current + * test results set. + */ + function deleteTestRun() { + var content_url = [ + testapiApiUrl, '/results/', ctrl.testId + ].join(''); + ctrl.deleteRequest = + $http.delete(content_url).success(function () { + $window.history.back(); + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * This will send a request to the API to delete the current + * test results set. + */ + function updateVerificationStatus() { + var content_url = [ + testapiApiUrl, '/results/', ctrl.testId + ].join(''); + var data = {'verification_status': ctrl.isVerified}; + ctrl.updateRequest = + $http.put(content_url, data).success( + function () { + ctrl.resultsData.verification_status = ctrl.isVerified; + raiseAlert('success', '', + 'Verification status changed!'); + }).error(function (error) { + ctrl.isVerified = ctrl.resultsData.verification_status; + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * This will contact the TestAPI API server to retrieve the JSON + * content of the guideline file corresponding to the selected + * version. A function to construct an object from the capability + * data will be called upon successful retrieval. + */ + function updateGuidelines() { + ctrl.guidelineData = null; + ctrl.showError = false; + var content_url = testapiApiUrl + '/guidelines/' + + ctrl.version; + ctrl.capsRequest = + $http.get(content_url).success(function (data) { + ctrl.guidelineData = data; + ctrl.schemaVersion = data.schema; + ctrl.buildCapabilitiesObject(); + }).error(function (error) { + ctrl.showError = true; + ctrl.guidelineData = null; + ctrl.error = 'Error retrieving guideline date: ' + + angular.toJson(error); + }); + } + + /** + * This will get all the capabilities relevant to the target and + * their corresponding statuses. + * @returns {Object} Object containing each capability and their status + */ + function getTargetCapabilities() { + var components = ctrl.guidelineData.components; + var targetCaps = {}; + + // The 'platform' target is comprised of multiple components, so + // we need to get the capabilities belonging to each of its + // components. + if (ctrl.target === 'platform') { + var platform_components = + ctrl.guidelineData.platform.required; + + // This will contain status priority values, where lower + // values mean higher priorities. + var statusMap = { + required: 1, + advisory: 2, + deprecated: 3, + removed: 4 + }; + + // For each component required for the platform program. + angular.forEach(platform_components, function (component) { + // Get each capability list belonging to each status. + angular.forEach(components[component], + function (caps, status) { + // For each capability. + angular.forEach(caps, function(cap) { + // If the capability has already been added. + if (cap in targetCaps) { + // If the status priority value is less + // than the saved priority value, update + // the value. + if (statusMap[status] < + statusMap[targetCaps[cap]]) { + targetCaps[cap] = status; + } + } + else { + targetCaps[cap] = status; + } + }); + }); + }); + } + else { + angular.forEach(components[ctrl.target], + function (caps, status) { + angular.forEach(caps, function(cap) { + targetCaps[cap] = status; + }); + }); + } + return targetCaps; + } + + /** + * This will build the a capability object for schema version 1.2. + * This object will contain the information needed to form a report in + * the HTML template. + * @param {String} capId capability ID + */ + function buildCapabilityV1_2(capId) { + var cap = { + 'id': capId, + 'passedTests': [], + 'notPassedTests': [], + 'passedFlagged': [], + 'notPassedFlagged': [] + }; + var capDetails = ctrl.guidelineData.capabilities[capId]; + // Loop through each test belonging to the capability. + angular.forEach(capDetails.tests, + function (testId) { + // If the test ID is in the results' test list, add + // it to the passedTests array. + if (ctrl.resultsData.results.indexOf(testId) > -1) { + cap.passedTests.push(testId); + if (capDetails.flagged.indexOf(testId) > -1) { + cap.passedFlagged.push(testId); + } + } + else { + cap.notPassedTests.push(testId); + if (capDetails.flagged.indexOf(testId) > -1) { + cap.notPassedFlagged.push(testId); + } + } + }); + return cap; + } + + /** + * This will build the a capability object for schema version 1.3 and + * above. This object will contain the information needed to form a + * report in the HTML template. + * @param {String} capId capability ID + */ + function buildCapabilityV1_3(capId) { + var cap = { + 'id': capId, + 'passedTests': [], + 'notPassedTests': [], + 'passedFlagged': [], + 'notPassedFlagged': [] + }; + + // For cases where a capability listed in components is not + // in the capabilities object. + if (!(capId in ctrl.guidelineData.capabilities)) { + return cap; + } + + // Loop through each test belonging to the capability. + angular.forEach(ctrl.guidelineData.capabilities[capId].tests, + function (details, testId) { + var passed = false; + + // If the test ID is in the results' test list. + if (ctrl.resultsData.results.indexOf(testId) > -1) { + passed = true; + } + else if ('aliases' in details) { + var len = details.aliases.length; + for (var i = 0; i < len; i++) { + var alias = details.aliases[i]; + if (ctrl.resultsData.results.indexOf(alias) > -1) { + passed = true; + break; + } + } + } + + // Add to correct array based on whether the test was + // passed or not. + if (passed) { + cap.passedTests.push(testId); + if ('flagged' in details) { + cap.passedFlagged.push(testId); + } + } + else { + cap.notPassedTests.push(testId); + if ('flagged' in details) { + cap.notPassedFlagged.push(testId); + } + } + }); + return cap; + } + + /** + * This will check the schema version of the current capabilities file, + * and will call the correct method to build an object based on the + * capability data retrieved from the TestAPI API server. + */ + function buildCapabilitiesObject() { + // This is the object template where 'count' is the number of + // total tests that fall under the given status, and 'passedCount' + // is the number of tests passed. The 'caps' array will contain + // objects with details regarding each capability. + ctrl.caps = { + 'required': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0}, + 'advisory': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0}, + 'deprecated': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0}, + 'removed': {'caps': [], 'count': 0, 'passedCount': 0, + 'flagFailCount': 0, 'flagPassCount': 0} + }; + + switch (ctrl.schemaVersion) { + case '1.2': + var capMethod = 'buildCapabilityV1_2'; + break; + case '1.3': + case '1.4': + case '1.5': + case '1.6': + capMethod = 'buildCapabilityV1_3'; + break; + default: + ctrl.showError = true; + ctrl.guidelineData = null; + ctrl.error = 'The schema version for the guideline ' + + 'file selected (' + ctrl.schemaVersion + + ') is currently not supported.'; + return; + } + + // Get test details for each relevant capability and store + // them in the scope's 'caps' object. + var targetCaps = ctrl.getTargetCapabilities(); + angular.forEach(targetCaps, function(status, capId) { + var cap = ctrl[capMethod](capId); + ctrl.caps[status].count += + cap.passedTests.length + cap.notPassedTests.length; + ctrl.caps[status].passedCount += cap.passedTests.length; + ctrl.caps[status].flagPassCount += cap.passedFlagged.length; + ctrl.caps[status].flagFailCount += + cap.notPassedFlagged.length; + ctrl.caps[status].caps.push(cap); + }); + + ctrl.requiredPassPercent = (ctrl.caps.required.passedCount * + 100 / ctrl.caps.required.count); + + ctrl.totalRequiredFailCount = ctrl.caps.required.count - + ctrl.caps.required.passedCount; + ctrl.totalRequiredFlagCount = + ctrl.caps.required.flagFailCount + + ctrl.caps.required.flagPassCount; + ctrl.totalNonFlagCount = ctrl.caps.required.count - + ctrl.totalRequiredFlagCount; + ctrl.nonFlagPassCount = ctrl.totalNonFlagCount - + (ctrl.totalRequiredFailCount - + ctrl.caps.required.flagFailCount); + + ctrl.nonFlagRequiredPassPercent = (ctrl.nonFlagPassCount * + 100 / ctrl.totalNonFlagCount); + } + + /** + * This will check if a given test is flagged. + * @param {String} test ID of the test to check + * @param {Object} capObj capability that test is under + * @returns {Boolean} truthy value if test is flagged + */ + function isTestFlagged(test, capObj) { + if (!capObj) { + return false; + } + return (((ctrl.schemaVersion === '1.2') && + (capObj.flagged.indexOf(test) > -1)) || + ((ctrl.schemaVersion >= '1.3') && + (capObj.tests[test].flagged))); + } + + /** + * This will return the reason a test is flagged. An empty string + * will be returned if the passed in test is not flagged. + * @param {String} test ID of the test to check + * @param {String} capObj capability that test is under + * @returns {String} reason + */ + function getFlaggedReason(test, capObj) { + if ((ctrl.schemaVersion === '1.2') && + (ctrl.isTestFlagged(test, capObj))) { + + // Return a generic message since schema 1.2 does not + // provide flag reasons. + return 'Interop Working Group has flagged this test.'; + } + else if ((ctrl.schemaVersion >= '1.3') && + (ctrl.isTestFlagged(test, capObj))) { + + return capObj.tests[test].flagged.reason; + } + else { + return ''; + } + } + + /** + * This will check the if a capability should be shown based on the + * test filter selected. If a capability does not have any tests + * belonging under the given filter, it should not be shown. + * @param {Object} capability Built object for capability + * @returns {Boolean} true if capability should be shown + */ + function isCapabilityShown(capability) { + return ((ctrl.testStatus === 'total') || + (ctrl.testStatus === 'passed' && + capability.passedTests.length > 0) || + (ctrl.testStatus === 'not passed' && + capability.notPassedTests.length > 0) || + (ctrl.testStatus === 'flagged' && + (capability.passedFlagged.length + + capability.notPassedFlagged.length > 0))); + } + + /** + * This will check the if a test should be shown based on the test + * filter selected. + * @param {String} test ID of the test + * @param {Object} capability Built object for capability + * @return {Boolean} true if test should be shown + */ + function isTestShown(test, capability) { + return ((ctrl.testStatus === 'total') || + (ctrl.testStatus === 'passed' && + capability.passedTests.indexOf(test) > -1) || + (ctrl.testStatus === 'not passed' && + capability.notPassedTests.indexOf(test) > -1) || + (ctrl.testStatus === 'flagged' && + (capability.passedFlagged.indexOf(test) > -1 || + capability.notPassedFlagged.indexOf(test) > -1))); + } + + /** + * This will give the number of tests belonging under the selected + * test filter for a given capability. + * @param {Object} capability Built object for capability + * @returns {Number} number of tests under filter + */ + function getCapabilityTestCount(capability) { + if (ctrl.testStatus === 'total') { + return capability.passedTests.length + + capability.notPassedTests.length; + } + else if (ctrl.testStatus === 'passed') { + return capability.passedTests.length; + } + else if (ctrl.testStatus === 'not passed') { + return capability.notPassedTests.length; + } + else if (ctrl.testStatus === 'flagged') { + return capability.passedFlagged.length + + capability.notPassedFlagged.length; + } + else { + return 0; + } + } + + /** + * This will give the number of tests belonging under the selected + * test filter for a given status. + * @param {String} capability status + * @returns {Number} number of tests for status under filter + */ + function getStatusTestCount(status) { + if (!ctrl.caps) { + return -1; + } + else if (ctrl.testStatus === 'total') { + return ctrl.caps[status].count; + } + else if (ctrl.testStatus === 'passed') { + return ctrl.caps[status].passedCount; + } + else if (ctrl.testStatus === 'not passed') { + return ctrl.caps[status].count - + ctrl.caps[status].passedCount; + } + else if (ctrl.testStatus === 'flagged') { + return ctrl.caps[status].flagFailCount + + ctrl.caps[status].flagPassCount; + } + else { + return -1; + } + } + + /** + * This will open the modal that will show the full list of passed + * tests for the current results. + */ + function openFullTestListModal() { + $uibModal.open({ + templateUrl: '/components/results-report/partials' + + '/fullTestListModal.html', + backdrop: true, + windowClass: 'modal', + animation: true, + controller: 'FullTestListModalController as modal', + size: 'lg', + resolve: { + tests: function () { + return ctrl.resultsData.results; + } + } + }); + } + + /** + * This will open the modal that will all a user to edit test run + * metadata. + */ + function openEditTestModal() { + $uibModal.open({ + templateUrl: '/components/results-report/partials' + + '/editTestModal.html', + backdrop: true, + windowClass: 'modal', + animation: true, + controller: 'EditTestModalController as modal', + size: 'lg', + resolve: { + resultsData: function () { + return ctrl.resultsData; + } + } + }); + } + + getResults(); + } + + angular + .module('testapiApp') + .controller('FullTestListModalController', FullTestListModalController); + + FullTestListModalController.$inject = ['$uibModalInstance', 'tests']; + + /** + * Full Test List Modal Controller + * This controller is for the modal that appears if a user wants to see the + * full list of passed tests on a report page. + */ + function FullTestListModalController($uibModalInstance, tests) { + var ctrl = this; + + ctrl.tests = tests; + + /** + * This function will close/dismiss the modal. + */ + ctrl.close = function () { + $uibModalInstance.dismiss('exit'); + }; + + /** + * This function will return a string representing the sorted + * tests list separated by newlines. + */ + ctrl.getTestListString = function () { + return ctrl.tests.sort().join('\n'); + }; + } + + angular + .module('testapiApp') + .controller('EditTestModalController', EditTestModalController); + + EditTestModalController.$inject = [ + '$uibModalInstance', '$http', '$state', 'raiseAlert', + 'testapiApiUrl', 'resultsData' + ]; + + /** + * Edit Test Modal Controller + * This controller is for the modal that appears if a user wants to edit + * test run metadata. + */ + function EditTestModalController($uibModalInstance, $http, $state, + raiseAlert, testapiApiUrl, resultsData) { + + var ctrl = this; + + ctrl.getVersionList = getVersionList; + ctrl.getUserProducts = getUserProducts; + ctrl.associateProductVersion = associateProductVersion; + ctrl.getProductVersions = getProductVersions; + ctrl.saveChanges = saveChanges; + + ctrl.resultsData = resultsData; + ctrl.metaCopy = angular.copy(resultsData.meta); + ctrl.prodVersionCopy = angular.copy(resultsData.product_version); + + ctrl.getVersionList(); + ctrl.getUserProducts(); + + /** + * Retrieve an array of available capability files from the TestAPI + * API server, sort this array reverse-alphabetically, and store it in + * a scoped variable. + * Sample API return array: ["2015.03.json", "2015.04.json"] + */ + function getVersionList() { + if (ctrl.versionList) { + return; + } + var content_url = testapiApiUrl + '/guidelines'; + ctrl.versionsRequest = + $http.get(content_url).success(function (data) { + ctrl.versionList = data.sort().reverse(); + }).error(function (error) { + raiseAlert('danger', error.title, + 'Unable to retrieve version list'); + }); + } + + /** + * Get products user has management rights to or all products depending + * on the passed in parameter value. + */ + function getUserProducts() { + var contentUrl = testapiApiUrl + '/products'; + ctrl.productsRequest = + $http.get(contentUrl).success(function (data) { + ctrl.products = {}; + angular.forEach(data.products, function(prod) { + if (prod.can_manage) { + ctrl.products[prod.id] = prod; + } + }); + if (ctrl.prodVersionCopy) { + ctrl.selectedProduct = ctrl.products[ + ctrl.prodVersionCopy.product_info.id + ]; + } + ctrl.getProductVersions(); + }).error(function (error) { + ctrl.products = null; + ctrl.showError = true; + ctrl.error = + 'Error retrieving Products listing from server: ' + + angular.toJson(error); + }); + } + + /** + * Send a PUT request to the API server to associate a product with + * a test result. + */ + function associateProductVersion() { + var verId = (ctrl.selectedVersion ? + ctrl.selectedVersion.id : null); + var testId = resultsData.id; + var url = testapiApiUrl + '/results/' + testId; + ctrl.associateRequest = $http.put(url, {'product_version_id': + verId}) + .error(function (error) { + ctrl.showError = true; + ctrl.showSuccess = false; + ctrl.error = + 'Error associating product version with test run: ' + + angular.toJson(error); + }); + } + + /** + * Get all versions for a product. + */ + function getProductVersions() { + if (!ctrl.selectedProduct) { + ctrl.productVersions = []; + ctrl.selectedVersion = null; + return; + } + + var url = testapiApiUrl + '/products/' + + ctrl.selectedProduct.id + '/versions'; + ctrl.getVersionsRequest = $http.get(url) + .success(function (data) { + ctrl.productVersions = data; + if (ctrl.prodVersionCopy && + ctrl.prodVersionCopy.product_info.id == + ctrl.selectedProduct.id) { + ctrl.selectedVersion = ctrl.prodVersionCopy; + } + else { + angular.forEach(data, function(ver) { + if (!ver.version) { + ctrl.selectedVersion = ver; + } + }); + } + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * Send a PUT request to the server with the changes. + */ + function saveChanges() { + ctrl.showError = false; + ctrl.showSuccess = false; + var metaBaseUrl = [ + testapiApiUrl, '/results/', resultsData.id, '/meta/' + ].join(''); + var metaFields = ['target', 'guideline', 'shared']; + var meta = ctrl.metaCopy; + angular.forEach(metaFields, function(field) { + var oldMetaValue = (field in ctrl.resultsData.meta) ? + ctrl.resultsData.meta[field] : ''; + if (field in meta && oldMetaValue != meta[field]) { + var metaUrl = metaBaseUrl + field; + if (meta[field]) { + ctrl.assocRequest = $http.post(metaUrl, meta[field]) + .success(function(data) { + ctrl.resultsData.meta[field] = meta[field]; + }) + .error(function (error) { + ctrl.showError = true; + ctrl.showSuccess = false; + ctrl.error = + 'Error associating metadata with ' + + 'test run: ' + angular.toJson(error); + }); + } + else { + ctrl.unassocRequest = $http.delete(metaUrl) + .success(function (data) { + delete ctrl.resultsData.meta[field]; + delete meta[field]; + }) + .error(function (error) { + ctrl.showError = true; + ctrl.showSuccess = false; + ctrl.error = + 'Error associating metadata with ' + + 'test run: ' + angular.toJson(error); + }); + } + } + }); + ctrl.associateProductVersion(); + if (!ctrl.showError) { + ctrl.showSuccess = true; + $state.reload(); + } + } + + /** + * This function will close/dismiss the modal. + */ + ctrl.close = function () { + $uibModalInstance.dismiss('exit'); + }; + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results/results.html b/utils/test/testapi/3rd_party/static/testapi-ui/components/results/results.html new file mode 100644 index 000000000..2a43cd1e2 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results/results.html @@ -0,0 +1,247 @@ +<h3>{{ctrl.pageHeader}}</h3> +<p>{{ctrl.pageParagraph}}</p> + +<div class="result-filters"> + <h4>Filters</h4> + <div class="row"> + <div class="col-md-3"> + <label for="cpid">Start Date</label> + <p class="input-group"> + <input type="text" class="form-control" + uib-datepicker-popup="{{ctrl.format}}" + ng-model="ctrl.startDate" is-open="ctrl.startOpen" + close-text="Close" /> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'startOpen')"> + <i class="glyphicon glyphicon-calendar"></i> + </button> + </span> + </p> + </div> + <div class="col-md-3"> + <label for="cpid">End Date</label> + <p class="input-group"> + <input type="text" class="form-control" + uib-datepicker-popup="{{ctrl.format}}" + ng-model="ctrl.endDate" is-open="ctrl.endOpen" + close-text="Close" /> + <span class="input-group-btn"> + <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'endOpen')"> + <i class="glyphicon glyphicon-calendar"></i> + </button> + </span> + </p> + </div> + <div class="col-md-3" style="margin-top:24px;"> + <button type="submit" class="btn btn-primary" ng-click="ctrl.update()">Filter</button> + <button type="submit" class="btn btn-primary btn-danger" ng-click="ctrl.clearFilters()">Clear</button> + </div> + </div> +</div> + +<div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div> +<div cg-busy="{promise:ctrl.resultsRequest,message:'Loading'}"></div> + +<div ng-show="ctrl.data" class="results-table"> + <table ng-show="ctrl.data" class="table table-striped table-hover"> + <thead> + <tr> + <th ng-if="ctrl.isUserResults"></th> + <th>Upload Date</th> + <th>Test Run ID</th> + <th ng-if="ctrl.isUserResults">Vendor</th> + <th ng-if="ctrl.isUserResults">Product (version)</th> + <th ng-if="ctrl.isUserResults">Target Program</th> + <th ng-if="ctrl.isUserResults">Guideline</th> + <th ng-if="ctrl.isUserResults">Verified</th> + <th ng-if="ctrl.isUserResults">Shared</th> + </tr> + </thead> + + <tbody> + <tr ng-repeat-start="(index, result) in ctrl.data.results"> + <td ng-if="ctrl.isUserResults"> + <a ng-if="!result.expanded" + class="glyphicon glyphicon-plus" + ng-click="result.expanded = true"> + </a> + <a ng-if="result.expanded" + class="glyphicon glyphicon-minus" + ng-click="result.expanded = false"> + </a> + </td> + <td>{{result.created_at}}</td> + <td><a ui-sref="resultsDetail({testID: result.id})"> + {{result.id.slice(0, 8)}}...{{result.id.slice(-8)}} + </a> + </td> + <td ng-if="ctrl.isUserResults"> + {{ctrl.vendors[result.product_version.product_info.organization_id].name || '-'}} + </td> + <td ng-if="ctrl.isUserResults">{{result.product_version.product_info.name || '-'}} + <span ng-if="result.product_version.version"> + ({{result.product_version.version}}) + </span> + </td> + <td ng-if="ctrl.isUserResults">{{ctrl.targetMappings[result.meta.target] || '-'}}</td> + <td ng-if="ctrl.isUserResults">{{result.meta.guideline.slice(0, -5) || '-'}}</td> + <td ng-if="ctrl.isUserResults"> + <span ng-if="result.verification_status" class="glyphicon glyphicon-ok"></span> + <span ng-if="!result.verification_status">-</span> + + </td> + <td ng-if="ctrl.isUserResults"> + <span ng-show="result.meta.shared" class="glyphicon glyphicon-share"></span> + </td> + </tr> + <tr ng-if="result.expanded" ng-repeat-end> + <td></td> + <td colspan="3"> + <strong>Publicly Shared:</strong> + <span ng-if="result.meta.shared == 'true' && !result.sharedEdit">Yes</span> + <span ng-if="!result.meta.shared && !result.sharedEdit"> + <em>No</em> + </span> + <select ng-if="result.sharedEdit" + ng-model="result.meta.shared" + class="form-inline"> + <option value="true">Yes</option> + <option value="">No</option> + </select> + <a ng-if="!result.sharedEdit" + ng-click="result.sharedEdit = true" + title="Edit" + class="glyphicon glyphicon-pencil"></a> + <a ng-if="result.sharedEdit" + ng-click="ctrl.associateMeta(index,'shared',result.meta.shared)" + title="Save" + class="glyphicon glyphicon-floppy-disk"></a> + <br /> + + <strong>Associated Guideline:</strong> + <span ng-if="!result.meta.guideline && !result.guidelineEdit"> + <em>None</em> + </span> + <span ng-if="result.meta.guideline && !result.guidelineEdit"> + {{result.meta.guideline.slice(0, -5)}} + </span> + <select ng-if="result.guidelineEdit" + ng-model="result.meta.guideline" + ng-options="o as o.slice(0, -5) for o in ctrl.versionList" + class="form-inline"> + <option value="">None</option> + </select> + <a ng-if="!result.guidelineEdit" + ng-click="ctrl.getVersionList();result.guidelineEdit = true" + title="Edit" + class="glyphicon glyphicon-pencil"></a> + <a ng-if="result.guidelineEdit" + ng-click="ctrl.associateMeta(index, 'guideline', result.meta.guideline)" + title="Save" + class="glyphicon glyphicon-floppy-disk"> + </a> + <br /> + + <strong>Associated Target Program:</strong> + <span ng-if="!result.meta.target && !result.targetEdit"> + <em>None</em> + </span> + <span ng-if="result.meta.target && !result.targetEdit"> + {{ctrl.targetMappings[result.meta.target]}}</span> + <select ng-if="result.targetEdit" + ng-model="result.meta.target" + class="form-inline"> + <option value="">None</option> + <option value="platform">OpenStack Powered Platform</option> + <option value="compute">OpenStack Powered Compute</option> + <option value="object">OpenStack Powered Object Storage</option> + </select> + <a ng-if="!result.targetEdit" + ng-click="result.targetEdit = true;" + title="Edit" + class="glyphicon glyphicon-pencil"> + </a> + <a ng-if="result.targetEdit" + ng-click="ctrl.associateMeta(index, 'target', result.meta.target)" + title="Save" + class="glyphicon glyphicon-floppy-disk"> + </a> + <br /> + + <strong>Associated Product:</strong> + <span ng-if="!result.product_version && !result.productEdit"> + <em>None</em> + </span> + <span ng-if="result.product_version && !result.productEdit"> + <span ng-if="ctrl.products[result.product_version.product_info.id].product_type == 0"> + <a ui-sref="distro({id: result.product_version.product_info.id})"> + {{ctrl.products[result.product_version.product_info.id].name}} + <small ng-if="result.product_version.version"> + ({{result.product_version.version}}) + </small> + </a> + </span> + <span ng-if="ctrl.products[result.product_version.product_info.id].product_type != 0"> + <a ui-sref="cloud({id: result.product_version.product_info.id})"> + {{ctrl.products[result.product_version.product_info.id].name}} + <small ng-if="result.product_version.version"> + ({{result.product_version.version}}) + </small> + </a> + </span> + </span> + + <select ng-if="result.productEdit" + ng-options="product as product.name for product in ctrl.products | arrayConverter | orderBy: 'name' track by product.id" + ng-model="result.selectedProduct" + ng-change="ctrl.getProductVersions(result)"> + <option value="">-- No Product --</option> + </select> + + <span ng-if="result.productVersions.length && result.productEdit"> + <span class="glyphicon glyphicon-arrow-right" style="padding-right:3px;color:#303030;"></span> + Version: + <select ng-options="version as version.version for version in result.productVersions | orderBy: 'version' track by version.id" + ng-model="result.selectedVersion"> + </select> + + </span> + <a ng-if="!result.productEdit" + ng-click="ctrl.prepVersionEdit(result)" + title="Edit" + class="glyphicon glyphicon-pencil"> + </a> + <a ng-if="result.productEdit" + ng-click="ctrl.associateProductVersion(result)" + confirm="Once you associate this test to this product, ownership + will be transferred to the product's vendor admins. + Continue?" + title="Save" + class="glyphicon glyphicon-floppy-disk"> + </a> + <br /> + </td> + </tr> + </tbody> + </table> + + <div class="pages"> + <uib-pagination + total-items="ctrl.totalItems" + ng-model="ctrl.currentPage" + items-per-page="ctrl.itemsPerPage" + max-size="ctrl.maxSize" + class="pagination-sm" + boundary-links="true" + rotate="false" + num-pages="ctrl.numPages" + ng-change="ctrl.update()"> + </uib-pagination> + </div> +</div> + +<div ng-show="ctrl.showError" class="alert alert-danger" role="alert"> + <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> + <span class="sr-only">Error:</span> + {{ctrl.error}} +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js b/utils/test/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js new file mode 100644 index 000000000..2b0338c87 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/components/results/resultsController.js @@ -0,0 +1,339 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('ResultsController', ResultsController); + + ResultsController.$inject = [ + '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert' + ]; + + /** + * TestAPI Results Controller + * This controller is for the '/results' page where a user can browse + * a listing of community uploaded results. + */ + function ResultsController($scope, $http, $filter, $state, testapiApiUrl, + raiseAlert) { + var ctrl = this; + + ctrl.update = update; + ctrl.open = open; + ctrl.clearFilters = clearFilters; + ctrl.associateMeta = associateMeta; + ctrl.getVersionList = getVersionList; + ctrl.getUserProducts = getUserProducts; + ctrl.getVendors = getVendors; + ctrl.associateProductVersion = associateProductVersion; + ctrl.getProductVersions = getProductVersions; + ctrl.prepVersionEdit = prepVersionEdit; + + /** Mappings of Interop WG components to marketing program names. */ + ctrl.targetMappings = { + 'platform': 'Openstack Powered Platform', + 'compute': 'OpenStack Powered Compute', + 'object': 'OpenStack Powered Object Storage' + }; + + /** Initial page to be on. */ + ctrl.currentPage = 1; + + /** + * How many results should display on each page. Since pagination + * is server-side implemented, this value should match the + * 'results_per_page' configuration of the TestAPI server which + * defaults to 20. + */ + ctrl.itemsPerPage = 20; + + /** + * How many page buttons should be displayed at max before adding + * the '...' button. + */ + ctrl.maxSize = 5; + + /** The upload date lower limit to be used in filtering results. */ + ctrl.startDate = ''; + + /** The upload date upper limit to be used in filtering results. */ + ctrl.endDate = ''; + + /** The date format for the date picker. */ + ctrl.format = 'yyyy-MM-dd'; + + /** Check to see if this page should display user-specific results. */ + ctrl.isUserResults = $state.current.name === 'userResults'; + + // Should only be on user-results-page if authenticated. + if (ctrl.isUserResults && !$scope.auth.isAuthenticated) { + $state.go('home'); + } + + ctrl.pageHeader = ctrl.isUserResults ? + 'Private test results' : 'Community test results'; + + ctrl.pageParagraph = ctrl.isUserResults ? + 'Your most recently uploaded test results are listed here.' : + 'The most recently uploaded community test results are listed ' + + 'here.'; + + if (ctrl.isUserResults) { + ctrl.authRequest = $scope.auth.doSignCheck() + .then(ctrl.update); + ctrl.getUserProducts(); + } else { + ctrl.update(); + } + + ctrl.getVendors(); + + /** + * This will contact the TestAPI API to get a listing of test run + * results. + */ + function update() { + ctrl.showError = false; + // Construct the API URL based on user-specified filters. + var content_url = testapiApiUrl + '/results' + + '?page=' + ctrl.currentPage; + var start = $filter('date')(ctrl.startDate, 'yyyy-MM-dd'); + if (start) { + content_url = + content_url + '&start_date=' + start + ' 00:00:00'; + } + var end = $filter('date')(ctrl.endDate, 'yyyy-MM-dd'); + if (end) { + content_url = content_url + '&end_date=' + end + ' 23:59:59'; + } + if (ctrl.isUserResults) { + content_url = content_url + '&signed'; + } + ctrl.resultsRequest = + $http.get(content_url).success(function (data) { + ctrl.data = data; + ctrl.totalItems = ctrl.data.pagination.total_pages * + ctrl.itemsPerPage; + ctrl.currentPage = ctrl.data.pagination.current_page; + }).error(function (error) { + ctrl.data = null; + ctrl.totalItems = 0; + ctrl.showError = true; + ctrl.error = + 'Error retrieving results listing from server: ' + + angular.toJson(error); + }); + } + + /** + * This is called when the date filter calendar is opened. It + * does some event handling, and sets a scope variable so the UI + * knows which calendar was opened. + * @param {Object} $event - The Event object + * @param {String} openVar - Tells which calendar was opened + */ + function open($event, openVar) { + $event.preventDefault(); + $event.stopPropagation(); + ctrl[openVar] = true; + } + + /** + * This function will clear all filters and update the results + * listing. + */ + function clearFilters() { + ctrl.startDate = null; + ctrl.endDate = null; + ctrl.update(); + } + + /** + * This will send an API request in order to associate a metadata + * key-value pair with the given testId + * @param {Number} index - index of the test object in the results list + * @param {String} key - metadata key + * @param {String} value - metadata value + */ + function associateMeta(index, key, value) { + var testId = ctrl.data.results[index].id; + var metaUrl = [ + testapiApiUrl, '/results/', testId, '/meta/', key + ].join(''); + + var editFlag = key + 'Edit'; + if (value) { + ctrl.associateRequest = $http.post(metaUrl, value) + .success(function () { + ctrl.data.results[index][editFlag] = false; + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + else { + ctrl.unassociateRequest = $http.delete(metaUrl) + .success(function () { + ctrl.data.results[index][editFlag] = false; + }).error(function (error) { + if (error.code == 404) { + // Key doesn't exist, so count it as a success, + // and don't raise an alert. + ctrl.data.results[index][editFlag] = false; + } + else { + raiseAlert('danger', error.title, error.detail); + } + }); + } + } + + /** + * Retrieve an array of available capability files from the TestAPI + * API server, sort this array reverse-alphabetically, and store it in + * a scoped variable. + * Sample API return array: ["2015.03.json", "2015.04.json"] + */ + function getVersionList() { + if (ctrl.versionList) { + return; + } + var content_url = testapiApiUrl + '/guidelines'; + ctrl.versionsRequest = + $http.get(content_url).success(function (data) { + ctrl.versionList = data.sort().reverse(); + }).error(function (error) { + raiseAlert('danger', error.title, + 'Unable to retrieve version list'); + }); + } + + /** + * Get products user has management rights to or all products depending + * on the passed in parameter value. + */ + function getUserProducts() { + if (ctrl.products) { + return; + } + var contentUrl = testapiApiUrl + '/products'; + ctrl.productsRequest = + $http.get(contentUrl).success(function (data) { + ctrl.products = {}; + angular.forEach(data.products, function(prod) { + if (prod.can_manage) { + ctrl.products[prod.id] = prod; + } + }); + }).error(function (error) { + ctrl.products = null; + ctrl.showError = true; + ctrl.error = + 'Error retrieving Products listing from server: ' + + angular.toJson(error); + }); + } + + /** + * This will contact the TestAPI API to get a listing of + * vendors. + */ + function getVendors() { + var contentUrl = testapiApiUrl + '/vendors'; + ctrl.vendorsRequest = + $http.get(contentUrl).success(function (data) { + ctrl.vendors = {}; + data.vendors.forEach(function(vendor) { + ctrl.vendors[vendor.id] = vendor; + }); + }).error(function (error) { + ctrl.vendors = null; + ctrl.showError = true; + ctrl.error = + 'Error retrieving vendor listing from server: ' + + angular.toJson(error); + }); + } + + /** + * Send a PUT request to the API server to associate a product with + * a test result. + */ + function associateProductVersion(result) { + var verId = (result.selectedVersion ? + result.selectedVersion.id : null); + var testId = result.id; + var url = testapiApiUrl + '/results/' + testId; + ctrl.associateRequest = $http.put(url, {'product_version_id': + verId}) + .success(function (data) { + result.product_version = result.selectedVersion; + if (result.selectedVersion) { + result.product_version.product_info = + result.selectedProduct; + } + result.productEdit = false; + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * Get all versions for a product. + */ + function getProductVersions(result) { + if (!result.selectedProduct) { + result.productVersions = []; + result.selectedVersion = null; + return; + } + + var url = testapiApiUrl + '/products/' + + result.selectedProduct.id + '/versions'; + ctrl.getVersionsRequest = $http.get(url) + .success(function (data) { + result.productVersions = data; + + // If the test result isn't already associated to a + // version, default it to the null version. + if (!result.product_version) { + angular.forEach(data, function(ver) { + if (!ver.version) { + result.selectedVersion = ver; + } + }); + } + }).error(function (error) { + raiseAlert('danger', error.title, error.detail); + }); + } + + /** + * Instantiate variables needed for editing product/version + * associations. + */ + function prepVersionEdit(result) { + result.productEdit = true; + if (result.product_version) { + result.selectedProduct = + ctrl.products[result.product_version.product_info.id]; + } + result.selectedVersion = result.product_version; + ctrl.getProductVersions(result); + } + + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/config.json b/utils/test/testapi/3rd_party/static/testapi-ui/config.json new file mode 100644 index 000000000..c88d46c25 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/config.json @@ -0,0 +1 @@ +{"testapiApiUrl": "http://10.62.105.17:9711"} diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/favicon-16x16.png b/utils/test/testapi/3rd_party/static/testapi-ui/favicon-16x16.png Binary files differnew file mode 100755 index 000000000..e08c8a15e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/favicon-16x16.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/favicon-32x32.png b/utils/test/testapi/3rd_party/static/testapi-ui/favicon-32x32.png Binary files differnew file mode 100755 index 000000000..7bf57e2f7 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/favicon-32x32.png diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/favicon.ico b/utils/test/testapi/3rd_party/static/testapi-ui/favicon.ico Binary files differnew file mode 100644 index 000000000..156019aae --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/favicon.ico diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/index.html b/utils/test/testapi/3rd_party/static/testapi-ui/index.html new file mode 100644 index 000000000..4bf3c0d1b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/index.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<!-- + Copyright (c) 2015 IBM Corp. + + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. +--> +<html id="ng-app"> + <head> + <meta charset="utf-8"> + <meta name="description" content="TestAPI"> + <meta name="viewport" content="width=device-width"> + <title>TestAPI</title> + + <link rel="icon" type="image/png" href="swagger/testapi-ui/favicon-16x16.png" sizes="16x16" /> + <link rel="icon" type="image/png" href="swagger/testapi-ui/favicon-32x32.png" sizes="32x32" /> + + <link rel="stylesheet" href="swagger/testapi-ui/assets/lib/bootstrap/dist/css/bootstrap.min.css"> + <link rel="stylesheet" href="swagger/testapi-ui/assets/lib/angular-busy/dist/angular-busy.min.css"> + <link rel="stylesheet" href="swagger/testapi-ui/assets/css/style.css"> + + <script src="swagger/testapi-ui/assets/lib/angular/angular.min.js"></script> + <script src="swagger/testapi-ui/assets/lib/angular-ui-router/release/angular-ui-router.min.js"></script> + <script src="swagger/testapi-ui/assets/lib/angular-resource/angular-resource.min.js"></script> + <script src="swagger/testapi-ui/assets/lib/angular-bootstrap/ui-bootstrap-tpls.min.js"></script> + <script src="swagger/testapi-ui/assets/lib/angular-busy/dist/angular-busy.min.js"></script> + <script src="swagger/testapi-ui/assets/lib/angular-confirm-modal/angular-confirm.js"></script> + <script src="swagger/testapi-ui/app.js"></script> + + <!-- Controllers --> + <script src="swagger/testapi-ui/shared/header/headerController.js"></script> + <script src="swagger/testapi-ui/shared/alerts/alertModalFactory.js"></script> + <script src="swagger/testapi-ui/shared/alerts/confirmModalFactory.js"></script> + <script src="swagger/testapi-ui/components/guidelines/guidelinesController.js"></script> + <script src="swagger/testapi-ui/components/results/resultsController.js"></script> + <script src="swagger/testapi-ui/components/results-report/resultsReportController.js"></script> + <script src="swagger/testapi-ui/components/profile/profileController.js"></script> + <script src="swagger/testapi-ui/components/auth-failure/authFailureController.js"></script> + <script src="swagger/testapi-ui/components/logout/logoutController.js"></script> + + <!-- Filters --> + <script src="swagger/testapi-ui/shared/filters.js"></script> + + </head> + + <body class="container"> + <header ng-include src="'swagger/testapi-ui/shared/header/header.html'"></header> + + <div ui-view></div> + </body> +</html> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/robots.txt b/utils/test/testapi/3rd_party/static/testapi-ui/robots.txt new file mode 100644 index 000000000..93c442085 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/robots.txt @@ -0,0 +1,4 @@ +# robotstxt.org + +User-agent: * + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/alertModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/alertModal.html new file mode 100644 index 000000000..59fd50014 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/alertModal.html @@ -0,0 +1,8 @@ +<div class="modal-body" style="padding:0px"> + <div class="alert alert-{{alert.data.mode}}" style="margin-bottom:0px"> + <button type="button" class="close" data-ng-click="alert.close()" > + <span class="glyphicon glyphicon-remove-circle"></span> + </button> + <strong>{{alert.data.title}}</strong> {{alert.data.text}} + </div> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/alertModalFactory.js b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/alertModalFactory.js new file mode 100644 index 000000000..3ce76c912 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/alertModalFactory.js @@ -0,0 +1,74 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .factory('raiseAlert', raiseAlert); + + raiseAlert.$inject = ['$uibModal']; + + /** + * This allows alert pop-ups to be raised. Just inject it as a dependency + * in the calling controller. + */ + function raiseAlert($uibModal) { + return function(mode, title, text) { + $uibModal.open({ + templateUrl: '/shared/alerts/alertModal.html', + controller: 'RaiseAlertModalController as alert', + backdrop: true, + keyboard: true, + backdropClick: true, + size: 'md', + resolve: { + data: function () { + return { + mode: mode, + title: title, + text: text + }; + } + } + }); + }; + } + + angular + .module('testapiApp') + .controller('RaiseAlertModalController', RaiseAlertModalController); + + RaiseAlertModalController.$inject = ['$uibModalInstance', 'data']; + + /** + * This is the controller for the alert pop-up. + */ + function RaiseAlertModalController($uibModalInstance, data) { + var ctrl = this; + + ctrl.close = close; + ctrl.data = data; + + /** + * This method will close the alert modal. The modal will close + * when the user clicks the close button or clicks outside of the + * modal. + */ + function close() { + $uibModalInstance.close(); + } + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html new file mode 100644 index 000000000..82478a51b --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModal.html @@ -0,0 +1,13 @@ +<div class="modal-header"><h3 class="modal-title">Confirm</h3></div> +<div class="modal-body"> + <div class="form-group"> + <label for="confirmText">{{confirmModal.data.text}}:</label> + <textarea type="text" class="form-control" + rows="5" ng-model="confirmModal.inputText" id="confirmText"> + </textarea> + </div> +</div> +<div class="modal-footer"> + <button class="btn btn-primary" ng-click="confirmModal.confirm()">Ok</button> + <button class="btn btn-default" ng-click="confirmModal.cancel()">Cancel</button> +</div> diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js new file mode 100644 index 000000000..76c74df9e --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/alerts/confirmModalFactory.js @@ -0,0 +1,67 @@ +(function () { + 'use strict'; + + angular + .module('testapiApp') + .factory('confirmModal', confirmModal); + + confirmModal.$inject = ['$uibModal']; + + /** + * Opens confirm modal dialog with input textbox + */ + function confirmModal($uibModal) { + return function(text, successHandler) { + $uibModal.open({ + templateUrl: '/shared/alerts/confirmModal.html', + controller: 'CustomConfirmModalController as confirmModal', + size: 'md', + resolve: { + data: function () { + return { + text: text, + successHandler: successHandler + }; + } + } + }); + }; + } + + angular + .module('testapiApp') + .controller('CustomConfirmModalController', + CustomConfirmModalController); + + CustomConfirmModalController.$inject = ['$uibModalInstance', 'data']; + + /** + * This is the controller for the alert pop-up. + */ + function CustomConfirmModalController($uibModalInstance, data) { + var ctrl = this; + + ctrl.confirm = confirm; + ctrl.cancel = cancel; + + ctrl.data = angular.copy(data); + + /** + * Initiate confirmation and call the success handler with the + * input text. + */ + function confirm() { + $uibModalInstance.close(); + if (angular.isDefined(ctrl.data.successHandler)) { + ctrl.data.successHandler(ctrl.inputText); + } + } + + /** + * Close the confirm modal without initiating changes. + */ + function cancel() { + $uibModalInstance.dismiss('cancel'); + } + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/filters.js b/utils/test/testapi/3rd_party/static/testapi-ui/shared/filters.js new file mode 100644 index 000000000..4a4b7bde1 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/filters.js @@ -0,0 +1,55 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + /** + * Convert an object of objects to an array of objects to use with ng-repeat + * filters. + */ + angular + .module('testapiApp') + .filter('arrayConverter', arrayConverter); + + /** + * Convert an object of objects to an array of objects to use with ng-repeat + * filters. + */ + function arrayConverter() { + return function (objects) { + var array = []; + angular.forEach(objects, function (object, key) { + if (!('id' in object)) { + object.id = key; + } + array.push(object); + }); + return array; + }; + } + + angular + .module('testapiApp') + .filter('capitalize', capitalize); + + /** + * Angular filter that will capitalize the first letter of a string. + */ + function capitalize() { + return function (string) { + return string.substring(0, 1).toUpperCase() + string.substring(1); + }; + } +})(); diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/header/header.html b/utils/test/testapi/3rd_party/static/testapi-ui/shared/header/header.html new file mode 100644 index 000000000..78361b8a5 --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/header/header.html @@ -0,0 +1,52 @@ +<div class="heading"><a ui-sref="home"><img src="swagger/testapi-ui/assets/img/testapi-logo.png" alt="TestAPI"></a> +TestAPI +</div> +<nav class="navbar navbar-default" role="navigation" ng-controller="HeaderController as header"> + <div class="container-fluid"> + <!-- Brand and toggle get grouped for better mobile display --> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" ng-click="header.navbarCollapsed = !header.navbarCollapsed"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + </div> + + <div class="collapse navbar-collapse" id="navbar" uib-collapse="header.navbarCollapsed"> + <ul class="nav navbar-nav"> + <li ng-class="{ active: header.isActive('/')}"><a ui-sref="home">Home</a></li> + <li ng-class="{ active: header.isActive('/about')}"><a ui-sref="about">About</a></li> + <li ng-class="{ active: header.isActive('/guidelines')}"><a ui-sref="guidelines">OPNFV Powered™ Guidelines</a></li> + <li ng-class="{ active: header.isActive('/community_results')}"><a ui-sref="communityResults">Community Results</a></li> + <!-- + <li ng-class="{ active: header.isCatalogActive('public')}" class="dropdown" uib-dropdown> + <a role="button" class="dropdown-toggle" uib-dropdown-toggle> + Catalog <strong class="caret"></strong> + </a> + <ul class="dropdown-menu"> + <li><a ui-sref="publicVendors">Vendors</a></li> + <li><a ui-sref="publicProducts">Products</a></li> + </ul> + </li> + --> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li ng-class="{ active: header.isActive('/user_results')}" ng-if="auth.isAuthenticated"><a ui-sref="userResults">My Results</a></li> + <li ng-if="auth.isAuthenticated" ng-class="{ active: header.isCatalogActive('user')}" class="dropdown" uib-dropdown> + <a role="button" class="dropdown-toggle" uib-dropdown-toggle> + My Catalog <strong class="caret"></strong> + </a> + <ul class="dropdown-menu"> + <li><a ui-sref="userVendors">My Vendors</a></li> + <li><a ui-sref="userProducts">My Products</a></li> + </ul> + </li> + <li ng-class="{ active: header.isActive('/profile')}" ng-if="auth.isAuthenticated"><a ui-sref="profile">Profile</a></li> + <li ng-if="auth.isAuthenticated"><a href="" ng-click="auth.doSignOut()">Sign Out</a></li> + <li ng-if="!auth.isAuthenticated"><a href="" ng-click="auth.doSignIn()">Sign In / Sign Up</a></li> + </ul> + </div> + </div> +</nav> + diff --git a/utils/test/testapi/3rd_party/static/testapi-ui/shared/header/headerController.js b/utils/test/testapi/3rd_party/static/testapi-ui/shared/header/headerController.js new file mode 100644 index 000000000..0a14a415f --- /dev/null +++ b/utils/test/testapi/3rd_party/static/testapi-ui/shared/header/headerController.js @@ -0,0 +1,63 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(function () { + 'use strict'; + + angular + .module('testapiApp') + .controller('HeaderController', HeaderController); + + HeaderController.$inject = ['$location']; + + /** + * TestAPI Header Controller + * This controller is for the header template which contains the site + * navigation. + */ + function HeaderController($location) { + var ctrl = this; + + ctrl.isActive = isActive; + ctrl.isCatalogActive = isCatalogActive; + + /** Whether the Navbar is collapsed for small displays. */ + ctrl.navbarCollapsed = true; + + /** + * This determines whether a button should be in the active state based + * on the URL. + */ + function isActive(viewLocation) { + var path = $location.path().substr(0, viewLocation.length); + if (path === viewLocation) { + // Make sure "/" only matches when viewLocation is "/". + if (!($location.path().substr(0).length > 1 && + viewLocation.length === 1 )) { + return true; + } + } + return false; + } + + /** This determines the active state for the catalog dropdown. Type + * parameter should be passed in to specify if the catalog is the + * public or user one. + */ + function isCatalogActive(type) { + return ctrl.isActive('/' + type + '_vendors') + || ctrl.isActive('/' + type + '_products'); + } + } +})(); |