From d3e250995c2f925ebe0ee14fc2cb7abef46e59e3 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 28 Dec 2017 23:21:15 -0500 Subject: [PATCH] Update 2017-12-29T04:21:13.885Z --- index.html | 14 +- js/app.min.js | 2 +- js/fallback-styles.min.js | 2 +- js/lib.min.js | 36536 +++++++++++++++++++++++++++++++++++- u2f-connector.html | 2 +- 5 files changed, 36545 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index 04794a39..afe12ca5 100644 --- a/index.html +++ b/index.html @@ -15,9 +15,9 @@ - + - + @@ -35,11 +35,11 @@ integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"> - - + + - - - + + + diff --git a/js/app.min.js b/js/app.min.js index 42b2e2ea..4618e381 100644 --- a/js/app.min.js +++ b/js/app.min.js @@ -1 +1 @@ -angular.module("bit",["ui.router","ngMessages","angular-jwt","ui.bootstrap.showErrors","toastr","angulartics","angulartics.google.analytics","angular-stripe","credit-cards","angular-promise-polyfill","bit.directives","bit.filters","bit.services","bit.global","bit.accounts","bit.vault","bit.settings","bit.tools","bit.organization","bit.reports"]),angular.module("bit").constant("appSettings",{apiUri:"/api",identityUri:"/identity",iconsUri:"https://icons.bitwarden.com",stripeKey:"pk_live_bpN0P37nMxrMQkcaHXtAybJk",braintreeKey:"production_qfbsv8kc_njj2zjtyngtjmbjd",selfHosted:!1,version:"1.22.0",environment:"Production"}),angular.module("bit.accounts",["ui.bootstrap","ngCookies"]),angular.module("bit.directives",[]),angular.module("bit.global",[]),angular.module("bit.filters",[]),angular.module("bit.reports",["toastr","ngSanitize"]),angular.module("bit.organization",["ui.bootstrap"]),angular.module("bit.services",["ngResource","ngStorage","angular-jwt"]),angular.module("bit.settings",["ui.bootstrap","toastr"]),angular.module("bit.tools",["ui.bootstrap","toastr"]),angular.module("bit.vault",["ui.bootstrap","ngclipboard"]),angular.module("bit").factory("apiInterceptor",["$injector","$q","toastr","appSettings","utilsService",function(e,t,n,o,r){return{request:function(e){return e.url.indexOf(o.apiUri+"/")>-1&&(e.headers["Device-Type"]=r.getDeviceType()),e},response:function(o){return 401!==o.status&&403!==o.status||(e.get("authService").logOut(),e.get("$state").go("frontend.login.info").then(function(){n.warning("Your login session has expired.","Logged out")})),o||t.when(o)},responseError:function(o){return 401!==o.status&&403!==o.status||(e.get("authService").logOut(),e.get("$state").go("frontend.login.info").then(function(){n.warning("Your login session has expired.","Logged out")})),t.reject(o)}}}]),angular.module("bit").config(["$stateProvider","$urlRouterProvider","$httpProvider","jwtInterceptorProvider","jwtOptionsProvider","$uibTooltipProvider","toastrConfig","$locationProvider","$qProvider","appSettings","stripeProvider",function(e,t,n,o,r,a,i,s,l,c,u){angular.extend(c,window.bitwardenAppSettings),l.errorOnUnhandledRejections(!1),s.hashPrefix("");var d;o.tokenGetter=["options","tokenService","authService",function(e,t,n){if(-1!==e.url.indexOf(c.apiUri+"/")){if(d)return d;var o=t.getToken();if(o){if(!t.tokenNeedsRefresh(o))return o;var r=n.refreshAccessToken();if(r)return d=r.then(function(e){return d=null,e||o})}}}],u.setPublishableKey(c.stripeKey),angular.extend(i,{closeButton:!0,progressBar:!0,showMethod:"slideDown",target:".toast-target"}),a.options({popupDelay:600,appendToBody:!0}),(-1!==navigator.userAgent.indexOf("MSIE")||navigator.appVersion.indexOf("Trident/")>0)&&(n.defaults.headers.get||(n.defaults.headers.get={}),n.defaults.headers.get["Cache-Control"]="no-cache",n.defaults.headers.get.Pragma="no-cache"),n.interceptors.push("apiInterceptor"),n.interceptors.push("jwtInterceptor"),t.otherwise("/"),e.state("backend",{templateUrl:"app/views/backendLayout.html",abstract:!0,data:{authorize:!0}}).state("backend.user",{templateUrl:"app/views/userLayout.html",abstract:!0}).state("backend.user.vault",{url:"^/vault",templateUrl:"app/vault/views/vault.html",controller:"vaultController",data:{pageTitle:"My Vault",controlSidebar:!0},params:{refreshFromServer:!1}}).state("backend.user.settings",{url:"^/settings",templateUrl:"app/settings/views/settings.html",controller:"settingsController",data:{pageTitle:"Settings"}}).state("backend.user.settingsDomains",{url:"^/settings/domains",templateUrl:"app/settings/views/settingsDomains.html",controller:"settingsDomainsController",data:{pageTitle:"Domain Settings"}}).state("backend.user.settingsTwoStep",{url:"^/settings/two-step",templateUrl:"app/settings/views/settingsTwoStep.html",controller:"settingsTwoStepController",data:{pageTitle:"Two-step Login"}}).state("backend.user.settingsCreateOrg",{url:"^/settings/create-organization",templateUrl:"app/settings/views/settingsCreateOrganization.html",controller:"settingsCreateOrganizationController",data:{pageTitle:"Create Organization"}}).state("backend.user.settingsBilling",{url:"^/settings/billing",templateUrl:"app/settings/views/settingsBilling.html",controller:"settingsBillingController",data:{pageTitle:"Billing"}}).state("backend.user.settingsPremium",{url:"^/settings/premium",templateUrl:"app/settings/views/settingsPremium.html",controller:"settingsPremiumController",data:{pageTitle:"Go Premium"}}).state("backend.user.tools",{url:"^/tools",templateUrl:"app/tools/views/tools.html",controller:"toolsController",data:{pageTitle:"Tools"}}).state("backend.user.reportsBreach",{url:"^/reports/breach",templateUrl:"app/reports/views/reportsBreach.html",controller:"reportsBreachController",data:{pageTitle:"Data Breach Report"}}).state("backend.user.apps",{url:"^/apps",templateUrl:"app/views/apps.html",controller:"appsController",data:{pageTitle:"Get the Apps"}}).state("backend.org",{templateUrl:"app/views/organizationLayout.html",abstract:!0}).state("backend.org.dashboard",{url:"^/organization/:orgId",templateUrl:"app/organization/views/organizationDashboard.html",controller:"organizationDashboardController",data:{pageTitle:"Organization Dashboard"}}).state("backend.org.people",{url:"/organization/:orgId/people?viewEvents&search",templateUrl:"app/organization/views/organizationPeople.html",controller:"organizationPeopleController",data:{pageTitle:"Organization People"}}).state("backend.org.collections",{url:"/organization/:orgId/collections?search",templateUrl:"app/organization/views/organizationCollections.html",controller:"organizationCollectionsController",data:{pageTitle:"Organization Collections"}}).state("backend.org.settings",{url:"/organization/:orgId/settings",templateUrl:"app/organization/views/organizationSettings.html",controller:"organizationSettingsController",data:{pageTitle:"Organization Settings"}}).state("backend.org.billing",{url:"/organization/:orgId/billing",templateUrl:"app/organization/views/organizationBilling.html",controller:"organizationBillingController",data:{pageTitle:"Organization Billing"}}).state("backend.org.vault",{url:"/organization/:orgId/vault?viewEvents&search",templateUrl:"app/organization/views/organizationVault.html",controller:"organizationVaultController",data:{pageTitle:"Organization Vault"}}).state("backend.org.groups",{url:"/organization/:orgId/groups?search",templateUrl:"app/organization/views/organizationGroups.html",controller:"organizationGroupsController",data:{pageTitle:"Organization Groups"}}).state("backend.org.events",{url:"/organization/:orgId/events",templateUrl:"app/organization/views/organizationEvents.html",controller:"organizationEventsController",data:{pageTitle:"Organization Events"}}).state("frontend",{templateUrl:"app/views/frontendLayout.html",abstract:!0,data:{authorize:!1}}).state("frontend.login",{templateUrl:"app/accounts/views/accountsLogin.html",controller:"accountsLoginController",params:{returnState:null,email:null,premium:null,org:null},data:{bodyClass:"login-page"}}).state("frontend.login.info",{url:"^/?org&premium&email",templateUrl:"app/accounts/views/accountsLoginInfo.html",data:{pageTitle:"Log In"}}).state("frontend.login.twoFactor",{url:"^/two-step?org&premium&email",templateUrl:"app/accounts/views/accountsLoginTwoFactor.html",data:{pageTitle:"Log In (Two-step)"}}).state("frontend.logout",{url:"^/logout",controller:"accountsLogoutController",data:{authorize:!0}}).state("frontend.passwordHint",{url:"^/password-hint",templateUrl:"app/accounts/views/accountsPasswordHint.html",controller:"accountsPasswordHintController",data:{pageTitle:"Master Password Hint",bodyClass:"login-page"}}).state("frontend.recover",{url:"^/recover",templateUrl:"app/accounts/views/accountsRecover.html",controller:"accountsRecoverController",data:{pageTitle:"Recover Account",bodyClass:"login-page"}}).state("frontend.recover-delete",{url:"^/recover-delete",templateUrl:"app/accounts/views/accountsRecoverDelete.html",controller:"accountsRecoverDeleteController",data:{pageTitle:"Delete Account",bodyClass:"login-page"}}).state("frontend.verify-recover-delete",{url:"^/verify-recover-delete?userId&token&email",templateUrl:"app/accounts/views/accountsVerifyRecoverDelete.html",controller:"accountsVerifyRecoverDeleteController",data:{pageTitle:"Confirm Delete Account",bodyClass:"login-page"}}).state("frontend.register",{url:"^/register?org&premium",templateUrl:"app/accounts/views/accountsRegister.html",controller:"accountsRegisterController",params:{returnState:null,email:null,org:null,premium:null},data:{pageTitle:"Register",bodyClass:"register-page"}}).state("frontend.organizationAccept",{url:"^/accept-organization?organizationId&organizationUserId&token&email&organizationName",templateUrl:"app/accounts/views/accountsOrganizationAccept.html",controller:"accountsOrganizationAcceptController",data:{pageTitle:"Accept Organization Invite",bodyClass:"login-page",skipAuthorize:!0}}).state("frontend.verifyEmail",{url:"^/verify-email?userId&token",templateUrl:"app/accounts/views/accountsVerifyEmail.html",controller:"accountsVerifyEmailController",data:{pageTitle:"Verifying Email",bodyClass:"login-page",skipAuthorize:!0}})}]).run(["$rootScope","authService","$state",function(e,t,n){e.$on("$stateChangeSuccess",function(){$("html, body").animate({scrollTop:0},200)}),e.$on("$stateChangeStart",function(o,r,a){if(!r.data||!r.data.authorize){if(r.data&&r.data.skipAuthorize)return;if(!t.isAuthenticated())return;return o.preventDefault(),void n.go("backend.user.vault")}if(!t.isAuthenticated())return o.preventDefault(),t.logOut(),void n.go("frontend.login.info");r.name.indexOf("backend.org.")>-1&&a.orgId&&(e.vaultCiphers=e.vaultGroupings=null,t.getUserProfile().then(function(e){var t=e.organizations;t&&a.orgId in t&&2===t[a.orgId].status&&2!==t[a.orgId].type||(o.preventDefault(),n.go("backend.user.vault"))}))})}]),angular.module("bit").constant("constants",{rememberedEmailCookieName:"bit.rememberedEmail",encType:{AesCbc256_B64:0,AesCbc128_HmacSha256_B64:1,AesCbc256_HmacSha256_B64:2,Rsa2048_OaepSha256_B64:3,Rsa2048_OaepSha1_B64:4,Rsa2048_OaepSha256_HmacSha256_B64:5,Rsa2048_OaepSha1_HmacSha256_B64:6},orgUserType:{owner:0,admin:1,user:2},orgUserStatus:{invited:0,accepted:1,confirmed:2},twoFactorProvider:{u2f:4,yubikey:3,duo:2,authenticator:0,email:1,remember:5},cipherType:{login:1,secureNote:2,card:3,identity:4},fieldType:{text:0,hidden:1,boolean:2},deviceType:{android:0,ios:1,chromeExt:2,firefoxExt:3,operaExt:4,edgeExt:5,windowsDesktop:6,macOsDesktop:7,linuxDesktop:8,chrome:9,firefox:10,opera:11,edge:12,ie:13,unknown:14,uwp:16,safari:17,vivaldi:18,vivaldiExt:19},eventType:{User_LoggedIn:1e3,User_ChangedPassword:1001,User_Enabled2fa:1002,User_Disabled2fa:1003,User_Recovered2fa:1004,User_FailedLogIn:1005,User_FailedLogIn2fa:1006,Cipher_Created:1100,Cipher_Updated:1101,Cipher_Deleted:1102,Cipher_AttachmentCreated:1103,Cipher_AttachmentDeleted:1104,Cipher_Shared:1105,Cipher_UpdatedCollections:1106,Collection_Created:1300,Collection_Updated:1301,Collection_Deleted:1302,Group_Created:1400,Group_Updated:1401,Group_Deleted:1402,OrganizationUser_Invited:1500,OrganizationUser_Confirmed:1501,OrganizationUser_Updated:1502,OrganizationUser_Removed:1503,OrganizationUser_UpdatedGroups:1504,Organization_Updated:1600},twoFactorProviderInfo:[{type:0,name:"Authenticator App",description:"Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.",enabled:!1,active:!0,free:!0,image:"authapp.png",displayOrder:0,priority:1,requiresUsb:!1},{type:3,name:"YubiKey OTP Security Key",description:"Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices.",enabled:!1,active:!0,image:"yubico.png",displayOrder:1,priority:3,requiresUsb:!0},{type:2,name:"Duo",description:"Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.",enabled:!1,active:!0,image:"duo.png",displayOrder:2,priority:2,requiresUsb:!1},{type:4,name:"FIDO U2F Security Key",description:"Use any FIDO U2F enabled security key to access your account.",enabled:!1,active:!0,image:"fido.png",displayOrder:3,priority:4,requiresUsb:!0},{type:1,name:"Email",description:"Verification codes will be emailed to you.",enabled:!1,active:!0,free:!0,image:"gmail.png",displayOrder:4,priority:0,requiresUsb:!1}],plans:{free:{basePrice:0,noAdditionalSeats:!0,noPayment:!0,upgradeSortOrder:-1},families:{basePrice:1,annualBasePrice:12,baseSeats:5,noAdditionalSeats:!0,annualPlanType:"familiesAnnually",upgradeSortOrder:1},teams:{basePrice:5,annualBasePrice:60,monthlyBasePrice:8,baseSeats:5,seatPrice:2,annualSeatPrice:24,monthlySeatPrice:2.5,monthPlanType:"teamsMonthly",annualPlanType:"teamsAnnually",upgradeSortOrder:2},enterprise:{seatPrice:3,annualSeatPrice:36,monthlySeatPrice:4,monthPlanType:"enterpriseMonthly",annualPlanType:"enterpriseAnnually",upgradeSortOrder:3}},storageGb:{price:.33,monthlyPrice:.5,yearlyPrice:4},premium:{price:10,yearlyPrice:10}}),angular.module("bit.accounts").controller("accountsLoginController",["$scope","$rootScope","$cookies","apiService","cryptoService","authService","$state","constants","$analytics","$uibModal","$timeout","$window","$filter","toastr",function(e,t,n,o,r,a,i,s,l,c,u,d,p,m){e.state=i,e.twoFactorProviderConstants=s.twoFactorProvider,e.rememberTwoFactor={checked:!1};var g=!0;e.returnState=i.params.returnState,e.stateEmail=i.params.email,!e.returnState&&i.params.org?e.returnState={name:"backend.user.settingsCreateOrg",params:{plan:i.params.org}}:!e.returnState&&i.params.premium&&(e.returnState={name:"backend.user.settingsPremium"}),!(i.current.name.indexOf("twoFactor")>-1)||e.twoFactorProviders&&e.twoFactorProviders.length||i.go("frontend.login.info",{returnState:e.returnState});var f=n.get(s.rememberedEmailCookieName);f||e.stateEmail?(e.model={email:e.stateEmail||f,rememberEmail:null!==f},u(function(){$("#masterPassword").focus()})):u(function(){$("#email").focus()});var h,v;e.twoFactorProviders=null,e.twoFactorProvider=null,e.login=function(t){e.loginPromise=a.logIn(t.email,t.masterPassword).then(function(o){if(t.rememberEmail){var r=new Date;r.setFullYear(r.getFullYear()+10),n.put(s.rememberedEmailCookieName,t.email,{expires:r})}else n.remove(s.rememberedEmailCookieName);o&&Object.keys(o).length>0?(h=t.email,v=t.masterPassword,e.twoFactorProviders=function(e){if(function(){var e;return e=navigator.userAgent||navigator.vendor||window.opera,(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(e)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(e.substr(0,4)))&&(a29867=!0),!a29867&&!navigator.userAgent.match(/iPad/i)}())return e;for(var t=Object.keys(e),n=0;no){if(a[0].type===s.twoFactorProvider.u2f&&!u2f.isSupported)continue;n=a[0].type,o=a[0].priority}}if(null===n)return null;return parseInt(n)}(e.twoFactorProviders),l.eventTrack("Logged In To Two-step"),i.go("frontend.login.twoFactor",{returnState:e.returnState}).then(function(){u(function(){$("#code").focus(),b()})})):(l.eventTrack("Logged In"),y()),t.masterPassword=""})};e.twoFactor=function(t){e.twoFactorProvider!==s.twoFactorProvider.email&&e.twoFactorProvider!==s.twoFactorProvider.authenticator||(t=t.replace(" ","")),e.twoFactorPromise=a.logIn(h,v,t,e.twoFactorProvider,e.rememberTwoFactor.checked||!1),e.twoFactorPromise.then(function(){l.eventTrack("Logged In From Two-step"),y()},function(){e.twoFactorProvider===s.twoFactorProvider.u2f&&b()})},e.anotherMethod=function(){c.open({animation:!0,templateUrl:"app/accounts/views/accountsTwoFactorMethods.html",controller:"accountsTwoFactorMethodsController",resolve:{providers:function(){return e.twoFactorProviders}}}).result.then(function(t){e.twoFactorProvider=t,u(function(){$("#code").focus(),b()})})},e.sendEmail=function(t){if(e.twoFactorProvider===s.twoFactorProvider.email)return r.makeKeyAndHash(h,v).then(function(e){return o.twoFactor.sendEmailLogin({email:h,masterPasswordHash:e.hash}).$promise}).then(function(){t&&m.success("Verification email sent to "+e.twoFactorEmail+".")},function(){m.error("Could not send verification email.")})},e.$on("$destroy",function(){g=!0});function y(){e.returnState?i.go(e.returnState.name,e.returnState.params):i.go("backend.user.vault")}function b(){g=!0;var t;if(e.twoFactorProvider===s.twoFactorProvider.duo)t=e.twoFactorProviders[s.twoFactorProvider.duo],d.Duo.init({host:t.Host,sig_request:t.Signature,submit_callback:function(t){var n=$(t).find('input[name="sig_response"]').val();e.twoFactor(n)}});else if(e.twoFactorProvider===s.twoFactorProvider.u2f){g=!1,t=e.twoFactorProviders[s.twoFactorProvider.u2f];w(JSON.parse(t.Challenges))}else e.twoFactorProvider===s.twoFactorProvider.email&&(t=e.twoFactorProviders[s.twoFactorProvider.email],e.twoFactorEmail=t.Email,Object.keys(e.twoFactorProviders).length>1&&e.sendEmail(!1))}function w(t){g||t.length<1||e.twoFactorProvider!==s.twoFactorProvider.u2f||(console.log("listening for u2f key..."),d.u2f.sign(t[0].appId,t[0].challenge,[{version:t[0].version,keyHandle:t[0].keyHandle}],function(n){if(e.twoFactorProvider===s.twoFactorProvider.u2f)return n.errorCode?(console.log(n.errorCode),void u(function(){w(t)},5===n.errorCode?0:1e3)):void e.twoFactor(JSON.stringify(n))},10))}}]),angular.module("bit.accounts").controller("accountsLogoutController",["$scope","authService","$state","$analytics",function(e,t,n,o){t.logOut(),o.eventTrack("Logged Out"),n.go("frontend.login.info")}]),angular.module("bit.accounts").controller("accountsOrganizationAcceptController",["$scope","$state","apiService","authService","toastr","$analytics",function(e,t,n,o,r,a){e.state={name:t.current.name,params:t.params},t.params.organizationId&&t.params.organizationUserId&&t.params.token&&t.params.email&&t.params.organizationName?e.$on("$viewContentLoaded",function(){o.isAuthenticated()?(e.accepting=!0,n.organizationUsers.accept({orgId:t.params.organizationId,id:t.params.organizationUserId},{token:t.params.token},function(){a.eventTrack("Accepted Invitation"),t.go("backend.user.vault",null,{location:"replace"}).then(function(){r.success("You can access this organization once an administrator confirms your membership. We'll send an email when that happens.","Invite Accepted",{timeOut:1e4})})},function(){a.eventTrack("Failed To Accept Invitation"),t.go("backend.user.vault",null,{location:"replace"}).then(function(){r.error("Unable to accept invitation.","Error")})})):e.loading=!1}):t.go("frontend.login.info").then(function(){r.error("Invalid parameters.")})}]),angular.module("bit.accounts").controller("accountsPasswordHintController",["$scope","$rootScope","apiService","$analytics",function(e,t,n,o){e.success=!1,e.submit=function(t){e.submitPromise=n.accounts.postPasswordHint({email:t.email},function(){o.eventTrack("Requested Password Hint"),e.success=!0}).$promise}}]),angular.module("bit.accounts").controller("accountsRecoverController",["$scope","apiService","cryptoService","$analytics",function(e,t,n,o){e.success=!1,e.submit=function(r){var a=r.email.toLowerCase();e.submitPromise=n.makeKeyAndHash(r.email,r.masterPassword).then(function(e){return t.twoFactor.recover({email:a,masterPasswordHash:e.hash,recoveryCode:r.code.replace(/\s/g,"").toLowerCase()}).$promise}).then(function(){o.eventTrack("Recovered 2FA"),e.success=!0})}}]),angular.module("bit.accounts").controller("accountsRecoverDeleteController",["$scope","$rootScope","apiService","$analytics",function(e,t,n,o){e.success=!1,e.submit=function(t){e.submitPromise=n.accounts.postDeleteRecover({email:t.email},function(){o.eventTrack("Started Delete Recovery"),e.success=!0}).$promise}}]),angular.module("bit.accounts").controller("accountsRegisterController",["$scope","$location","apiService","cryptoService","validationService","$analytics","$state","$timeout",function(e,t,n,o,r,a,i,s){var l=t.search(),c=i.params;e.createOrg=c.org,!c.returnState&&c.org?e.returnState={name:"backend.user.settingsCreateOrg",params:{plan:i.params.org}}:!c.returnState&&c.premium?e.returnState={name:"backend.user.settingsPremium",params:{plan:i.params.org}}:e.returnState=c.returnState,e.success=!1,e.model={email:l.email?l.email:c.email},e.readOnlyEmail=null!==c.email,s(function(){e.model.email?$("#name").focus():$("#email").focus()}),e.registerPromise=null,e.register=function(t){var i=!1;if(e.model.masterPassword.length<8&&(r.addError(t,"MasterPassword","Master password must be at least 8 characters long.",!0),i=!0),e.model.masterPassword!==e.model.confirmMasterPassword&&(r.addError(t,"ConfirmMasterPassword","Master password confirmation does not match.",!0),i=!0),!i){var s,l,c=e.model.email.toLowerCase();e.registerPromise=o.makeKeyAndHash(c,e.model.masterPassword).then(function(e){return s=e,l=o.makeEncKey(e.key),o.makeKeyPair(l.encKey)}).then(function(t){var o={name:e.model.name,email:c,masterPasswordHash:s.hash,masterPasswordHint:e.model.masterPasswordHint,key:l.encKeyEnc,keys:{publicKey:t.publicKey,encryptedPrivateKey:t.privateKeyEnc}};return n.accounts.register(o).$promise},function(e){return r.addError(t,null,"Problem generating keys.",!0),!1}).then(function(t){!1!==t&&(e.success=!0,a.eventTrack("Registered"))})}}}]),angular.module("bit.accounts").controller("accountsTwoFactorMethodsController",["$scope","$uibModalInstance","$analytics","providers","constants",function(e,t,n,o,r){n.eventTrack("accountsTwoFactorMethodsController",{category:"Modal"}),e.providers=[],o.hasOwnProperty(r.twoFactorProvider.authenticator)&&a(r.twoFactorProvider.authenticator),o.hasOwnProperty(r.twoFactorProvider.yubikey)&&a(r.twoFactorProvider.yubikey),o.hasOwnProperty(r.twoFactorProvider.email)&&a(r.twoFactorProvider.email),o.hasOwnProperty(r.twoFactorProvider.duo)&&a(r.twoFactorProvider.duo),o.hasOwnProperty(r.twoFactorProvider.u2f)&&u2f.isSupported&&a(r.twoFactorProvider.u2f),e.choose=function(e){t.close(e.type)},e.close=function(){t.dismiss("close")};function a(t){for(var n=0;n1&&(n=function(e,t){var n=e.split(" ");if(n&&n.length>1){for(var o="",r=0;r').attr({y:"50%",x:"50%",dy:"0.35em","pointer-events":"auto",fill:i,"font-family":s}).text(a).css({"font-weight":l,"font-size":c+"px"})),g=o.bgColor?o.bgColor:function(e){var t=0,n=0;for(n=0;n>8*n&255).toString(16)).substr(-2);return o}(r),f=(u=o.width,d=o.height,p=g,angular.element("").attr({xmlns:"http://www.w3.org/2000/svg","pointer-events":"none",width:u,height:d}).css({"background-color":p,width:u+"px",height:d+"px"}));f.append(m);var h=angular.element("
").append(f).html(),v="data:image/svg+xml;base64,"+window.btoa(unescape(encodeURIComponent(h))),y=angular.element("").attr({src:v,title:e.data});"true"===o.round&&y.css("border-radius","50%"),"true"===o.border&&y.css("border",o.borderStyle),o.class&&y.addClass(o.class),"true"===o.dynamic?(t.empty(),t.append(y)):t.replaceWith(y)}}}}),angular.module("bit.directives").directive("masterPassword",["cryptoService","authService",function(e,t){return{require:"ngModel",restrict:"A",link:function(n,o,r,a){t.getUserProfile().then(function(t){a.$parsers.unshift(function(n){if(n)return e.makeKey(n,t.email).then(function(t){var o=t.keyB64===e.getKey().keyB64;return a.$setValidity("masterPassword",o),o?n:void 0})}),a.$formatters.unshift(function(n){if(n)return e.makeKey(n,t.email).then(function(t){var o=t.keyB64===e.getKey().keyB64;return a.$setValidity("masterPassword",o),n})})})}}}]),angular.module("bit.directives").directive("pageTitle",["$rootScope","$timeout","appSettings",function(e,t,n){return{link:function(n,o){e.$on("$stateChangeStart",function(e,n,r,a,i){var s="bitwarden Web Vault";n.data&&n.data.pageTitle&&(s=n.data.pageTitle+" - "+s),t(function(){o.text(s)})})}}}]),angular.module("bit.directives").directive("passwordMeter",function(){return{template:'
{{value}}%
',restrict:"A",scope:{password:"=passwordMeter",username:"=passwordMeterUsername",outerClass:"@?"},link:function(e){var t=function(e){e.value=function(e,t){if(!t||t===e)return 0;var n=t.length;return e&&""!==e&&(-1!==e.indexOf(t)&&(n-=15),-1!==t.indexOf(e)&&(n-=e.length)),t.length>0&&t.length<=4?n+=t.length:t.length>=5&&t.length<=7?n+=6:t.length>=8&&t.length<=15?n+=12:t.length>=16&&(n+=18),t.match(/[a-z]/)&&(n+=1),t.match(/[A-Z]/)&&(n+=5),t.match(/\d/)&&(n+=5),t.match(/.*\d.*\d.*\d/)&&(n+=5),t.match(/[!,@,#,$,%,^,&,*,?,_,~]/)&&(n+=5),t.match(/.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~]/)&&(n+=5),t.match(/(?=.*[a-z])(?=.*[A-Z])/)&&(n+=2),t.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)&&(n+=2),t.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&,*,?,_,~])/)&&(n+=2),n=Math.round(2*n),Math.max(0,Math.min(100,n))}(e.username,e.password),e.valueClass=function(e){switch(Math.round(e/33)){case 0:case 1:return"danger";case 2:return"warning";case 3:return"success"}}(e.value)};e.$watch("password",function(){t(e)}),e.$watch("username",function(){t(e)})}}}),angular.module("bit.directives").directive("passwordViewer",function(){return{restrict:"A",link:function(e,t,n){var o=n.passwordViewer;o&&(t.onclick=function(e){},t.on("click",function(e){var n=$(o);n&&"password"===n.attr("type")?(t.removeClass("fa-eye").addClass("fa-eye-slash"),n.attr("type","text")):n&&"text"===n.attr("type")&&(t.removeClass("fa-eye-slash").addClass("fa-eye"),n.attr("type","password"))}))}}}),angular.module("bit.directives").directive("stopClick",function(){return function(e,t,n){$(t).click(function(e){e.preventDefault()})}}),angular.module("bit.directives").directive("stopProp",function(){return function(e,t,n){$(t).click(function(e){e.stopPropagation()})}}),angular.module("bit.directives").directive("totp",["$timeout","$q",function(e,t){return{template:'
{{sec}}{{codeFormatted}}
',restrict:"A",scope:{key:"=totp"},link:function(n){var o=null,r=new function(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",n=function(e,t,n){return t+1>=e.length&&(e=Array(t+1-e.length).join(n)+e),e},o=function(e){return parseInt(e,16)},r=function(e){for(var t=new Uint8Array(e.length/2),n=0;n>>4).toString(16)),n.push((15&t[o]).toString(16));return n.join("")}(e)}).catch(function(e){return null})};this.getCode=function(e){var s,l=Math.round((new Date).getTime()/1e3),c=n((s=Math.floor(l/30),(s<15.5?"0":"")+Math.round(s).toString(16)),16,"0"),u=r(c),d=a(e);return d.length&&u.length?i(d,u).then(function(e){if(!e)return null;var t=o(e.substring(e.length-1)),n=(o(e.substr(2*t,8))&o("7fffffff"))+"";return n=n.substr(n.length-6,6)}):t(function(e,t){e(null)})}},a=function(t){r.getCode(t.key).then(function(n){e(function(){n?(t.codeFormatted=n.substring(0,3)+" "+n.substring(3),t.code=n):(t.code=null,o&&clearInterval(o))})})},i=function(t){e(function(){var e=Math.round((new Date).getTime()/1e3)%30,n=30-e;t.sec=n,t.dash=(2.62*e).toFixed(2),t.low=n<=7,0===e&&a(t)})};n.$watch("key",function(){if(!n.key)return n.code=null,void(o&&clearInterval(o));a(n),i(n),o&&clearInterval(o),o=setInterval(function(){i(n)},1e3)}),n.$on("$destroy",function(){o&&clearInterval(o)}),n.clipboardError=function(e){alert("Your web browser does not support easy clipboard copying.")}}}}]),angular.module("bit.global").controller("appsController",["$scope","$state",function(e,t){}]),angular.module("bit.global").controller("mainController",["$scope","$state","authService","appSettings","toastr","$window","$document","cryptoService","$uibModal","apiService",function(e,t,n,o,r,a,i,s,l,c){var u=this;u.skinClass=o.selfHosted?"skin-blue-light":"skin-blue",u.bodyClass="",u.usingControlSidebar=u.openControlSidebar=!1,u.searchVaultText=null,u.version=o.version,u.outdatedBrowser=-1!==a.navigator.userAgent.indexOf("MSIE")||-1!==a.navigator.userAgent.indexOf("SamsungBrowser"),e.currentYear=(new Date).getFullYear(),e.$on("$viewContentLoaded",function(){n.getUserProfile().then(function(e){u.userProfile=e}),$.AdminLTE&&($.AdminLTE.layout&&($.AdminLTE.layout.fix(),$.AdminLTE.layout.fixSidebar()),$.AdminLTE.pushMenu&&$.AdminLTE.pushMenu.expandOnHover(),i.off("click",".sidebar li a"))}),e.$on("$stateChangeSuccess",function(e,t,n,o,r){u.usingEncKey=!!s.getEncKey(),u.searchVaultText=null,t.data.bodyClass?u.bodyClass=t.data.bodyClass:(u.bodyClass="",u.usingControlSidebar=!!t.data.controlSidebar,u.openControlSidebar=u.usingControlSidebar&&i.width()>768)}),e.$on("setSearchVaultText",function(e,t){u.searchVaultText=t}),e.addCipher=function(){e.$broadcast("vaultAddCipher")},e.addFolder=function(){e.$broadcast("vaultAddFolder")},e.addOrganizationCipher=function(){e.$broadcast("organizationVaultAddCipher")},e.addOrganizationCollection=function(){e.$broadcast("organizationCollectionsAdd")},e.inviteOrganizationUser=function(){e.$broadcast("organizationPeopleInvite")},e.addOrganizationGroup=function(){e.$broadcast("organizationGroupsAdd")},e.updateKey=function(){l.open({animation:!0,templateUrl:"app/settings/views/settingsUpdateKey.html",controller:"settingsUpdateKeyController"})},e.verifyEmail=function(){e.sendingVerify||(e.sendingVerify=!0,c.accounts.verifyEmail({},null).$promise.then(function(){r.success("Verification email sent."),e.sendingVerify=!1,e.verifyEmailSent=!0}).catch(function(){r.success("Verification email failed."),e.sendingVerify=!1}))},e.updateBrowser=function(){a.open("https://browser-update.org/update.html","_blank")};var d,p,m,g={scrollbarWidth:function(){if(!d){var e=$("body");e.addClass("bit-position-body-scrollbar-measure"),d=a.innerWidth-e[0].clientWidth,d=isFinite(d)?d:0,e.removeClass("bit-position-body-scrollbar-measure")}return d},scrollbarInfo:function(){return{width:g.scrollbarWidth(),visible:i.height()>$(a).height()}}};$(window).on("show.bs.dropdown",function(e){var t=m=$(e.target),n=t.data("appendTo");if(!n)return!0;p=t.find(".dropdown-menu");$(n).append(p.detach());var o=t.offset(),r={display:"block",top:o.top+t.outerHeight()-("body"!==n?$(window).scrollTop():0)};if(p.hasClass("dropdown-menu-right")){var i=g.scrollbarInfo(),s=0;i.visible&&i.width&&(s=i.width),r.right=a.innerWidth-s-(o.left+t.prop("offsetWidth"))+"px",r.left="auto"}else r.left=o.left+"px",r.right="auto";p.css(r)}),$(window).on("hide.bs.dropdown",function(e){if(!p)return!0;$(e.target).append(p.detach()),p.hide(),p=null,m=null}),e.$on("removeAppendedDropdownMenu",function(e,t){if(!p&&!m)return!0;m.append(p.detach()),p.hide(),p=null,m=null})}]),angular.module("bit.global").controller("paidOrgRequiredController",["$scope","$state","$uibModalInstance","$analytics","$uibModalStack","orgId","constants","authService",function(e,t,n,o,r,a,i,s){o.eventTrack("paidOrgRequiredController",{category:"Modal"}),s.getUserProfile().then(function(t){e.admin=t.organizations[a].type!==i.orgUserType.user}),e.go=function(){e.admin&&(o.eventTrack("Get Paid Org"),t.go("backend.org.billing",{orgId:a}).then(function(){r.dismissAll()}))},e.close=function(){n.dismiss("close")}}]),angular.module("bit.global").controller("premiumRequiredController",["$scope","$state","$uibModalInstance","$analytics","$uibModalStack",function(e,t,n,o,r){o.eventTrack("premiumRequiredController",{category:"Modal"}),e.go=function(){o.eventTrack("Get Premium"),t.go("backend.user.settingsPremium").then(function(){r.dismissAll()})},e.close=function(){n.dismiss("close")}}]),angular.module("bit.global").controller("sideNavController",["$scope","$state","authService","toastr","$analytics","constants","appSettings",function(e,t,n,o,r,a,i){e.$state=t,e.params=t.params,e.orgs=[],e.name="",i.selfHosted?(e.orgIconBgColor="#ffffff",e.orgIconBorder="3px solid #a0a0a0",e.orgIconTextColor="#333333"):(e.orgIconBgColor="#2c3b41",e.orgIconBorder="3px solid #1a2226",e.orgIconTextColor="#ffffff"),n.getUserProfile().then(function(n){if(e.name=n.extended&&n.extended.name?n.extended.name:n.email,n.organizations)if(t.includes("backend.org")&&t.params.orgId in n.organizations)e.orgProfile=n.organizations[t.params.orgId];else{var o=[];for(var r in n.organizations)n.organizations.hasOwnProperty(r)&&(n.organizations[r].enabled||n.organizations[r].type<2)&&o.push(n.organizations[r]);e.orgs=o}}),e.viewOrganization=function(e){e.type!==a.orgUserType.user?(r.eventTrack("View Organization From Side Nav"),t.go("backend.org.dashboard",{orgId:e.id})):o.error("You cannot manage this organization.")},e.searchVault=function(){t.go("backend.user.vault")},e.searchOrganizationVault=function(){t.go("backend.org.vault",{orgId:t.params.orgId})},e.isOrgOwner=function(e){return e&&e.type===a.orgUserType.owner}}]),angular.module("bit.global").controller("topNavController",["$scope",function(e){e.toggleControlSidebar=function(){var e=$("body");e.hasClass("control-sidebar-open")?e.removeClass("control-sidebar-open"):e.addClass("control-sidebar-open")}}]),angular.module("bit.filters").filter("enumLabelClass",function(){return function(e,t){if("number"!=typeof e)return e.toString();var n;switch(t){case"OrgUserStatus":switch(e){case 0:n="label-default";break;case 1:n="label-warning";break;case 2:default:n="label-success"}break;default:n="label-default"}return n}}),angular.module("bit.filters").filter("enumName",function(){return function(e,t){if("number"!=typeof e)return e.toString();var n;switch(t){case"OrgUserStatus":switch(e){case 0:n="Invited";break;case 1:n="Accepted";break;case 2:default:n="Confirmed"}break;case"OrgUserType":switch(e){case 0:n="Owner";break;case 1:n="Admin";break;case 2:default:n="User"}break;default:n=e.toString()}return n}}),angular.module("bit.tools").controller("reportsBreachController",["$scope","apiService","toastr","authService",function(e,t,n,o){e.loading=!0,e.error=!1,e.breachAccounts=[],e.email=null,e.$on("$viewContentLoaded",function(){o.getUserProfile().then(function(n){return e.email=n.email,t.hibp.get({email:e.email}).$promise}).then(function(t){for(var n=[],o=0;o1;var n=0;if(e.expiration=t.Expiration,t.License,e.plan={name:t.Plan,type:t.PlanType,seats:t.Seats},e.storage=null,e&&t.MaxStorageGb&&(e.storage={currentGb:t.StorageGb||0,maxGb:t.MaxStorageGb,currentName:t.StorageName||"0 GB"},e.storage.percentage=+(e.storage.currentGb/e.storage.maxGb*100).toFixed(2)),e.subscription=null,t.Subscription&&(e.subscription={trialEndDate:t.Subscription.TrialEndDate,cancelledDate:t.Subscription.CancelledDate,status:t.Subscription.Status,cancelled:t.Subscription.Cancelled,markedForCancel:!t.Subscription.Cancelled&&t.Subscription.CancelAtEndDate}),e.nextInvoice=null,t.UpcomingInvoice&&(e.nextInvoice={date:t.UpcomingInvoice.Date,amount:t.UpcomingInvoice.Amount}),t.Subscription&&t.Subscription.Items)for(e.subscription.items=[],n=0;n=s},e.submit=function(i){var s=r.encryptCollection(i,t.params.orgId);if(e.useGroups){s.groups=[];for(var l in e.selectedGroups)if(e.selectedGroups.hasOwnProperty(l))for(var c=0;c0&&(n[0].name=t.name)})},e.users=function(e){o.open({animation:!0,templateUrl:"app/organization/views/organizationCollectionsUsers.html",controller:"organizationCollectionsUsersController",size:"lg",resolve:{collection:function(){return e}}}).result.then(function(){})},e.groups=function(e){o.open({animation:!0,templateUrl:"app/organization/views/organizationCollectionsGroups.html",controller:"organizationCollectionsGroupsController",resolve:{collection:function(){return e}}}).result.then(function(){})},e.delete=function(o){confirm("Are you sure you want to delete this collection ("+o.name+")?")&&n.collections.del({orgId:t.params.orgId,id:o.id},function(){var t=e.collections.indexOf(o);t>-1&&e.collections.splice(t,1),s.eventTrack("Deleted Collection"),i.success(o.name+" has been deleted.","Collection Deleted")},function(){i.error(o.name+" was not able to be deleted.","Error")})}}]),angular.module("bit.organization").controller("organizationCollectionsEditController",["$scope","$state","$uibModalInstance","apiService","cipherService","$analytics","id","authService",function(e,t,n,o,r,a,i,s){a.eventTrack("organizationCollectionsEditController",{category:"Modal"});var l=0;e.collection={},e.groups=[],e.selectedGroups={},e.loading=!0,e.useGroups=!1,n.opened.then(function(){return o.collections.getDetails({orgId:t.params.orgId,id:i}).$promise}).then(function(t){e.collection=r.decryptCollection(t);var n={};if(t.Groups)for(var o=0;o=l},e.submit=function(s){var l=r.encryptCollection(s,t.params.orgId);if(e.useGroups){l.groups=[];for(var c in e.selectedGroups)if(e.selectedGroups.hasOwnProperty(c))for(var u=0;u-1&&e.users.splice(t,1)},function(){s.error("Unable to remove user.","Error")})},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationDashboardController",["$scope","authService","$state","appSettings",function(e,t,n,o){e.selfHosted=o.selfHosted,e.$on("$viewContentLoaded",function(){t.getUserProfile().then(function(t){t.organizations&&(e.orgProfile=t.organizations[n.params.orgId])})}),e.goBilling=function(){n.go("backend.org.billing",{orgId:n.params.orgId})}}]),angular.module("bit.organization").controller("organizationDeleteController",["$scope","$state","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics",function(e,t,n,o,r,a,i,s){s.eventTrack("organizationDeleteController",{category:"Modal"}),e.submit=function(){e.submitPromise=r.hashPassword(e.masterPassword).then(function(e){return n.organizations.del({id:t.params.orgId},{masterPasswordHash:e}).$promise}).then(function(){return o.dismiss("cancel"),a.removeProfileOrganization(t.params.orgId),s.eventTrack("Deleted Organization"),t.go("backend.user.vault")}).then(function(){i.success("This organization and all associated data has been deleted.","Organization Deleted")})},e.close=function(){o.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationEventsController",["$scope","$state","apiService","$uibModal","$filter","toastr","$analytics","constants","eventService","$compile","$sce",function(e,t,n,o,r,a,i,s,l,c,u){e.events=[],e.orgUsers=[],e.loading=!0,e.continuationToken=null;var d=l.getDefaultDateFilters();e.filterStart=d.start,e.filterEnd=d.end,e.$on("$viewContentLoaded",function(){n.organizationUsers.list({orgId:t.params.orgId}).$promise.then(function(t){var n=[];for(p=0;p"+r.message+"")(e);n.push({message:u.trustAsHtml(a[0].outerHTML),appIcon:r.appIcon,appName:r.appName,userId:o,userName:o?m[o]||"-":"-",date:t.Data[p].Date,ip:t.Data[p].IpAddress})}e.events&&e.events.length>0?e.events=e.events.concat(n):e.events=n,e.loading=!1});alert(r.error)}}}]),angular.module("bit.organization").controller("organizationGroupsAddController",["$scope","$state","$uibModalInstance","apiService","cipherService","$analytics",function(e,t,n,o,r,a){a.eventTrack("organizationGroupsAddController",{category:"Modal"}),e.collections=[],e.selectedCollections={},e.loading=!0,n.opened.then(function(){return o.collections.listOrganization({orgId:t.params.orgId}).$promise}).then(function(n){e.collections=r.decryptCollections(n.Data,t.params.orgId,!0),e.loading=!1}),e.toggleCollectionSelectionAll=function(t){var n={};if(t.target.checked)for(var o=0;o0&&(n[0].name=t.name)})},e.users=function(e){o.open({animation:!0,templateUrl:"app/organization/views/organizationGroupsUsers.html",controller:"organizationGroupsUsersController",size:"lg",resolve:{group:function(){return e}}}).result.then(function(){})},e.delete=function(o){confirm("Are you sure you want to delete this group ("+o.name+")?")&&n.groups.del({orgId:t.params.orgId,id:o.id},function(){var t=e.groups.indexOf(o);t>-1&&e.groups.splice(t,1),i.eventTrack("Deleted Group"),a.success(o.name+" has been deleted.","Group Deleted")},function(){a.error(o.name+" was not able to be deleted.","Error")})}}]),angular.module("bit.organization").controller("organizationGroupsEditController",["$scope","$state","$uibModalInstance","apiService","cipherService","$analytics","id",function(e,t,n,o,r,a,i){a.eventTrack("organizationGroupsEditController",{category:"Modal"}),e.collections=[],e.selectedCollections={},e.loading=!0,n.opened.then(function(){return o.groups.getDetails({orgId:t.params.orgId,id:i}).$promise}).then(function(n){e.group={id:i,name:n.Name,externalId:n.ExternalId,accessAll:n.AccessAll};var r={};if(n.Collections)for(var a=0;a-1&&e.users.splice(t,1)},function(){i.error("Unable to remove user.","Error")})},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationPeopleController",["$scope","$state","$uibModal","cryptoService","apiService","authService","toastr","$analytics","$filter","$uibModalStack",function(e,t,n,o,r,a,i,s,l,c){e.users=[],e.useGroups=!1,e.useEvents=!1,e.$on("$viewContentLoaded",function(){u(),a.getUserProfile().then(function(n){if(n.organizations){var o=n.organizations[t.params.orgId];e.useGroups=!!o.useGroups,e.useEvents=!!o.useEvents}})}),e.reinvite=function(e){r.organizationUsers.reinvite({orgId:t.params.orgId,id:e.id},null,function(){s.eventTrack("Reinvited User"),i.success(e.email+" has been invited again.","User Invited")},function(){i.error("Unable to invite user.","Error")})},e.delete=function(n){confirm("Are you sure you want to remove this user ("+n.email+")?")&&r.organizationUsers.del({orgId:t.params.orgId,id:n.id},null,function(){s.eventTrack("Deleted User"),i.success(n.email+" has been removed.","User Removed");var t=e.users.indexOf(n);t>-1&&e.users.splice(t,1)},function(){i.error("Unable to remove user.","Error")})},e.confirm=function(e){r.users.getPublicKey({id:e.userId},function(n){var a=o.getOrgKey(t.params.orgId);if(a){var l=o.rsaEncrypt(a.key,n.PublicKey);r.organizationUsers.confirm({orgId:t.params.orgId,id:e.id},{key:l},function(){e.status=2,s.eventTrack("Confirmed User"),i.success(e.email+" has been confirmed.","User Confirmed")},function(){i.error("Unable to confirm user.","Error")})}else i.error("Unable to confirm user.","Error")},function(){i.error("Unable to confirm user.","Error")})},e.$on("organizationPeopleInvite",function(t,n){e.invite()}),e.invite=function(){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleInvite.html",controller:"organizationPeopleInviteController"}).result.then(function(){u()})},e.edit=function(e){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleEdit.html",controller:"organizationPeopleEditController",resolve:{orgUser:function(){return e}}}).result.then(function(){u()})},e.groups=function(e){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleGroups.html",controller:"organizationPeopleGroupsController",resolve:{orgUser:function(){return e}}}).result.then(function(){})},e.events=function(e){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleEvents.html",controller:"organizationPeopleEventsController",resolve:{orgUser:function(){return e},orgId:function(){return t.params.orgId}}})};function u(){r.organizationUsers.list({orgId:t.params.orgId},function(n){for(var o=[],r=0;r"+r.message+"")(e);n.push({message:l.trustAsHtml(i[0].outerHTML),appIcon:r.appIcon,appName:r.appName,date:t.Data[o].Date,ip:t.Data[o].IpAddress})}e.events&&e.events.length>0?e.events=e.events.concat(n):e.events=n,e.loading=!1});alert(r.error)}}e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationPeopleGroupsController",["$scope","$state","$uibModalInstance","apiService","orgUser","$analytics",function(e,t,n,o,r,a){a.eventTrack("organizationPeopleGroupsController",{category:"Modal"}),e.loading=!0,e.groups=[],e.selectedGroups={},e.orgUser=r,n.opened.then(function(){return o.groups.listOrganization({orgId:t.params.orgId}).$promise}).then(function(n){for(var a=[],i=0;i=t?e:new Array(t-e.length+1).join(n)+e}}]),angular.module("bit.organization").controller("organizationSettingsImportController",["$scope","$state","apiService","$uibModalInstance","cipherService","toastr","importService","$analytics","$sce","validationService","cryptoService",function(e,t,n,o,r,a,i,s,l,c,u){s.eventTrack("organizationSettingsImportController",{category:"Modal"}),e.model={source:""},e.source={},e.splitFeatured=!1,e.options=[{id:"bitwardencsv",name:"bitwarden (csv)",featured:!0,sort:1,instructions:l.trustAsHtml('Export using the web vault (vault.bitwarden.com). Log into the web vault and navigate to your organization\'s admin area. Then to go "Settings" > "Tools" > "Export".')},{id:"lastpass",name:"LastPass (csv)",featured:!0,sort:2,instructions:l.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-lastpass/')}],e.setSource=function(){for(var t=0;t-1&&e.cipher.fields.splice(n,1)},e.clipboardSuccess=function(e){e.clearSelection(),d(e)},e.clipboardError=function(e,t){t&&d(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")};function d(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.close=function(){n.dismiss("close")},e.showUpgrade=function(){c.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return l}}})}}]),angular.module("bit.organization").controller("organizationVaultAttachmentsController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","cipherId","$analytics","validationService","toastr","$timeout",function(e,t,n,o,r,a,i,s,l,c){i.eventTrack("organizationVaultAttachmentsController",{category:"Modal"}),e.cipher={},e.loading=!0,e.isPremium=!0,e.canUseAttachments=!0;var u=!1;t.ciphers.getAdmin({id:a},function(t){e.cipher=r.decryptCipher(t),e.loading=!1},function(){e.loading=!1}),e.save=function(c){var d=document.getElementById("file").files;if(d&&d.length){var p=o.getOrgKey(e.cipher.organizationId);e.savePromise=r.encryptAttachmentFile(p,d[0]).then(function(e){var n=new FormData,o=new Blob([e.data],{type:"application/octet-stream"});return n.append("data",o,e.fileName),t.ciphers.postAttachment({id:a},n).$promise}).then(function(e){i.eventTrack("Added Attachment"),l.success("The attachment has been added."),u=!0,n.close(!0)},function(e){var t=s.parseErrors(e);l.error(t.length?t[0]:"An error occurred.")})}else s.addError(c,"file","Select a file.",!0)},e.download=function(t){t.loading=!0;var n=o.getOrgKey(e.cipher.organizationId);r.downloadAndDecryptAttachment(n,t,!0).then(function(e){c(function(){t.loading=!1})},function(){c(function(){t.loading=!1})})},e.remove=function(n){confirm("Are you sure you want to delete this attachment ("+n.fileName+")?")&&(n.loading=!0,t.ciphers.delAttachment({id:a,attachmentId:n.id}).$promise.then(function(){n.loading=!1,i.eventTrack("Deleted Organization Attachment");var t=e.cipher.attachments.indexOf(n);t>-1&&e.cipher.attachments.splice(t,1)},function(){l.error("Cannot delete attachment."),n.loading=!1}))},e.close=function(){n.dismiss("cancel")},e.$on("modal.closing",function(t,o,r){u||(t.preventDefault(),u=!0,n.close(!!e.cipher.attachments&&e.cipher.attachments.length>0))})}]),angular.module("bit.organization").controller("organizationVaultCipherCollectionsController",["$scope","apiService","$uibModalInstance","cipherService","cipher","$analytics","collections",function(e,t,n,o,r,a,i){a.eventTrack("organizationVaultCipherCollectionsController",{category:"Modal"}),e.cipher={},e.collections=[],e.selectedCollections={},n.opened.then(function(){for(var t=[],n=0;n0?e.events=e.events.concat(n):e.events=n,e.loading=!1});alert(r.error)}}e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationVaultController",["$scope","apiService","cipherService","$analytics","$q","$state","$localStorage","$uibModal","$filter","authService","$uibModalStack",function(e,t,n,o,r,a,i,s,l,c,u){e.ciphers=[],e.collections=[],e.loading=!0,e.useEvents=!1,e.$on("$viewContentLoaded",function(){c.getUserProfile().then(function(t){if(t.organizations){var n=t.organizations[a.params.orgId];e.useEvents=!!n.useEvents}});var o=t.collections.listOrganization({orgId:a.params.orgId},function(t){for(var o=([{id:null,name:"Unassigned",collapsed:i.collapsedOrgCollections&&"unassigned"in i.collapsedOrgCollections}]),r=0;r-1:null===e.id}},e.collectionSort=function(e){return e.id?e.name.toLowerCase():"î º"},e.collapseExpand=function(e){i.collapsedOrgCollections||(i.collapsedOrgCollections={});var t=e.id||"unassigned";t in i.collapsedOrgCollections?delete i.collapsedOrgCollections[t]:i.collapsedOrgCollections[t]=!0},e.editCipher=function(t){s.open({animation:!0,templateUrl:"app/vault/views/vaultEditCipher.html",controller:"organizationVaultEditCipherController",resolve:{cipherId:function(){return t.id},orgId:function(){return a.params.orgId}}}).result.then(function(n){var o;"edit"===n.action?(o=e.ciphers.indexOf(t))>-1&&(n.data.collectionIds=e.ciphers[o].collectionIds,e.ciphers[o]=n.data):"delete"===n.action&&(o=e.ciphers.indexOf(t))>-1&&e.ciphers.splice(o,1)})},e.$on("organizationVaultAddCipher",function(t,n){e.addCipher()}),e.addCipher=function(){s.open({animation:!0,templateUrl:"app/vault/views/vaultAddCipher.html",controller:"organizationVaultAddCipherController",resolve:{orgId:function(){return a.params.orgId}}}).result.then(function(t){e.ciphers.push(t)})},e.editCollections=function(t){s.open({animation:!0,templateUrl:"app/organization/views/organizationVaultCipherCollections.html",controller:"organizationVaultCipherCollectionsController",resolve:{cipher:function(){return t},collections:function(){return e.collections}}}).result.then(function(e){e.collectionIds&&(t.collectionIds=e.collectionIds)})},e.viewEvents=function(e){s.open({animation:!0,templateUrl:"app/organization/views/organizationVaultCipherEvents.html",controller:"organizationVaultCipherEventsController",resolve:{cipher:function(){return e}}})},e.attachments=function(e){c.getUserProfile().then(function(t){return!!t.organizations[e.organizationId].maxStorageGb}).then(function(t){if(t){s.open({animation:!0,templateUrl:"app/vault/views/vaultAttachments.html",controller:"organizationVaultAttachmentsController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){e.hasAttachments=t})}else s.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return e.organizationId}}})})},e.removeCipher=function(e,n){if(confirm("Are you sure you want to remove this item ("+e.name+") from the collection ("+n.name+") ?")){for(var r={collectionIds:[]},a=0;a-1&&e.ciphers.splice(t,1)})}}]),angular.module("bit.organization").controller("organizationVaultEditCipherController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","passwordService","cipherId","$analytics","orgId","$uibModal","constants",function(e,t,n,o,r,a,i,s,l,c,u){s.eventTrack("organizationVaultEditCipherController",{category:"Modal"}),e.cipher={},e.hideFolders=e.hideFavorite=e.fromOrg=!0,e.constants=u,t.ciphers.getAdmin({id:i},function(t){e.cipher=r.decryptCipher(t),e.useTotp=e.cipher.organizationUseTotp}),e.save=function(o){var a=r.encryptCipher(o,e.cipher.type);e.savePromise=t.ciphers.putAdmin({id:i},a,function(e){s.eventTrack("Edited Organization Cipher");var t=r.decryptCipherPreview(e);n.close({action:"edit",data:t})}).$promise},e.generatePassword=function(){e.cipher.login.password&&!confirm("Are you sure you want to overwrite the current password?")||(s.eventTrack("Generated Password From Edit"),e.cipher.login.password=a.generatePassword({length:14,special:!0}))},e.addField=function(){e.cipher.login.fields||(e.cipher.login.fields=[]),e.cipher.fields.push({type:u.fieldType.text.toString(),name:null,value:null})},e.removeField=function(t){var n=e.cipher.fields.indexOf(t);n>-1&&e.cipher.fields.splice(n,1)},e.clipboardSuccess=function(e){e.clearSelection(),d(e)},e.clipboardError=function(e,t){t&&d(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")};function d(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.delete=function(){confirm("Are you sure you want to delete this item ("+e.cipher.name+")?")&&t.ciphers.delAdmin({id:e.cipher.id},function(){s.eventTrack("Deleted Organization Cipher From Edit"),n.close({action:"delete",data:e.cipher.id})})},e.close=function(){n.dismiss("cancel")},e.showUpgrade=function(){c.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return l}}})}}]),angular.module("bit.services").factory("apiService",["$resource","tokenService","appSettings","$httpParamSerializer","utilsService",function(e,t,n,o,r){var a={},i=n.apiUri,s=n.identityUri;a.folders=e(i+"/folders/:id",{},{get:{method:"GET",params:{id:"@id"}},list:{method:"GET",params:{}},post:{method:"POST",params:{}},put:{method:"POST",params:{id:"@id"}},del:{url:i+"/folders/:id/delete",method:"POST",params:{id:"@id"}}}),a.ciphers=e(i+"/ciphers/:id",{},{get:{method:"GET",params:{id:"@id"}},getAdmin:{url:i+"/ciphers/:id/admin",method:"GET",params:{id:"@id"}},getDetails:{url:i+"/ciphers/:id/details",method:"GET",params:{id:"@id"}},list:{method:"GET",params:{}},listDetails:{url:i+"/ciphers/details",method:"GET",params:{}},listOrganizationDetails:{url:i+"/ciphers/organization-details",method:"GET",params:{}},post:{method:"POST",params:{}},postAdmin:{url:i+"/ciphers/admin",method:"POST",params:{}},put:{method:"POST",params:{id:"@id"}},putAdmin:{url:i+"/ciphers/:id/admin",method:"POST",params:{id:"@id"}},import:{url:i+"/ciphers/import",method:"POST",params:{}},importOrg:{url:i+"/ciphers/import-organization?organizationId=:orgId",method:"POST",params:{orgId:"@orgId"}},putPartial:{url:i+"/ciphers/:id/partial",method:"POST",params:{id:"@id"}},putShare:{url:i+"/ciphers/:id/share",method:"POST",params:{id:"@id"}},putCollections:{url:i+"/ciphers/:id/collections",method:"POST",params:{id:"@id"}},putCollectionsAdmin:{url:i+"/ciphers/:id/collections-admin",method:"POST",params:{id:"@id"}},del:{url:i+"/ciphers/:id/delete",method:"POST",params:{id:"@id"}},delAdmin:{url:i+"/ciphers/:id/delete-admin",method:"POST",params:{id:"@id"}},delMany:{url:i+"/ciphers/delete",method:"POST"},moveMany:{url:i+"/ciphers/move",method:"POST"},purge:{url:i+"/ciphers/purge",method:"POST"},postAttachment:{url:i+"/ciphers/:id/attachment",method:"POST",headers:{"Content-Type":void 0},params:{id:"@id"}},postShareAttachment:{url:i+"/ciphers/:id/attachment/:attachmentId/share?organizationId=:orgId",method:"POST",headers:{"Content-Type":void 0},params:{id:"@id",attachmentId:"@attachmentId",orgId:"@orgId"}},delAttachment:{url:i+"/ciphers/:id/attachment/:attachmentId/delete",method:"POST",params:{id:"@id",attachmentId:"@attachmentId"}}}),a.organizations=e(i+"/organizations/:id",{},{get:{method:"GET",params:{id:"@id"}},getBilling:{url:i+"/organizations/:id/billing",method:"GET",params:{id:"@id"}},getLicense:{url:i+"/organizations/:id/license",method:"GET",params:{id:"@id"}},list:{method:"GET",params:{}},post:{method:"POST",params:{}},put:{method:"POST",params:{id:"@id"}},putPayment:{url:i+"/organizations/:id/payment",method:"POST",params:{id:"@id"}},putSeat:{url:i+"/organizations/:id/seat",method:"POST",params:{id:"@id"}},putStorage:{url:i+"/organizations/:id/storage",method:"POST",params:{id:"@id"}},putUpgrade:{url:i+"/organizations/:id/upgrade",method:"POST",params:{id:"@id"}},putCancel:{url:i+"/organizations/:id/cancel",method:"POST",params:{id:"@id"}},putReinstate:{url:i+"/organizations/:id/reinstate",method:"POST",params:{id:"@id"}},postLeave:{url:i+"/organizations/:id/leave",method:"POST",params:{id:"@id"}},postVerifyBank:{url:i+"/organizations/:id/verify-bank",method:"POST",params:{id:"@id"}},del:{url:i+"/organizations/:id/delete",method:"POST",params:{id:"@id"}},postLicense:{url:i+"/organizations/license",method:"POST",headers:{"Content-Type":void 0}},putLicense:{url:i+"/organizations/:id/license",method:"POST",headers:{"Content-Type":void 0}}}),a.organizationUsers=e(i+"/organizations/:orgId/users/:id",{},{get:{method:"GET",params:{id:"@id",orgId:"@orgId"}},list:{method:"GET",params:{orgId:"@orgId"}},listGroups:{url:i+"/organizations/:orgId/users/:id/groups",method:"GET",params:{id:"@id",orgId:"@orgId"},isArray:!0},invite:{url:i+"/organizations/:orgId/users/invite",method:"POST",params:{orgId:"@orgId"}},reinvite:{url:i+"/organizations/:orgId/users/:id/reinvite",method:"POST",params:{id:"@id",orgId:"@orgId"}},accept:{url:i+"/organizations/:orgId/users/:id/accept",method:"POST",params:{id:"@id",orgId:"@orgId"}},confirm:{url:i+"/organizations/:orgId/users/:id/confirm",method:"POST",params:{id:"@id",orgId:"@orgId"}},put:{method:"POST",params:{id:"@id",orgId:"@orgId"}},putGroups:{url:i+"/organizations/:orgId/users/:id/groups",method:"POST",params:{id:"@id",orgId:"@orgId"}},del:{url:i+"/organizations/:orgId/users/:id/delete",method:"POST",params:{id:"@id",orgId:"@orgId"}}}),a.collections=e(i+"/organizations/:orgId/collections/:id",{},{get:{method:"GET",params:{id:"@id",orgId:"@orgId"}},getDetails:{url:i+"/organizations/:orgId/collections/:id/details",method:"GET",params:{id:"@id",orgId:"@orgId"}},listMe:{url:i+"/collections?writeOnly=:writeOnly",method:"GET",params:{writeOnly:"@writeOnly"}},listOrganization:{method:"GET",params:{orgId:"@orgId"}},listUsers:{url:i+"/organizations/:orgId/collections/:id/users",method:"GET",params:{id:"@id",orgId:"@orgId"}},post:{method:"POST",params:{orgId:"@orgId"}},put:{method:"POST",params:{id:"@id",orgId:"@orgId"}},del:{url:i+"/organizations/:orgId/collections/:id/delete",method:"POST",params:{id:"@id",orgId:"@orgId"}},delUser:{url:i+"/organizations/:orgId/collections/:id/delete-user/:orgUserId",method:"POST",params:{id:"@id",orgId:"@orgId",orgUserId:"@orgUserId"}}}),a.groups=e(i+"/organizations/:orgId/groups/:id",{},{get:{method:"GET",params:{id:"@id",orgId:"@orgId"}},getDetails:{url:i+"/organizations/:orgId/groups/:id/details",method:"GET",params:{id:"@id",orgId:"@orgId"}},listOrganization:{method:"GET",params:{orgId:"@orgId"}},listUsers:{url:i+"/organizations/:orgId/groups/:id/users",method:"GET",params:{id:"@id",orgId:"@orgId"}},post:{method:"POST",params:{orgId:"@orgId"}},put:{method:"POST",params:{id:"@id",orgId:"@orgId"}},del:{url:i+"/organizations/:orgId/groups/:id/delete",method:"POST",params:{id:"@id",orgId:"@orgId"}},delUser:{url:i+"/organizations/:orgId/groups/:id/delete-user/:orgUserId",method:"POST",params:{id:"@id",orgId:"@orgId",orgUserId:"@orgUserId"}}}),a.accounts=e(i+"/accounts",{},{register:{url:i+"/accounts/register",method:"POST",params:{}},emailToken:{url:i+"/accounts/email-token",method:"POST",params:{}},email:{url:i+"/accounts/email",method:"POST",params:{}},verifyEmailToken:{url:i+"/accounts/verify-email-token",method:"POST",params:{}},verifyEmail:{url:i+"/accounts/verify-email",method:"POST",params:{}},postDeleteRecoverToken:{url:i+"/accounts/delete-recover-token",method:"POST",params:{}},postDeleteRecover:{url:i+"/accounts/delete-recover",method:"POST",params:{}},putPassword:{url:i+"/accounts/password",method:"POST",params:{}},getProfile:{url:i+"/accounts/profile",method:"GET",params:{}},putProfile:{url:i+"/accounts/profile",method:"POST",params:{}},getDomains:{url:i+"/accounts/domains",method:"GET",params:{}},putDomains:{url:i+"/accounts/domains",method:"POST",params:{}},postPasswordHint:{url:i+"/accounts/password-hint",method:"POST",params:{}},putSecurityStamp:{url:i+"/accounts/security-stamp",method:"POST",params:{}},putKeys:{url:i+"/accounts/keys",method:"POST",params:{}},putKey:{url:i+"/accounts/key",method:"POST",params:{}},import:{url:i+"/accounts/import",method:"POST",params:{}},postDelete:{url:i+"/accounts/delete",method:"POST",params:{}},putStorage:{url:i+"/accounts/storage",method:"POST",params:{}},putPayment:{url:i+"/accounts/payment",method:"POST",params:{}},putCancelPremium:{url:i+"/accounts/cancel-premium",method:"POST",params:{}},putReinstatePremium:{url:i+"/accounts/reinstate-premium",method:"POST",params:{}},getBilling:{url:i+"/accounts/billing",method:"GET",params:{}},postPremium:{url:i+"/accounts/premium",method:"POST",headers:{"Content-Type":void 0}},putLicense:{url:i+"/accounts/license",method:"POST",headers:{"Content-Type":void 0}}}),a.twoFactor=e(i+"/two-factor",{},{list:{method:"GET",params:{}},getEmail:{url:i+"/two-factor/get-email",method:"POST",params:{}},getU2f:{url:i+"/two-factor/get-u2f",method:"POST",params:{}},getDuo:{url:i+"/two-factor/get-duo",method:"POST",params:{}},getAuthenticator:{url:i+"/two-factor/get-authenticator",method:"POST",params:{}},getYubi:{url:i+"/two-factor/get-yubikey",method:"POST",params:{}},sendEmail:{url:i+"/two-factor/send-email",method:"POST",params:{}},sendEmailLogin:{url:i+"/two-factor/send-email-login",method:"POST",params:{}},putEmail:{url:i+"/two-factor/email",method:"POST",params:{}},putU2f:{url:i+"/two-factor/u2f",method:"POST",params:{}},putAuthenticator:{url:i+"/two-factor/authenticator",method:"POST",params:{}},putDuo:{url:i+"/two-factor/duo",method:"POST",params:{}},putYubi:{url:i+"/two-factor/yubikey",method:"POST",params:{}},disable:{url:i+"/two-factor/disable",method:"POST",params:{}},recover:{url:i+"/two-factor/recover",method:"POST",params:{}},getRecover:{url:i+"/two-factor/get-recover",method:"POST",params:{}}}),a.settings=e(i+"/settings",{},{getDomains:{url:i+"/settings/domains",method:"GET",params:{}},putDomains:{url:i+"/settings/domains",method:"POST",params:{}}}),a.users=e(i+"/users/:id",{},{getPublicKey:{url:i+"/users/:id/public-key",method:"GET",params:{id:"@id"}}}),a.events=e(i+"/events",{},{list:{method:"GET",params:{}},listOrganization:{url:i+"/organizations/:orgId/events",method:"GET",params:{id:"@orgId"}},listCipher:{url:i+"/ciphers/:id/events",method:"GET",params:{id:"@id"}},listOrganizationUser:{url:i+"/organizations/:orgId/users/:id/events",method:"GET",params:{orgId:"@orgId",id:"@id"}}}),a.identity=e(s+"/connect",{},{token:{url:s+"/connect/token",method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded; charset=utf-8","Device-Type":r.getDeviceType()},transformRequest:function(e){return o(e)},skipAuthorization:!0,params:{}}}),a.hibp=e("https://haveibeenpwned.com/api/v2/breachedaccount/:email",{},{get:{method:"GET",params:{email:"@email"},isArray:!0}});return a}]),angular.module("bit.services").factory("authService",["cryptoService","apiService","tokenService","$q","jwtHelper","$rootScope","constants",function(e,t,n,o,r,a,i){var s={},l=null;s.logIn=function(r,a,l,c,u){r=r.toLowerCase();var d,p=o.defer();return e.makeKeyAndHash(r,a).then(function(e){d=e;var o={username:r,password:e.hash,grant_type:"password",scope:"api offline_access",client_id:"web"};return l&&void 0!==c&&null!==c?(u=u||!1!==u,o.twoFactorToken=l,o.twoFactorProvider=c,o.twoFactorRemember=u?"1":"0"):n.getTwoFactorToken(r)&&(o.twoFactorToken=n.getTwoFactorToken(r),o.twoFactorProvider=i.twoFactorProvider.remember,o.twoFactorRemember="0"),t.identity.token(o).$promise}).then(function(t){if(t&&t.access_token)return n.setToken(t.access_token),n.setRefreshToken(t.refresh_token),e.setKey(d.key),t.TwoFactorToken&&n.setTwoFactorToken(t.TwoFactorToken,r),t.Key&&e.setEncKey(t.Key,d.key),t.PrivateKey?(e.setPrivateKey(t.PrivateKey),!0):e.makeKeyPair()}).then(function(n){if(!0!==n)return e.setPrivateKey(n.privateKeyEnc),t.accounts.putKeys({publicKey:n.publicKey,encryptedPrivateKey:n.privateKeyEnc}).$promise}).then(function(){return s.setUserProfile()}).then(function(){p.resolve()},function(e){s.logOut(),400===e.status&&e.data.TwoFactorProviders2&&Object.keys(e.data.TwoFactorProviders2).length?(n.clearTwoFactorToken(r),p.resolve(e.data.TwoFactorProviders2)):p.reject(e)}),p.promise},s.logOut=function(){n.clearTokens(),e.clearKeys(),a.vaultGroupings=a.vaultCiphers=null,l=null},s.getUserProfile=function(){if(!l)return s.setUserProfile();var e=o.defer();return e.resolve(l),e.promise};var c=null;return s.setUserProfile=function(){if(c&&0===c.promise.$$state.status)return c.promise;c=o.defer();return n.getToken()?(t.accounts.getProfile({},function(t){if(l={id:t.Id,email:t.Email,emailVerified:t.EmailVerified,premium:t.Premium,extended:{name:t.Name,twoFactorEnabled:t.TwoFactorEnabled,culture:t.Culture}},t.Organizations){for(var n={},o=0;o0,meta:{},icon:null},i=t.Data;if(i){switch(o.name=s.decryptProperty(i.Name,n,!1,!0),o.type){case r.cipherType.login:o.subTitle=s.decryptProperty(i.Username,n,!0,!0),o.meta.password=s.decryptProperty(i.Password,n,!0,!0),o.meta.uri=s.decryptProperty(i.Uri,n,!0,!0),function(e,t,n){if(!s.disableWebsiteIcons&&t){var o=t,r=!1;if(0===o.indexOf("androidapp://")?e.icon="fa-android":0===o.indexOf("iosapp://")?e.icon="fa-apple":-1===o.indexOf("://")&&o.indexOf(".")>-1?(o="http://"+o,r=!0):r=0===o.indexOf("http")&&o.indexOf(".")>-1,n&&r)try{var i=new URL(o);e.meta.image=a.iconsUri+"/"+i.hostname+"/icon.png"}catch(e){}}e.icon||(e.icon="fa-globe")}(o,o.meta.uri,!0);break;case r.cipherType.secureNote:o.subTitle=null,o.icon="fa-sticky-note-o";break;case r.cipherType.card:o.subTitle="",o.meta.number=s.decryptProperty(i.Number,n,!0,!0);var l=s.decryptProperty(i.Brand,n,!0,!0);l&&(o.subTitle=l),o.meta.number&&o.meta.number.length>=4&&(""!==o.subTitle&&(o.subTitle+=", "),o.subTitle+="*"+o.meta.number.substr(o.meta.number.length-4)),o.icon="fa-credit-card";break;case r.cipherType.identity:var c=s.decryptProperty(i.FirstName,n,!0,!0),u=s.decryptProperty(i.LastName,n,!0,!0);o.subTitle="",c&&(o.subTitle=c),u&&(""!==o.subTitle&&(o.subTitle+=" "),o.subTitle+=u),o.icon="fa-id-card-o"}""===o.subTitle&&(o.subTitle=null)}return o};s.decryptAttachment=function(t,n){if(!n)throw"encryptedAttachment is undefined or null";return{id:n.Id,url:n.Url,fileName:e.decrypt(n.FileName,t),size:n.SizeName}},s.downloadAndDecryptAttachment=function(t,r,a){var i=n.defer(),s=new XMLHttpRequest;return s.open("GET",r.url,!0),s.responseType="arraybuffer",s.onload=function(n){s.response?e.decryptFromBytes(s.response,t).then(function(e){if(a){var t=new Blob([e]);if(o.navigator.msSaveOrOpenBlob)o.navigator.msSaveBlob(t,r.fileName);else{var n=o.document.createElement("a");n.href=o.URL.createObjectURL(t),n.download=r.fileName,o.document.body.appendChild(n),n.click(),o.document.body.removeChild(n)}}i.resolve(new Uint8Array(e))}):i.reject("No response")},s.send(null),i.promise},s.decryptFields=function(e,t){var n=[];if(t)for(var o=0;o104857600)){var a=new FileReader;return a.readAsArrayBuffer(o),a.onload=function(n){e.encryptToBytes(n.target.result,t).then(function(n){r.resolve({fileName:e.encrypt(o.name,t),data:new Uint8Array(n),size:o.size})})},a.onerror=function(e){r.reject("Error reading file.")},r.promise}r.reject("Maximum file size is 100 MB.")}},s.encryptFields=function(e,t){if(!e||!e.length)return null;for(var n=[],o=0;o2){var d=forge.util.decode64(a[2]),p=g(l+c,n.macKey,!1);if(!h(n.macKey,d,p))return console.error("MAC failed."),null}var m=forge.util.createBuffer(c),f=forge.cipher.createDecipher("AES-CBC",n.encKey);return f.start({iv:l}),f.update(m),f.finish(),"utf8"===(o=o||"utf8")?f.output.toString("utf8"):f.output.getBytes()}catch(e){throw console.error("Caught unhandled error in decrypt: "+e),e}},u.decryptFromBytes=function(e,n){try{if(!e)throw"no encBuf.";var o=new Uint8Array(e),r=o[0],a=null,i=null,s=null;switch(r){case t.encType.AesCbc128_HmacSha256_B64:case t.encType.AesCbc256_HmacSha256_B64:if(o.length<=49)return console.error("Enc type ("+r+") not valid."),null;i=w(o,1,17),s=w(o,17,49),a=w(o,49);break;case t.encType.AesCbc256_B64:if(o.length<=17)return console.error("Enc type ("+r+") not valid."),null;i=w(o,1,17),a=w(o,17);break;default:return console.error("Enc type ("+r+") not supported."),null}return function(e,t,n,o,r){if(!(r=r||u.getEncKey()||u.getKey()))throw"Encryption key unavailable.";if(e!==r.encType)throw"encType unavailable.";var a=r.getBuffers(),i=null;return p.importKey("raw",a.encKey,{name:"AES-CBC"},!1,["decrypt"]).then(function(e){if(i=e,!r.macKey||!o)return null;var s=new Uint8Array(n.byteLength+t.byteLength);return s.set(new Uint8Array(n),0),s.set(new Uint8Array(t),n.byteLength),f(s.buffer,a.macKey)}).then(function(e){return null===e?null:function(e,t,n){var o,r;return window.crypto.subtle.importKey("raw",e,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]).then(function(e){return r=e,window.crypto.subtle.sign({name:"HMAC",hash:{name:"SHA-256"}},r,t)}).then(function(e){return o=e,window.crypto.subtle.sign({name:"HMAC",hash:{name:"SHA-256"}},r,n)}).then(function(e){if(o.byteLength!==e.byteLength)return!1;for(var t=new Uint8Array(o),n=new Uint8Array(e),r=0;r1){var l=forge.util.decode64(a[1]),c=g(s,o.macKey,!1);if(!h(o.macKey,l,c))return console.error("MAC failed."),null}var d;if(r===t.encType.Rsa2048_OaepSha256_B64||r===t.encType.Rsa2048_OaepSha256_HmacSha256_B64)d=forge.md.sha256.create();else{if(r!==t.encType.Rsa2048_OaepSha1_B64&&r!==t.encType.Rsa2048_OaepSha1_HmacSha256_B64)throw"encType unavailable.";d=forge.md.sha1.create()}return n.decrypt(s,"RSA-OAEP",{md:d})};function g(e,t,n){var o=forge.hmac.create();o.start("sha256",t),o.update(e);var r=o.digest();return n?forge.util.encode64(r.getBytes()):r.getBytes()}function f(e,t){return p.importKey("raw",t,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]).then(function(t){return p.sign({name:"HMAC",hash:{name:"SHA-256"}},t,e)})}function h(e,t,n){var o=forge.hmac.create();return o.start("sha256",e),o.update(t),t=o.digest().getBytes(),o.start(null,null),o.update(n),t===(n=o.digest().getBytes())}function v(e,n,o){if(n&&(e=forge.util.decode64(e)),!e)throw"Must provide keyBytes";var r=forge.util.createBuffer(e);if(!r||0===r.length())throw"Couldn't make buffer";var a=r.length();if(null===o||void 0===o)if(32===a)o=t.encType.AesCbc256_B64;else{if(64!==a)throw"Unable to determine encType.";o=t.encType.AesCbc256_HmacSha256_B64}if(this.key=e,this.keyB64=forge.util.encode64(e),this.encType=o,o===t.encType.AesCbc256_B64&&32===a)this.encKey=e,this.macKey=null;else if(o===t.encType.AesCbc128_HmacSha256_B64&&32===a)this.encKey=r.getBytes(16),this.macKey=r.getBytes(16);else{if(o!==t.encType.AesCbc256_HmacSha256_B64||64!==a)throw"Unsupported encType/key length.";this.encKey=r.getBytes(32),this.macKey=r.getBytes(32)}}v.prototype.getBuffers=function(){if(this.keyBuf)return this.keyBuf;var e=function(e){for(var t=o.atob(e),n=new Uint8Array(t.length),r=0;r"+t+"":""+t+""}function r(e){var t=e.GroupId.substring(0,8);return'"+t+""}function a(e){var t=e.CollectionId.substring(0,8);return'"+t+""}function i(e){var t=e.OrganizationUserId.substring(0,8);return'"+t+""}return n}]),angular.module("bit.services").factory("importService",["constants",function(e){var t={};t.import=function(t,p,m,g){if(p){switch(t){case"bitwardencsv":K=p,N=m,Papa.parse(K,{header:!0,encoding:"UTF-8",complete:function(t){l(t);var n=[],o=[],r=[],a=0;angular.forEach(t.data,function(t,i){var l=n.length,c=o.length,u=t.folder&&""!==t.folder,d=u;if(u)for(a=0;ag+2&&(f.value=m[a].substr(g+2)),p.fields.push(f)}}}switch(t.type){case"login":case null:case void 0:p.type=e.cipherType.login;var h=t.login_totp||t.totp,v=t.login_uri||t.uri,y=t.login_username||t.username,b=t.login_password||t.password;p.login={totp:h&&""!==h?h:null,uri:v&&""!==v?s(v):null,username:y&&""!==y?y:null,password:b&&""!==b?b:null};break;case"note":p.type=e.cipherType.secureNote,p.secureNote={type:0}}if(o.push(p),d&&n.push({name:t.folder}),u){var w={key:c,value:l};r.push(w)}}),N(n,o,r)}});break;case"lastpass":d(p,m,g,!1);break;case"safeincloudxml":!function(t,n,o){var r=[],a=[],i=[],l=[],c=0,d=0;u(t,function(t){var u=$(t).find("database");if(u.length){var p=u.find("> label");if(p.length)for(c=0;c card");if(g.length)for(c=0;c field");for(d=0;d200?h.notes+=C+": "+b+"\n":(h.fields||(h.fields=[]),h.fields.push({name:C,value:b,type:e.fieldType.text})))}}var S=f.find("> notes");for(d=0;d label_id")).length){var k=$(p[0]).text(),T=l[k];null!==k&&""!==k&&null!==T&&i.push({key:a.length-1,value:T})}}}n(r,a,i)}else o()},o)}(p,m,g);break;case"keepass2xml":!function(t,n,o){var r=[],a=[],s=[];u(t,function(e){var t=$(e).find("Root");if(t.length){var i=t.find("> Group");i.length&&(l($(i[0]),!0,""),n(r,a,s))}else o()},o);function l(t,n,o){var c=r.length,u=o;n||(""!==u&&(u+=" > "),u+=t.find("> Name").text(),r.push({name:u}));var d=t.find("> Entry");if(d.length)for(var p=0;p String"),v=0;v Key").text(),w=y.find("> Value").text();if(""!==w)switch(b){case"URL":f.login.uri=i(w);break;case"UserName":f.login.username=w;break;case"Password":f.login.password=w;break;case"Title":f.name=w;break;case"Notes":f.notes=null===f.notes?w+"\n":f.notes+w+"\n";break;default:w.length>200||w.indexOf("\n")>-1?(f.notes||(f.notes=""),f.notes+=b+": "+w+"\n"):(f.fields||(f.fields=[]),f.fields.push({name:b,value:w,type:e.fieldType.text}))}}null===f.name&&(f.name="--"),a.push(f),n||s.push({key:g,value:c})}var C=t.find("> Group");if(C.length)for(var S=0;S "):null,l=n.length,c=o.length,u=null!==s,d=u,p=0;if(u)for(p=0;p-1||l.length>200?(null===n.notes?n.notes="":n.notes+="\n",n.notes+=c+": "+l.split("\\r\\n").join("\n").split("\\n").join("\n")):(n.fields||(n.fields=[]),n.fields.push({name:c,value:l,type:e.fieldType.text}))}}}}c(t,function(t){var o=t.split(/(?:\r\n|\r|\n)/);for(s=0;s=6){var i=n.length,l=o.length,c=t[0]&&""!==t[0],u=c,d=0;if(c)for(d=0;d6)for(d=6;d200?(p.notes||(p.notes=""),p.notes+=t[d]+": "+t[d+1]+"\n"):(p.fields||(p.fields=[]),p.fields.push({name:t[d],value:t[d+1],type:e.fieldType.text}));if(o.push(p),u&&n.push({name:t[0]}),c){var m={key:l,value:i};r.push(m)}}}),z(n,o,r)}});break;case"passworddragonxml":!function(t,n,o){var r=[],a=[],i=[],l=0;u(t,function(t){var c=$(t).find("PasswordManager");if(c.length){var u=c.find("> record");if(u.length)for(var d=0;d Account-Name"),g=m.length?$(m):null,f=p.find("> User-Id"),h=f.length?$(f):null,v=p.find("> Password"),y=v.length?$(v):null,b=p.find("> URL"),w=b.length?$(b):null,C=p.find("> Notes"),S=C.length?$(C):null,k=p.find("> Category"),T=k.length?$(k):null,P=T?T.text():null,I=r.length,E=a.length,z=P&&""!==P&&"Unfiled"!==P,O=z;if(z)for(l=0;l Attribute-"+l,l<10&&(U+=", ");var x=p.find(U);if(x.length)for(l=0;l200?(A.notes||(A.notes=""),A.notes+=F+": "+M+"\n"):(A.fields||(A.fields=[]),A.fields.push({name:F,value:M,type:e.fieldType.text})))}if(a.push(A),O&&r.push({name:P}),z){var G={key:E,value:I};i.push(G)}}n(r,a,i)}else o()},o)}(p,m,g);break;case"enpasscsv":P=p,I=m,Papa.parse(P,{encoding:"UTF-8",complete:function(t){l(t);for(var n=[],o=0;o2&&r.length%2==0)for(var c=0;c200?(i.notes||(i.notes=""),i.notes+=d+": "+u+"\n"):(i.fields||(i.fields=[]),i.fields.push({name:d,value:u,type:e.fieldType.text})):i.login.totp=u:i.login.password=u:i.login.username=u:i.login.uri=s(u)}}n.push(i)}}I([],n,[])}});break;case"pwsafexml":!function(t,n,o){var r=[],a=[],i=[],l=0;u(t,function(t){var c=$(t).find("passwordsafe");if(c.length){var u=c.attr("delimiter"),d=c.find("> entry");if(d.length)for(var p=0;p title"),f=g.length?$(g):null,h=m.find("> username"),v=h.length?$(h):null,y=m.find("> email"),b=y.length?$(y):null,w=b?b.text():null,C=m.find("> password"),S=C.length?$(C):null,k=m.find("> url"),T=k.length?$(k):null,P=m.find("> notes"),I=P.length?$(P):null,E=I?I.text().split(u).join("\n"):null,z=m.find("> group"),O=z.length?$(z):null,A=O?O.text().split(".").join(" > "):null,U=r.length,x=a.length,D=A&&""!==A,F=D;if(D)for(l=0;l Groups > Group[ID="'+t+'"]');if(o.length){n&&""!==n&&(n=" > "+n),n=o.attr("Name")+n;var r=o.attr("ParentID");return c(e,r,n)}return n}u(t,function(t){var u=$(t).find("root > Database");if(u.length){var d=u.find("> Logins > Login");if(d.length)for(var p=0;p Accounts > Account > LoginLinks > Login[SourceLoginID="'+h+'"]');if(S.length){var k=S.parent().parent();k.length&&(v=k.attr("Name"),y=k.attr("Link"),w=k.attr("ParentID"),(b=k.attr("Comments"))&&(b=b.split("/n").join("\n")))}}w&&""!==w&&(C=c(u,w,""));var T=r.length,P=a.length,I=C&&""!==C,E=I;if(I)for(l=0;l=3){var i=n.length,l=o.length,c=t[0]&&""!==t[0]&&"Unassigned"!==t[0],u=c,d=0;if(c)for(d=0;d3)for(var m=3;m200?(o.notes||(o.notes=""),o.notes+=r+": "+t[r]+"\n"):(o.fields||(o.fields=[]),o.fields.push({name:r,value:t[r],type:e.fieldType.text})))}a.push(o)}),n(r,a,[])}})}(p,m);break;case"clipperzhtml":!function(t,r,i){var l=[],u=[];c(t,function(t){var i=$(t).find("textarea"),c=i&&i.length?i.val():null,d=c?JSON.parse(c):null;if(d&&d.length)for(var p=0;p200?(g.notes||(g.notes=""),g.notes+=h.label+": "+h.value+"\n"):(g.fields||(g.fields=[]),g.fields.push({name:h.label,value:h.value,type:e.fieldType.text}))}}""===g.notes&&(g.notes=null),u.push(g)}r(l,u,[])},i)}(p,m,g);break;case"avirajson":!function(t,n,o){var r=[],a=[],s=0;c(t,function(t){var o=JSON.parse(t);if(o&&o.accounts)for(s=0;s").join("")).find("table.nobr");if(s.length)for(var c=0;c200?(p.notes||(p.notes=""),p.notes+=h+": "+v+"\n"):(p.fields||(p.fields=[]),p.fields.push({name:h,value:v,type:e.fieldType.text}))}p.notes&&""!==p.notes||(p.notes=null),p.name&&""!==p.name||(p.name="--"),u.push(p)}r(l,u,[])},s)}(p,m,g);break;case"saferpasscsv":!function(t,n,o){var r=[],a=[];Papa.parse(t,{header:!0,encoding:"UTF-8",complete:function(t){l(t),angular.forEach(t.data,function(t,n){a.push({type:e.cipherType.login,favorite:!1,notes:t.notes&&""!==t.notes?t.notes:null,name:t.url&&""!==t.url?function(e){var t=document.createElement("a");return t.href=e,t.hostname.startsWith("www.")?t.hostname.replace("www.",""):t.hostname}(t.url):"--",login:{uri:t.url&&""!==t.url?s(t.url):null,username:t.username&&""!==t.username?t.username:null,password:t.password&&""!==t.password?t.password:null}})}),n(r,a,[])}})}(p,m);break;case"ascendocsv":b=p,w=m,Papa.parse(b,{encoding:"UTF-8",complete:function(t){l(t);for(var s=[],c=0;c2&&u.length%2==0)for(var m=0;m200?(p.notes||(p.notes=""),p.notes+=f+": "+g+"\n"):(p.fields||(p.fields=[]),p.fields.push({name:f,value:g,type:e.fieldType.text})))}s.push(p)}}w([],s,[])}});break;case"passwordbossjson":!function(t,n,o){var r=[],a=[],s=0;c(t,function(t){var o=JSON.parse(t);if(o&&o.length)for(s=0;s200?(c.notes||(c.notes=""),c.notes+=u+": "+d+"\n"):(c.fields||(c.fields=[]),c.fields.push({name:u,value:d,type:e.fieldType.text}))}""===c.notes&&(c.notes=null),a.push(c)}}n(r,a,[])},o)}(p,m,g);break;case"zohovaultcsv":!function(t,n,o){function r(t,n){if(t&&""!==t)for(var o=t.split(/(?:\r\n|\r|\n)/),r=0;ri?a.substring(i+1):null;if(s&&""!==s&&l&&""!==l&&"SecretType"!==s){var c=s.toLowerCase();"user name"===c?n.login.username=l:"password"===c?n.login.password=l:l.length>200?(n.notes||(n.notes=""),n.notes+=s+": "+l+"\n"):(n.fields||(n.fields=[]),n.fields.push({name:s,value:l,type:e.fieldType.text}))}}}}Papa.parse(t,{header:!0,encoding:"UTF-8",complete:function(t){l(t);var o=[],a=[],s=[];angular.forEach(t.data,function(t,n){var l=t.ChamberName,c=o.length,u=a.length,d=l&&""!==l,p=d,m=0;if(d)for(m=0;m2&&a(2,c,b),b.name&&"--"!==b.name&&"Web Logins"!==p&&"Servers"!==p&&"Email Accounts"!==p&&(b.name=p+": "+b.name),""===b.notes&&(b.notes=null),o.push(b),h&&n.push({name:u}),f){var w={key:g,value:m};r.push(w)}}y(n,o,r)}});break;case"meldiumcsv":f=p,h=m,Papa.parse(f,{header:!0,encoding:"UTF-8",complete:function(t){l(t);for(var n=[],o=0;o30&&(m.name=m.name.substring(0,30));for(var g in p.attributes)p.attributes.hasOwnProperty(g)&&"username_value"!==g&&"xdg:schema"!==g&&(""!==m.notes&&(m.notes+="\n"),m.notes+=g+": "+p.attributes[g]);""===m.notes&&(m.notes=null),a.push(m),s.push({key:u,value:c})}}n(r,a,s)},o)}(p,m,g);break;default:g()}var f,h,v,y,b,w,C,S,k,T,P,I,E,z,O,A,U,x,D,F,M,G,K,N}else g()},t.importOrg=function(t,n,o,r){if(n){switch(t){case"bitwardencsv":a=n,i=o,Papa.parse(a,{header:!0,encoding:"UTF-8",complete:function(t){l(t);var n,o=[],r=[],a=[];angular.forEach(t.data,function(t,i){var l=r.length;if(t.collections&&""!==t.collections){var c=t.collections.split(",");for(n=0;nf+2&&(h.value=g[n].substr(f+2)),m.fields.push(h)}}}switch(t.type){case"login":case null:case void 0:m.type=e.cipherType.login;var v=t.login_totp||t.totp,y=t.login_uri||t.uri,b=t.login_username||t.username,w=t.login_password||t.password;m.login={totp:v&&""!==v?v:null,uri:y&&""!==y?s(y):null,username:b&&""!==b?b:null,password:w&&""!==w?w:null};break;case"note":m.type=e.cipherType.secureNote,m.secureNote={type:0}}r.push(m)}),i(o,r,a)}});break;case"lastpass":d(n,o,r,!0);break;default:r()}var a,i}else r()};var n=["password","pass word","passphrase","pass phrase","pass","code","code word","codeword","secret","secret word","key","keyword","key word","keyphrase","key phrase","form_pw","wppassword","pin","pwd","pw","pword","passwd","p","serial","serial#","license key","reg #","passwort"],o=["user","name","user name","username","login name","email","e-mail","id","userid","user id","login","form_loginname","wpname","mail","loginid","login id","log","first name","last name","card#","account #","member","member #","nom","benutzername"],r=["url","hyper link","hyperlink","link","host","hostname","host name","server","address","hyper ref","href","web","website","web site","site","web-site","uri","ort","adresse"];function a(e,t){if(!e||""===e)return!1;e=e.trim().toLowerCase();for(var n=0;n=0&&(e="http://"+e),s(e)}function s(e){return e.length>1e3?e.substring(0,1e3):e}function l(e){if(e.errors&&e.errors.length)for(var t=0;t-1||!a[1]||""===a[1]||("Notes"===a[0]?o.notes?o.notes+="\n"+a[1]:o.notes=a[1]:t.hasOwnProperty(a[0])?o.dataObj[t[a[0]]]=a[1]:(o.notes?o.notes+="\n":o.notes="",o.notes+=a[0]+": "+a[1]))}return o}function c(e){var t={cardholderName:e.ccname&&""!==e.ccname?e.ccname:null,number:e.ccnum&&""!==e.ccnum?e.ccnum:null,brand:e.ccnum&&""!==e.ccnum?function(e){if(!e)return null;var t=new RegExp("^4");return null!=e.match(t)?"Visa":/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/.test(e)?"Mastercard":(t=new RegExp("^3[47]"),null!=e.match(t)?"Amex":(t=new RegExp("^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)"),null!=e.match(t)?"Discover":(t=new RegExp("^36"),null!=e.match(t)?"Diners Club":(t=new RegExp("^30[0-5]"),null!=e.match(t)?"Diners Club":(t=new RegExp("^35(2[89]|[3-8][0-9])"),null!=e.match(t)?"JCB":(t=new RegExp("^(4026|417500|4508|4844|491(3|7))"),null!=e.match(t)?"Visa":null))))))}(e.ccnum):null,code:e.cccsc&&""!==e.cccsc?e.cccsc:null};if(e.ccexp&&""!==e.ccexp&&e.ccexp.indexOf("-")>-1){var n=e.ccexp.split("-");n.length>1&&(t.expYear=n[0],t.expMonth=n[1],2===t.expMonth.length&&"0"===t.expMonth[0]&&(t.expMonth=t.expMonth[1]))}return t}function u(t){var o=[],a=[],l=[],u=0;angular.forEach(t,function(t,n){var d=o.length,p=a.length,m=t.grouping&&""!==t.grouping&&"(none)"!==t.grouping,g=m;if(m)for(u=0;u1&&"NoteType"===y[0]&&("Credit Card"===y[1]||"Address"===y[1])){var b=null;"Credit Card"===y[1]?(b=i(h,{Number:"number","Name on Card":"cardholderName","Security Code":"code"},[]),f.type=e.cipherType.card,f.card=b.dataObj):"Address"===y[1]&&(b=i(h,{Title:"title","First Name":"firstName","Last Name":"lastName","Middle Name":"middleName",Company:"company","Address 1":"address1","Address 2":"address2","Address 3":"address3","City / Town":"city",State:"state","Zip / Postal Code":"postalCode",Country:"country","Email Address":"email",Username:"username"},[]),f.type=e.cipherType.identity,f.identity=b.dataObj),v=!0,f.notes=b.notes}}v||(f.secureNote={type:0},f.notes=t.extra&&""!==t.extra?t.extra:null)}else if(f.type===e.cipherType.card)f.card=c(t),f.notes=t.notes&&""!==t.notes?t.notes:null;else if(f.type===e.cipherType.identity&&(f.identity={title:t.title&&""!==t.title?t.title:null,firstName:t.firstname&&""!==t.firstname?t.firstname:null,middleName:t.middlename&&""!==t.middlename?t.middlename:null,lastName:t.lastname&&""!==t.lastname?t.lastname:null,username:t.username&&""!==t.username?t.username:null,company:t.company&&""!==t.company?t.company:null,ssn:t.ssn&&""!==t.ssn?t.ssn:null,address1:t.address1&&""!==t.address1?t.address1:null,address2:t.address2&&""!==t.address2?t.address2:null,address3:t.address3&&""!==t.address3?t.address3:null,city:t.city&&""!==t.city?t.city:null,state:t.state&&""!==t.state?t.state:null,postalCode:t.zip&&""!==t.zip?t.zip:null,country:t.country&&""!==t.country?t.country:null,email:t.email&&""!==t.email?t.email:null,phone:t.phone&&""!==t.phone?t.phone:null},f.notes=t.notes&&""!==t.notes?t.notes:null,f.identity.title&&(f.identity.title=f.identity.title.charAt(0).toUpperCase()+f.identity.title.slice(1)),t.ccnum&&""!==t.ccnum)){var w=JSON.parse(JSON.stringify(f));w.identity=null,w.type=e.cipherType.card,w.card=c(t),a.push(w)}if(a.push(f),g&&o.push({name:t.grouping}),m){var C={key:p,value:d};l.push(C)}}),n(o,a,l)}}return t}]),angular.module("bit.services").factory("passwordService",function(){var e={};e.generatePassword=function(e){var n=angular.extend({},{length:10,ambiguous:!1,number:!0,minNumber:1,uppercase:!0,minUppercase:1,lowercase:!0,minLowercase:1,special:!1,minSpecial:1},e);n.uppercase&&n.minUppercase<0&&(n.minUppercase=1),n.lowercase&&n.minLowercase<0&&(n.minLowercase=1),n.number&&n.minNumber<0&&(n.minNumber=1),n.special&&n.minSpecial<0&&(n.minSpecial=1),(!n.length||n.length<1)&&(n.length=10);var o=n.minUppercase+n.minLowercase+n.minNumber+n.minSpecial;n.length0)for(var a=0;a0)for(var i=0;i0)for(var s=0;s0)for(var l=0;l53)throw new Exception("We cannot generate numbers larger than 53 bits.");var i=Math.ceil(a/8),s=Math.pow(2,a)-1,l=new Uint8Array(i);window.crypto.getRandomValues(l);for(var c=8*(i-1),u=0;u=r?t(e,n):e+o}return e}),angular.module("bit.services").factory("tokenService",["$sessionStorage","$localStorage","jwtHelper",function(e,t,n){var o={},r=null,a=null;return o.setToken=function(t){e.accessToken=t,r=t},o.getToken=function(){return r||(r=e.accessToken),r||null},o.clearToken=function(){r=null,delete e.accessToken},o.setRefreshToken=function(t){e.refreshToken=t,a=t},o.getRefreshToken=function(){return a||(a=e.refreshToken),a||null},o.clearRefreshToken=function(){a=null,delete e.refreshToken},o.setTwoFactorToken=function(e,n){t.twoFactor||(t.twoFactor={}),t.twoFactor[n]=e},o.getTwoFactorToken=function(e){return t.twoFactor?t.twoFactor[e]:null},o.clearTwoFactorToken=function(e){e?t.twoFactor&&t.twoFactor[e]&&delete t.twoFactor[e]:delete t.twoFactor},o.clearTokens=function(){o.clearToken(),o.clearRefreshToken()},o.tokenSecondsRemaining=function(e,t){var o=n.getTokenExpirationDate(e);if(t=t||0,null===o)return 0;var r=o.valueOf()-((new Date).valueOf()+1e3*t);return Math.round(r/1e3)},o.tokenNeedsRefresh=function(e,t){t=t||5;return o.tokenSecondsRemaining(e)<60*t},o}]),angular.module("bit.services").factory("utilsService",["constants",function(e){var t,n={};n.getDeviceType=function(n){if(t)return t;var o;return t=navigator.userAgent.indexOf(" Vivaldi/")>=0?e.deviceType.vivaldi:window.chrome&&window.chrome.webstore?e.deviceType.chrome:"undefined"!=typeof InstallTrigger?e.deviceType.firefox:window.opr&&opr.addons||window.opera||navigator.userAgent.indexOf(" OPR/")>=0?e.deviceType.firefox:/constructor/i.test(window.HTMLElement)||(o=!window.safari||"undefined"!=typeof safari&&safari.pushNotification,"[object SafariRemoteNotification]"===o.toString())?e.deviceType.opera:document.documentMode?e.deviceType.ie:window.StyleMedia?e.deviceType.edge:e.deviceType.unknown};return n}]),angular.module("bit.services").factory("validationService",function(){var e={};return e.addErrors=function(t,n){var o=n.data,r="An unexpected error has occurred.";if(t.$errors=[],o&&angular.isObject(o))if(o&&o.ErrorModel&&(o=o.ErrorModel),o.ValidationErrors){for(var a in o.ValidationErrors)if(o.ValidationErrors.hasOwnProperty(a))for(var i=0;i-1&&e.model.organizations.splice(n,1),r.success("You have left the organization."),c()})},function(e){r.error("Unable to leave this organization."),c()})},e.sessions=function(){n.open({animation:!0,templateUrl:"app/settings/views/settingsSessions.html",controller:"settingsSessionsController"})},e.delete=function(){n.open({animation:!0,templateUrl:"app/settings/views/settingsDelete.html",controller:"settingsDeleteController"})},e.purge=function(){n.open({animation:!0,templateUrl:"app/settings/views/settingsPurge.html",controller:"settingsPurgeController"})};function c(){$("html, body").animate({scrollTop:0},200)}}]),angular.module("bit.settings").controller("settingsCreateOrganizationController",["$scope","$state","apiService","cryptoService","toastr","$analytics","authService","constants","appSettings","validationService","stripe",function(e,t,n,o,r,a,i,s,l,c,u){e.plans=s.plans,e.storageGb=s.storageGb,e.paymentMethod="card",e.selfHosted=l.selfHosted,e.model={plan:"free",additionalSeats:0,interval:"year",ownedBusiness:!1,additionalStorageGb:null},e.totalPrice=function(){return"month"===e.model.interval?(e.model.additionalSeats||0)*(e.plans[e.model.plan].monthlySeatPrice||0)+(e.model.additionalStorageGb||0)*e.storageGb.monthlyPrice+(e.plans[e.model.plan].monthlyBasePrice||0):(e.model.additionalSeats||0)*(e.plans[e.model.plan].annualSeatPrice||0)+(e.model.additionalStorageGb||0)*e.storageGb.yearlyPrice+(e.plans[e.model.plan].annualBasePrice||0)},e.changePaymentMethod=function(t){e.paymentMethod=t},e.changedPlan=function(){e.plans[e.model.plan].hasOwnProperty("monthPlanType")&&(e.model.interval="year"),e.plans[e.model.plan].noAdditionalSeats?e.model.additionalSeats=0:e.model.additionalSeats||e.plans[e.model.plan].baseSeats||e.plans[e.model.plan].noAdditionalSeats||(e.model.additionalSeats=1)},e.changedBusiness=function(){e.model.ownedBusiness&&(e.model.plan="teams")},e.submit=function(s,l){var d=o.makeShareKey(),p=o.encrypt("Default Collection",d.key);if(e.selfHosted){var m=document.getElementById("file").files;if(!m||!m.length)return void c.addError(l,"file","Select a license file.",!0);var g=new FormData;g.append("license",m[0]),g.append("key",d.ct),g.append("collectionName",p),e.submitPromise=n.organizations.postLicense(g).$promise.then(v)}else if("free"===s.plan){var f={name:s.name,planType:s.plan,key:d.ct,billingEmail:s.billingEmail,collectionName:p};e.submitPromise=n.organizations.post(f).$promise.then(v)}else{var h=null;if("card"===e.paymentMethod)h=u.card.createToken(s.card);else{if("bank"!==e.paymentMethod)return;s.bank.currency="USD",s.bank.country="US",h=u.bankAccount.createToken(s.bank)}e.submitPromise=h.then(function(t){var o={name:s.name,planType:"month"===s.interval?e.plans[s.plan].monthPlanType:e.plans[s.plan].annualPlanType,key:d.ct,paymentToken:t.id,additionalSeats:s.additionalSeats,additionalStorageGb:s.additionalStorageGb,billingEmail:s.billingEmail,businessName:s.ownedBusiness?s.businessName:null,country:"card"===e.paymentMethod?s.card.address_country:null,collectionName:p};return n.organizations.post(o).$promise},function(e){throw e.message}).then(v)}function v(e){a.eventTrack("Created Organization"),i.addProfileOrganizationOwner(e,d.ct),i.refreshAccessToken().then(function(){y(e.Id)},function(){y(e.Id)})}function y(e){t.go("backend.org.dashboard",{orgId:e}).then(function(){r.success("Your new organization is ready to go!","Organization Created")})}}}]),angular.module("bit.settings").controller("settingsDeleteController",["$scope","$state","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics","tokenService",function(e,t,n,o,r,a,i,s,l){s.eventTrack("settingsDeleteController",{category:"Modal"}),e.submit=function(c){var u;e.submitPromise=a.getUserProfile().then(function(e){return u=e,r.hashPassword(c.masterPassword)}).then(function(e){return n.accounts.postDelete({masterPasswordHash:e}).$promise}).then(function(){return o.dismiss("cancel"),a.logOut(),l.clearTwoFactorToken(u.email),s.eventTrack("Deleted Account"),t.go("frontend.login.info")}).then(function(){i.success("Your account has been closed and all associated data has been deleted.","Account Deleted")})},e.close=function(){o.dismiss("cancel")}}]),angular.module("bit.settings").controller("settingsDomainsController",["$scope","$state","apiService","toastr","$analytics","$uibModal",function(e,t,n,o,r,a){e.globalEquivalentDomains=[],e.equivalentDomains=[],n.settings.getDomains({},function(t){var n;if(t.EquivalentDomains)for(n=0;n

bitwarden two-step login recovery code:

'+e.code+'

'+new Date+"

"),t.print(),t.close()}};e.close=function(){n.close()}}]),angular.module("bit.settings").controller("settingsTwoStepU2fController",["$scope","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics","constants","$timeout","$window",function(e,t,n,o,r,a,i,s,l,c){i.eventTrack("settingsTwoStepU2fController",{category:"Modal"});var u,d=!1;e.deviceResponse=null,e.deviceListening=!1,e.deviceError=!1,l(function(){$("#masterPassword").focus()}),e.auth=function(n){e.authPromise=o.hashPassword(n.masterPassword).then(function(e){return u=e,t.twoFactor.getU2f({},{masterPasswordHash:u}).$promise}).then(function(t){return e.enabled=t.Enabled,e.challenge=t.Challenge,e.authed=!0,e.readDevice()})},e.readDevice=function(){d||e.enabled||(console.log("listening for key..."),e.deviceResponse=null,e.deviceError=!1,e.deviceListening=!0,c.u2f.register(e.challenge.AppId,[{version:e.challenge.Version,challenge:e.challenge.Challenge}],[],function(t){e.deviceListening=!1;{if(5!==t.errorCode)return t.errorCode?(l(function(){e.deviceError=!0}),void console.log("error: "+t.errorCode)):void l(function(){e.deviceResponse=JSON.stringify(t)});e.readDevice()}},10))},e.submit=function(){e.enabled?function(){if(!confirm("Are you sure you want to disable the U2F provider?"))return;e.submitPromise=t.twoFactor.disable({},{masterPasswordHash:u,type:s.twoFactorProvider.u2f},function(t){i.eventTrack("Disabled Two-step U2F"),a.success("U2F has been disabled."),e.enabled=t.Enabled,e.close()}).$promise}():e.submitPromise=t.twoFactor.putU2f({},{deviceResponse:e.deviceResponse,masterPasswordHash:u},function(t){i.eventTrack("Enabled Two-step U2F"),e.enabled=t.Enabled,e.challenge=null,e.deviceResponse=null,e.deviceError=!1}).$promise};e.close=function(){d=!0,n.close(e.enabled)},e.$on("modal.closing",function(t,n,o){d||(t.preventDefault(),e.close())})}]),angular.module("bit.settings").controller("settingsTwoStepYubiController",["$scope","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics","constants","$timeout",function(e,t,n,o,r,a,i,s,l){i.eventTrack("settingsTwoStepYubiController",{category:"Modal"});var c;l(function(){$("#masterPassword").focus()}),e.auth=function(n){var a=null;e.authPromise=o.hashPassword(n.masterPassword).then(function(e){return c=e,t.twoFactor.getYubi({},{masterPasswordHash:c}).$promise}).then(function(e){return a=e,r.getUserProfile()}).then(function(t){t,u(a),e.authed=!0})},e.remove=function(e){e.key=null,e.existingKey=null},e.submit=function(n){e.submitPromise=t.twoFactor.putYubi({},{key1:n.key1.key,key2:n.key2.key,key3:n.key3.key,nfc:n.nfc,masterPasswordHash:c},function(e){i.eventTrack("Saved Two-step YubiKey"),a.success("YubiKey saved."),u(e)}).$promise},e.disable=function(){confirm("Are you sure you want to disable the YubiKey provider?")&&(e.disableLoading=!0,e.submitPromise=t.twoFactor.disable({},{masterPasswordHash:c,type:s.twoFactorProvider.yubikey},function(t){e.disableLoading=!1,i.eventTrack("Disabled Two-step YubiKey"),a.success("YubiKey has been disabled."),e.enabled=t.Enabled,e.close()},function(t){a.error("Failed to disable."),e.disableLoading=!1}).$promise)};function u(t){e.enabled=t.Enabled,e.updateModel={key1:{key:t.Key1,existingKey:d(t.Key1,"*",44)},key2:{key:t.Key2,existingKey:d(t.Key2,"*",44)},key3:{key:t.Key3,existingKey:d(t.Key3,"*",44)},nfc:!0===t.Nfc||!t.Enabled}}function d(e,t,n){if(!e||!t||e.length>=n)return e;for(var o=(n-e.length)/t.length,r=0;r=t?e:new Array(t-e.length+1).join(n)+e}}]),angular.module("bit.tools").controller("toolsImportController",["$scope","$state","apiService","$uibModalInstance","cryptoService","cipherService","toastr","importService","$analytics","$sce","validationService",function(e,t,n,o,r,a,i,s,l,c,u){l.eventTrack("toolsImportController",{category:"Modal"}),e.model={source:""},e.source={},e.splitFeatured=!0,e.options=[{id:"bitwardencsv",name:"bitwarden (csv)",featured:!0,sort:1,instructions:c.trustAsHtml('Export using the web vault (vault.bitwarden.com). Log into the web vault and navigate to "Tools" > "Export".')},{id:"lastpass",name:"LastPass (csv)",featured:!0,sort:2,instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-lastpass/')},{id:"chromecsv",name:"Chrome (csv)",featured:!0,sort:3,instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-chrome/')},{id:"firefoxpasswordexportercsvxml",name:"Firefox Password Exporter (xml)",featured:!0,sort:4,instructions:c.trustAsHtml('Use the Password Exporter addon for FireFox to export your passwords to a XML file. After installing the addon, type about:addons in your FireFox navigation bar. Locate the Password Exporter addon and click the "Options" button. In the dialog that pops up, click the "Export Passwords" button to save the XML file.')},{id:"keepass2xml",name:"KeePass 2 (xml)",featured:!0,sort:5,instructions:c.trustAsHtml('Using the KeePass 2 desktop application, navigate to "File" > "Export" and select the KeePass XML (2.x) option.')},{id:"keepassxcsv",name:"KeePassX (csv)",instructions:c.trustAsHtml('Using the KeePassX desktop application, navigate to "Database" > "Export to CSV file" and save the CSV file.')},{id:"dashlanecsv",name:"Dashlane (csv)",featured:!0,sort:7,instructions:c.trustAsHtml('Using the Dashlane desktop application, navigate to "File" > "Export" > "Unsecured archive (readable) in CSV format" and save the CSV file.')},{id:"1password1pif",name:"1Password (1pif)",featured:!0,sort:6,instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-1password/')},{id:"1password6wincsv",name:"1Password 6 Windows (csv)",instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-1password/')},{id:"roboformhtml",name:"RoboForm (html)",instructions:c.trustAsHtml('Using the RoboForm Editor desktop application, navigate to "RoboForm" (top left) > "Print List" > "Logins". When the following print dialog pops up click on the "Save" button and save the HTML file.')},{id:"keepercsv",name:"Keeper (csv)",instructions:c.trustAsHtml('Log into the Keeper web vault (keepersecurity.com/vault). Navigate to "Backup" (top right) and find the "Export to Text File" option. Click "Export Now" to save the TXT/CSV file.')},{id:"enpasscsv",name:"Enpass (csv)",instructions:c.trustAsHtml('Using the Enpass desktop application, navigate to "File" > "Export" > "As CSV". Select "Yes" to the warning alert and save the CSV file. Note that the importer only fully supports files exported while Enpass is set to the English language, so adjust your settings accordingly.')},{id:"safeincloudxml",name:"SafeInCloud (xml)",instructions:c.trustAsHtml('Using the SaveInCloud desktop application, navigate to "File" > "Export" > "As XML" and save the XML file.')},{id:"pwsafexml",name:"Password Safe (xml)",instructions:c.trustAsHtml('Using the Password Safe desktop application, navigate to "File" > "Export To" > "XML format..." and save the XML file.')},{id:"stickypasswordxml",name:"Sticky Password (xml)",instructions:c.trustAsHtml('Using the Sticky Password desktop application, navigate to "Menu" (top right) > "Export" > "Export all". Select the unencrypted format XML option and then the "Save to file" button. Save the XML file.')},{id:"msecurecsv",name:"mSecure (csv)",instructions:c.trustAsHtml('Using the mSecure desktop application, navigate to "File" > "Export" > "CSV File..." and save the CSV file.')},{id:"truekeycsv",name:"True Key (csv)",instructions:c.trustAsHtml('Using the True Key desktop application, click the gear icon (top right) and then navigate to "App Settings". Click the "Export" button, enter your password and save the CSV file.')},{id:"passwordbossjson",name:"Password Boss (json)",instructions:c.trustAsHtml('Using the Password Boss desktop application, navigate to "File" > "Export data" > "Password Boss JSON - not encrypted" and save the JSON file.')},{id:"zohovaultcsv",name:"Zoho Vault (csv)",instructions:c.trustAsHtml('Log into the Zoho web vault (vault.zoho.com). Navigate to "Tools" > "Export Secrets". Select "All Secrets" and click the "Zoho Vault Format CSV" button. Highlight and copy the data from the textarea. Open a text editor like Notepad and paste the data. Save the data from the text editor as zoho_export.csv.')},{id:"splashidcsv",name:"SplashID (csv)",instructions:c.trustAsHtml('Using the SplashID Safe desktop application, click on the SplashID blue lock logo in the top right corner. Navigate to "Export" > "Export as CSV" and save the CSV file.')},{id:"passworddragonxml",name:"Password Dragon (xml)",instructions:c.trustAsHtml('Using the Password Dragon desktop application, navigate to "File" > "Export" > "To XML". In the dialog that pops up select "All Rows" and check all fields. Click the "Export" button and save the XML file.')},{id:"padlockcsv",name:"Padlock (csv)",instructions:c.trustAsHtml('Using the Padlock desktop application, click the hamburger icon in the top left corner and navigate to "Settings". Click the "Export Data" option. Ensure that the "CSV" option is selected from the dropdown. Highlight and copy the data from the textarea. Open a text editor like Notepad and paste the data. Save the data from the text editor as padlock_export.csv.')},{id:"clipperzhtml",name:"Clipperz (html)",instructions:c.trustAsHtml('Log into the Clipperz web application (clipperz.is/app). Click the hamburger menu icon in the top right to expand the navigation bar. Navigate to "Data" > "Export". Click the "download HTML+JSON" button to save the HTML file.')},{id:"avirajson",name:"Avira (json)",instructions:c.trustAsHtml('Using the Avira browser extension, click your username in the top right corner and navigate to "Settings". Locate the "Export Data" section and click "Export". In the dialog that pops up, click the "Export Password Manager Data" button to save the TXT/JSON file.')},{id:"saferpasscsv",name:"SaferPass (csv)",instructions:c.trustAsHtml('Using the SaferPass browser extension, click the hamburger icon in the top left corner and navigate to "Settings". Click the "Export accounts" button to save the CSV file.')},{id:"upmcsv",name:"Universal Password Manager (csv)",instructions:c.trustAsHtml('Using the Universal Password Manager desktop application, navigate to "Database" > "Export" and save the CSV file.')},{id:"ascendocsv",name:"Ascendo DataVault (csv)",instructions:c.trustAsHtml('Using the Ascendo DataVault desktop application, navigate to "Tools" > "Export". In the dialog that pops up, select the "All Items (DVX, CSV)" option. Click the "Ok" button to save the CSV file.')},{id:"meldiumcsv",name:"Meldium (csv)",instructions:c.trustAsHtml('Using the Meldium web vault, navigate to "Settings". Locate the "Export data" function and click "Show me my data" to save the CSV file.')},{id:"passkeepcsv",name:"PassKeep (csv)",instructions:c.trustAsHtml('Using the PassKeep mobile app, navigate to "Backup/Restore". Locate the "CSV Backup/Restore" section and click "Backup to CSV" to save the CSV file.')},{id:"operacsv",name:"Opera (csv)",instructions:c.trustAsHtml('The process for importing from Opera is exactly the same as importing from Google Chrome. See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-chrome/')},{id:"vivaldicsv",name:"Vivaldi (csv)",instructions:c.trustAsHtml('The process for importing from Vivaldi is exactly the same as importing from Google Chrome. See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-chrome/')},{id:"gnomejson",name:"GNOME Passwords and Keys/Seahorse (json)",instructions:c.trustAsHtml('Make sure you have python-keyring and python-gnomekeyring installed. Save the GNOME Keyring Import/Export python script by Luke Plant to your desktop as pw_helper.py. Open terminal and run chmod +rx Desktop/pw_helper.py and then python Desktop/pw_helper.py export Desktop/my_passwords.json. Then upload the resulting my_passwords.json file here to bitwarden.')}],e.setSource=function(){for(var t=0;t-1&&e.cipher.fields.splice(n,1)},e.toggleFavorite=function(){e.cipher.favorite=!e.cipher.favorite},e.clipboardSuccess=function(e){e.clearSelection(),g(e)},e.clipboardError=function(e,t){t&&g(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")},e.folderSort=function(e){return e.id?e.name.toLowerCase():"î º"};function g(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.close=function(){n.dismiss("close")},e.showUpgrade=function(){d.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}}]),angular.module("bit.vault").controller("vaultAddFolderController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","$analytics",function(e,t,n,o,r,a){a.eventTrack("vaultAddFolderController",{category:"Modal"}),e.savePromise=null,e.save=function(o){var i=r.encryptFolder(o);e.savePromise=t.folders.post(i,function(e){a.eventTrack("Created Folder");var t=r.decryptFolder(e);n.close(t)}).$promise},e.close=function(){n.dismiss("close")}}]),angular.module("bit.vault").controller("vaultAttachmentsController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","cipherId","$analytics","validationService","toastr","$timeout","authService","$uibModal",function(e,t,n,o,r,a,i,s,l,c,u,d){i.eventTrack("vaultAttachmentsController",{category:"Modal"}),e.cipher={},e.readOnly=!0,e.loading=!0,e.isPremium=!0,e.canUseAttachments=!0;var p=!1;u.getUserProfile().then(function(n){return e.isPremium=n.premium,t.ciphers.get({id:a}).$promise}).then(function(t){e.cipher=r.decryptCipher(t),e.readOnly=!e.cipher.edit,e.canUseAttachments=e.isPremium||e.cipher.organizationId,e.loading=!1},function(){e.loading=!1}),e.save=function(n){var o=document.getElementById("file"),c=o.files;c&&c.length?e.savePromise=r.encryptAttachmentFile(m(),c[0]).then(function(e){var n=new FormData,o=new Blob([e.data],{type:"application/octet-stream"});return n.append("data",o,e.fileName),t.ciphers.postAttachment({id:a},n).$promise}).then(function(t){i.eventTrack("Added Attachment"),e.cipher=r.decryptCipher(t),o.type="",o.type="file",o.value=""},function(e){var t=s.parseErrors(e);l.error(t.length?t[0]:"An error occurred.")}):s.addError(n,"file","Select a file.",!0)},e.download=function(t){if(t.loading=!0,!e.canUseAttachments)return t.loading=!1,void alert("Premium membership is required to use this feature.");r.downloadAndDecryptAttachment(m(),t,!0).then(function(e){c(function(){t.loading=!1})},function(){c(function(){t.loading=!1})})};function m(){return e.cipher.organizationId?o.getOrgKey(e.cipher.organizationId):null}e.remove=function(n){confirm("Are you sure you want to delete this attachment ("+n.fileName+")?")&&(n.loading=!0,t.ciphers.delAttachment({id:a,attachmentId:n.id}).$promise.then(function(){n.loading=!1,i.eventTrack("Deleted Attachment");var t=e.cipher.attachments.indexOf(n);t>-1&&e.cipher.attachments.splice(t,1)},function(){l.error("Cannot delete attachment."),n.loading=!1}))},e.close=function(){n.dismiss("cancel")},e.$on("modal.closing",function(t,o,r){p||(t.preventDefault(),p=!0,n.close(!!e.cipher.attachments&&e.cipher.attachments.length>0))}),e.showUpgrade=function(){d.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}}]),angular.module("bit.vault").controller("vaultCipherCollectionsController",["$scope","apiService","$uibModalInstance","cipherService","cipherId","$analytics",function(e,t,n,o,r,a){a.eventTrack("vaultCipherCollectionsController",{category:"Modal"}),e.cipher={},e.readOnly=!1,e.loadingCipher=!0,e.loadingCollections=!0,e.selectedCollections={},e.collections=[];var i=null;n.opened.then(function(){t.ciphers.getDetails({id:r}).$promise.then(function(t){if(e.loadingCipher=!1,e.readOnly=!t.Edit,t.Edit&&t.OrganizationId){1===t.Type&&(e.cipher=o.decryptCipherPreview(t));var n={};if(t.CollectionIds)for(var r=0;r-1&&(t.sort=n)})}),d.vaultCiphers=e.ciphers=o("orderBy")(t,["sort","name","subTitle"]);var n=function(e,t){var n=[],o=0,r=e.length;for(;o0){e.ciphers=n[0];var r=200;angular.forEach(n,function(t,n){n>0&&u(function(){Array.prototype.push.apply(e.ciphers,t)},r+=200)})}}function y(){d.vaultCiphers=e.ciphers=o("orderBy")(d.vaultCiphers,["name","subTitle"])}function b(e){return e.id?e.name.toLowerCase():"î º"}e.clipboardError=function(e){alert("Your web browser does not support easy clipboard copying. Edit the item and copy it manually instead.")},e.collapseExpand=function(e,t){c.collapsedFolders||(c.collapsedFolders={});var n=t?"favorite":e.id||"none";n in c.collapsedFolders?delete c.collapsedFolders[n]:c.collapsedFolders[n]=!0},e.collapseAll=function(){if(c.collapsedFolders||(c.collapsedFolders={}),c.collapsedFolders.none=!0,c.collapsedFolders.favorite=!0,d.vaultGroupings)for(var e=0;e-1&&(d.vaultCiphers[o]=t.data),y()}else"partialEdit"===t.action?(n.folderId=t.data.folderId,n.favorite=t.data.favorite):"delete"===t.action&&k(n)})},e.$on("vaultAddCipher",function(t,n){e.addCipher()}),e.addCipher=function(e,n){t.open({animation:!0,templateUrl:"app/vault/views/vaultAddCipher.html",controller:"vaultAddCipherController",resolve:{selectedFolder:function(){return e&&e.folder?e:null},checkedFavorite:function(){return n}}}).result.then(function(e){d.vaultCiphers.push(e),y()})},e.deleteCipher=function(e){confirm("Are you sure you want to delete this item ("+e.name+")?")&&n.ciphers.del({id:e.id},function(){m.eventTrack("Deleted Item"),k(e)})},e.attachments=function(e){a.getUserProfile().then(function(t){return{isPremium:t.premium,orgUseStorage:e.organizationId&&!!t.organizations[e.organizationId].maxStorageGb}}).then(function(n){if(!e.hasAttachments){if(e.organizationId&&!n.orgUseStorage)return void t.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return e.organizationId}}});if(!e.organizationId&&!n.isPremium)return void t.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}if(e.organizationId||r.getEncKey()){t.open({animation:!0,templateUrl:"app/vault/views/vaultAttachments.html",controller:"vaultAttachmentsController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){e.hasAttachments=t})}else i.error("You cannot use this feature until you update your encryption key.","Feature Unavailable")})},e.editFolder=function(e){t.open({animation:!0,templateUrl:"app/vault/views/vaultEditFolder.html",controller:"vaultEditFolderController",size:"sm",resolve:{folderId:function(){return e.id}}}).result.then(function(t){e.name=t.name})},e.$on("vaultAddFolder",function(t,n){e.addFolder()}),e.addFolder=function(){t.open({animation:!0,templateUrl:"app/vault/views/vaultAddFolder.html",controller:"vaultAddFolderController",size:"sm"}).result.then(function(e){e.folder=!0,d.vaultGroupings.push(e),h(d.vaultGroupings)})},e.deleteFolder=function(t){confirm("Are you sure you want to delete this folder ("+t.name+")?")&&n.folders.del({id:t.id},function(){m.eventTrack("Deleted Folder");var n=d.vaultGroupings.indexOf(t);n>-1&&(d.vaultGroupings.splice(n,1),e.folderCount--)})},e.canDeleteFolder=function(e){if(!e||!e.id||!d.vaultCiphers)return!1;var t=o("filter")(d.vaultCiphers,{folderId:e.id});return t&&0===t.length},e.share=function(e){t.open({animation:!0,templateUrl:"app/vault/views/vaultShareCipher.html",controller:"vaultShareCipherController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){e.organizationId=t})},e.editCollections=function(e){t.open({animation:!0,templateUrl:"app/vault/views/vaultCipherCollections.html",controller:"vaultCipherCollectionsController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){t.collectionIds&&!t.collectionIds.length?k(e):t.collectionIds&&(e.collectionIds=t.collectionIds)})},e.filterGrouping=function(t){e.groupingIdFilter=t.id,$.AdminLTE&&$.AdminLTE.layout&&u(function(){$.AdminLTE.layout.fix()},0)},e.filterType=function(t){e.typeFilter=t,$.AdminLTE&&$.AdminLTE.layout&&u(function(){$.AdminLTE.layout.fix()},0)},e.clearFilters=function(){e.groupingIdFilter=void 0,e.typeFilter=void 0,$.AdminLTE&&$.AdminLTE.layout&&u(function(){$.AdminLTE.layout.fix()},0)},e.groupingFilter=function(t){return void 0===e.groupingIdFilter||t.id===e.groupingIdFilter},e.cipherFilter=function(t){return function(n){var o=null===t;return!o&&t.folder&&n.folderId===t.id?o=!0:!o&&t.collection&&n.collectionIds.indexOf(t.id)>-1&&(o=!0),o&&(void 0===e.typeFilter||n.type===e.typeFilter)}},e.unselectAll=function(){S(!1)},e.selectFolder=function(e,t){$(t.currentTarget).closest(".box").find('input[name="cipherSelection"]').prop("checked",!0)},e.select=function(e){var t=$(e.currentTarget).closest("tr").find('input[name="cipherSelection"]');t.prop("checked",!t.prop("checked"))};function w(e,t,n){return n.indexOf(e)===t}function C(){return $('input[name="cipherSelection"]:checked').map(function(){return $(this).val()}).get().filter(w)}function S(e){$('input[name="cipherSelection"]').prop("checked",e)}e.bulkMove=function(){var e=C();if(0!==e.length){t.open({animation:!0,templateUrl:"app/vault/views/vaultMoveCiphers.html",controller:"vaultMoveCiphersController",size:"sm",resolve:{ids:function(){return e}}}).result.then(function(t){for(var n=0;n-1&&d.vaultCiphers.splice(n,1),(n=e.ciphers.indexOf(t))>-1&&e.ciphers.splice(n,1)}}]),angular.module("bit.vault").controller("vaultEditCipherController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","passwordService","cipherId","$analytics","$rootScope","authService","$uibModal","constants","$filter",function(e,t,n,o,r,a,i,s,l,c,u,d,p){s.eventTrack("vaultEditCipherController",{category:"Modal"}),e.folders=p("filter")(l.vaultGroupings,{folder:!0}),e.cipher={},e.readOnly=!1,e.constants=d,c.getUserProfile().then(function(n){return e.useTotp=n.premium,t.ciphers.get({id:i}).$promise}).then(function(t){e.cipher=r.decryptCipher(t),e.readOnly=!e.cipher.edit,e.useTotp=e.useTotp||e.cipher.organizationUseTotp}),e.save=function(o){if(e.readOnly)e.savePromise=t.ciphers.putPartial({id:i},{folderId:o.folderId,favorite:o.favorite},function(e){s.eventTrack("Partially Edited Cipher"),n.close({action:"partialEdit",data:{id:i,favorite:o.favorite,folderId:o.folderId&&""!==o.folderId?o.folderId:null}})}).$promise;else{var a=r.encryptCipher(o,e.cipher.type);e.savePromise=t.ciphers.put({id:i},a,function(e){s.eventTrack("Edited Cipher");var t=r.decryptCipherPreview(e);n.close({action:"edit",data:t})}).$promise}},e.generatePassword=function(){e.cipher.login.password&&!confirm("Are you sure you want to overwrite the current password?")||(s.eventTrack("Generated Password From Edit"),e.cipher.login.password=a.generatePassword({length:14,special:!0}))},e.addField=function(){e.cipher.fields||(e.cipher.fields=[]),e.cipher.fields.push({type:d.fieldType.text.toString(),name:null,value:null})},e.removeField=function(t){var n=e.cipher.fields.indexOf(t);n>-1&&e.cipher.fields.splice(n,1)},e.toggleFavorite=function(){e.cipher.favorite=!e.cipher.favorite},e.clipboardSuccess=function(e){e.clearSelection(),m(e)},e.clipboardError=function(e,t){t&&m(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")},e.folderSort=function(e){return e.id?e.name.toLowerCase():"î º"};function m(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.delete=function(){confirm("Are you sure you want to delete this item ("+e.cipher.name+")?")&&t.ciphers.del({id:e.cipher.id},function(){s.eventTrack("Deleted Cipher From Edit"),n.close({action:"delete",data:e.cipher.id})})},e.close=function(){n.dismiss("cancel")},e.showUpgrade=function(){u.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}}]),angular.module("bit.vault").controller("vaultEditFolderController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","folderId","$analytics",function(e,t,n,o,r,a,i){i.eventTrack("vaultEditFolderController",{category:"Modal"}),e.folder={},t.folders.get({id:a},function(t){e.folder=r.decryptFolder(t)}),e.savePromise=null,e.save=function(o){var s=r.encryptFolder(o);e.savePromise=t.folders.put({id:a},s,function(e){i.eventTrack("Edited Folder");var t=r.decryptFolder(e);n.close(t)}).$promise},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.vault").controller("vaultMoveCiphersController",["$scope","apiService","$uibModalInstance","ids","$analytics","$rootScope","$filter",function(e,t,n,o,r,a,i){r.eventTrack("vaultMoveCiphersController",{category:"Modal"}),e.folders=i("filter")(a.vaultGroupings,{folder:!0}),e.count=o.length,e.save=function(){e.savePromise=t.ciphers.moveMany({ids:o,folderId:e.folderId},function(){r.eventTrack("Bulk Moved Ciphers"),n.close(e.folderId||null)}).$promise},e.folderSort=function(e){return e.id?e.name.toLowerCase():"!"},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.vault").controller("vaultShareCipherController",["$scope","apiService","$uibModalInstance","authService","cipherService","cipherId","$analytics","$state","cryptoService","$q","toastr",function(e,t,n,o,r,a,i,s,l,c,u){i.eventTrack("vaultShareCipherController",{category:"Modal"}),e.model={},e.cipher={},e.collections=[],e.selectedCollections={},e.organizations=[];var d={};e.loadingCollections=!0,e.loading=!0,e.readOnly=!1,t.ciphers.get({id:a}).$promise.then(function(t){return e.readOnly=!t.Edit,t.Edit&&(e.cipher=r.decryptCipher(t)),t.Edit}).then(function(t){if(e.loading=!1,t)return o.getUserProfile()}).then(function(n){if(n&&n.organizations){var o=[],a=!1;for(var i in n.organizations)n.organizations.hasOwnProperty(i)&&n.organizations[i].enabled&&(o.push({id:n.organizations[i].id,name:n.organizations[i].name}),d[n.organizations[i].id]=0,a||(a=!0,e.model.organizationId=n.organizations[i].id));e.organizations=o,t.collections.listMe({writeOnly:!0},function(t){for(var n=[],o=0;o-1&&(e.headers["Device-Type"]=r.getDeviceType()),e},response:function(o){return 401!==o.status&&403!==o.status||(e.get("authService").logOut(),e.get("$state").go("frontend.login.info").then(function(){n.warning("Your login session has expired.","Logged out")})),o||t.when(o)},responseError:function(o){return 401!==o.status&&403!==o.status||(e.get("authService").logOut(),e.get("$state").go("frontend.login.info").then(function(){n.warning("Your login session has expired.","Logged out")})),t.reject(o)}}}]),angular.module("bit").config(["$stateProvider","$urlRouterProvider","$httpProvider","jwtInterceptorProvider","jwtOptionsProvider","$uibTooltipProvider","toastrConfig","$locationProvider","$qProvider","appSettings","stripeProvider",function(e,t,n,o,r,a,i,s,l,c,u){angular.extend(c,window.bitwardenAppSettings),l.errorOnUnhandledRejections(!1),s.hashPrefix("");var d;o.tokenGetter=["options","tokenService","authService",function(e,t,n){if(-1!==e.url.indexOf(c.apiUri+"/")){if(d)return d;var o=t.getToken();if(o){if(!t.tokenNeedsRefresh(o))return o;var r=n.refreshAccessToken();if(r)return d=r.then(function(e){return d=null,e||o})}}}],u.setPublishableKey(c.stripeKey),angular.extend(i,{closeButton:!0,progressBar:!0,showMethod:"slideDown",target:".toast-target"}),a.options({popupDelay:600,appendToBody:!0}),(-1!==navigator.userAgent.indexOf("MSIE")||navigator.appVersion.indexOf("Trident/")>0)&&(n.defaults.headers.get||(n.defaults.headers.get={}),n.defaults.headers.get["Cache-Control"]="no-cache",n.defaults.headers.get.Pragma="no-cache"),n.interceptors.push("apiInterceptor"),n.interceptors.push("jwtInterceptor"),t.otherwise("/"),e.state("backend",{templateUrl:"app/views/backendLayout.html",abstract:!0,data:{authorize:!0}}).state("backend.user",{templateUrl:"app/views/userLayout.html",abstract:!0}).state("backend.user.vault",{url:"^/vault",templateUrl:"app/vault/views/vault.html",controller:"vaultController",data:{pageTitle:"My Vault",controlSidebar:!0},params:{refreshFromServer:!1}}).state("backend.user.settings",{url:"^/settings",templateUrl:"app/settings/views/settings.html",controller:"settingsController",data:{pageTitle:"Settings"}}).state("backend.user.settingsDomains",{url:"^/settings/domains",templateUrl:"app/settings/views/settingsDomains.html",controller:"settingsDomainsController",data:{pageTitle:"Domain Settings"}}).state("backend.user.settingsTwoStep",{url:"^/settings/two-step",templateUrl:"app/settings/views/settingsTwoStep.html",controller:"settingsTwoStepController",data:{pageTitle:"Two-step Login"}}).state("backend.user.settingsCreateOrg",{url:"^/settings/create-organization",templateUrl:"app/settings/views/settingsCreateOrganization.html",controller:"settingsCreateOrganizationController",data:{pageTitle:"Create Organization"}}).state("backend.user.settingsBilling",{url:"^/settings/billing",templateUrl:"app/settings/views/settingsBilling.html",controller:"settingsBillingController",data:{pageTitle:"Billing"}}).state("backend.user.settingsPremium",{url:"^/settings/premium",templateUrl:"app/settings/views/settingsPremium.html",controller:"settingsPremiumController",data:{pageTitle:"Go Premium"}}).state("backend.user.tools",{url:"^/tools",templateUrl:"app/tools/views/tools.html",controller:"toolsController",data:{pageTitle:"Tools"}}).state("backend.user.reportsBreach",{url:"^/reports/breach",templateUrl:"app/reports/views/reportsBreach.html",controller:"reportsBreachController",data:{pageTitle:"Data Breach Report"}}).state("backend.user.apps",{url:"^/apps",templateUrl:"app/views/apps.html",controller:"appsController",data:{pageTitle:"Get the Apps"}}).state("backend.org",{templateUrl:"app/views/organizationLayout.html",abstract:!0}).state("backend.org.dashboard",{url:"^/organization/:orgId",templateUrl:"app/organization/views/organizationDashboard.html",controller:"organizationDashboardController",data:{pageTitle:"Organization Dashboard"}}).state("backend.org.people",{url:"/organization/:orgId/people?viewEvents&search",templateUrl:"app/organization/views/organizationPeople.html",controller:"organizationPeopleController",data:{pageTitle:"Organization People"}}).state("backend.org.collections",{url:"/organization/:orgId/collections?search",templateUrl:"app/organization/views/organizationCollections.html",controller:"organizationCollectionsController",data:{pageTitle:"Organization Collections"}}).state("backend.org.settings",{url:"/organization/:orgId/settings",templateUrl:"app/organization/views/organizationSettings.html",controller:"organizationSettingsController",data:{pageTitle:"Organization Settings"}}).state("backend.org.billing",{url:"/organization/:orgId/billing",templateUrl:"app/organization/views/organizationBilling.html",controller:"organizationBillingController",data:{pageTitle:"Organization Billing"}}).state("backend.org.vault",{url:"/organization/:orgId/vault?viewEvents&search",templateUrl:"app/organization/views/organizationVault.html",controller:"organizationVaultController",data:{pageTitle:"Organization Vault"}}).state("backend.org.groups",{url:"/organization/:orgId/groups?search",templateUrl:"app/organization/views/organizationGroups.html",controller:"organizationGroupsController",data:{pageTitle:"Organization Groups"}}).state("backend.org.events",{url:"/organization/:orgId/events",templateUrl:"app/organization/views/organizationEvents.html",controller:"organizationEventsController",data:{pageTitle:"Organization Events"}}).state("frontend",{templateUrl:"app/views/frontendLayout.html",abstract:!0,data:{authorize:!1}}).state("frontend.login",{templateUrl:"app/accounts/views/accountsLogin.html",controller:"accountsLoginController",params:{returnState:null,email:null,premium:null,org:null},data:{bodyClass:"login-page"}}).state("frontend.login.info",{url:"^/?org&premium&email",templateUrl:"app/accounts/views/accountsLoginInfo.html",data:{pageTitle:"Log In"}}).state("frontend.login.twoFactor",{url:"^/two-step?org&premium&email",templateUrl:"app/accounts/views/accountsLoginTwoFactor.html",data:{pageTitle:"Log In (Two-step)"}}).state("frontend.logout",{url:"^/logout",controller:"accountsLogoutController",data:{authorize:!0}}).state("frontend.passwordHint",{url:"^/password-hint",templateUrl:"app/accounts/views/accountsPasswordHint.html",controller:"accountsPasswordHintController",data:{pageTitle:"Master Password Hint",bodyClass:"login-page"}}).state("frontend.recover",{url:"^/recover",templateUrl:"app/accounts/views/accountsRecover.html",controller:"accountsRecoverController",data:{pageTitle:"Recover Account",bodyClass:"login-page"}}).state("frontend.recover-delete",{url:"^/recover-delete",templateUrl:"app/accounts/views/accountsRecoverDelete.html",controller:"accountsRecoverDeleteController",data:{pageTitle:"Delete Account",bodyClass:"login-page"}}).state("frontend.verify-recover-delete",{url:"^/verify-recover-delete?userId&token&email",templateUrl:"app/accounts/views/accountsVerifyRecoverDelete.html",controller:"accountsVerifyRecoverDeleteController",data:{pageTitle:"Confirm Delete Account",bodyClass:"login-page"}}).state("frontend.register",{url:"^/register?org&premium",templateUrl:"app/accounts/views/accountsRegister.html",controller:"accountsRegisterController",params:{returnState:null,email:null,org:null,premium:null},data:{pageTitle:"Register",bodyClass:"register-page"}}).state("frontend.organizationAccept",{url:"^/accept-organization?organizationId&organizationUserId&token&email&organizationName",templateUrl:"app/accounts/views/accountsOrganizationAccept.html",controller:"accountsOrganizationAcceptController",data:{pageTitle:"Accept Organization Invite",bodyClass:"login-page",skipAuthorize:!0}}).state("frontend.verifyEmail",{url:"^/verify-email?userId&token",templateUrl:"app/accounts/views/accountsVerifyEmail.html",controller:"accountsVerifyEmailController",data:{pageTitle:"Verifying Email",bodyClass:"login-page",skipAuthorize:!0}})}]).run(["$rootScope","authService","$state",function(e,t,n){e.$on("$stateChangeSuccess",function(){$("html, body").animate({scrollTop:0},200)}),e.$on("$stateChangeStart",function(o,r,a){if(!r.data||!r.data.authorize){if(r.data&&r.data.skipAuthorize)return;if(!t.isAuthenticated())return;return o.preventDefault(),void n.go("backend.user.vault")}if(!t.isAuthenticated())return o.preventDefault(),t.logOut(),void n.go("frontend.login.info");r.name.indexOf("backend.org.")>-1&&a.orgId&&(e.vaultCiphers=e.vaultGroupings=null,t.getUserProfile().then(function(e){var t=e.organizations;t&&a.orgId in t&&2===t[a.orgId].status&&2!==t[a.orgId].type||(o.preventDefault(),n.go("backend.user.vault"))}))})}]),angular.module("bit").constant("constants",{rememberedEmailCookieName:"bit.rememberedEmail",encType:{AesCbc256_B64:0,AesCbc128_HmacSha256_B64:1,AesCbc256_HmacSha256_B64:2,Rsa2048_OaepSha256_B64:3,Rsa2048_OaepSha1_B64:4,Rsa2048_OaepSha256_HmacSha256_B64:5,Rsa2048_OaepSha1_HmacSha256_B64:6},orgUserType:{owner:0,admin:1,user:2},orgUserStatus:{invited:0,accepted:1,confirmed:2},twoFactorProvider:{u2f:4,yubikey:3,duo:2,authenticator:0,email:1,remember:5},cipherType:{login:1,secureNote:2,card:3,identity:4},fieldType:{text:0,hidden:1,boolean:2},deviceType:{android:0,ios:1,chromeExt:2,firefoxExt:3,operaExt:4,edgeExt:5,windowsDesktop:6,macOsDesktop:7,linuxDesktop:8,chrome:9,firefox:10,opera:11,edge:12,ie:13,unknown:14,uwp:16,safari:17,vivaldi:18,vivaldiExt:19},eventType:{User_LoggedIn:1e3,User_ChangedPassword:1001,User_Enabled2fa:1002,User_Disabled2fa:1003,User_Recovered2fa:1004,User_FailedLogIn:1005,User_FailedLogIn2fa:1006,Cipher_Created:1100,Cipher_Updated:1101,Cipher_Deleted:1102,Cipher_AttachmentCreated:1103,Cipher_AttachmentDeleted:1104,Cipher_Shared:1105,Cipher_UpdatedCollections:1106,Collection_Created:1300,Collection_Updated:1301,Collection_Deleted:1302,Group_Created:1400,Group_Updated:1401,Group_Deleted:1402,OrganizationUser_Invited:1500,OrganizationUser_Confirmed:1501,OrganizationUser_Updated:1502,OrganizationUser_Removed:1503,OrganizationUser_UpdatedGroups:1504,Organization_Updated:1600},twoFactorProviderInfo:[{type:0,name:"Authenticator App",description:"Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.",enabled:!1,active:!0,free:!0,image:"authapp.png",displayOrder:0,priority:1,requiresUsb:!1},{type:3,name:"YubiKey OTP Security Key",description:"Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices.",enabled:!1,active:!0,image:"yubico.png",displayOrder:1,priority:3,requiresUsb:!0},{type:2,name:"Duo",description:"Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.",enabled:!1,active:!0,image:"duo.png",displayOrder:2,priority:2,requiresUsb:!1},{type:4,name:"FIDO U2F Security Key",description:"Use any FIDO U2F enabled security key to access your account.",enabled:!1,active:!0,image:"fido.png",displayOrder:3,priority:4,requiresUsb:!0},{type:1,name:"Email",description:"Verification codes will be emailed to you.",enabled:!1,active:!0,free:!0,image:"gmail.png",displayOrder:4,priority:0,requiresUsb:!1}],plans:{free:{basePrice:0,noAdditionalSeats:!0,noPayment:!0,upgradeSortOrder:-1},families:{basePrice:1,annualBasePrice:12,baseSeats:5,noAdditionalSeats:!0,annualPlanType:"familiesAnnually",upgradeSortOrder:1},teams:{basePrice:5,annualBasePrice:60,monthlyBasePrice:8,baseSeats:5,seatPrice:2,annualSeatPrice:24,monthlySeatPrice:2.5,monthPlanType:"teamsMonthly",annualPlanType:"teamsAnnually",upgradeSortOrder:2},enterprise:{seatPrice:3,annualSeatPrice:36,monthlySeatPrice:4,monthPlanType:"enterpriseMonthly",annualPlanType:"enterpriseAnnually",upgradeSortOrder:3}},storageGb:{price:.33,monthlyPrice:.5,yearlyPrice:4},premium:{price:10,yearlyPrice:10}}),angular.module("bit.accounts").controller("accountsLoginController",["$scope","$rootScope","$cookies","apiService","cryptoService","authService","$state","constants","$analytics","$uibModal","$timeout","$window","$filter","toastr",function(e,t,n,o,r,a,i,s,l,c,u,d,p,m){e.state=i,e.twoFactorProviderConstants=s.twoFactorProvider,e.rememberTwoFactor={checked:!1};var g=!0;e.returnState=i.params.returnState,e.stateEmail=i.params.email,!e.returnState&&i.params.org?e.returnState={name:"backend.user.settingsCreateOrg",params:{plan:i.params.org}}:!e.returnState&&i.params.premium&&(e.returnState={name:"backend.user.settingsPremium"}),!(i.current.name.indexOf("twoFactor")>-1)||e.twoFactorProviders&&e.twoFactorProviders.length||i.go("frontend.login.info",{returnState:e.returnState});var f=n.get(s.rememberedEmailCookieName);f||e.stateEmail?(e.model={email:e.stateEmail||f,rememberEmail:null!==f},u(function(){$("#masterPassword").focus()})):u(function(){$("#email").focus()});var h,v;e.twoFactorProviders=null,e.twoFactorProvider=null,e.login=function(t){e.loginPromise=a.logIn(t.email,t.masterPassword).then(function(o){if(t.rememberEmail){var r=new Date;r.setFullYear(r.getFullYear()+10),n.put(s.rememberedEmailCookieName,t.email,{expires:r})}else n.remove(s.rememberedEmailCookieName);o&&Object.keys(o).length>0?(h=t.email,v=t.masterPassword,e.twoFactorProviders=function(e){if(function(){var e;return e=navigator.userAgent||navigator.vendor||window.opera,(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(e)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(e.substr(0,4)))&&(a1842=!0),!a1842&&!navigator.userAgent.match(/iPad/i)}())return e;for(var t=Object.keys(e),n=0;no){if(a[0].type===s.twoFactorProvider.u2f&&!u2f.isSupported)continue;n=a[0].type,o=a[0].priority}}if(null===n)return null;return parseInt(n)}(e.twoFactorProviders),l.eventTrack("Logged In To Two-step"),i.go("frontend.login.twoFactor",{returnState:e.returnState}).then(function(){u(function(){$("#code").focus(),b()})})):(l.eventTrack("Logged In"),y()),t.masterPassword=""})};e.twoFactor=function(t){e.twoFactorProvider!==s.twoFactorProvider.email&&e.twoFactorProvider!==s.twoFactorProvider.authenticator||(t=t.replace(" ","")),e.twoFactorPromise=a.logIn(h,v,t,e.twoFactorProvider,e.rememberTwoFactor.checked||!1),e.twoFactorPromise.then(function(){l.eventTrack("Logged In From Two-step"),y()},function(){e.twoFactorProvider===s.twoFactorProvider.u2f&&b()})},e.anotherMethod=function(){c.open({animation:!0,templateUrl:"app/accounts/views/accountsTwoFactorMethods.html",controller:"accountsTwoFactorMethodsController",resolve:{providers:function(){return e.twoFactorProviders}}}).result.then(function(t){e.twoFactorProvider=t,u(function(){$("#code").focus(),b()})})},e.sendEmail=function(t){if(e.twoFactorProvider===s.twoFactorProvider.email)return r.makeKeyAndHash(h,v).then(function(e){return o.twoFactor.sendEmailLogin({email:h,masterPasswordHash:e.hash}).$promise}).then(function(){t&&m.success("Verification email sent to "+e.twoFactorEmail+".")},function(){m.error("Could not send verification email.")})},e.$on("$destroy",function(){g=!0});function y(){e.returnState?i.go(e.returnState.name,e.returnState.params):i.go("backend.user.vault")}function b(){g=!0;var t;if(e.twoFactorProvider===s.twoFactorProvider.duo)t=e.twoFactorProviders[s.twoFactorProvider.duo],d.Duo.init({host:t.Host,sig_request:t.Signature,submit_callback:function(t){var n=$(t).find('input[name="sig_response"]').val();e.twoFactor(n)}});else if(e.twoFactorProvider===s.twoFactorProvider.u2f){g=!1,t=e.twoFactorProviders[s.twoFactorProvider.u2f];w(JSON.parse(t.Challenges))}else e.twoFactorProvider===s.twoFactorProvider.email&&(t=e.twoFactorProviders[s.twoFactorProvider.email],e.twoFactorEmail=t.Email,Object.keys(e.twoFactorProviders).length>1&&e.sendEmail(!1))}function w(t){g||t.length<1||e.twoFactorProvider!==s.twoFactorProvider.u2f||(console.log("listening for u2f key..."),d.u2f.sign(t[0].appId,t[0].challenge,[{version:t[0].version,keyHandle:t[0].keyHandle}],function(n){if(e.twoFactorProvider===s.twoFactorProvider.u2f)return n.errorCode?(console.log(n.errorCode),void u(function(){w(t)},5===n.errorCode?0:1e3)):void e.twoFactor(JSON.stringify(n))},10))}}]),angular.module("bit.accounts").controller("accountsLogoutController",["$scope","authService","$state","$analytics",function(e,t,n,o){t.logOut(),o.eventTrack("Logged Out"),n.go("frontend.login.info")}]),angular.module("bit.accounts").controller("accountsOrganizationAcceptController",["$scope","$state","apiService","authService","toastr","$analytics",function(e,t,n,o,r,a){e.state={name:t.current.name,params:t.params},t.params.organizationId&&t.params.organizationUserId&&t.params.token&&t.params.email&&t.params.organizationName?e.$on("$viewContentLoaded",function(){o.isAuthenticated()?(e.accepting=!0,n.organizationUsers.accept({orgId:t.params.organizationId,id:t.params.organizationUserId},{token:t.params.token},function(){a.eventTrack("Accepted Invitation"),t.go("backend.user.vault",null,{location:"replace"}).then(function(){r.success("You can access this organization once an administrator confirms your membership. We'll send an email when that happens.","Invite Accepted",{timeOut:1e4})})},function(){a.eventTrack("Failed To Accept Invitation"),t.go("backend.user.vault",null,{location:"replace"}).then(function(){r.error("Unable to accept invitation.","Error")})})):e.loading=!1}):t.go("frontend.login.info").then(function(){r.error("Invalid parameters.")})}]),angular.module("bit.accounts").controller("accountsPasswordHintController",["$scope","$rootScope","apiService","$analytics",function(e,t,n,o){e.success=!1,e.submit=function(t){e.submitPromise=n.accounts.postPasswordHint({email:t.email},function(){o.eventTrack("Requested Password Hint"),e.success=!0}).$promise}}]),angular.module("bit.accounts").controller("accountsRecoverController",["$scope","apiService","cryptoService","$analytics",function(e,t,n,o){e.success=!1,e.submit=function(r){var a=r.email.toLowerCase();e.submitPromise=n.makeKeyAndHash(r.email,r.masterPassword).then(function(e){return t.twoFactor.recover({email:a,masterPasswordHash:e.hash,recoveryCode:r.code.replace(/\s/g,"").toLowerCase()}).$promise}).then(function(){o.eventTrack("Recovered 2FA"),e.success=!0})}}]),angular.module("bit.accounts").controller("accountsRecoverDeleteController",["$scope","$rootScope","apiService","$analytics",function(e,t,n,o){e.success=!1,e.submit=function(t){e.submitPromise=n.accounts.postDeleteRecover({email:t.email},function(){o.eventTrack("Started Delete Recovery"),e.success=!0}).$promise}}]),angular.module("bit.accounts").controller("accountsRegisterController",["$scope","$location","apiService","cryptoService","validationService","$analytics","$state","$timeout",function(e,t,n,o,r,a,i,s){var l=t.search(),c=i.params;e.createOrg=c.org,!c.returnState&&c.org?e.returnState={name:"backend.user.settingsCreateOrg",params:{plan:i.params.org}}:!c.returnState&&c.premium?e.returnState={name:"backend.user.settingsPremium",params:{plan:i.params.org}}:e.returnState=c.returnState,e.success=!1,e.model={email:l.email?l.email:c.email},e.readOnlyEmail=null!==c.email,s(function(){e.model.email?$("#name").focus():$("#email").focus()}),e.registerPromise=null,e.register=function(t){var i=!1;if(e.model.masterPassword.length<8&&(r.addError(t,"MasterPassword","Master password must be at least 8 characters long.",!0),i=!0),e.model.masterPassword!==e.model.confirmMasterPassword&&(r.addError(t,"ConfirmMasterPassword","Master password confirmation does not match.",!0),i=!0),!i){var s,l,c=e.model.email.toLowerCase();e.registerPromise=o.makeKeyAndHash(c,e.model.masterPassword).then(function(e){return s=e,l=o.makeEncKey(e.key),o.makeKeyPair(l.encKey)}).then(function(t){var o={name:e.model.name,email:c,masterPasswordHash:s.hash,masterPasswordHint:e.model.masterPasswordHint,key:l.encKeyEnc,keys:{publicKey:t.publicKey,encryptedPrivateKey:t.privateKeyEnc}};return n.accounts.register(o).$promise},function(e){return r.addError(t,null,"Problem generating keys.",!0),!1}).then(function(t){!1!==t&&(e.success=!0,a.eventTrack("Registered"))})}}}]),angular.module("bit.accounts").controller("accountsTwoFactorMethodsController",["$scope","$uibModalInstance","$analytics","providers","constants",function(e,t,n,o,r){n.eventTrack("accountsTwoFactorMethodsController",{category:"Modal"}),e.providers=[],o.hasOwnProperty(r.twoFactorProvider.authenticator)&&a(r.twoFactorProvider.authenticator),o.hasOwnProperty(r.twoFactorProvider.yubikey)&&a(r.twoFactorProvider.yubikey),o.hasOwnProperty(r.twoFactorProvider.email)&&a(r.twoFactorProvider.email),o.hasOwnProperty(r.twoFactorProvider.duo)&&a(r.twoFactorProvider.duo),o.hasOwnProperty(r.twoFactorProvider.u2f)&&u2f.isSupported&&a(r.twoFactorProvider.u2f),e.choose=function(e){t.close(e.type)},e.close=function(){t.dismiss("close")};function a(t){for(var n=0;n1&&(n=function(e,t){var n=e.split(" ");if(n&&n.length>1){for(var o="",r=0;r').attr({y:"50%",x:"50%",dy:"0.35em","pointer-events":"auto",fill:i,"font-family":s}).text(a).css({"font-weight":l,"font-size":c+"px"})),g=o.bgColor?o.bgColor:function(e){var t=0,n=0;for(n=0;n>8*n&255).toString(16)).substr(-2);return o}(r),f=(u=o.width,d=o.height,p=g,angular.element("").attr({xmlns:"http://www.w3.org/2000/svg","pointer-events":"none",width:u,height:d}).css({"background-color":p,width:u+"px",height:d+"px"}));f.append(m);var h=angular.element("
").append(f).html(),v="data:image/svg+xml;base64,"+window.btoa(unescape(encodeURIComponent(h))),y=angular.element("").attr({src:v,title:e.data});"true"===o.round&&y.css("border-radius","50%"),"true"===o.border&&y.css("border",o.borderStyle),o.class&&y.addClass(o.class),"true"===o.dynamic?(t.empty(),t.append(y)):t.replaceWith(y)}}}}),angular.module("bit.directives").directive("masterPassword",["cryptoService","authService",function(e,t){return{require:"ngModel",restrict:"A",link:function(n,o,r,a){t.getUserProfile().then(function(t){a.$parsers.unshift(function(n){if(n)return e.makeKey(n,t.email).then(function(t){var o=t.keyB64===e.getKey().keyB64;return a.$setValidity("masterPassword",o),o?n:void 0})}),a.$formatters.unshift(function(n){if(n)return e.makeKey(n,t.email).then(function(t){var o=t.keyB64===e.getKey().keyB64;return a.$setValidity("masterPassword",o),n})})})}}}]),angular.module("bit.directives").directive("pageTitle",["$rootScope","$timeout","appSettings",function(e,t,n){return{link:function(n,o){e.$on("$stateChangeStart",function(e,n,r,a,i){var s="bitwarden Web Vault";n.data&&n.data.pageTitle&&(s=n.data.pageTitle+" - "+s),t(function(){o.text(s)})})}}}]),angular.module("bit.directives").directive("passwordMeter",function(){return{template:'
{{value}}%
',restrict:"A",scope:{password:"=passwordMeter",username:"=passwordMeterUsername",outerClass:"@?"},link:function(e){var t=function(e){e.value=function(e,t){if(!t||t===e)return 0;var n=t.length;return e&&""!==e&&(-1!==e.indexOf(t)&&(n-=15),-1!==t.indexOf(e)&&(n-=e.length)),t.length>0&&t.length<=4?n+=t.length:t.length>=5&&t.length<=7?n+=6:t.length>=8&&t.length<=15?n+=12:t.length>=16&&(n+=18),t.match(/[a-z]/)&&(n+=1),t.match(/[A-Z]/)&&(n+=5),t.match(/\d/)&&(n+=5),t.match(/.*\d.*\d.*\d/)&&(n+=5),t.match(/[!,@,#,$,%,^,&,*,?,_,~]/)&&(n+=5),t.match(/.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~]/)&&(n+=5),t.match(/(?=.*[a-z])(?=.*[A-Z])/)&&(n+=2),t.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)&&(n+=2),t.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&,*,?,_,~])/)&&(n+=2),n=Math.round(2*n),Math.max(0,Math.min(100,n))}(e.username,e.password),e.valueClass=function(e){switch(Math.round(e/33)){case 0:case 1:return"danger";case 2:return"warning";case 3:return"success"}}(e.value)};e.$watch("password",function(){t(e)}),e.$watch("username",function(){t(e)})}}}),angular.module("bit.directives").directive("passwordViewer",function(){return{restrict:"A",link:function(e,t,n){var o=n.passwordViewer;o&&(t.onclick=function(e){},t.on("click",function(e){var n=$(o);n&&"password"===n.attr("type")?(t.removeClass("fa-eye").addClass("fa-eye-slash"),n.attr("type","text")):n&&"text"===n.attr("type")&&(t.removeClass("fa-eye-slash").addClass("fa-eye"),n.attr("type","password"))}))}}}),angular.module("bit.directives").directive("stopClick",function(){return function(e,t,n){$(t).click(function(e){e.preventDefault()})}}),angular.module("bit.directives").directive("stopProp",function(){return function(e,t,n){$(t).click(function(e){e.stopPropagation()})}}),angular.module("bit.directives").directive("totp",["$timeout","$q",function(e,t){return{template:'
{{sec}}{{codeFormatted}}
',restrict:"A",scope:{key:"=totp"},link:function(n){var o=null,r=new function(){var e="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",n=function(e,t,n){return t+1>=e.length&&(e=Array(t+1-e.length).join(n)+e),e},o=function(e){return parseInt(e,16)},r=function(e){for(var t=new Uint8Array(e.length/2),n=0;n>>4).toString(16)),n.push((15&t[o]).toString(16));return n.join("")}(e)}).catch(function(e){return null})};this.getCode=function(e){var s,l=Math.round((new Date).getTime()/1e3),c=n((s=Math.floor(l/30),(s<15.5?"0":"")+Math.round(s).toString(16)),16,"0"),u=r(c),d=a(e);return d.length&&u.length?i(d,u).then(function(e){if(!e)return null;var t=o(e.substring(e.length-1)),n=(o(e.substr(2*t,8))&o("7fffffff"))+"";return n=n.substr(n.length-6,6)}):t(function(e,t){e(null)})}},a=function(t){r.getCode(t.key).then(function(n){e(function(){n?(t.codeFormatted=n.substring(0,3)+" "+n.substring(3),t.code=n):(t.code=null,o&&clearInterval(o))})})},i=function(t){e(function(){var e=Math.round((new Date).getTime()/1e3)%30,n=30-e;t.sec=n,t.dash=(2.62*e).toFixed(2),t.low=n<=7,0===e&&a(t)})};n.$watch("key",function(){if(!n.key)return n.code=null,void(o&&clearInterval(o));a(n),i(n),o&&clearInterval(o),o=setInterval(function(){i(n)},1e3)}),n.$on("$destroy",function(){o&&clearInterval(o)}),n.clipboardError=function(e){alert("Your web browser does not support easy clipboard copying.")}}}}]),angular.module("bit.global").controller("appsController",["$scope","$state",function(e,t){}]),angular.module("bit.global").controller("mainController",["$scope","$state","authService","appSettings","toastr","$window","$document","cryptoService","$uibModal","apiService",function(e,t,n,o,r,a,i,s,l,c){var u=this;u.skinClass=o.selfHosted?"skin-blue-light":"skin-blue",u.bodyClass="",u.usingControlSidebar=u.openControlSidebar=!1,u.searchVaultText=null,u.version=o.version,u.outdatedBrowser=-1!==a.navigator.userAgent.indexOf("MSIE")||-1!==a.navigator.userAgent.indexOf("SamsungBrowser"),e.currentYear=(new Date).getFullYear(),e.$on("$viewContentLoaded",function(){n.getUserProfile().then(function(e){u.userProfile=e}),$.AdminLTE&&($.AdminLTE.layout&&($.AdminLTE.layout.fix(),$.AdminLTE.layout.fixSidebar()),$.AdminLTE.pushMenu&&$.AdminLTE.pushMenu.expandOnHover(),i.off("click",".sidebar li a"))}),e.$on("$stateChangeSuccess",function(e,t,n,o,r){u.usingEncKey=!!s.getEncKey(),u.searchVaultText=null,t.data.bodyClass?u.bodyClass=t.data.bodyClass:(u.bodyClass="",u.usingControlSidebar=!!t.data.controlSidebar,u.openControlSidebar=u.usingControlSidebar&&i.width()>768)}),e.$on("setSearchVaultText",function(e,t){u.searchVaultText=t}),e.addCipher=function(){e.$broadcast("vaultAddCipher")},e.addFolder=function(){e.$broadcast("vaultAddFolder")},e.addOrganizationCipher=function(){e.$broadcast("organizationVaultAddCipher")},e.addOrganizationCollection=function(){e.$broadcast("organizationCollectionsAdd")},e.inviteOrganizationUser=function(){e.$broadcast("organizationPeopleInvite")},e.addOrganizationGroup=function(){e.$broadcast("organizationGroupsAdd")},e.updateKey=function(){l.open({animation:!0,templateUrl:"app/settings/views/settingsUpdateKey.html",controller:"settingsUpdateKeyController"})},e.verifyEmail=function(){e.sendingVerify||(e.sendingVerify=!0,c.accounts.verifyEmail({},null).$promise.then(function(){r.success("Verification email sent."),e.sendingVerify=!1,e.verifyEmailSent=!0}).catch(function(){r.success("Verification email failed."),e.sendingVerify=!1}))},e.updateBrowser=function(){a.open("https://browser-update.org/update.html","_blank")};var d,p,m,g={scrollbarWidth:function(){if(!d){var e=$("body");e.addClass("bit-position-body-scrollbar-measure"),d=a.innerWidth-e[0].clientWidth,d=isFinite(d)?d:0,e.removeClass("bit-position-body-scrollbar-measure")}return d},scrollbarInfo:function(){return{width:g.scrollbarWidth(),visible:i.height()>$(a).height()}}};$(window).on("show.bs.dropdown",function(e){var t=m=$(e.target),n=t.data("appendTo");if(!n)return!0;p=t.find(".dropdown-menu");$(n).append(p.detach());var o=t.offset(),r={display:"block",top:o.top+t.outerHeight()-("body"!==n?$(window).scrollTop():0)};if(p.hasClass("dropdown-menu-right")){var i=g.scrollbarInfo(),s=0;i.visible&&i.width&&(s=i.width),r.right=a.innerWidth-s-(o.left+t.prop("offsetWidth"))+"px",r.left="auto"}else r.left=o.left+"px",r.right="auto";p.css(r)}),$(window).on("hide.bs.dropdown",function(e){if(!p)return!0;$(e.target).append(p.detach()),p.hide(),p=null,m=null}),e.$on("removeAppendedDropdownMenu",function(e,t){if(!p&&!m)return!0;m.append(p.detach()),p.hide(),p=null,m=null})}]),angular.module("bit.global").controller("paidOrgRequiredController",["$scope","$state","$uibModalInstance","$analytics","$uibModalStack","orgId","constants","authService",function(e,t,n,o,r,a,i,s){o.eventTrack("paidOrgRequiredController",{category:"Modal"}),s.getUserProfile().then(function(t){e.admin=t.organizations[a].type!==i.orgUserType.user}),e.go=function(){e.admin&&(o.eventTrack("Get Paid Org"),t.go("backend.org.billing",{orgId:a}).then(function(){r.dismissAll()}))},e.close=function(){n.dismiss("close")}}]),angular.module("bit.global").controller("premiumRequiredController",["$scope","$state","$uibModalInstance","$analytics","$uibModalStack",function(e,t,n,o,r){o.eventTrack("premiumRequiredController",{category:"Modal"}),e.go=function(){o.eventTrack("Get Premium"),t.go("backend.user.settingsPremium").then(function(){r.dismissAll()})},e.close=function(){n.dismiss("close")}}]),angular.module("bit.global").controller("sideNavController",["$scope","$state","authService","toastr","$analytics","constants","appSettings",function(e,t,n,o,r,a,i){e.$state=t,e.params=t.params,e.orgs=[],e.name="",i.selfHosted?(e.orgIconBgColor="#ffffff",e.orgIconBorder="3px solid #a0a0a0",e.orgIconTextColor="#333333"):(e.orgIconBgColor="#2c3b41",e.orgIconBorder="3px solid #1a2226",e.orgIconTextColor="#ffffff"),n.getUserProfile().then(function(n){if(e.name=n.extended&&n.extended.name?n.extended.name:n.email,n.organizations)if(t.includes("backend.org")&&t.params.orgId in n.organizations)e.orgProfile=n.organizations[t.params.orgId];else{var o=[];for(var r in n.organizations)n.organizations.hasOwnProperty(r)&&(n.organizations[r].enabled||n.organizations[r].type<2)&&o.push(n.organizations[r]);e.orgs=o}}),e.viewOrganization=function(e){e.type!==a.orgUserType.user?(r.eventTrack("View Organization From Side Nav"),t.go("backend.org.dashboard",{orgId:e.id})):o.error("You cannot manage this organization.")},e.searchVault=function(){t.go("backend.user.vault")},e.searchOrganizationVault=function(){t.go("backend.org.vault",{orgId:t.params.orgId})},e.isOrgOwner=function(e){return e&&e.type===a.orgUserType.owner}}]),angular.module("bit.global").controller("topNavController",["$scope",function(e){e.toggleControlSidebar=function(){var e=$("body");e.hasClass("control-sidebar-open")?e.removeClass("control-sidebar-open"):e.addClass("control-sidebar-open")}}]),angular.module("bit.filters").filter("enumLabelClass",function(){return function(e,t){if("number"!=typeof e)return e.toString();var n;switch(t){case"OrgUserStatus":switch(e){case 0:n="label-default";break;case 1:n="label-warning";break;case 2:default:n="label-success"}break;default:n="label-default"}return n}}),angular.module("bit.filters").filter("enumName",function(){return function(e,t){if("number"!=typeof e)return e.toString();var n;switch(t){case"OrgUserStatus":switch(e){case 0:n="Invited";break;case 1:n="Accepted";break;case 2:default:n="Confirmed"}break;case"OrgUserType":switch(e){case 0:n="Owner";break;case 1:n="Admin";break;case 2:default:n="User"}break;default:n=e.toString()}return n}}),angular.module("bit.tools").controller("reportsBreachController",["$scope","apiService","toastr","authService",function(e,t,n,o){e.loading=!0,e.error=!1,e.breachAccounts=[],e.email=null,e.$on("$viewContentLoaded",function(){o.getUserProfile().then(function(n){return e.email=n.email,t.hibp.get({email:e.email}).$promise}).then(function(t){for(var n=[],o=0;o0,meta:{},icon:null},i=t.Data;if(i){switch(o.name=s.decryptProperty(i.Name,n,!1,!0),o.type){case r.cipherType.login:o.subTitle=s.decryptProperty(i.Username,n,!0,!0),o.meta.password=s.decryptProperty(i.Password,n,!0,!0),o.meta.uri=s.decryptProperty(i.Uri,n,!0,!0),function(e,t,n){if(!s.disableWebsiteIcons&&t){var o=t,r=!1;if(0===o.indexOf("androidapp://")?e.icon="fa-android":0===o.indexOf("iosapp://")?e.icon="fa-apple":-1===o.indexOf("://")&&o.indexOf(".")>-1?(o="http://"+o,r=!0):r=0===o.indexOf("http")&&o.indexOf(".")>-1,n&&r)try{var i=new URL(o);e.meta.image=a.iconsUri+"/"+i.hostname+"/icon.png"}catch(e){}}e.icon||(e.icon="fa-globe")}(o,o.meta.uri,!0);break;case r.cipherType.secureNote:o.subTitle=null,o.icon="fa-sticky-note-o";break;case r.cipherType.card:o.subTitle="",o.meta.number=s.decryptProperty(i.Number,n,!0,!0);var l=s.decryptProperty(i.Brand,n,!0,!0);l&&(o.subTitle=l),o.meta.number&&o.meta.number.length>=4&&(""!==o.subTitle&&(o.subTitle+=", "),o.subTitle+="*"+o.meta.number.substr(o.meta.number.length-4)),o.icon="fa-credit-card";break;case r.cipherType.identity:var c=s.decryptProperty(i.FirstName,n,!0,!0),u=s.decryptProperty(i.LastName,n,!0,!0);o.subTitle="",c&&(o.subTitle=c),u&&(""!==o.subTitle&&(o.subTitle+=" "),o.subTitle+=u),o.icon="fa-id-card-o"}""===o.subTitle&&(o.subTitle=null)}return o};s.decryptAttachment=function(t,n){if(!n)throw"encryptedAttachment is undefined or null";return{id:n.Id,url:n.Url,fileName:e.decrypt(n.FileName,t),size:n.SizeName}},s.downloadAndDecryptAttachment=function(t,r,a){var i=n.defer(),s=new XMLHttpRequest;return s.open("GET",r.url,!0),s.responseType="arraybuffer",s.onload=function(n){s.response?e.decryptFromBytes(s.response,t).then(function(e){if(a){var t=new Blob([e]);if(o.navigator.msSaveOrOpenBlob)o.navigator.msSaveBlob(t,r.fileName);else{var n=o.document.createElement("a");n.href=o.URL.createObjectURL(t),n.download=r.fileName,o.document.body.appendChild(n),n.click(),o.document.body.removeChild(n)}}i.resolve(new Uint8Array(e))}):i.reject("No response")},s.send(null),i.promise},s.decryptFields=function(e,t){var n=[];if(t)for(var o=0;o104857600)){var a=new FileReader;return a.readAsArrayBuffer(o),a.onload=function(n){e.encryptToBytes(n.target.result,t).then(function(n){r.resolve({fileName:e.encrypt(o.name,t),data:new Uint8Array(n),size:o.size})})},a.onerror=function(e){r.reject("Error reading file.")},r.promise}r.reject("Maximum file size is 100 MB.")}},s.encryptFields=function(e,t){if(!e||!e.length)return null;for(var n=[],o=0;o2){var d=forge.util.decode64(a[2]),p=g(l+c,n.macKey,!1);if(!h(n.macKey,d,p))return console.error("MAC failed."),null}var m=forge.util.createBuffer(c),f=forge.cipher.createDecipher("AES-CBC",n.encKey);return f.start({iv:l}),f.update(m),f.finish(),"utf8"===(o=o||"utf8")?f.output.toString("utf8"):f.output.getBytes()}catch(e){throw console.error("Caught unhandled error in decrypt: "+e),e}},u.decryptFromBytes=function(e,n){try{if(!e)throw"no encBuf.";var o=new Uint8Array(e),r=o[0],a=null,i=null,s=null;switch(r){case t.encType.AesCbc128_HmacSha256_B64:case t.encType.AesCbc256_HmacSha256_B64:if(o.length<=49)return console.error("Enc type ("+r+") not valid."),null;i=w(o,1,17),s=w(o,17,49),a=w(o,49);break;case t.encType.AesCbc256_B64:if(o.length<=17)return console.error("Enc type ("+r+") not valid."),null;i=w(o,1,17),a=w(o,17);break;default:return console.error("Enc type ("+r+") not supported."),null}return function(e,t,n,o,r){if(!(r=r||u.getEncKey()||u.getKey()))throw"Encryption key unavailable.";if(e!==r.encType)throw"encType unavailable.";var a=r.getBuffers(),i=null;return p.importKey("raw",a.encKey,{name:"AES-CBC"},!1,["decrypt"]).then(function(e){if(i=e,!r.macKey||!o)return null;var s=new Uint8Array(n.byteLength+t.byteLength);return s.set(new Uint8Array(n),0),s.set(new Uint8Array(t),n.byteLength),f(s.buffer,a.macKey)}).then(function(e){return null===e?null:function(e,t,n){var o,r;return window.crypto.subtle.importKey("raw",e,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]).then(function(e){return r=e,window.crypto.subtle.sign({name:"HMAC",hash:{name:"SHA-256"}},r,t)}).then(function(e){return o=e,window.crypto.subtle.sign({name:"HMAC",hash:{name:"SHA-256"}},r,n)}).then(function(e){if(o.byteLength!==e.byteLength)return!1;for(var t=new Uint8Array(o),n=new Uint8Array(e),r=0;r1){var l=forge.util.decode64(a[1]),c=g(s,o.macKey,!1);if(!h(o.macKey,l,c))return console.error("MAC failed."),null}var d;if(r===t.encType.Rsa2048_OaepSha256_B64||r===t.encType.Rsa2048_OaepSha256_HmacSha256_B64)d=forge.md.sha256.create();else{if(r!==t.encType.Rsa2048_OaepSha1_B64&&r!==t.encType.Rsa2048_OaepSha1_HmacSha256_B64)throw"encType unavailable.";d=forge.md.sha1.create()}return n.decrypt(s,"RSA-OAEP",{md:d})};function g(e,t,n){var o=forge.hmac.create();o.start("sha256",t),o.update(e);var r=o.digest();return n?forge.util.encode64(r.getBytes()):r.getBytes()}function f(e,t){return p.importKey("raw",t,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]).then(function(t){return p.sign({name:"HMAC",hash:{name:"SHA-256"}},t,e)})}function h(e,t,n){var o=forge.hmac.create();return o.start("sha256",e),o.update(t),t=o.digest().getBytes(),o.start(null,null),o.update(n),t===(n=o.digest().getBytes())}function v(e,n,o){if(n&&(e=forge.util.decode64(e)),!e)throw"Must provide keyBytes";var r=forge.util.createBuffer(e);if(!r||0===r.length())throw"Couldn't make buffer";var a=r.length();if(null===o||void 0===o)if(32===a)o=t.encType.AesCbc256_B64;else{if(64!==a)throw"Unable to determine encType.";o=t.encType.AesCbc256_HmacSha256_B64}if(this.key=e,this.keyB64=forge.util.encode64(e),this.encType=o,o===t.encType.AesCbc256_B64&&32===a)this.encKey=e,this.macKey=null;else if(o===t.encType.AesCbc128_HmacSha256_B64&&32===a)this.encKey=r.getBytes(16),this.macKey=r.getBytes(16);else{if(o!==t.encType.AesCbc256_HmacSha256_B64||64!==a)throw"Unsupported encType/key length.";this.encKey=r.getBytes(32),this.macKey=r.getBytes(32)}}v.prototype.getBuffers=function(){if(this.keyBuf)return this.keyBuf;var e=function(e){for(var t=o.atob(e),n=new Uint8Array(t.length),r=0;r"+t+"":""+t+""}function r(e){var t=e.GroupId.substring(0,8);return'"+t+""}function a(e){var t=e.CollectionId.substring(0,8);return'"+t+""}function i(e){var t=e.OrganizationUserId.substring(0,8);return'"+t+""}return n}]),angular.module("bit.services").factory("importService",["constants",function(e){var t={};t.import=function(t,p,m,g){if(p){switch(t){case"bitwardencsv":K=p,N=m,Papa.parse(K,{header:!0,encoding:"UTF-8",complete:function(t){l(t);var n=[],o=[],r=[],a=0;angular.forEach(t.data,function(t,i){var l=n.length,c=o.length,u=t.folder&&""!==t.folder,d=u;if(u)for(a=0;ag+2&&(f.value=m[a].substr(g+2)),p.fields.push(f)}}}switch(t.type){case"login":case null:case void 0:p.type=e.cipherType.login;var h=t.login_totp||t.totp,v=t.login_uri||t.uri,y=t.login_username||t.username,b=t.login_password||t.password;p.login={totp:h&&""!==h?h:null,uri:v&&""!==v?s(v):null,username:y&&""!==y?y:null,password:b&&""!==b?b:null};break;case"note":p.type=e.cipherType.secureNote,p.secureNote={type:0}}if(o.push(p),d&&n.push({name:t.folder}),u){var w={key:c,value:l};r.push(w)}}),N(n,o,r)}});break;case"lastpass":d(p,m,g,!1);break;case"safeincloudxml":!function(t,n,o){var r=[],a=[],i=[],l=[],c=0,d=0;u(t,function(t){var u=$(t).find("database");if(u.length){var p=u.find("> label");if(p.length)for(c=0;c card");if(g.length)for(c=0;c field");for(d=0;d200?h.notes+=C+": "+b+"\n":(h.fields||(h.fields=[]),h.fields.push({name:C,value:b,type:e.fieldType.text})))}}var S=f.find("> notes");for(d=0;d label_id")).length){var k=$(p[0]).text(),T=l[k];null!==k&&""!==k&&null!==T&&i.push({key:a.length-1,value:T})}}}n(r,a,i)}else o()},o)}(p,m,g);break;case"keepass2xml":!function(t,n,o){var r=[],a=[],s=[];u(t,function(e){var t=$(e).find("Root");if(t.length){var i=t.find("> Group");i.length&&(l($(i[0]),!0,""),n(r,a,s))}else o()},o);function l(t,n,o){var c=r.length,u=o;n||(""!==u&&(u+=" > "),u+=t.find("> Name").text(),r.push({name:u}));var d=t.find("> Entry");if(d.length)for(var p=0;p String"),v=0;v Key").text(),w=y.find("> Value").text();if(""!==w)switch(b){case"URL":f.login.uri=i(w);break;case"UserName":f.login.username=w;break;case"Password":f.login.password=w;break;case"Title":f.name=w;break;case"Notes":f.notes=null===f.notes?w+"\n":f.notes+w+"\n";break;default:w.length>200||w.indexOf("\n")>-1?(f.notes||(f.notes=""),f.notes+=b+": "+w+"\n"):(f.fields||(f.fields=[]),f.fields.push({name:b,value:w,type:e.fieldType.text}))}}null===f.name&&(f.name="--"),a.push(f),n||s.push({key:g,value:c})}var C=t.find("> Group");if(C.length)for(var S=0;S "):null,l=n.length,c=o.length,u=null!==s,d=u,p=0;if(u)for(p=0;p-1||l.length>200?(null===n.notes?n.notes="":n.notes+="\n",n.notes+=c+": "+l.split("\\r\\n").join("\n").split("\\n").join("\n")):(n.fields||(n.fields=[]),n.fields.push({name:c,value:l,type:e.fieldType.text}))}}}}c(t,function(t){var o=t.split(/(?:\r\n|\r|\n)/);for(s=0;s=6){var i=n.length,l=o.length,c=t[0]&&""!==t[0],u=c,d=0;if(c)for(d=0;d6)for(d=6;d200?(p.notes||(p.notes=""),p.notes+=t[d]+": "+t[d+1]+"\n"):(p.fields||(p.fields=[]),p.fields.push({name:t[d],value:t[d+1],type:e.fieldType.text}));if(o.push(p),u&&n.push({name:t[0]}),c){var m={key:l,value:i};r.push(m)}}}),z(n,o,r)}});break;case"passworddragonxml":!function(t,n,o){var r=[],a=[],i=[],l=0;u(t,function(t){var c=$(t).find("PasswordManager");if(c.length){var u=c.find("> record");if(u.length)for(var d=0;d Account-Name"),g=m.length?$(m):null,f=p.find("> User-Id"),h=f.length?$(f):null,v=p.find("> Password"),y=v.length?$(v):null,b=p.find("> URL"),w=b.length?$(b):null,C=p.find("> Notes"),S=C.length?$(C):null,k=p.find("> Category"),T=k.length?$(k):null,P=T?T.text():null,I=r.length,E=a.length,z=P&&""!==P&&"Unfiled"!==P,O=z;if(z)for(l=0;l Attribute-"+l,l<10&&(U+=", ");var x=p.find(U);if(x.length)for(l=0;l200?(A.notes||(A.notes=""),A.notes+=F+": "+M+"\n"):(A.fields||(A.fields=[]),A.fields.push({name:F,value:M,type:e.fieldType.text})))}if(a.push(A),O&&r.push({name:P}),z){var G={key:E,value:I};i.push(G)}}n(r,a,i)}else o()},o)}(p,m,g);break;case"enpasscsv":P=p,I=m,Papa.parse(P,{encoding:"UTF-8",complete:function(t){l(t);for(var n=[],o=0;o2&&r.length%2==0)for(var c=0;c200?(i.notes||(i.notes=""),i.notes+=d+": "+u+"\n"):(i.fields||(i.fields=[]),i.fields.push({name:d,value:u,type:e.fieldType.text})):i.login.totp=u:i.login.password=u:i.login.username=u:i.login.uri=s(u)}}n.push(i)}}I([],n,[])}});break;case"pwsafexml":!function(t,n,o){var r=[],a=[],i=[],l=0;u(t,function(t){var c=$(t).find("passwordsafe");if(c.length){var u=c.attr("delimiter"),d=c.find("> entry");if(d.length)for(var p=0;p title"),f=g.length?$(g):null,h=m.find("> username"),v=h.length?$(h):null,y=m.find("> email"),b=y.length?$(y):null,w=b?b.text():null,C=m.find("> password"),S=C.length?$(C):null,k=m.find("> url"),T=k.length?$(k):null,P=m.find("> notes"),I=P.length?$(P):null,E=I?I.text().split(u).join("\n"):null,z=m.find("> group"),O=z.length?$(z):null,A=O?O.text().split(".").join(" > "):null,U=r.length,x=a.length,D=A&&""!==A,F=D;if(D)for(l=0;l Groups > Group[ID="'+t+'"]');if(o.length){n&&""!==n&&(n=" > "+n),n=o.attr("Name")+n;var r=o.attr("ParentID");return c(e,r,n)}return n}u(t,function(t){var u=$(t).find("root > Database");if(u.length){var d=u.find("> Logins > Login");if(d.length)for(var p=0;p Accounts > Account > LoginLinks > Login[SourceLoginID="'+h+'"]');if(S.length){var k=S.parent().parent();k.length&&(v=k.attr("Name"),y=k.attr("Link"),w=k.attr("ParentID"),(b=k.attr("Comments"))&&(b=b.split("/n").join("\n")))}}w&&""!==w&&(C=c(u,w,""));var T=r.length,P=a.length,I=C&&""!==C,E=I;if(I)for(l=0;l=3){var i=n.length,l=o.length,c=t[0]&&""!==t[0]&&"Unassigned"!==t[0],u=c,d=0;if(c)for(d=0;d3)for(var m=3;m200?(o.notes||(o.notes=""),o.notes+=r+": "+t[r]+"\n"):(o.fields||(o.fields=[]),o.fields.push({name:r,value:t[r],type:e.fieldType.text})))}a.push(o)}),n(r,a,[])}})}(p,m);break;case"clipperzhtml":!function(t,r,i){var l=[],u=[];c(t,function(t){var i=$(t).find("textarea"),c=i&&i.length?i.val():null,d=c?JSON.parse(c):null;if(d&&d.length)for(var p=0;p200?(g.notes||(g.notes=""),g.notes+=h.label+": "+h.value+"\n"):(g.fields||(g.fields=[]),g.fields.push({name:h.label,value:h.value,type:e.fieldType.text}))}}""===g.notes&&(g.notes=null),u.push(g)}r(l,u,[])},i)}(p,m,g);break;case"avirajson":!function(t,n,o){var r=[],a=[],s=0;c(t,function(t){var o=JSON.parse(t);if(o&&o.accounts)for(s=0;s").join("")).find("table.nobr");if(s.length)for(var c=0;c200?(p.notes||(p.notes=""),p.notes+=h+": "+v+"\n"):(p.fields||(p.fields=[]),p.fields.push({name:h,value:v,type:e.fieldType.text}))}p.notes&&""!==p.notes||(p.notes=null),p.name&&""!==p.name||(p.name="--"),u.push(p)}r(l,u,[])},s)}(p,m,g);break;case"saferpasscsv":!function(t,n,o){var r=[],a=[];Papa.parse(t,{header:!0,encoding:"UTF-8",complete:function(t){l(t),angular.forEach(t.data,function(t,n){a.push({type:e.cipherType.login,favorite:!1,notes:t.notes&&""!==t.notes?t.notes:null,name:t.url&&""!==t.url?function(e){var t=document.createElement("a");return t.href=e,t.hostname.startsWith("www.")?t.hostname.replace("www.",""):t.hostname}(t.url):"--",login:{uri:t.url&&""!==t.url?s(t.url):null,username:t.username&&""!==t.username?t.username:null,password:t.password&&""!==t.password?t.password:null}})}),n(r,a,[])}})}(p,m);break;case"ascendocsv":b=p,w=m,Papa.parse(b,{encoding:"UTF-8",complete:function(t){l(t);for(var s=[],c=0;c2&&u.length%2==0)for(var m=0;m200?(p.notes||(p.notes=""),p.notes+=f+": "+g+"\n"):(p.fields||(p.fields=[]),p.fields.push({name:f,value:g,type:e.fieldType.text})))}s.push(p)}}w([],s,[])}});break;case"passwordbossjson":!function(t,n,o){var r=[],a=[],s=0;c(t,function(t){var o=JSON.parse(t);if(o&&o.length)for(s=0;s200?(c.notes||(c.notes=""),c.notes+=u+": "+d+"\n"):(c.fields||(c.fields=[]),c.fields.push({name:u,value:d,type:e.fieldType.text}))}""===c.notes&&(c.notes=null),a.push(c)}}n(r,a,[])},o)}(p,m,g);break;case"zohovaultcsv":!function(t,n,o){function r(t,n){if(t&&""!==t)for(var o=t.split(/(?:\r\n|\r|\n)/),r=0;ri?a.substring(i+1):null;if(s&&""!==s&&l&&""!==l&&"SecretType"!==s){var c=s.toLowerCase();"user name"===c?n.login.username=l:"password"===c?n.login.password=l:l.length>200?(n.notes||(n.notes=""),n.notes+=s+": "+l+"\n"):(n.fields||(n.fields=[]),n.fields.push({name:s,value:l,type:e.fieldType.text}))}}}}Papa.parse(t,{header:!0,encoding:"UTF-8",complete:function(t){l(t);var o=[],a=[],s=[];angular.forEach(t.data,function(t,n){var l=t.ChamberName,c=o.length,u=a.length,d=l&&""!==l,p=d,m=0;if(d)for(m=0;m2&&a(2,c,b),b.name&&"--"!==b.name&&"Web Logins"!==p&&"Servers"!==p&&"Email Accounts"!==p&&(b.name=p+": "+b.name),""===b.notes&&(b.notes=null),o.push(b),h&&n.push({name:u}),f){var w={key:g,value:m};r.push(w)}}y(n,o,r)}});break;case"meldiumcsv":f=p,h=m,Papa.parse(f,{header:!0,encoding:"UTF-8",complete:function(t){l(t);for(var n=[],o=0;o30&&(m.name=m.name.substring(0,30));for(var g in p.attributes)p.attributes.hasOwnProperty(g)&&"username_value"!==g&&"xdg:schema"!==g&&(""!==m.notes&&(m.notes+="\n"),m.notes+=g+": "+p.attributes[g]);""===m.notes&&(m.notes=null),a.push(m),s.push({key:u,value:c})}}n(r,a,s)},o)}(p,m,g);break;default:g()}var f,h,v,y,b,w,C,S,k,T,P,I,E,z,O,A,U,x,D,F,M,G,K,N}else g()},t.importOrg=function(t,n,o,r){if(n){switch(t){case"bitwardencsv":a=n,i=o,Papa.parse(a,{header:!0,encoding:"UTF-8",complete:function(t){l(t);var n,o=[],r=[],a=[];angular.forEach(t.data,function(t,i){var l=r.length;if(t.collections&&""!==t.collections){var c=t.collections.split(",");for(n=0;nf+2&&(h.value=g[n].substr(f+2)),m.fields.push(h)}}}switch(t.type){case"login":case null:case void 0:m.type=e.cipherType.login;var v=t.login_totp||t.totp,y=t.login_uri||t.uri,b=t.login_username||t.username,w=t.login_password||t.password;m.login={totp:v&&""!==v?v:null,uri:y&&""!==y?s(y):null,username:b&&""!==b?b:null,password:w&&""!==w?w:null};break;case"note":m.type=e.cipherType.secureNote,m.secureNote={type:0}}r.push(m)}),i(o,r,a)}});break;case"lastpass":d(n,o,r,!0);break;default:r()}var a,i}else r()};var n=["password","pass word","passphrase","pass phrase","pass","code","code word","codeword","secret","secret word","key","keyword","key word","keyphrase","key phrase","form_pw","wppassword","pin","pwd","pw","pword","passwd","p","serial","serial#","license key","reg #","passwort"],o=["user","name","user name","username","login name","email","e-mail","id","userid","user id","login","form_loginname","wpname","mail","loginid","login id","log","first name","last name","card#","account #","member","member #","nom","benutzername"],r=["url","hyper link","hyperlink","link","host","hostname","host name","server","address","hyper ref","href","web","website","web site","site","web-site","uri","ort","adresse"];function a(e,t){if(!e||""===e)return!1;e=e.trim().toLowerCase();for(var n=0;n=0&&(e="http://"+e),s(e)}function s(e){return e.length>1e3?e.substring(0,1e3):e}function l(e){if(e.errors&&e.errors.length)for(var t=0;t-1||!a[1]||""===a[1]||("Notes"===a[0]?o.notes?o.notes+="\n"+a[1]:o.notes=a[1]:t.hasOwnProperty(a[0])?o.dataObj[t[a[0]]]=a[1]:(o.notes?o.notes+="\n":o.notes="",o.notes+=a[0]+": "+a[1]))}return o}function c(e){var t={cardholderName:e.ccname&&""!==e.ccname?e.ccname:null,number:e.ccnum&&""!==e.ccnum?e.ccnum:null,brand:e.ccnum&&""!==e.ccnum?function(e){if(!e)return null;var t=new RegExp("^4");return null!=e.match(t)?"Visa":/^(5[1-5][0-9]{14}|2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12}))$/.test(e)?"Mastercard":(t=new RegExp("^3[47]"),null!=e.match(t)?"Amex":(t=new RegExp("^(6011|622(12[6-9]|1[3-9][0-9]|[2-8][0-9]{2}|9[0-1][0-9]|92[0-5]|64[4-9])|65)"),null!=e.match(t)?"Discover":(t=new RegExp("^36"),null!=e.match(t)?"Diners Club":(t=new RegExp("^30[0-5]"),null!=e.match(t)?"Diners Club":(t=new RegExp("^35(2[89]|[3-8][0-9])"),null!=e.match(t)?"JCB":(t=new RegExp("^(4026|417500|4508|4844|491(3|7))"),null!=e.match(t)?"Visa":null))))))}(e.ccnum):null,code:e.cccsc&&""!==e.cccsc?e.cccsc:null};if(e.ccexp&&""!==e.ccexp&&e.ccexp.indexOf("-")>-1){var n=e.ccexp.split("-");n.length>1&&(t.expYear=n[0],t.expMonth=n[1],2===t.expMonth.length&&"0"===t.expMonth[0]&&(t.expMonth=t.expMonth[1]))}return t}function u(t){var o=[],a=[],l=[],u=0;angular.forEach(t,function(t,n){var d=o.length,p=a.length,m=t.grouping&&""!==t.grouping&&"(none)"!==t.grouping,g=m;if(m)for(u=0;u1&&"NoteType"===y[0]&&("Credit Card"===y[1]||"Address"===y[1])){var b=null;"Credit Card"===y[1]?(b=i(h,{Number:"number","Name on Card":"cardholderName","Security Code":"code"},[]),f.type=e.cipherType.card,f.card=b.dataObj):"Address"===y[1]&&(b=i(h,{Title:"title","First Name":"firstName","Last Name":"lastName","Middle Name":"middleName",Company:"company","Address 1":"address1","Address 2":"address2","Address 3":"address3","City / Town":"city",State:"state","Zip / Postal Code":"postalCode",Country:"country","Email Address":"email",Username:"username"},[]),f.type=e.cipherType.identity,f.identity=b.dataObj),v=!0,f.notes=b.notes}}v||(f.secureNote={type:0},f.notes=t.extra&&""!==t.extra?t.extra:null)}else if(f.type===e.cipherType.card)f.card=c(t),f.notes=t.notes&&""!==t.notes?t.notes:null;else if(f.type===e.cipherType.identity&&(f.identity={title:t.title&&""!==t.title?t.title:null,firstName:t.firstname&&""!==t.firstname?t.firstname:null,middleName:t.middlename&&""!==t.middlename?t.middlename:null,lastName:t.lastname&&""!==t.lastname?t.lastname:null,username:t.username&&""!==t.username?t.username:null,company:t.company&&""!==t.company?t.company:null,ssn:t.ssn&&""!==t.ssn?t.ssn:null,address1:t.address1&&""!==t.address1?t.address1:null,address2:t.address2&&""!==t.address2?t.address2:null,address3:t.address3&&""!==t.address3?t.address3:null,city:t.city&&""!==t.city?t.city:null,state:t.state&&""!==t.state?t.state:null,postalCode:t.zip&&""!==t.zip?t.zip:null,country:t.country&&""!==t.country?t.country:null,email:t.email&&""!==t.email?t.email:null,phone:t.phone&&""!==t.phone?t.phone:null},f.notes=t.notes&&""!==t.notes?t.notes:null,f.identity.title&&(f.identity.title=f.identity.title.charAt(0).toUpperCase()+f.identity.title.slice(1)),t.ccnum&&""!==t.ccnum)){var w=JSON.parse(JSON.stringify(f));w.identity=null,w.type=e.cipherType.card,w.card=c(t),a.push(w)}if(a.push(f),g&&o.push({name:t.grouping}),m){var C={key:p,value:d};l.push(C)}}),n(o,a,l)}}return t}]),angular.module("bit.services").factory("passwordService",function(){var e={};e.generatePassword=function(e){var n=angular.extend({},{length:10,ambiguous:!1,number:!0,minNumber:1,uppercase:!0,minUppercase:1,lowercase:!0,minLowercase:1,special:!1,minSpecial:1},e);n.uppercase&&n.minUppercase<0&&(n.minUppercase=1),n.lowercase&&n.minLowercase<0&&(n.minLowercase=1),n.number&&n.minNumber<0&&(n.minNumber=1),n.special&&n.minSpecial<0&&(n.minSpecial=1),(!n.length||n.length<1)&&(n.length=10);var o=n.minUppercase+n.minLowercase+n.minNumber+n.minSpecial;n.length0)for(var a=0;a0)for(var i=0;i0)for(var s=0;s0)for(var l=0;l53)throw new Exception("We cannot generate numbers larger than 53 bits.");var i=Math.ceil(a/8),s=Math.pow(2,a)-1,l=new Uint8Array(i);window.crypto.getRandomValues(l);for(var c=8*(i-1),u=0;u=r?t(e,n):e+o}return e}),angular.module("bit.services").factory("tokenService",["$sessionStorage","$localStorage","jwtHelper",function(e,t,n){var o={},r=null,a=null;return o.setToken=function(t){e.accessToken=t,r=t},o.getToken=function(){return r||(r=e.accessToken),r||null},o.clearToken=function(){r=null,delete e.accessToken},o.setRefreshToken=function(t){e.refreshToken=t,a=t},o.getRefreshToken=function(){return a||(a=e.refreshToken),a||null},o.clearRefreshToken=function(){a=null,delete e.refreshToken},o.setTwoFactorToken=function(e,n){t.twoFactor||(t.twoFactor={}),t.twoFactor[n]=e},o.getTwoFactorToken=function(e){return t.twoFactor?t.twoFactor[e]:null},o.clearTwoFactorToken=function(e){e?t.twoFactor&&t.twoFactor[e]&&delete t.twoFactor[e]:delete t.twoFactor},o.clearTokens=function(){o.clearToken(),o.clearRefreshToken()},o.tokenSecondsRemaining=function(e,t){var o=n.getTokenExpirationDate(e);if(t=t||0,null===o)return 0;var r=o.valueOf()-((new Date).valueOf()+1e3*t);return Math.round(r/1e3)},o.tokenNeedsRefresh=function(e,t){t=t||5;return o.tokenSecondsRemaining(e)<60*t},o}]),angular.module("bit.services").factory("utilsService",["constants",function(e){var t,n={};n.getDeviceType=function(n){if(t)return t;var o;return t=navigator.userAgent.indexOf(" Vivaldi/")>=0?e.deviceType.vivaldi:window.chrome&&window.chrome.webstore?e.deviceType.chrome:"undefined"!=typeof InstallTrigger?e.deviceType.firefox:window.opr&&opr.addons||window.opera||navigator.userAgent.indexOf(" OPR/")>=0?e.deviceType.firefox:/constructor/i.test(window.HTMLElement)||(o=!window.safari||"undefined"!=typeof safari&&safari.pushNotification,"[object SafariRemoteNotification]"===o.toString())?e.deviceType.opera:document.documentMode?e.deviceType.ie:window.StyleMedia?e.deviceType.edge:e.deviceType.unknown};return n}]),angular.module("bit.services").factory("validationService",function(){var e={};return e.addErrors=function(t,n){var o=n.data,r="An unexpected error has occurred.";if(t.$errors=[],o&&angular.isObject(o))if(o&&o.ErrorModel&&(o=o.ErrorModel),o.ValidationErrors){for(var a in o.ValidationErrors)if(o.ValidationErrors.hasOwnProperty(a))for(var i=0;i1;var n=0;if(e.expiration=t.Expiration,t.License,e.plan={name:t.Plan,type:t.PlanType,seats:t.Seats},e.storage=null,e&&t.MaxStorageGb&&(e.storage={currentGb:t.StorageGb||0,maxGb:t.MaxStorageGb,currentName:t.StorageName||"0 GB"},e.storage.percentage=+(e.storage.currentGb/e.storage.maxGb*100).toFixed(2)),e.subscription=null,t.Subscription&&(e.subscription={trialEndDate:t.Subscription.TrialEndDate,cancelledDate:t.Subscription.CancelledDate,status:t.Subscription.Status,cancelled:t.Subscription.Cancelled,markedForCancel:!t.Subscription.Cancelled&&t.Subscription.CancelAtEndDate}),e.nextInvoice=null,t.UpcomingInvoice&&(e.nextInvoice={date:t.UpcomingInvoice.Date,amount:t.UpcomingInvoice.Amount}),t.Subscription&&t.Subscription.Items)for(e.subscription.items=[],n=0;n=s},e.submit=function(i){var s=r.encryptCollection(i,t.params.orgId);if(e.useGroups){s.groups=[];for(var l in e.selectedGroups)if(e.selectedGroups.hasOwnProperty(l))for(var c=0;c0&&(n[0].name=t.name)})},e.users=function(e){o.open({animation:!0,templateUrl:"app/organization/views/organizationCollectionsUsers.html",controller:"organizationCollectionsUsersController",size:"lg",resolve:{collection:function(){return e}}}).result.then(function(){})},e.groups=function(e){o.open({animation:!0,templateUrl:"app/organization/views/organizationCollectionsGroups.html",controller:"organizationCollectionsGroupsController",resolve:{collection:function(){return e}}}).result.then(function(){})},e.delete=function(o){confirm("Are you sure you want to delete this collection ("+o.name+")?")&&n.collections.del({orgId:t.params.orgId,id:o.id},function(){var t=e.collections.indexOf(o);t>-1&&e.collections.splice(t,1),s.eventTrack("Deleted Collection"),i.success(o.name+" has been deleted.","Collection Deleted")},function(){i.error(o.name+" was not able to be deleted.","Error")})}}]),angular.module("bit.organization").controller("organizationCollectionsEditController",["$scope","$state","$uibModalInstance","apiService","cipherService","$analytics","id","authService",function(e,t,n,o,r,a,i,s){a.eventTrack("organizationCollectionsEditController",{category:"Modal"});var l=0;e.collection={},e.groups=[],e.selectedGroups={},e.loading=!0,e.useGroups=!1,n.opened.then(function(){return o.collections.getDetails({orgId:t.params.orgId,id:i}).$promise}).then(function(t){e.collection=r.decryptCollection(t);var n={};if(t.Groups)for(var o=0;o=l},e.submit=function(s){var l=r.encryptCollection(s,t.params.orgId);if(e.useGroups){l.groups=[];for(var c in e.selectedGroups)if(e.selectedGroups.hasOwnProperty(c))for(var u=0;u-1&&e.users.splice(t,1)},function(){s.error("Unable to remove user.","Error")})},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationDashboardController",["$scope","authService","$state","appSettings",function(e,t,n,o){e.selfHosted=o.selfHosted,e.$on("$viewContentLoaded",function(){t.getUserProfile().then(function(t){t.organizations&&(e.orgProfile=t.organizations[n.params.orgId])})}),e.goBilling=function(){n.go("backend.org.billing",{orgId:n.params.orgId})}}]),angular.module("bit.organization").controller("organizationDeleteController",["$scope","$state","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics",function(e,t,n,o,r,a,i,s){s.eventTrack("organizationDeleteController",{category:"Modal"}),e.submit=function(){e.submitPromise=r.hashPassword(e.masterPassword).then(function(e){return n.organizations.del({id:t.params.orgId},{masterPasswordHash:e}).$promise}).then(function(){return o.dismiss("cancel"),a.removeProfileOrganization(t.params.orgId),s.eventTrack("Deleted Organization"),t.go("backend.user.vault")}).then(function(){i.success("This organization and all associated data has been deleted.","Organization Deleted")})},e.close=function(){o.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationEventsController",["$scope","$state","apiService","$uibModal","$filter","toastr","$analytics","constants","eventService","$compile","$sce",function(e,t,n,o,r,a,i,s,l,c,u){e.events=[],e.orgUsers=[],e.loading=!0,e.continuationToken=null;var d=l.getDefaultDateFilters();e.filterStart=d.start,e.filterEnd=d.end,e.$on("$viewContentLoaded",function(){n.organizationUsers.list({orgId:t.params.orgId}).$promise.then(function(t){var n=[];for(p=0;p"+r.message+"")(e);n.push({message:u.trustAsHtml(a[0].outerHTML),appIcon:r.appIcon,appName:r.appName,userId:o,userName:o?m[o]||"-":"-",date:t.Data[p].Date,ip:t.Data[p].IpAddress})}e.events&&e.events.length>0?e.events=e.events.concat(n):e.events=n,e.loading=!1});alert(r.error)}}}]),angular.module("bit.organization").controller("organizationGroupsAddController",["$scope","$state","$uibModalInstance","apiService","cipherService","$analytics",function(e,t,n,o,r,a){a.eventTrack("organizationGroupsAddController",{category:"Modal"}),e.collections=[],e.selectedCollections={},e.loading=!0,n.opened.then(function(){return o.collections.listOrganization({orgId:t.params.orgId}).$promise}).then(function(n){e.collections=r.decryptCollections(n.Data,t.params.orgId,!0),e.loading=!1}),e.toggleCollectionSelectionAll=function(t){var n={};if(t.target.checked)for(var o=0;o0&&(n[0].name=t.name)})},e.users=function(e){o.open({animation:!0,templateUrl:"app/organization/views/organizationGroupsUsers.html",controller:"organizationGroupsUsersController",size:"lg",resolve:{group:function(){return e}}}).result.then(function(){})},e.delete=function(o){confirm("Are you sure you want to delete this group ("+o.name+")?")&&n.groups.del({orgId:t.params.orgId,id:o.id},function(){var t=e.groups.indexOf(o);t>-1&&e.groups.splice(t,1),i.eventTrack("Deleted Group"),a.success(o.name+" has been deleted.","Group Deleted")},function(){a.error(o.name+" was not able to be deleted.","Error")})}}]),angular.module("bit.organization").controller("organizationGroupsEditController",["$scope","$state","$uibModalInstance","apiService","cipherService","$analytics","id",function(e,t,n,o,r,a,i){a.eventTrack("organizationGroupsEditController",{category:"Modal"}),e.collections=[],e.selectedCollections={},e.loading=!0,n.opened.then(function(){return o.groups.getDetails({orgId:t.params.orgId,id:i}).$promise}).then(function(n){e.group={id:i,name:n.Name,externalId:n.ExternalId,accessAll:n.AccessAll};var r={};if(n.Collections)for(var a=0;a-1&&e.users.splice(t,1)},function(){i.error("Unable to remove user.","Error")})},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationPeopleController",["$scope","$state","$uibModal","cryptoService","apiService","authService","toastr","$analytics","$filter","$uibModalStack",function(e,t,n,o,r,a,i,s,l,c){e.users=[],e.useGroups=!1,e.useEvents=!1,e.$on("$viewContentLoaded",function(){u(),a.getUserProfile().then(function(n){if(n.organizations){var o=n.organizations[t.params.orgId];e.useGroups=!!o.useGroups,e.useEvents=!!o.useEvents}})}),e.reinvite=function(e){r.organizationUsers.reinvite({orgId:t.params.orgId,id:e.id},null,function(){s.eventTrack("Reinvited User"),i.success(e.email+" has been invited again.","User Invited")},function(){i.error("Unable to invite user.","Error")})},e.delete=function(n){confirm("Are you sure you want to remove this user ("+n.email+")?")&&r.organizationUsers.del({orgId:t.params.orgId,id:n.id},null,function(){s.eventTrack("Deleted User"),i.success(n.email+" has been removed.","User Removed");var t=e.users.indexOf(n);t>-1&&e.users.splice(t,1)},function(){i.error("Unable to remove user.","Error")})},e.confirm=function(e){r.users.getPublicKey({id:e.userId},function(n){var a=o.getOrgKey(t.params.orgId);if(a){var l=o.rsaEncrypt(a.key,n.PublicKey);r.organizationUsers.confirm({orgId:t.params.orgId,id:e.id},{key:l},function(){e.status=2,s.eventTrack("Confirmed User"),i.success(e.email+" has been confirmed.","User Confirmed")},function(){i.error("Unable to confirm user.","Error")})}else i.error("Unable to confirm user.","Error")},function(){i.error("Unable to confirm user.","Error")})},e.$on("organizationPeopleInvite",function(t,n){e.invite()}),e.invite=function(){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleInvite.html",controller:"organizationPeopleInviteController"}).result.then(function(){u()})},e.edit=function(e){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleEdit.html",controller:"organizationPeopleEditController",resolve:{orgUser:function(){return e}}}).result.then(function(){u()})},e.groups=function(e){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleGroups.html",controller:"organizationPeopleGroupsController",resolve:{orgUser:function(){return e}}}).result.then(function(){})},e.events=function(e){n.open({animation:!0,templateUrl:"app/organization/views/organizationPeopleEvents.html",controller:"organizationPeopleEventsController",resolve:{orgUser:function(){return e},orgId:function(){return t.params.orgId}}})};function u(){r.organizationUsers.list({orgId:t.params.orgId},function(n){for(var o=[],r=0;r"+r.message+"")(e);n.push({message:l.trustAsHtml(i[0].outerHTML),appIcon:r.appIcon,appName:r.appName,date:t.Data[o].Date,ip:t.Data[o].IpAddress})}e.events&&e.events.length>0?e.events=e.events.concat(n):e.events=n,e.loading=!1});alert(r.error)}}e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationPeopleGroupsController",["$scope","$state","$uibModalInstance","apiService","orgUser","$analytics",function(e,t,n,o,r,a){a.eventTrack("organizationPeopleGroupsController",{category:"Modal"}),e.loading=!0,e.groups=[],e.selectedGroups={},e.orgUser=r,n.opened.then(function(){return o.groups.listOrganization({orgId:t.params.orgId}).$promise}).then(function(n){for(var a=[],i=0;i=t?e:new Array(t-e.length+1).join(n)+e}}]),angular.module("bit.organization").controller("organizationSettingsImportController",["$scope","$state","apiService","$uibModalInstance","cipherService","toastr","importService","$analytics","$sce","validationService","cryptoService",function(e,t,n,o,r,a,i,s,l,c,u){s.eventTrack("organizationSettingsImportController",{category:"Modal"}),e.model={source:""},e.source={},e.splitFeatured=!1,e.options=[{id:"bitwardencsv",name:"bitwarden (csv)",featured:!0,sort:1,instructions:l.trustAsHtml('Export using the web vault (vault.bitwarden.com). Log into the web vault and navigate to your organization\'s admin area. Then to go "Settings" > "Tools" > "Export".')},{id:"lastpass",name:"LastPass (csv)",featured:!0,sort:2,instructions:l.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-lastpass/')}],e.setSource=function(){for(var t=0;t-1&&e.cipher.fields.splice(n,1)},e.clipboardSuccess=function(e){e.clearSelection(),d(e)},e.clipboardError=function(e,t){t&&d(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")};function d(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.close=function(){n.dismiss("close")},e.showUpgrade=function(){c.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return l}}})}}]),angular.module("bit.organization").controller("organizationVaultAttachmentsController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","cipherId","$analytics","validationService","toastr","$timeout",function(e,t,n,o,r,a,i,s,l,c){i.eventTrack("organizationVaultAttachmentsController",{category:"Modal"}),e.cipher={},e.loading=!0,e.isPremium=!0,e.canUseAttachments=!0;var u=!1;t.ciphers.getAdmin({id:a},function(t){e.cipher=r.decryptCipher(t),e.loading=!1},function(){e.loading=!1}),e.save=function(c){var d=document.getElementById("file").files;if(d&&d.length){var p=o.getOrgKey(e.cipher.organizationId);e.savePromise=r.encryptAttachmentFile(p,d[0]).then(function(e){var n=new FormData,o=new Blob([e.data],{type:"application/octet-stream"});return n.append("data",o,e.fileName),t.ciphers.postAttachment({id:a},n).$promise}).then(function(e){i.eventTrack("Added Attachment"),l.success("The attachment has been added."),u=!0,n.close(!0)},function(e){var t=s.parseErrors(e);l.error(t.length?t[0]:"An error occurred.")})}else s.addError(c,"file","Select a file.",!0)},e.download=function(t){t.loading=!0;var n=o.getOrgKey(e.cipher.organizationId);r.downloadAndDecryptAttachment(n,t,!0).then(function(e){c(function(){t.loading=!1})},function(){c(function(){t.loading=!1})})},e.remove=function(n){confirm("Are you sure you want to delete this attachment ("+n.fileName+")?")&&(n.loading=!0,t.ciphers.delAttachment({id:a,attachmentId:n.id}).$promise.then(function(){n.loading=!1,i.eventTrack("Deleted Organization Attachment");var t=e.cipher.attachments.indexOf(n);t>-1&&e.cipher.attachments.splice(t,1)},function(){l.error("Cannot delete attachment."),n.loading=!1}))},e.close=function(){n.dismiss("cancel")},e.$on("modal.closing",function(t,o,r){u||(t.preventDefault(),u=!0,n.close(!!e.cipher.attachments&&e.cipher.attachments.length>0))})}]),angular.module("bit.organization").controller("organizationVaultCipherCollectionsController",["$scope","apiService","$uibModalInstance","cipherService","cipher","$analytics","collections",function(e,t,n,o,r,a,i){a.eventTrack("organizationVaultCipherCollectionsController",{category:"Modal"}),e.cipher={},e.collections=[],e.selectedCollections={},n.opened.then(function(){for(var t=[],n=0;n0?e.events=e.events.concat(n):e.events=n,e.loading=!1});alert(r.error)}}e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("organizationVaultController",["$scope","apiService","cipherService","$analytics","$q","$state","$localStorage","$uibModal","$filter","authService","$uibModalStack",function(e,t,n,o,r,a,i,s,l,c,u){e.ciphers=[],e.collections=[],e.loading=!0,e.useEvents=!1,e.$on("$viewContentLoaded",function(){c.getUserProfile().then(function(t){if(t.organizations){var n=t.organizations[a.params.orgId];e.useEvents=!!n.useEvents}});var o=t.collections.listOrganization({orgId:a.params.orgId},function(t){for(var o=([{id:null,name:"Unassigned",collapsed:i.collapsedOrgCollections&&"unassigned"in i.collapsedOrgCollections}]),r=0;r-1:null===e.id}},e.collectionSort=function(e){return e.id?e.name.toLowerCase():"î º"},e.collapseExpand=function(e){i.collapsedOrgCollections||(i.collapsedOrgCollections={});var t=e.id||"unassigned";t in i.collapsedOrgCollections?delete i.collapsedOrgCollections[t]:i.collapsedOrgCollections[t]=!0},e.editCipher=function(t){s.open({animation:!0,templateUrl:"app/vault/views/vaultEditCipher.html",controller:"organizationVaultEditCipherController",resolve:{cipherId:function(){return t.id},orgId:function(){return a.params.orgId}}}).result.then(function(n){var o;"edit"===n.action?(o=e.ciphers.indexOf(t))>-1&&(n.data.collectionIds=e.ciphers[o].collectionIds,e.ciphers[o]=n.data):"delete"===n.action&&(o=e.ciphers.indexOf(t))>-1&&e.ciphers.splice(o,1)})},e.$on("organizationVaultAddCipher",function(t,n){e.addCipher()}),e.addCipher=function(){s.open({animation:!0,templateUrl:"app/vault/views/vaultAddCipher.html",controller:"organizationVaultAddCipherController",resolve:{orgId:function(){return a.params.orgId}}}).result.then(function(t){e.ciphers.push(t)})},e.editCollections=function(t){s.open({animation:!0,templateUrl:"app/organization/views/organizationVaultCipherCollections.html",controller:"organizationVaultCipherCollectionsController",resolve:{cipher:function(){return t},collections:function(){return e.collections}}}).result.then(function(e){e.collectionIds&&(t.collectionIds=e.collectionIds)})},e.viewEvents=function(e){s.open({animation:!0,templateUrl:"app/organization/views/organizationVaultCipherEvents.html",controller:"organizationVaultCipherEventsController",resolve:{cipher:function(){return e}}})},e.attachments=function(e){c.getUserProfile().then(function(t){return!!t.organizations[e.organizationId].maxStorageGb}).then(function(t){if(t){s.open({animation:!0,templateUrl:"app/vault/views/vaultAttachments.html",controller:"organizationVaultAttachmentsController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){e.hasAttachments=t})}else s.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return e.organizationId}}})})},e.removeCipher=function(e,n){if(confirm("Are you sure you want to remove this item ("+e.name+") from the collection ("+n.name+") ?")){for(var r={collectionIds:[]},a=0;a-1&&e.ciphers.splice(t,1)})}}]),angular.module("bit.organization").controller("organizationVaultEditCipherController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","passwordService","cipherId","$analytics","orgId","$uibModal","constants",function(e,t,n,o,r,a,i,s,l,c,u){s.eventTrack("organizationVaultEditCipherController",{category:"Modal"}),e.cipher={},e.hideFolders=e.hideFavorite=e.fromOrg=!0,e.constants=u,t.ciphers.getAdmin({id:i},function(t){e.cipher=r.decryptCipher(t),e.useTotp=e.cipher.organizationUseTotp}),e.save=function(o){var a=r.encryptCipher(o,e.cipher.type);e.savePromise=t.ciphers.putAdmin({id:i},a,function(e){s.eventTrack("Edited Organization Cipher");var t=r.decryptCipherPreview(e);n.close({action:"edit",data:t})}).$promise},e.generatePassword=function(){e.cipher.login.password&&!confirm("Are you sure you want to overwrite the current password?")||(s.eventTrack("Generated Password From Edit"),e.cipher.login.password=a.generatePassword({length:14,special:!0}))},e.addField=function(){e.cipher.login.fields||(e.cipher.login.fields=[]),e.cipher.fields.push({type:u.fieldType.text.toString(),name:null,value:null})},e.removeField=function(t){var n=e.cipher.fields.indexOf(t);n>-1&&e.cipher.fields.splice(n,1)},e.clipboardSuccess=function(e){e.clearSelection(),d(e)},e.clipboardError=function(e,t){t&&d(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")};function d(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.delete=function(){confirm("Are you sure you want to delete this item ("+e.cipher.name+")?")&&t.ciphers.delAdmin({id:e.cipher.id},function(){s.eventTrack("Deleted Organization Cipher From Edit"),n.close({action:"delete",data:e.cipher.id})})},e.close=function(){n.dismiss("cancel")},e.showUpgrade=function(){c.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return l}}})}}]),angular.module("bit.vault").controller("settingsAddEditEquivalentDomainController",["$scope","$uibModalInstance","$analytics","domainIndex","domains",function(e,t,n,o,r){n.eventTrack("settingsAddEditEquivalentDomainController",{category:"Modal"}),e.domains=r,e.index=o,e.submit=function(r){n.eventTrack((o?"Edited":"Added")+" Equivalent Domain"),t.close({domains:e.domains,index:o})},e.close=function(){t.dismiss("close")}}]),angular.module("bit.settings").controller("settingsBillingAdjustStorageController",["$scope","$state","$uibModalInstance","apiService","$analytics","toastr","add",function(e,t,n,o,r,a,i){r.eventTrack("settingsBillingAdjustStorageController",{category:"Modal"}),e.add=i,e.storageAdjustment=0,e.submit=function(){var t={storageGbAdjustment:e.storageAdjustment};i||(t.storageGbAdjustment*=-1),e.submitPromise=o.accounts.putStorage(null,t).$promise.then(function(t){i?(r.eventTrack("Added Storage"),a.success("You have added "+e.storageAdjustment+" GB.")):(r.eventTrack("Removed Storage"),a.success("You have removed "+e.storageAdjustment+" GB.")),n.close()})},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.organization").controller("settingsBillingChangePaymentController",["$scope","$state","$uibModalInstance","apiService","$analytics","toastr","existingPaymentMethod","appSettings","$timeout","stripe",function(e,t,n,o,r,a,i,s,l,c){r.eventTrack("settingsBillingChangePaymentController",{category:"Modal"}),e.existingPaymentMethod=i,e.paymentMethod="card",e.dropinLoaded=!1,e.showPaymentOptions=!1,e.hideBank=!0,e.card={};var u=null;e.changePaymentMethod=function(t){e.paymentMethod=t,"paypal"===e.paymentMethod&&braintree.dropin.create({authorization:s.braintreeKey,container:"#bt-dropin-container",paymentOptionPriority:["paypal"],paypal:{flow:"vault",buttonStyle:{label:"pay",size:"medium",shape:"pill",color:"blue"}}},function(t,n){t?console.error(t):(u=n,l(function(){e.dropinLoaded=!0}))})},e.submit=function(){e.submitPromise=(t=e.card,"paypal"===e.paymentMethod?u.requestPaymentMethod().then(function(e){return e.nonce}).catch(function(e){throw e.message}):c.card.createToken(t).then(function(e){return e.id}).catch(function(e){throw e.message})).then(function(e){if(!e)throw"No payment token.";var t={paymentToken:e};return o.accounts.putPayment(null,t).$promise},function(e){throw e}).then(function(t){e.card=null,i?(r.eventTrack("Changed Payment Method"),a.success("You have changed your payment method.")):(r.eventTrack("Added Payment Method"),a.success("You have added a payment method.")),n.close()});var t},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.settings").controller("settingsBillingController",["$scope","apiService","authService","$state","$uibModal","toastr","$analytics","appSettings",function(e,t,n,o,r,a,i,s){e.selfHosted=s.selfHosted,e.charges=[],e.paymentSource=null,e.subscription=null,e.loading=!0;var l=null;e.expiration=null,e.$on("$viewContentLoaded",function(){c()}),e.changePayment=function(){if(!e.selfHosted){r.open({animation:!0,templateUrl:"app/settings/views/settingsBillingChangePayment.html",controller:"settingsBillingChangePaymentController",resolve:{existingPaymentMethod:function(){return e.paymentSource?e.paymentSource.description:null}}}).result.then(function(){c()})}},e.adjustStorage=function(t){if(!e.selfHosted){r.open({animation:!0,templateUrl:"app/settings/views/settingsBillingAdjustStorage.html",controller:"settingsBillingAdjustStorageController",resolve:{add:function(){return t}}}).result.then(function(){c()})}},e.cancel=function(){e.selfHosted||confirm("Are you sure you want to cancel? You will lose access to all premium features at the end of this billing cycle.")&&t.accounts.putCancelPremium({},{}).$promise.then(function(e){i.eventTrack("Canceled Premium"),a.success("Premium subscription has been canceled."),c()})},e.reinstate=function(){e.selfHosted||confirm("Are you sure you want to remove the cancellation request and reinstate your premium membership?")&&t.accounts.putReinstatePremium({},{}).$promise.then(function(e){i.eventTrack("Reinstated Premium"),a.success("Premium cancellation request has been removed."),c()})},e.updateLicense=function(){if(e.selfHosted){r.open({animation:!0,templateUrl:"app/settings/views/settingsBillingUpdateLicense.html",controller:"settingsBillingUpdateLicenseController"}).result.then(function(){c()})}},e.license=function(){if(!e.selfHosted){var t=JSON.stringify(l,null,2),n=new Blob([t]);if(window.navigator.msSaveOrOpenBlob)window.navigator.msSaveBlob(n,"bitwarden_premium_license.json");else{var o=window.document.createElement("a");o.href=window.URL.createObjectURL(n,{type:"text/plain"}),o.download="bitwarden_premium_license.json",document.body.appendChild(o),o.click(),document.body.removeChild(o)}}};function c(){n.getUserProfile().then(function(n){return e.premium=n.premium,n.premium?t.accounts.getBilling({}).$promise:null}).then(function(t){if(!t)return o.go("backend.user.settingsPremium");var n=0;if(e.expiration=t.Expiration,l=t.License,e.storage=null,t&&t.MaxStorageGb&&(e.storage={currentGb:t.StorageGb||0,maxGb:t.MaxStorageGb,currentName:t.StorageName||"0 GB"},e.storage.percentage=+(e.storage.currentGb/e.storage.maxGb*100).toFixed(2)),e.subscription=null,t&&t.Subscription&&(e.subscription={trialEndDate:t.Subscription.TrialEndDate,cancelledDate:t.Subscription.CancelledDate,status:t.Subscription.Status,cancelled:t.Subscription.Cancelled,markedForCancel:!t.Subscription.Cancelled&&t.Subscription.CancelAtEndDate}),e.nextInvoice=null,t&&t.UpcomingInvoice&&(e.nextInvoice={date:t.UpcomingInvoice.Date,amount:t.UpcomingInvoice.Amount}),t&&t.Subscription&&t.Subscription.Items)for(e.subscription.items=[],n=0;n-1&&e.model.organizations.splice(n,1),r.success("You have left the organization."),c()})},function(e){r.error("Unable to leave this organization."),c()})},e.sessions=function(){n.open({animation:!0,templateUrl:"app/settings/views/settingsSessions.html",controller:"settingsSessionsController"})},e.delete=function(){n.open({animation:!0,templateUrl:"app/settings/views/settingsDelete.html",controller:"settingsDeleteController"})},e.purge=function(){n.open({animation:!0,templateUrl:"app/settings/views/settingsPurge.html",controller:"settingsPurgeController"})};function c(){$("html, body").animate({scrollTop:0},200)}}]),angular.module("bit.settings").controller("settingsCreateOrganizationController",["$scope","$state","apiService","cryptoService","toastr","$analytics","authService","constants","appSettings","validationService","stripe",function(e,t,n,o,r,a,i,s,l,c,u){e.plans=s.plans,e.storageGb=s.storageGb,e.paymentMethod="card",e.selfHosted=l.selfHosted,e.model={plan:"free",additionalSeats:0,interval:"year",ownedBusiness:!1,additionalStorageGb:null},e.totalPrice=function(){return"month"===e.model.interval?(e.model.additionalSeats||0)*(e.plans[e.model.plan].monthlySeatPrice||0)+(e.model.additionalStorageGb||0)*e.storageGb.monthlyPrice+(e.plans[e.model.plan].monthlyBasePrice||0):(e.model.additionalSeats||0)*(e.plans[e.model.plan].annualSeatPrice||0)+(e.model.additionalStorageGb||0)*e.storageGb.yearlyPrice+(e.plans[e.model.plan].annualBasePrice||0)},e.changePaymentMethod=function(t){e.paymentMethod=t},e.changedPlan=function(){e.plans[e.model.plan].hasOwnProperty("monthPlanType")&&(e.model.interval="year"),e.plans[e.model.plan].noAdditionalSeats?e.model.additionalSeats=0:e.model.additionalSeats||e.plans[e.model.plan].baseSeats||e.plans[e.model.plan].noAdditionalSeats||(e.model.additionalSeats=1)},e.changedBusiness=function(){e.model.ownedBusiness&&(e.model.plan="teams")},e.submit=function(s,l){var d=o.makeShareKey(),p=o.encrypt("Default Collection",d.key);if(e.selfHosted){var m=document.getElementById("file").files;if(!m||!m.length)return void c.addError(l,"file","Select a license file.",!0);var g=new FormData;g.append("license",m[0]),g.append("key",d.ct),g.append("collectionName",p),e.submitPromise=n.organizations.postLicense(g).$promise.then(v)}else if("free"===s.plan){var f={name:s.name,planType:s.plan,key:d.ct,billingEmail:s.billingEmail,collectionName:p};e.submitPromise=n.organizations.post(f).$promise.then(v)}else{var h=null;if("card"===e.paymentMethod)h=u.card.createToken(s.card);else{if("bank"!==e.paymentMethod)return;s.bank.currency="USD",s.bank.country="US",h=u.bankAccount.createToken(s.bank)}e.submitPromise=h.then(function(t){var o={name:s.name,planType:"month"===s.interval?e.plans[s.plan].monthPlanType:e.plans[s.plan].annualPlanType,key:d.ct,paymentToken:t.id,additionalSeats:s.additionalSeats,additionalStorageGb:s.additionalStorageGb,billingEmail:s.billingEmail,businessName:s.ownedBusiness?s.businessName:null,country:"card"===e.paymentMethod?s.card.address_country:null,collectionName:p};return n.organizations.post(o).$promise},function(e){throw e.message}).then(v)}function v(e){a.eventTrack("Created Organization"),i.addProfileOrganizationOwner(e,d.ct),i.refreshAccessToken().then(function(){y(e.Id)},function(){y(e.Id)})}function y(e){t.go("backend.org.dashboard",{orgId:e}).then(function(){r.success("Your new organization is ready to go!","Organization Created")})}}}]),angular.module("bit.settings").controller("settingsDeleteController",["$scope","$state","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics","tokenService",function(e,t,n,o,r,a,i,s,l){s.eventTrack("settingsDeleteController",{category:"Modal"}),e.submit=function(c){var u;e.submitPromise=a.getUserProfile().then(function(e){return u=e,r.hashPassword(c.masterPassword)}).then(function(e){return n.accounts.postDelete({masterPasswordHash:e}).$promise}).then(function(){return o.dismiss("cancel"),a.logOut(),l.clearTwoFactorToken(u.email),s.eventTrack("Deleted Account"),t.go("frontend.login.info")}).then(function(){i.success("Your account has been closed and all associated data has been deleted.","Account Deleted")})},e.close=function(){o.dismiss("cancel")}}]),angular.module("bit.settings").controller("settingsDomainsController",["$scope","$state","apiService","toastr","$analytics","$uibModal",function(e,t,n,o,r,a){e.globalEquivalentDomains=[],e.equivalentDomains=[],n.settings.getDomains({},function(t){var n;if(t.EquivalentDomains)for(n=0;n

bitwarden two-step login recovery code:

'+e.code+'

'+new Date+"

"),t.print(),t.close()}};e.close=function(){n.close()}}]),angular.module("bit.settings").controller("settingsTwoStepU2fController",["$scope","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics","constants","$timeout","$window",function(e,t,n,o,r,a,i,s,l,c){i.eventTrack("settingsTwoStepU2fController",{category:"Modal"});var u,d=!1;e.deviceResponse=null,e.deviceListening=!1,e.deviceError=!1,l(function(){$("#masterPassword").focus()}),e.auth=function(n){e.authPromise=o.hashPassword(n.masterPassword).then(function(e){return u=e,t.twoFactor.getU2f({},{masterPasswordHash:u}).$promise}).then(function(t){return e.enabled=t.Enabled,e.challenge=t.Challenge,e.authed=!0,e.readDevice()})},e.readDevice=function(){d||e.enabled||(console.log("listening for key..."),e.deviceResponse=null,e.deviceError=!1,e.deviceListening=!0,c.u2f.register(e.challenge.AppId,[{version:e.challenge.Version,challenge:e.challenge.Challenge}],[],function(t){e.deviceListening=!1;{if(5!==t.errorCode)return t.errorCode?(l(function(){e.deviceError=!0}),void console.log("error: "+t.errorCode)):void l(function(){e.deviceResponse=JSON.stringify(t)});e.readDevice()}},10))},e.submit=function(){e.enabled?function(){if(!confirm("Are you sure you want to disable the U2F provider?"))return;e.submitPromise=t.twoFactor.disable({},{masterPasswordHash:u,type:s.twoFactorProvider.u2f},function(t){i.eventTrack("Disabled Two-step U2F"),a.success("U2F has been disabled."),e.enabled=t.Enabled,e.close()}).$promise}():e.submitPromise=t.twoFactor.putU2f({},{deviceResponse:e.deviceResponse,masterPasswordHash:u},function(t){i.eventTrack("Enabled Two-step U2F"),e.enabled=t.Enabled,e.challenge=null,e.deviceResponse=null,e.deviceError=!1}).$promise};e.close=function(){d=!0,n.close(e.enabled)},e.$on("modal.closing",function(t,n,o){d||(t.preventDefault(),e.close())})}]),angular.module("bit.settings").controller("settingsTwoStepYubiController",["$scope","apiService","$uibModalInstance","cryptoService","authService","toastr","$analytics","constants","$timeout",function(e,t,n,o,r,a,i,s,l){i.eventTrack("settingsTwoStepYubiController",{category:"Modal"});var c;l(function(){$("#masterPassword").focus()}),e.auth=function(n){var a=null;e.authPromise=o.hashPassword(n.masterPassword).then(function(e){return c=e,t.twoFactor.getYubi({},{masterPasswordHash:c}).$promise}).then(function(e){return a=e,r.getUserProfile()}).then(function(t){t,u(a),e.authed=!0})},e.remove=function(e){e.key=null,e.existingKey=null},e.submit=function(n){e.submitPromise=t.twoFactor.putYubi({},{key1:n.key1.key,key2:n.key2.key,key3:n.key3.key,nfc:n.nfc,masterPasswordHash:c},function(e){i.eventTrack("Saved Two-step YubiKey"),a.success("YubiKey saved."),u(e)}).$promise},e.disable=function(){confirm("Are you sure you want to disable the YubiKey provider?")&&(e.disableLoading=!0,e.submitPromise=t.twoFactor.disable({},{masterPasswordHash:c,type:s.twoFactorProvider.yubikey},function(t){e.disableLoading=!1,i.eventTrack("Disabled Two-step YubiKey"),a.success("YubiKey has been disabled."),e.enabled=t.Enabled,e.close()},function(t){a.error("Failed to disable."),e.disableLoading=!1}).$promise)};function u(t){e.enabled=t.Enabled,e.updateModel={key1:{key:t.Key1,existingKey:d(t.Key1,"*",44)},key2:{key:t.Key2,existingKey:d(t.Key2,"*",44)},key3:{key:t.Key3,existingKey:d(t.Key3,"*",44)},nfc:!0===t.Nfc||!t.Enabled}}function d(e,t,n){if(!e||!t||e.length>=n)return e;for(var o=(n-e.length)/t.length,r=0;r=t?e:new Array(t-e.length+1).join(n)+e}}]),angular.module("bit.tools").controller("toolsImportController",["$scope","$state","apiService","$uibModalInstance","cryptoService","cipherService","toastr","importService","$analytics","$sce","validationService",function(e,t,n,o,r,a,i,s,l,c,u){l.eventTrack("toolsImportController",{category:"Modal"}),e.model={source:""},e.source={},e.splitFeatured=!0,e.options=[{id:"bitwardencsv",name:"bitwarden (csv)",featured:!0,sort:1,instructions:c.trustAsHtml('Export using the web vault (vault.bitwarden.com). Log into the web vault and navigate to "Tools" > "Export".')},{id:"lastpass",name:"LastPass (csv)",featured:!0,sort:2,instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-lastpass/')},{id:"chromecsv",name:"Chrome (csv)",featured:!0,sort:3,instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-chrome/')},{id:"firefoxpasswordexportercsvxml",name:"Firefox Password Exporter (xml)",featured:!0,sort:4,instructions:c.trustAsHtml('Use the Password Exporter addon for FireFox to export your passwords to a XML file. After installing the addon, type about:addons in your FireFox navigation bar. Locate the Password Exporter addon and click the "Options" button. In the dialog that pops up, click the "Export Passwords" button to save the XML file.')},{id:"keepass2xml",name:"KeePass 2 (xml)",featured:!0,sort:5,instructions:c.trustAsHtml('Using the KeePass 2 desktop application, navigate to "File" > "Export" and select the KeePass XML (2.x) option.')},{id:"keepassxcsv",name:"KeePassX (csv)",instructions:c.trustAsHtml('Using the KeePassX desktop application, navigate to "Database" > "Export to CSV file" and save the CSV file.')},{id:"dashlanecsv",name:"Dashlane (csv)",featured:!0,sort:7,instructions:c.trustAsHtml('Using the Dashlane desktop application, navigate to "File" > "Export" > "Unsecured archive (readable) in CSV format" and save the CSV file.')},{id:"1password1pif",name:"1Password (1pif)",featured:!0,sort:6,instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-1password/')},{id:"1password6wincsv",name:"1Password 6 Windows (csv)",instructions:c.trustAsHtml('See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-1password/')},{id:"roboformhtml",name:"RoboForm (html)",instructions:c.trustAsHtml('Using the RoboForm Editor desktop application, navigate to "RoboForm" (top left) > "Print List" > "Logins". When the following print dialog pops up click on the "Save" button and save the HTML file.')},{id:"keepercsv",name:"Keeper (csv)",instructions:c.trustAsHtml('Log into the Keeper web vault (keepersecurity.com/vault). Navigate to "Backup" (top right) and find the "Export to Text File" option. Click "Export Now" to save the TXT/CSV file.')},{id:"enpasscsv",name:"Enpass (csv)",instructions:c.trustAsHtml('Using the Enpass desktop application, navigate to "File" > "Export" > "As CSV". Select "Yes" to the warning alert and save the CSV file. Note that the importer only fully supports files exported while Enpass is set to the English language, so adjust your settings accordingly.')},{id:"safeincloudxml",name:"SafeInCloud (xml)",instructions:c.trustAsHtml('Using the SaveInCloud desktop application, navigate to "File" > "Export" > "As XML" and save the XML file.')},{id:"pwsafexml",name:"Password Safe (xml)",instructions:c.trustAsHtml('Using the Password Safe desktop application, navigate to "File" > "Export To" > "XML format..." and save the XML file.')},{id:"stickypasswordxml",name:"Sticky Password (xml)",instructions:c.trustAsHtml('Using the Sticky Password desktop application, navigate to "Menu" (top right) > "Export" > "Export all". Select the unencrypted format XML option and then the "Save to file" button. Save the XML file.')},{id:"msecurecsv",name:"mSecure (csv)",instructions:c.trustAsHtml('Using the mSecure desktop application, navigate to "File" > "Export" > "CSV File..." and save the CSV file.')},{id:"truekeycsv",name:"True Key (csv)",instructions:c.trustAsHtml('Using the True Key desktop application, click the gear icon (top right) and then navigate to "App Settings". Click the "Export" button, enter your password and save the CSV file.')},{id:"passwordbossjson",name:"Password Boss (json)",instructions:c.trustAsHtml('Using the Password Boss desktop application, navigate to "File" > "Export data" > "Password Boss JSON - not encrypted" and save the JSON file.')},{id:"zohovaultcsv",name:"Zoho Vault (csv)",instructions:c.trustAsHtml('Log into the Zoho web vault (vault.zoho.com). Navigate to "Tools" > "Export Secrets". Select "All Secrets" and click the "Zoho Vault Format CSV" button. Highlight and copy the data from the textarea. Open a text editor like Notepad and paste the data. Save the data from the text editor as zoho_export.csv.')},{id:"splashidcsv",name:"SplashID (csv)",instructions:c.trustAsHtml('Using the SplashID Safe desktop application, click on the SplashID blue lock logo in the top right corner. Navigate to "Export" > "Export as CSV" and save the CSV file.')},{id:"passworddragonxml",name:"Password Dragon (xml)",instructions:c.trustAsHtml('Using the Password Dragon desktop application, navigate to "File" > "Export" > "To XML". In the dialog that pops up select "All Rows" and check all fields. Click the "Export" button and save the XML file.')},{id:"padlockcsv",name:"Padlock (csv)",instructions:c.trustAsHtml('Using the Padlock desktop application, click the hamburger icon in the top left corner and navigate to "Settings". Click the "Export Data" option. Ensure that the "CSV" option is selected from the dropdown. Highlight and copy the data from the textarea. Open a text editor like Notepad and paste the data. Save the data from the text editor as padlock_export.csv.')},{id:"clipperzhtml",name:"Clipperz (html)",instructions:c.trustAsHtml('Log into the Clipperz web application (clipperz.is/app). Click the hamburger menu icon in the top right to expand the navigation bar. Navigate to "Data" > "Export". Click the "download HTML+JSON" button to save the HTML file.')},{id:"avirajson",name:"Avira (json)",instructions:c.trustAsHtml('Using the Avira browser extension, click your username in the top right corner and navigate to "Settings". Locate the "Export Data" section and click "Export". In the dialog that pops up, click the "Export Password Manager Data" button to save the TXT/JSON file.')},{id:"saferpasscsv",name:"SaferPass (csv)",instructions:c.trustAsHtml('Using the SaferPass browser extension, click the hamburger icon in the top left corner and navigate to "Settings". Click the "Export accounts" button to save the CSV file.')},{id:"upmcsv",name:"Universal Password Manager (csv)",instructions:c.trustAsHtml('Using the Universal Password Manager desktop application, navigate to "Database" > "Export" and save the CSV file.')},{id:"ascendocsv",name:"Ascendo DataVault (csv)",instructions:c.trustAsHtml('Using the Ascendo DataVault desktop application, navigate to "Tools" > "Export". In the dialog that pops up, select the "All Items (DVX, CSV)" option. Click the "Ok" button to save the CSV file.')},{id:"meldiumcsv",name:"Meldium (csv)",instructions:c.trustAsHtml('Using the Meldium web vault, navigate to "Settings". Locate the "Export data" function and click "Show me my data" to save the CSV file.')},{id:"passkeepcsv",name:"PassKeep (csv)",instructions:c.trustAsHtml('Using the PassKeep mobile app, navigate to "Backup/Restore". Locate the "CSV Backup/Restore" section and click "Backup to CSV" to save the CSV file.')},{id:"operacsv",name:"Opera (csv)",instructions:c.trustAsHtml('The process for importing from Opera is exactly the same as importing from Google Chrome. See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-chrome/')},{id:"vivaldicsv",name:"Vivaldi (csv)",instructions:c.trustAsHtml('The process for importing from Vivaldi is exactly the same as importing from Google Chrome. See detailed instructions on our help site at https://help.bitwarden.com/article/import-from-chrome/')},{id:"gnomejson",name:"GNOME Passwords and Keys/Seahorse (json)",instructions:c.trustAsHtml('Make sure you have python-keyring and python-gnomekeyring installed. Save the GNOME Keyring Import/Export python script by Luke Plant to your desktop as pw_helper.py. Open terminal and run chmod +rx Desktop/pw_helper.py and then python Desktop/pw_helper.py export Desktop/my_passwords.json. Then upload the resulting my_passwords.json file here to bitwarden.')}],e.setSource=function(){for(var t=0;t-1&&e.cipher.fields.splice(n,1)},e.toggleFavorite=function(){e.cipher.favorite=!e.cipher.favorite},e.clipboardSuccess=function(e){e.clearSelection(),g(e)},e.clipboardError=function(e,t){t&&g(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")},e.folderSort=function(e){return e.id?e.name.toLowerCase():"î º"};function g(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.close=function(){n.dismiss("close")},e.showUpgrade=function(){d.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}}]),angular.module("bit.vault").controller("vaultAddFolderController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","$analytics",function(e,t,n,o,r,a){a.eventTrack("vaultAddFolderController",{category:"Modal"}),e.savePromise=null,e.save=function(o){var i=r.encryptFolder(o);e.savePromise=t.folders.post(i,function(e){a.eventTrack("Created Folder");var t=r.decryptFolder(e);n.close(t)}).$promise},e.close=function(){n.dismiss("close")}}]),angular.module("bit.vault").controller("vaultAttachmentsController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","cipherId","$analytics","validationService","toastr","$timeout","authService","$uibModal",function(e,t,n,o,r,a,i,s,l,c,u,d){i.eventTrack("vaultAttachmentsController",{category:"Modal"}),e.cipher={},e.readOnly=!0,e.loading=!0,e.isPremium=!0,e.canUseAttachments=!0;var p=!1;u.getUserProfile().then(function(n){return e.isPremium=n.premium,t.ciphers.get({id:a}).$promise}).then(function(t){e.cipher=r.decryptCipher(t),e.readOnly=!e.cipher.edit,e.canUseAttachments=e.isPremium||e.cipher.organizationId,e.loading=!1},function(){e.loading=!1}),e.save=function(n){var o=document.getElementById("file"),c=o.files;c&&c.length?e.savePromise=r.encryptAttachmentFile(m(),c[0]).then(function(e){var n=new FormData,o=new Blob([e.data],{type:"application/octet-stream"});return n.append("data",o,e.fileName),t.ciphers.postAttachment({id:a},n).$promise}).then(function(t){i.eventTrack("Added Attachment"),e.cipher=r.decryptCipher(t),o.type="",o.type="file",o.value=""},function(e){var t=s.parseErrors(e);l.error(t.length?t[0]:"An error occurred.")}):s.addError(n,"file","Select a file.",!0)},e.download=function(t){if(t.loading=!0,!e.canUseAttachments)return t.loading=!1,void alert("Premium membership is required to use this feature.");r.downloadAndDecryptAttachment(m(),t,!0).then(function(e){c(function(){t.loading=!1})},function(){c(function(){t.loading=!1})})};function m(){return e.cipher.organizationId?o.getOrgKey(e.cipher.organizationId):null}e.remove=function(n){confirm("Are you sure you want to delete this attachment ("+n.fileName+")?")&&(n.loading=!0,t.ciphers.delAttachment({id:a,attachmentId:n.id}).$promise.then(function(){n.loading=!1,i.eventTrack("Deleted Attachment");var t=e.cipher.attachments.indexOf(n);t>-1&&e.cipher.attachments.splice(t,1)},function(){l.error("Cannot delete attachment."),n.loading=!1}))},e.close=function(){n.dismiss("cancel")},e.$on("modal.closing",function(t,o,r){p||(t.preventDefault(),p=!0,n.close(!!e.cipher.attachments&&e.cipher.attachments.length>0))}),e.showUpgrade=function(){d.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}}]),angular.module("bit.vault").controller("vaultCipherCollectionsController",["$scope","apiService","$uibModalInstance","cipherService","cipherId","$analytics",function(e,t,n,o,r,a){a.eventTrack("vaultCipherCollectionsController",{category:"Modal"}),e.cipher={},e.readOnly=!1,e.loadingCipher=!0,e.loadingCollections=!0,e.selectedCollections={},e.collections=[];var i=null;n.opened.then(function(){t.ciphers.getDetails({id:r}).$promise.then(function(t){if(e.loadingCipher=!1,e.readOnly=!t.Edit,t.Edit&&t.OrganizationId){1===t.Type&&(e.cipher=o.decryptCipherPreview(t));var n={};if(t.CollectionIds)for(var r=0;r-1&&(t.sort=n)})}),d.vaultCiphers=e.ciphers=o("orderBy")(t,["sort","name","subTitle"]);var n=function(e,t){var n=[],o=0,r=e.length;for(;o0){e.ciphers=n[0];var r=200;angular.forEach(n,function(t,n){n>0&&u(function(){Array.prototype.push.apply(e.ciphers,t)},r+=200)})}}function y(){d.vaultCiphers=e.ciphers=o("orderBy")(d.vaultCiphers,["name","subTitle"])}function b(e){return e.id?e.name.toLowerCase():"î º"}e.clipboardError=function(e){alert("Your web browser does not support easy clipboard copying. Edit the item and copy it manually instead.")},e.collapseExpand=function(e,t){c.collapsedFolders||(c.collapsedFolders={});var n=t?"favorite":e.id||"none";n in c.collapsedFolders?delete c.collapsedFolders[n]:c.collapsedFolders[n]=!0},e.collapseAll=function(){if(c.collapsedFolders||(c.collapsedFolders={}),c.collapsedFolders.none=!0,c.collapsedFolders.favorite=!0,d.vaultGroupings)for(var e=0;e-1&&(d.vaultCiphers[o]=t.data),y()}else"partialEdit"===t.action?(n.folderId=t.data.folderId,n.favorite=t.data.favorite):"delete"===t.action&&k(n)})},e.$on("vaultAddCipher",function(t,n){e.addCipher()}),e.addCipher=function(e,n){t.open({animation:!0,templateUrl:"app/vault/views/vaultAddCipher.html",controller:"vaultAddCipherController",resolve:{selectedFolder:function(){return e&&e.folder?e:null},checkedFavorite:function(){return n}}}).result.then(function(e){d.vaultCiphers.push(e),y()})},e.deleteCipher=function(e){confirm("Are you sure you want to delete this item ("+e.name+")?")&&n.ciphers.del({id:e.id},function(){m.eventTrack("Deleted Item"),k(e)})},e.attachments=function(e){a.getUserProfile().then(function(t){return{isPremium:t.premium,orgUseStorage:e.organizationId&&!!t.organizations[e.organizationId].maxStorageGb}}).then(function(n){if(!e.hasAttachments){if(e.organizationId&&!n.orgUseStorage)return void t.open({animation:!0,templateUrl:"app/views/paidOrgRequired.html",controller:"paidOrgRequiredController",resolve:{orgId:function(){return e.organizationId}}});if(!e.organizationId&&!n.isPremium)return void t.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}if(e.organizationId||r.getEncKey()){t.open({animation:!0,templateUrl:"app/vault/views/vaultAttachments.html",controller:"vaultAttachmentsController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){e.hasAttachments=t})}else i.error("You cannot use this feature until you update your encryption key.","Feature Unavailable")})},e.editFolder=function(e){t.open({animation:!0,templateUrl:"app/vault/views/vaultEditFolder.html",controller:"vaultEditFolderController",size:"sm",resolve:{folderId:function(){return e.id}}}).result.then(function(t){e.name=t.name})},e.$on("vaultAddFolder",function(t,n){e.addFolder()}),e.addFolder=function(){t.open({animation:!0,templateUrl:"app/vault/views/vaultAddFolder.html",controller:"vaultAddFolderController",size:"sm"}).result.then(function(e){e.folder=!0,d.vaultGroupings.push(e),h(d.vaultGroupings)})},e.deleteFolder=function(t){confirm("Are you sure you want to delete this folder ("+t.name+")?")&&n.folders.del({id:t.id},function(){m.eventTrack("Deleted Folder");var n=d.vaultGroupings.indexOf(t);n>-1&&(d.vaultGroupings.splice(n,1),e.folderCount--)})},e.canDeleteFolder=function(e){if(!e||!e.id||!d.vaultCiphers)return!1;var t=o("filter")(d.vaultCiphers,{folderId:e.id});return t&&0===t.length},e.share=function(e){t.open({animation:!0,templateUrl:"app/vault/views/vaultShareCipher.html",controller:"vaultShareCipherController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){e.organizationId=t})},e.editCollections=function(e){t.open({animation:!0,templateUrl:"app/vault/views/vaultCipherCollections.html",controller:"vaultCipherCollectionsController",resolve:{cipherId:function(){return e.id}}}).result.then(function(t){t.collectionIds&&!t.collectionIds.length?k(e):t.collectionIds&&(e.collectionIds=t.collectionIds)})},e.filterGrouping=function(t){e.groupingIdFilter=t.id,$.AdminLTE&&$.AdminLTE.layout&&u(function(){$.AdminLTE.layout.fix()},0)},e.filterType=function(t){e.typeFilter=t,$.AdminLTE&&$.AdminLTE.layout&&u(function(){$.AdminLTE.layout.fix()},0)},e.clearFilters=function(){e.groupingIdFilter=void 0,e.typeFilter=void 0,$.AdminLTE&&$.AdminLTE.layout&&u(function(){$.AdminLTE.layout.fix()},0)},e.groupingFilter=function(t){return void 0===e.groupingIdFilter||t.id===e.groupingIdFilter},e.cipherFilter=function(t){return function(n){var o=null===t;return!o&&t.folder&&n.folderId===t.id?o=!0:!o&&t.collection&&n.collectionIds.indexOf(t.id)>-1&&(o=!0),o&&(void 0===e.typeFilter||n.type===e.typeFilter)}},e.unselectAll=function(){S(!1)},e.selectFolder=function(e,t){$(t.currentTarget).closest(".box").find('input[name="cipherSelection"]').prop("checked",!0)},e.select=function(e){var t=$(e.currentTarget).closest("tr").find('input[name="cipherSelection"]');t.prop("checked",!t.prop("checked"))};function w(e,t,n){return n.indexOf(e)===t}function C(){return $('input[name="cipherSelection"]:checked').map(function(){return $(this).val()}).get().filter(w)}function S(e){$('input[name="cipherSelection"]').prop("checked",e)}e.bulkMove=function(){var e=C();if(0!==e.length){t.open({animation:!0,templateUrl:"app/vault/views/vaultMoveCiphers.html",controller:"vaultMoveCiphersController",size:"sm",resolve:{ids:function(){return e}}}).result.then(function(t){for(var n=0;n-1&&d.vaultCiphers.splice(n,1),(n=e.ciphers.indexOf(t))>-1&&e.ciphers.splice(n,1)}}]),angular.module("bit.vault").controller("vaultEditCipherController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","passwordService","cipherId","$analytics","$rootScope","authService","$uibModal","constants","$filter",function(e,t,n,o,r,a,i,s,l,c,u,d,p){s.eventTrack("vaultEditCipherController",{category:"Modal"}),e.folders=p("filter")(l.vaultGroupings,{folder:!0}),e.cipher={},e.readOnly=!1,e.constants=d,c.getUserProfile().then(function(n){return e.useTotp=n.premium,t.ciphers.get({id:i}).$promise}).then(function(t){e.cipher=r.decryptCipher(t),e.readOnly=!e.cipher.edit,e.useTotp=e.useTotp||e.cipher.organizationUseTotp}),e.save=function(o){if(e.readOnly)e.savePromise=t.ciphers.putPartial({id:i},{folderId:o.folderId,favorite:o.favorite},function(e){s.eventTrack("Partially Edited Cipher"),n.close({action:"partialEdit",data:{id:i,favorite:o.favorite,folderId:o.folderId&&""!==o.folderId?o.folderId:null}})}).$promise;else{var a=r.encryptCipher(o,e.cipher.type);e.savePromise=t.ciphers.put({id:i},a,function(e){s.eventTrack("Edited Cipher");var t=r.decryptCipherPreview(e);n.close({action:"edit",data:t})}).$promise}},e.generatePassword=function(){e.cipher.login.password&&!confirm("Are you sure you want to overwrite the current password?")||(s.eventTrack("Generated Password From Edit"),e.cipher.login.password=a.generatePassword({length:14,special:!0}))},e.addField=function(){e.cipher.fields||(e.cipher.fields=[]),e.cipher.fields.push({type:d.fieldType.text.toString(),name:null,value:null})},e.removeField=function(t){var n=e.cipher.fields.indexOf(t);n>-1&&e.cipher.fields.splice(n,1)},e.toggleFavorite=function(){e.cipher.favorite=!e.cipher.favorite},e.clipboardSuccess=function(e){e.clearSelection(),m(e)},e.clipboardError=function(e,t){t&&m(e),alert("Your web browser does not support easy clipboard copying. Copy it manually instead.")},e.folderSort=function(e){return e.id?e.name.toLowerCase():"î º"};function m(e){var t=$(e.trigger).parent().prev();"text"===t.attr("type")&&t.select()}e.delete=function(){confirm("Are you sure you want to delete this item ("+e.cipher.name+")?")&&t.ciphers.del({id:e.cipher.id},function(){s.eventTrack("Deleted Cipher From Edit"),n.close({action:"delete",data:e.cipher.id})})},e.close=function(){n.dismiss("cancel")},e.showUpgrade=function(){u.open({animation:!0,templateUrl:"app/views/premiumRequired.html",controller:"premiumRequiredController"})}}]),angular.module("bit.vault").controller("vaultEditFolderController",["$scope","apiService","$uibModalInstance","cryptoService","cipherService","folderId","$analytics",function(e,t,n,o,r,a,i){i.eventTrack("vaultEditFolderController",{category:"Modal"}),e.folder={},t.folders.get({id:a},function(t){e.folder=r.decryptFolder(t)}),e.savePromise=null,e.save=function(o){var s=r.encryptFolder(o);e.savePromise=t.folders.put({id:a},s,function(e){i.eventTrack("Edited Folder");var t=r.decryptFolder(e);n.close(t)}).$promise},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.vault").controller("vaultMoveCiphersController",["$scope","apiService","$uibModalInstance","ids","$analytics","$rootScope","$filter",function(e,t,n,o,r,a,i){r.eventTrack("vaultMoveCiphersController",{category:"Modal"}),e.folders=i("filter")(a.vaultGroupings,{folder:!0}),e.count=o.length,e.save=function(){e.savePromise=t.ciphers.moveMany({ids:o,folderId:e.folderId},function(){r.eventTrack("Bulk Moved Ciphers"),n.close(e.folderId||null)}).$promise},e.folderSort=function(e){return e.id?e.name.toLowerCase():"!"},e.close=function(){n.dismiss("cancel")}}]),angular.module("bit.vault").controller("vaultShareCipherController",["$scope","apiService","$uibModalInstance","authService","cipherService","cipherId","$analytics","$state","cryptoService","$q","toastr",function(e,t,n,o,r,a,i,s,l,c,u){i.eventTrack("vaultShareCipherController",{category:"Modal"}),e.model={},e.cipher={},e.collections=[],e.selectedCollections={},e.organizations=[];var d={};e.loadingCollections=!0,e.loading=!0,e.readOnly=!1,t.ciphers.get({id:a}).$promise.then(function(t){return e.readOnly=!t.Edit,t.Edit&&(e.cipher=r.decryptCipher(t)),t.Edit}).then(function(t){if(e.loading=!1,t)return o.getUserProfile()}).then(function(n){if(n&&n.organizations){var o=[],a=!1;for(var i in n.organizations)n.organizations.hasOwnProperty(i)&&n.organizations[i].enabled&&(o.push({id:n.organizations[i].id,name:n.organizations[i].name}),d[n.organizations[i].id]=0,a||(a=!0,e.model.organizationId=n.organizations[i].id));e.organizations=o,t.collections.listMe({writeOnly:!0},function(t){for(var n=[],o=0;o')}loadStylesheetIfMissing("visibility","hidden",["lib/bootstrap/css/bootstrap.min.css"]),loadStylesheetIfMissing("fontFamily","FontAwesome",["lib/font-awesome/css/font-awesome.min.css"]); \ No newline at end of file +var cacheTag="xcy7292uik9";function loadStylesheetIfMissing(e,t,i){var s=document.getElementsByTagName("SCRIPT"),l=s[s.length-1].previousElementSibling,n=document.defaultView&&document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(l):l.currentStyle;if(n&&n[e]!==t)for(var o=0;o')}loadStylesheetIfMissing("visibility","hidden",["lib/bootstrap/css/bootstrap.min.css"]),loadStylesheetIfMissing("fontFamily","FontAwesome",["lib/font-awesome/css/font-awesome.min.css"]); \ No newline at end of file diff --git a/js/lib.min.js b/js/lib.min.js index b45c5307..2d274b06 100644 --- a/js/lib.min.js +++ b/js/lib.min.js @@ -1 +1,36535 @@ -!function(t,e){"use strict";var n=window.angulartics||(window.angulartics={});n.waitForVendorCount=0,n.waitForVendorApi=function(t,e,r,i,a){a||n.waitForVendorCount++,i||(i=r,r=void 0),!Object.prototype.hasOwnProperty.call(window,t)||void 0!==r&&void 0===window[t][r]?setTimeout(function(){n.waitForVendorApi(t,e,r,i,!0)},e):(n.waitForVendorCount--,i(window[t]))},t.module("angulartics",[]).provider("$analytics",function(){var e=this,r={pageTracking:{autoTrackFirstPage:!0,autoTrackVirtualPages:!0,trackRelativePath:!1,trackRoutes:!0,trackStates:!0,autoBasePath:!1,basePath:"",excludedRoutes:[],queryKeysWhitelisted:[],queryKeysBlacklisted:[]},eventTracking:{},bufferFlushDelay:1e3,trackExceptions:!1,optOut:!1,developerMode:!1},i=["pageTrack","eventTrack","exceptionTrack","transactionTrack","setAlias","setUsername","setUserProperties","setUserPropertiesOnce","setSuperProperties","setSuperPropertiesOnce","incrementProperty","userTimings","clearCookies"],a={},o={},s={};function u(e,n,r){return o[e]||(o[e]=[]),o[e].push(n),s[n]=r,function(){if(!this.settings.optOut){var n=Array.prototype.slice.apply(arguments);return this.$inject(["$q",t.bind(this,function(r){return r.all(o[e].map(function(e){var i=s[e]||{};if(i.async){var a=r.defer(),o=t.copy(n);return o.unshift(a.resolve),e.apply(this,o),a.promise}return r.when(e.apply(this,n))},this))})])}}}var l={settings:r};l.setOptOut=function(t){this.settings.optOut=t,g()},l.getOptOut=function(){return this.settings.optOut};var c={$get:["$injector",function(t){return d(t)}],api:l,settings:r,virtualPageviews:function(t){this.settings.pageTracking.autoTrackVirtualPages=t},trackStates:function(t){this.settings.pageTracking.trackStates=t},trackRoutes:function(t){this.settings.pageTracking.trackRoutes=t},excludeRoutes:function(t){this.settings.pageTracking.excludedRoutes=t},queryKeysWhitelist:function(t){this.settings.pageTracking.queryKeysWhitelisted=t},queryKeysBlacklist:function(t){this.settings.pageTracking.queryKeysBlacklisted=t},firstPageview:function(t){this.settings.pageTracking.autoTrackFirstPage=t},withBase:function(e){this.settings.pageTracking.basePath=e?t.element(document).find("base").attr("href"):""},withAutoBase:function(t){this.settings.pageTracking.autoBasePath=t},trackExceptions:function(t){this.settings.trackExceptions=t},developerMode:function(t){this.settings.developerMode=t}};function p(e,n,i){if(!r.developerMode){l[e]=u(e,n,i);var o=r[e],s=o?o.bufferFlushDelay:null,c=null!==s?s:r.bufferFlushDelay;t.forEach(a[e],function(t,e){r=function(){n.apply(this,t)},(i=e*c)?setTimeout(r,i):r();var r,i})}}var d=function(e){return t.extend(l,{$inject:e.invoke})};function f(t){var e,r="register"+(e=t,e.replace(/^./,function(t){return t.toUpperCase()}));c[r]=function(e,n){p(t,e,n)},l[t]=u(t,(i=t,function(){n.waitForVendorCount&&(a[i]||(a[i]=[]),a[i].push(arguments))}));var i}function h(n,r,i){t.forEach(r,i);for(var a in n)e[a]=n[a]}var g=function(){h(c,i,f)};h(c,i,f)}).run(["$rootScope","$window","$analytics","$injector",function(e,n,r,i){function a(t,e,n){if(/\?/.test(t)&&e.length>0){for(var r=t.split("?"),i=r[0],a=r[1].split("&"),o=[],s=0;s-1)&&o.push(a[l]);var c="white"==n?o:function(t,e){for(var n=[],r=0;r0?i+"?"+c.join("&"):i}return t}function o(t,e){(function(t){for(var e=0;e-1)return!0}return!1})(t)||(t=a(t,r.settings.pageTracking.queryKeysWhitelisted,"white"),t=a(t,r.settings.pageTracking.queryKeysBlacklisted,"black"),r.pageTrack(t,e))}r.settings.pageTracking.autoTrackFirstPage&&i.invoke(["$location",function(t){var e=!0;if(i.has("$route")){var a=i.get("$route");if(a)for(var s in a.routes){e=!1;break}else null===a&&(e=!1)}else if(i.has("$state")){var u=i.get("$state");u.get().length>1&&(e=!1)}if(e)if(r.settings.pageTracking.autoBasePath&&(r.settings.pageTracking.basePath=n.location.pathname),r.settings.pageTracking.trackRelativePath){var l=r.settings.pageTracking.basePath+t.url();o(l,t)}else o(t.absUrl(),t)}]);r.settings.pageTracking.autoTrackVirtualPages&&i.invoke(["$location",function(t){r.settings.pageTracking.autoBasePath&&(r.settings.pageTracking.basePath=n.location.pathname+"#");var a=!0;if(r.settings.pageTracking.trackRoutes&&i.has("$route")){var s=i.get("$route");if(s)for(var u in s.routes){a=!1;break}else null===s&&(a=!1);e.$on("$routeChangeSuccess",function(e,n){if(!n||!(n.$$route||n).redirectTo){var i=r.settings.pageTracking.basePath+t.url();o(i,t)}})}r.settings.pageTracking.trackStates&&(i.has("$state")&&!i.has("$transitions")&&(a=!1,e.$on("$stateChangeSuccess",function(e,n){var i=r.settings.pageTracking.basePath+t.url();o(i,t)})),i.has("$state")&&i.has("$transitions")&&(a=!1,i.invoke(["$transitions",function(e){e.onSuccess({},function(e){var n=e.options();if(n.notify){var i=r.settings.pageTracking.basePath+t.url();o(i,t)}})}]))),a&&e.$on("$locationChangeSuccess",function(e,n){if(!n||!(n.$$route||n).redirectTo)if(r.settings.pageTracking.trackRelativePath){var i=r.settings.pageTracking.basePath+t.url();o(i,t)}else o(t.absUrl(),t)})}]);r.settings.developerMode&&t.forEach(r,function(t,e){"function"==typeof t&&(r[e]=function(){})})}]).directive("analyticsOn",["$analytics",function(e){return{restrict:"A",link:function(n,a,o){var s=o.analyticsOn||"click",u={};t.forEach(o.$attr,function(t,e){"analytics"===(n=e).substr(0,9)&&-1===["On","Event","If","Properties","EventType"].indexOf(n.substr(9))&&(u[i(e)]=o[e],o.$observe(e,function(t){u[i(e)]=t}));var n}),t.element(a[0]).on(s,function(i){var s,l=o.analyticsEvent||(s=a[0],r(s)?s.innerText||s.value:s.id||s.name||s.tagName);u.eventType=i.type,o.analyticsIf&&!n.$eval(o.analyticsIf)||(o.analyticsProperties&&t.extend(u,n.$eval(o.analyticsProperties)),e.eventTrack(l,u))})}}}]).config(["$provide",function(t){t.decorator("$exceptionHandler",["$delegate","$injector",function(t,e){return function(n,r){var i=t(n,r),a=e.get("$analytics");return a.settings.trackExceptions&&a.exceptionTrack(n,r),i}}])}]);function r(t){return["a:","button:","button:button","button:submit","input:button","input:submit"].indexOf(t.tagName.toLowerCase()+":"+(t.type||""))>=0}function i(t){var e=t.slice(9);return void 0!==e&&null!==e&&e.length>0?e.substring(0,1).toLowerCase()+e.substring(1):e}}(angular),function(){var t;(t=angular.module("ui.bootstrap.showErrors",[])).directive("showErrors",["$timeout","showErrorsConfig","$interpolate",function(t,e,n){var r,i,a;return i=function(t){var n;return n=e.trigger,t&&null!=t.trigger&&(n=t.trigger),n},r=function(t){var n;return n=e.showSuccess,t&&null!=t.showSuccess&&(n=t.showSuccess),n},a=function(e,a,o,s){var u,l,c,p,d,f,h,g;if(u=!1,d=e.$eval(o.showErrors),f=r(d),g=i(d),l=a[0].querySelector(".form-control[name]"),p=angular.element(l),!(c=n(p.attr("name")||"")(e)))throw"show-errors element has no child input elements with a 'name' attribute and a 'form-control' class";return p.bind(g,function(){return u=!0,h(s[c].$invalid)}),e.$watch(function(){return s[c]&&s[c].$invalid},function(t){if(u)return h(t)}),e.$on("show-errors-check-validity",function(){return h(s[c].$invalid)}),e.$on("show-errors-reset",function(){return t(function(){return a.removeClass("has-error"),a.removeClass("has-success"),u=!1},0,!1)}),function(t){if(a.toggleClass("has-error",t),f)return a.toggleClass("has-success",!t)}},{restrict:"A",require:"^form",compile:function(t,e){if(-1===e.showErrors.indexOf("skipFormGroupCheck")&&!t.hasClass("form-group")&&!t.hasClass("input-group"))throw"show-errors element does not have the 'form-group' or 'input-group' class";return a}}}]),t.provider("showErrorsConfig",function(){var t,e;t=!1,e="blur",this.showSuccess=function(e){return t=e},this.trigger=function(t){return e=t},this.$get=function(){return{showSuccess:t,trigger:e}}})}.call(this),function(t,e){"use strict";e.module("ngCookies",["ng"]).info({angularVersion:"1.6.7"}).provider("$cookies",[function(){var t=this.defaults={};function n(n){return n?e.extend({},t,n):t}this.$get=["$$cookieReader","$$cookieWriter",function(t,r){return{get:function(e){return t()[e]},getObject:function(t){var n=this.get(t);return n?e.fromJson(n):n},getAll:function(){return t()},put:function(t,e,i){r(t,e,n(i))},putObject:function(t,n,r){this.put(t,e.toJson(n),r)},remove:function(t,e){r(t,void 0,n(e))}}}]}]),e.module("ngCookies").factory("$cookieStore",["$cookies",function(t){return{get:function(e){return t.getObject(e)},put:function(e,n){t.putObject(e,n)},remove:function(e){t.remove(e)}}}]);function n(t,n,r){var i=r.baseHref(),a=t[0];return function(t,r,o){a.cookie=function(t,r,a){var o,s;s=(a=a||{}).expires,o=e.isDefined(a.path)?a.path:i,e.isUndefined(r)&&(s="Thu, 01 Jan 1970 00:00:00 GMT",r=""),e.isString(s)&&(s=new Date(s));var u=encodeURIComponent(t)+"="+encodeURIComponent(r);u+=o?";path="+o:"",u+=a.domain?";domain="+a.domain:"",u+=s?";expires="+s.toUTCString():"";var l=(u+=a.secure?";secure":"").length+1;return l>4096&&n.warn("Cookie '"+t+"' possibly not set or overflowed because it was too large ("+l+" > 4096 bytes)!"),u}(t,r,o)}}n.$inject=["$document","$log","$browser"],e.module("ngCookies").provider("$$cookieWriter",function(){this.$get=n})}(window,window.angular),angular.module("angular-jwt",["angular-jwt.options","angular-jwt.interceptor","angular-jwt.jwt","angular-jwt.authManager"]),angular.module("angular-jwt.authManager",[]).provider("authManager",function(){this.$get=["$rootScope","$injector","$location","jwtHelper","jwtInterceptor","jwtOptions",function(t,e,n,r,i,a){var o=a.getConfig();function s(t){return Array.isArray(t)?e.invoke(t,this,{options:null}):t()}function u(t){if(Array.isArray(t)||angular.isFunction(t))return e.invoke(t,o,{});throw new Error("unauthenticatedRedirector must be a function")}t.isAuthenticated=!1;function l(){t.isAuthenticated=!0}function c(){t.isAuthenticated=!1}var p=e.has("$state")?"$stateChangeStart":"$routeChangeStart";return t.$on(p,function(t,e){if(!e)return!1;var n=e.$$route?e.$$route:e.data;if(n&&!0===n.requiresLogin){var i=s(o.tokenGetter);i&&!r.isTokenExpired(i)||(t.preventDefault(),u(o.unauthenticatedRedirector))}}),{authenticate:l,unauthenticate:c,getToken:function(){return s(o.tokenGetter)},redirect:function(){return u(o.unauthenticatedRedirector)},checkAuthOnRefresh:function(){t.$on("$locationChangeStart",function(){var e=s(o.tokenGetter);e&&(r.isTokenExpired(e)?t.$broadcast("tokenHasExpired",e):l())})},redirectWhenUnauthenticated:function(){t.$on("unauthenticated",function(){u(o.unauthenticatedRedirector),c()})},isAuthenticated:function(){var t=s(o.tokenGetter);if(t)return!r.isTokenExpired(t)}}}]}),angular.module("angular-jwt.interceptor",[]).provider("jwtInterceptor",function(){this.urlParam,this.authHeader,this.authPrefix,this.whiteListedDomains,this.tokenGetter;var t=this;this.$get=["$q","$injector","$rootScope","urlUtils","jwtOptions",function(e,n,r,i,a){var o=angular.extend({},a.getConfig(),t);return{request:function(t){if(t.skipAuthorization||!function(t){if(!i.isSameOrigin(t)&&!o.whiteListedDomains.length)throw new Error("As of v0.1.0, requests to domains other than the application's origin must be white listed. Use jwtOptionsProvider.config({ whiteListedDomains: [] }); to whitelist.");for(var e=i.urlResolve(t).hostname.toLowerCase(),n=0;n(new Date).valueOf()+1e3*e)}}]),angular.module("angular-jwt.options",[]).provider("jwtOptions",function(){var t={};this.config=function(e){t=e},this.$get=function(){var e={urlParam:null,authHeader:"Authorization",authPrefix:"Bearer ",whiteListedDomains:[],tokenGetter:function(){return null},loginPath:"/",unauthenticatedRedirectPath:"/",unauthenticatedRedirector:["$location",function(t){t.path(this.unauthenticatedRedirectPath)}]};function n(){this.config=angular.extend({},e,t)}return n.prototype.getConfig=function(){return this.config},new n}}),angular.module("angular-jwt.interceptor").service("urlUtils",function(){var t=document.createElement("a"),e=n(window.location.href);function n(e){var n=e;return t.setAttribute("href",n),n=t.href,t.setAttribute("href",n),{href:t.href,protocol:t.protocol?t.protocol.replace(/:$/,""):"",host:t.host,search:t.search?t.search.replace(/^\?/,""):"",hash:t.hash?t.hash.replace(/^#/,""):"",hostname:t.hostname,port:t.port,pathname:"/"===t.pathname.charAt(0)?t.pathname:"/"+t.pathname}}return{urlResolve:n,isSameOrigin:function(t){var r=angular.isString(t)?n(t):t;return r.protocol===e.protocol&&r.host===e.host}}});var app=angular.module("angular-promise-polyfill",[]).run(["$q","$window",function(t,e){e.Promise=function(e){return t(e)},e.Promise.all=t.all.bind(t),e.Promise.reject=t.reject.bind(t),e.Promise.resolve=t.when.bind(t),e.Promise.race=function(e){for(var n=t.defer(),r=0;r=new Date(e,t)},month:{parse:function(t){return i(t)},isValid:r},year:{parse:a,format:function(t,e){return t=t.toString(),e?t.substr(2,4):t},isValid:function(t){return"number"==typeof t&&(t=i(t))>0},isPast:function(t){return(new Date).getFullYear()>t}}}},{"is-valid-month":21,"parse-int":24,"parse-year":25}],13:[function(t,e,n){"use strict";e.exports={card:t("./card"),cvc:t("./cvc"),expiration:t("./expiration")}},{"./card":10,"./cvc":11,"./expiration":12}],14:[function(t,e,n){"use strict";var r=t("creditcards-types"),i=t("to-camel-case"),a=t("xtend");e.exports=a(r,{get:function(t){return r.types[i(t)]}})},{"creditcards-types":7,"to-camel-case":26,xtend:29}],15:[function(t,e,n){"use strict";var r=t("zero-fill"),i=t("parse-int"),a=r(2);e.exports=function(t,e){var n=(e=e||new Date).getFullYear().toString().substr(0,2);return t=i(t),i(n+a(t))}},{"parse-int":24,"zero-fill":31}],16:[function(t,e,n){"use strict";e.exports=(r=[0,2,4,6,8,1,3,5,7,9],function(t){if("string"!=typeof t)throw new TypeError("Expected string input");if(!t)return!1;for(var e,n=t.length,i=1,a=0;n;)e=parseInt(t.charAt(--n),10),a+=(i^=1)?r[e]:e;return!!a&&a%10==0});var r},{}],17:[function(t,e,n){"use strict";var r=Array.prototype.slice,i=Object.prototype.toString;e.exports=function(t){var e=this;if("function"!=typeof e||"[object Function]"!==i.call(e))throw new TypeError("Function.prototype.bind called on incompatible "+e);for(var n,a=r.call(arguments,1),o=Math.max(0,e.length-a.length),s=[],u=0;u=1&&t<=12)}},{"is-integer":20}],22:[function(t,e,n){e.exports=Array.isArray||function(t){return"[object Array]"==Object.prototype.toString.call(t)}},{}],23:[function(t,e,n){"use strict";e.exports=Number.isNaN||function(t){return t!=t}},{}],24:[function(t,e,n){"use strict";var r=t("is-integer");e.exports=function(t){return"number"==typeof t?r(t)?t:void 0:"string"==typeof t&&/^-?\d+$/.test(t)?parseInt(t,10):void 0}},{"is-integer":20}],25:[function(t,e,n){"use strict";var r=t("parse-int"),i=t("expand-year");e.exports=function(t,e,n){if(null!=(t=r(t)))return e?i(t,n):t}},{"expand-year":15,"parse-int":24}],26:[function(t,e,n){var r=t("to-space-case");e.exports=function(t){return r(t).replace(/\s(\w)/g,function(t,e){return e.toUpperCase()})}},{"to-space-case":28}],27:[function(t,e,n){e.exports=function(t){var e;return r.test(t)?t.toLowerCase():i.test(t)?(e=t,e.replace(a,function(t,e,n){return e+" "+n.toLowerCase().split("").join(" ")})).toLowerCase():t.toLowerCase();return t.toLowerCase()};var r=/\s/,i=/([a-z][A-Z]|[A-Z][a-z])/;var a=/(.)([A-Z]+)/g},{}],28:[function(t,e,n){var r=t("to-no-case");e.exports=function(t){return r(t).replace(/[\W_]+(.|$)/g,function(t,e){return e?" "+e:""}).trim()}},{"to-no-case":27}],29:[function(t,e,n){e.exports=function(){for(var t={},e=0;e0?new Array(e+(/\./.test(n)?2:1)).join(r)+n:n+"")}},{}]},{},[3])(3)}),function(t,e){"use strict";var n,r,i,a;e.module("ngMessages",[],function(){n=e.forEach,r=e.isArray,i=e.isString,a=e.element}).info({angularVersion:"1.6.7"}).directive("ngMessages",["$animate",function(t){return{require:"ngMessages",restrict:"AE",controller:["$element","$scope","$attrs",function(i,a,o){var s=this,u=0,l=0;this.getAttachId=function(){return l++};var c,p,d=this.messages={};this.render=function(u){c=!1,p=u=u||{};for(var l=e(a,o.ngMessagesMultiple)||e(a,o.multiple),d=[],f={},h=s.head,g=!1,m=0;null!=h;){m++;var v=h.message,y=!1;g||n(u,function(t,e){if(!y&&r(t)&&v.test(e)){if(f[e])return;f[e]=!0,y=!0,v.attach()}}),y?g=!l:d.push(v),h=h.next}n(d,function(t){t.detach()}),d.length!==m?t.setClass(i,"ng-active","ng-inactive"):t.setClass(i,"ng-inactive","ng-active")},a.$watchCollection(o.ngMessages||o.for,s.render),i.on("$destroy",function(){n(d,function(t){t.message.detach()})}),this.reRender=function(){c||(c=!0,a.$evalAsync(function(){c&&p&&s.render(p)}))},this.register=function(t,e){var n=u.toString();d[n]={message:e},function(t,e,n){var r=d[n];if(s.head){var i=f(t,e);i?(r.next=i.next,i.next=r):(r.next=s.head,s.head=r)}else s.head=r}(i[0],t,n),t.$$ngMessageNode=n,u++,s.reRender()},this.deregister=function(t){var e=t.$$ngMessageNode;delete t.$$ngMessageNode,function(t,e,n){var r=d[n],i=f(t,e);i?i.next=r.next:s.head=r.next}(i[0],t,e),delete d[e],s.reRender()};function f(t,e){for(var n=e,r=[];n&&n!==t;){var i=n.$$ngMessageNode;if(i&&i.length)return d[i];n.childNodes.length&&-1===r.indexOf(n)?(r.push(n),n=n.childNodes[n.childNodes.length-1]):n.previousSibling?n=n.previousSibling:(n=n.parentNode,r.push(n))}}}]};function e(t,e){return i(e)&&0===e.length||r(t.$eval(e))}function r(t){return i(t)?t.length:!!t}}]).directive("ngMessagesInclude",["$templateRequest","$document","$compile",function(t,e,n){return{restrict:"AE",require:"^^ngMessages",link:function(e,a,o){var s=o.ngMessagesInclude||o.src;t(s).then(function(t){e.$$destroyed||(i(t)&&!t.trim()?r(a,s):n(t)(e,function(t){a.after(t),r(a,s)}))})}};function r(t,r){var i=n.$$createComment?n.$$createComment("ngMessagesInclude",r):e[0].createComment(" ngMessagesInclude: "+r+" "),o=a(i);t.after(o),t.remove()}}]).directive("ngMessage",o()).directive("ngMessageExp",o());function o(){return["$animate",function(t){return{restrict:"AE",transclude:"element",priority:1,terminal:!0,require:"^^ngMessages",link:function(e,n,i,a,o){var s,u=n[0],l=i.ngMessage||i.when,c=i.ngMessageExp||i.whenExp,p=function(t){s=t?r(t)?t:t.split(/[\s,]+/):null,a.reRender()};c?(p(e.$eval(c)),e.$watchCollection(c,p)):p(l);var d,f;a.register(u,f={test:function(t){return function(t,e){if(t)return r(t)?t.indexOf(e)>=0:t.hasOwnProperty(e)}(s,t)},attach:function(){d||o(function(e,r){t.enter(e,null,n);var i=(d=e).$$attachId=a.getAttachId();d.on("$destroy",function(){d&&d.$$attachId===i&&(a.deregister(u),f.detach()),r.$destroy()})})},detach:function(){if(d){var e=d;d=null,t.leave(e)}}})}}}]}}(window,window.angular),function(t,e){"use strict";var n,r,i,a,o,s,u,l,c,p=e.$$minErr("$sanitize");e.module("ngSanitize",[]).provider("$sanitize",function(){var d=!1;this.$get=["$$sanitizeUri",function(t){return d&&r(C,$),function(e){var n=[];return l(e,c(n,function(e,n){return!/^unsafe:/.test(t(e,n))})),n.join("")}}],this.enableSvg=function(t){return a(t)?(d=t,this):d},n=e.bind,r=e.extend,i=e.forEach,a=e.isDefined,o=e.lowercase,s=e.noop,l=function(t,e){null===t||void 0===t?t="":"string"!=typeof t&&(t=""+t);var n=A(t);if(!n)return"";var r=5;do{if(0===r)throw p("uinput","Failed to sanitize html because the input is unstable");r--,t=n.innerHTML,n=A(t)}while(t!==n.innerHTML);for(var i=n.firstChild;i;){switch(i.nodeType){case 1:e.start(i.nodeName.toLowerCase(),B(i.attributes));break;case 3:e.chars(i.textContent)}var a;if(!((a=i.firstChild)||(1===i.nodeType&&e.end(i.nodeName.toLowerCase()),a=M("nextSibling",i))))for(;null==a&&(i=M("parentNode",i))!==n;)a=M("nextSibling",i),1===i.nodeType&&e.end(i.nodeName.toLowerCase());i=a}for(;i=n.firstChild;)n.removeChild(i)},c=function(t,e){var r=!1,a=n(t,t.push);return{start:function(t,n){t=o(t),!r&&k[t]&&(r=t),r||!0!==C[t]||(a("<"),a(t),i(n,function(n,r){var i=o(r),s="img"===t&&"src"===i||"background"===i;!0!==E[i]||!0===x[i]&&!e(n,s)||(a(" "),a(r),a('="'),a(I(n)),a('"'))}),a(">"))},end:function(t){t=o(t),r||!0!==C[t]||!0===g[t]||(a("")),t==r&&(r=!1)},chars:function(t){r||a(I(t))}}},u=t.Node.prototype.contains||function(t){return!!(16&this.compareDocumentPosition(t))};var f=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,h=/([^#-~ |!])/g,g=D("area,br,col,hr,img,wbr"),m=D("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),v=D("rp,rt"),y=r({},v,m),b=r({},m,D("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul")),w=r({},v,D("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),$=D("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,stop,svg,switch,text,title,tspan"),k=D("script,style"),C=r({},g,b,w,y),x=D("background,cite,href,longdesc,src,xlink:href"),T=D("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,valign,value,vspace,width"),S=D("accent-height,accumulate,additive,alphabetic,arabic-form,ascent,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan",!0),E=r({},x,S,T);function D(t,e){var n,r={},i=t.split(",");for(n=0;n"+e;try{e=encodeURI(e)}catch(t){return}var n=new t.XMLHttpRequest;n.responseType="document",n.open("GET","data:text/html;charset=utf-8,"+e,!1),n.send(null);var r=n.response.body;return r.firstChild.remove(),r}}(t,t.document);function B(t){for(var e={},n=0,r=t.length;n/g,">")}function O(e){for(;e;){if(e.nodeType===t.Node.ELEMENT_NODE)for(var n=e.attributes,r=0,i=n.length;r"\u201d\u2019]/i,r=/^mailto:/i,i=e.$$minErr("linky"),a=e.isDefined,o=e.isFunction,u=e.isObject,l=e.isString;return function(e,p,d){if(null==e||""===e)return e;if(!l(e))throw i("notstring","Expected string but received: {0}",e);for(var f,h,g,m=o(d)?d:u(d)?function(){return d}:function(){return{}},v=e,y=[];f=v.match(n);)h=f[0],f[2]||f[4]||(h=(f[3]?"http://":"mailto:")+h),g=f.index,b(v.substr(0,g)),w(h,f[0].replace(r,"")),v=v.substring(g+f[0].length);return b(v),t(y.join(""));function b(t){t&&y.push(function(t){var e=[];return c(e,s).chars(t),e.join("")}(t))}function w(t,e){var n,r=m(t);y.push("'),b(e),y.push("")}}}])}(window,window.angular),function(t,e){"use strict";var n=e.$$minErr("$resource"),r=/^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;function i(t,i){if(null==(a=i)||""===a||"hasOwnProperty"===a||!r.test("."+a))throw n("badmember",'Dotted member path "@{0}" is invalid.',i);for(var a,o=i.split("."),s=0,u=o.length;s=0&&t.scope.refreshTimer(e)}};function f(e,n){var r=function(t){for(var e=0;e=e&&l[e-1].open.resolve(),l.length||(s.remove(),s=null,d=o.defer())}))}function h(n,a,h,m){return angular.isObject(h)&&(m=h,h=null),function(n){var a=g();if(function(){var t=a.preventDuplicates&&n.message===c,e=a.preventOpenDuplicates&&p[n.message];if(t||e)return!0;return c=n.message,p[n.message]=!0,!1}())return;var h=function(){var t={toastId:u++,isOpened:!1,scope:r.$new(),open:o.defer()};t.iconClass=n.iconClass,n.optionsOverride&&(angular.extend(a,function(t){for(var e=["containerId","iconClasses","maxOpened","newestOnTop","positionClass","preventDuplicates","preventOpenDuplicates","templates"],n=0,r=e.length;n");return e.get("$compile")(n)(t)}(t.scope),t}();if(l.push(h),a.autoDismiss&&a.maxOpened&&l.length>a.maxOpened)for(var m=l.slice(0,l.length-a.maxOpened),v=0,y=m.length;v")).attr("id",e.containerId),s.addClass(e.positionClass),s.css({"pointer-events":"auto"});var n=angular.element(document.querySelector(e.target));if(!n||!n.length)throw"Target for toasts doesn't exist";return t.enter(s,n).then(function(){d.resolve()}),d.promise})(a).then(function(){if(h.isOpened=!0,a.newestOnTop)t.enter(h.el,s).then(function(){h.scope.init()});else{var e=s[0].lastChild?angular.element(s[0].lastChild):null;t.enter(h.el,s,e).then(function(){h.scope.init()})}})}),h}({iconClass:n,message:a,optionsOverride:m,title:h})}function g(){return angular.extend({},a)}}}(),function(){"use strict";angular.module("toastr").constant("toastrConfig",{allowHtml:!1,autoDismiss:!1,closeButton:!1,closeHtml:"",containerId:"toast-container",extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},maxOpened:0,messageClass:"toast-message",newestOnTop:!0,onHidden:null,onShown:null,onTap:null,positionClass:"toast-top-right",preventDuplicates:!1,preventOpenDuplicates:!1,progressBar:!1,tapToDismiss:!0,target:"body",templates:{toast:"directives/toast/toast.html",progressbar:"directives/progressbar/progressbar.html"},timeOut:5e3,titleClass:"toast-title",toastClass:"toast"})}(),function(){"use strict";angular.module("toastr").directive("progressBar",t),t.$inject=["toastrConfig"];function t(t){return{require:"^toast",templateUrl:function(){return t.templates.progressbar},link:function(t,e,n,r){var i,a,o;r.progressBar=t,t.start=function(t){i&&clearInterval(i),a=parseFloat(t),o=(new Date).getTime()+a,i=setInterval(s,10)},t.stop=function(){i&&clearInterval(i)};function s(){var t=(o-(new Date).getTime())/a*100;e.css("width",t+"%")}t.$on("$destroy",function(){clearInterval(i)})}}}}(),function(){"use strict";angular.module("toastr").controller("ToastController",function(){this.progressBar=null,this.startProgressBar=function(t){this.progressBar&&this.progressBar.start(t)},this.stopProgressBar=function(){this.progressBar&&this.progressBar.stop()}})}(),function(){"use strict";angular.module("toastr").directive("toast",t),t.$inject=["$injector","$interval","toastrConfig","toastr"];function t(t,e,n,r){return{templateUrl:function(){return n.templates.toast},controller:"ToastController",link:function(n,i,a,o){var s;if(n.toastClass=n.options.toastClass,n.titleClass=n.options.titleClass,n.messageClass=n.options.messageClass,n.progressBar=n.options.progressBar,n.options.closeHtml){var u=angular.element(n.options.closeHtml),l=t.get("$compile");u.addClass("toast-close-button"),u.attr("ng-click","close(true, $event)"),l(u)(n),i.children().prepend(u)}n.init=function(){n.options.timeOut&&(s=c(n.options.timeOut)),n.options.onShown&&n.options.onShown()},i.on("mouseenter",function(){n.progressBar=!1,o.stopProgressBar(),s&&e.cancel(s)}),n.tapToast=function(){angular.isFunction(n.options.onTap)&&n.options.onTap(),n.options.tapToDismiss&&n.close(!0)},n.close=function(t,e){e&&angular.isFunction(e.stopPropagation)&&e.stopPropagation(),r.remove(n.toastId,t)},n.refreshTimer=function(t){s&&(e.cancel(s),s=c(t||n.options.timeOut))},i.on("mouseleave",function(){0===n.options.timeOut&&0===n.options.extendedTimeOut||(n.$apply(function(){n.progressBar=n.options.progressBar}),s=c(n.options.extendedTimeOut))});function c(t){return o.startProgressBar(t),e(function(){o.stopProgressBar(),r.remove(n.toastId)},t,1)}}}}}(),function(){"use strict";angular.module("toastr",[]).factory("toastr",t),t.$inject=["$animate","$injector","$document","$rootScope","$sce","toastrConfig","$q"];function t(t,e,n,r,i,a,o){var s,u=0,l=[],c="",p={},d=o.defer();return{active:function(){return l.length},clear:function(t){if(1===arguments.length&&!t)return;if(t)f(t.toastId);else for(var e=0;e=0&&t.scope.refreshTimer(e)}};function f(e,n){var r=function(t){for(var e=0;e=e&&l[e-1].open.resolve(),l.length||(s.remove(),s=null,d=o.defer())}))}function h(n,a,h,m){return angular.isObject(h)&&(m=h,h=null),function(n){var a=g();if(function(){var t=a.preventDuplicates&&n.message===c,e=a.preventOpenDuplicates&&p[n.message];if(t||e)return!0;return c=n.message,p[n.message]=!0,!1}())return;var h=function(){var t={toastId:u++,isOpened:!1,scope:r.$new(),open:o.defer()};t.iconClass=n.iconClass,n.optionsOverride&&(angular.extend(a,function(t){for(var e=["containerId","iconClasses","maxOpened","newestOnTop","positionClass","preventDuplicates","preventOpenDuplicates","templates"],n=0,r=e.length;n");return e.get("$compile")(n)(t)}(t.scope),t}();if(l.push(h),a.autoDismiss&&a.maxOpened&&l.length>a.maxOpened)for(var m=l.slice(0,l.length-a.maxOpened),v=0,y=m.length;v")).attr("id",e.containerId),s.addClass(e.positionClass),s.css({"pointer-events":"auto"});var n=angular.element(document.querySelector(e.target));if(!n||!n.length)throw"Target for toasts doesn't exist";return t.enter(s,n).then(function(){d.resolve()}),d.promise})(a).then(function(){if(h.isOpened=!0,a.newestOnTop)t.enter(h.el,s).then(function(){h.scope.init()});else{var e=s[0].lastChild?angular.element(s[0].lastChild):null;t.enter(h.el,s,e).then(function(){h.scope.init()})}})}),h}({iconClass:n,message:a,optionsOverride:m,title:h})}function g(){return angular.extend({},a)}}}(),function(){"use strict";angular.module("toastr").constant("toastrConfig",{allowHtml:!1,autoDismiss:!1,closeButton:!1,closeHtml:"",containerId:"toast-container",extendedTimeOut:1e3,iconClasses:{error:"toast-error",info:"toast-info",success:"toast-success",warning:"toast-warning"},maxOpened:0,messageClass:"toast-message",newestOnTop:!0,onHidden:null,onShown:null,onTap:null,positionClass:"toast-top-right",preventDuplicates:!1,preventOpenDuplicates:!1,progressBar:!1,tapToDismiss:!0,target:"body",templates:{toast:"directives/toast/toast.html",progressbar:"directives/progressbar/progressbar.html"},timeOut:5e3,titleClass:"toast-title",toastClass:"toast"})}(),function(){"use strict";angular.module("toastr").directive("progressBar",t),t.$inject=["toastrConfig"];function t(t){return{require:"^toast",templateUrl:function(){return t.templates.progressbar},link:function(t,e,n,r){var i,a,o;r.progressBar=t,t.start=function(t){i&&clearInterval(i),a=parseFloat(t),o=(new Date).getTime()+a,i=setInterval(s,10)},t.stop=function(){i&&clearInterval(i)};function s(){var t=(o-(new Date).getTime())/a*100;e.css("width",t+"%")}t.$on("$destroy",function(){clearInterval(i)})}}}}(),function(){"use strict";angular.module("toastr").controller("ToastController",function(){this.progressBar=null,this.startProgressBar=function(t){this.progressBar&&this.progressBar.start(t)},this.stopProgressBar=function(){this.progressBar&&this.progressBar.stop()}})}(),function(){"use strict";angular.module("toastr").directive("toast",t),t.$inject=["$injector","$interval","toastrConfig","toastr"];function t(t,e,n,r){return{templateUrl:function(){return n.templates.toast},controller:"ToastController",link:function(n,i,a,o){var s;if(n.toastClass=n.options.toastClass,n.titleClass=n.options.titleClass,n.messageClass=n.options.messageClass,n.progressBar=n.options.progressBar,n.options.closeHtml){var u=angular.element(n.options.closeHtml),l=t.get("$compile");u.addClass("toast-close-button"),u.attr("ng-click","close(true, $event)"),l(u)(n),i.children().prepend(u)}n.init=function(){n.options.timeOut&&(s=c(n.options.timeOut)),n.options.onShown&&n.options.onShown()},i.on("mouseenter",function(){n.progressBar=!1,o.stopProgressBar(),s&&e.cancel(s)}),n.tapToast=function(){angular.isFunction(n.options.onTap)&&n.options.onTap(),n.options.tapToDismiss&&n.close(!0)},n.close=function(t,e){e&&angular.isFunction(e.stopPropagation)&&e.stopPropagation(),r.remove(n.toastId,t)},n.refreshTimer=function(t){s&&(e.cancel(s),s=c(t||n.options.timeOut))},i.on("mouseleave",function(){0===n.options.timeOut&&0===n.options.extendedTimeOut||(n.$apply(function(){n.progressBar=n.options.progressBar}),s=c(n.options.extendedTimeOut))});function c(t){return o.startProgressBar(t),e(function(){o.stopProgressBar(),r.remove(n.toastId)},t,1)}}}}}(),angular.module("toastr").run(["$templateCache",function(t){t.put("directives/progressbar/progressbar.html",'
\n'),t.put("directives/toast/toast.html",'
\n
\n
{{title}}
\n
{{message}}
\n
\n
\n
\n \n
\n')}]),function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).angularStripe=t()}}(function(){return function t(e,n,r){function i(o,s){if(!n[o]){if(!e[o]){var u="function"==typeof require&&require;if(!s&&u)return u(o,!0);if(a)return a(o,!0);var l=new Error("Cannot find module '"+o+"'");throw l.code="MODULE_NOT_FOUND",l}var c=n[o]={exports:{}};e[o][0].call(c.exports,function(t){var n=e[o][1][t];return i(n||t)},c,c.exports,t,e,n,r)}return n[o].exports}for(var a="function"==typeof require&&require,o=0;o>>0,a=[];for(e=r(t,e),n=r(t,n,i);e>>0;return e=null==e?n||0:e<0?Math.max(r+e,0):Math.min(e,r)}},{}],9:[function(t,e,n){"use strict";var r=t("./raw"),i=[],a=[],o=r.makeRequestCallFromTimer(function(){if(a.length)throw a.shift()});e.exports=s;function s(t){var e;(e=i.length?i.pop():new u).task=t,r(e)}function u(){this.task=null}u.prototype.call=function(){try{this.task.call()}catch(t){s.onerror?s.onerror(t):(a.push(t),o())}finally{this.task=null,i[i.length]=this}}},{"./raw":10}],10:[function(t,e,n){(function(t){"use strict";e.exports=n;function n(t){i.length||(r(),!0),i[i.length]=t}var r,i=[],a=0,o=1024;function s(){for(;ao){for(var e=0,n=i.length-a;e0){var o=n.indexOf(this);~o?n.splice(o+1):n.push(this),~o?r.splice(o,1/0,i):r.push(i),~n.indexOf(a)&&(a=e.call(this,i,a))}else n.push(a);return null==t?a:t.call(this,i,a)}}},{}],25:[function(t,e,n){"use strict";var r=t("assert-ok"),i=t("assert-equal"),a=t("dot-prop"),o=t("to-array"),s=t("array-last"),u=t("dezalgo"),l=t("call-all-fns");e.exports=function(t,e){r(Array.isArray(t),"methods are required"),i(typeof e,"function","load fn is required");var n=null,c=null,p=[];return e(function(t,e){c=t,n=e,l(p)(t,e),p=null}),t.reduce(function(t,e){a.set(t,e,(r=e,function(){var t=arguments;!function(t){if(t=u(t),n||c)return t(c,n);p.push(t)}(function(e,n){if(!e)return a.get(n,r).apply(null,t);var i=s(o(t));return"function"==typeof i?i(e):void 0})}));var r;return t},{})}},{"array-last":7,"assert-equal":11,"assert-ok":13,"call-all-fns":14,dezalgo:16,"dot-prop":26,"to-array":34}],26:[function(t,e,n){"use strict";var r=t("is-obj");e.exports.get=function(t,e){if(!r(t)||"string"!=typeof e)return t;for(var n=i(e),a=0;a0&&(n+=r[0]);for(var a=1;a0?r=t.substring(0,e):!n&&t&&t.length>0&&(r=t),{base:r,query:n?t.substring(e+1):void 0}}(t);return e.base&&(a=e.base),function(t){var e={};return t?(o(t.split("&"),function(t){var r=t.split("="),i=n(r[0]),a=decodeURIComponent(i.val),o=r[1]&&decodeURIComponent(r[1]);"array"===i.type?(e[a]||(e[a]=[]),e[a].push(o)):"string"===i.type&&(e[a]=o)}),e):e}(e.query)}return t});return e?i.apply({},u):a+function(t){var e,n=[];for(var i in t)t.hasOwnProperty(i)&&(void 0!==t[i]?(e=encodeURIComponent(i),r(t[i])?o(t[i],function(t){n.push(e+"[]="+encodeURIComponent(t))}):n.push(e+"="+encodeURIComponent(t[i]))):n.push(encodeURIComponent(i)));return n.length?"?"+n.join("&"):""}(i.apply({},u))},o=function(t,e){for(var n=0,r=t.length;n0&&c===n?n>=u.length?(c=u.length-1,t.active=c,d(c),s.select(u[u.length-1])):(c=n,t.active=c,d(c),s.select(u[n])):c>n&&(c--,t.active=c),0===u.length&&(c=null,t.active=null)},s.select=t.select=function(n,r){var a=f(n.slide);void 0===r&&(r=a>s.getCurrentIndex()?"next":"prev"),n.slide.index===c||t.$currentTransition||function(n,r,a){if(p)return;if(angular.extend(n,{direction:a}),angular.extend(u[c].slide||{},{direction:a}),i.enabled(e)&&!t.$currentTransition&&u[r].element&&s.slides.length>1){u[r].element.data(l,n.direction);var o=s.getCurrentIndex();angular.isNumber(o)&&u[o].element&&u[o].element.data(l,n.direction),t.$currentTransition=!0,i.on("addClass",u[r].element,function(e,n){"close"===n&&(t.$currentTransition=null,i.off("addClass",e))})}t.active=n.index,c=n.index,d(r),g()}(n.slide,a,r)},t.indexOfSlide=function(t){return+t.slide.index},t.isActive=function(e){return t.active===e.slide.index},t.isPrevDisabled=function(){return 0===t.active&&t.noWrap()},t.isNextDisabled=function(){return t.active===u.length-1&&t.noWrap()},t.pause=function(){t.noPause||(o=!1,h())},t.play=function(){o||(o=!0,g())},e.on("mouseenter",t.pause),e.on("mouseleave",t.play),t.$on("$destroy",function(){p=!0,h()}),t.$watch("noTransition",function(t){i.enabled(e,!t)}),t.$watch("interval",g),t.$watchCollection("slides",function(e){e.length||(t.$currentTransition=null)}),t.$watch("active",function(t){if(angular.isNumber(t)&&c!==t){for(var e=0;e0&&(a=n(m,e))}function m(){var e=+t.interval;o&&!isNaN(e)&&e>0&&u.length?t.next():t.pause()}}]).directive("uibCarousel",function(){return{transclude:!0,controller:"UibCarouselController",controllerAs:"carousel",restrict:"A",templateUrl:function(t,e){return e.templateUrl||"uib/template/carousel/carousel.html"},scope:{active:"=",interval:"=",noTransition:"=",noPause:"=",noWrap:"&"}}}).directive("uibSlide",["$animate",function(t){return{require:"^uibCarousel",restrict:"A",transclude:!0,templateUrl:function(t,e){return e.templateUrl||"uib/template/carousel/slide.html"},scope:{actual:"=?",index:"=?"},link:function(e,n,r,i){n.addClass("item"),i.addSlide(e,n),e.$on("$destroy",function(){i.removeSlide(e)}),e.$watch("active",function(e){t[e?"addClass":"removeClass"](n,"active")})}}}]).animation(".item",["$animateCss",function(t){var e="uib-slideDirection";function n(t,e,n){t.removeClass(e),n&&n()}return{beforeAddClass:function(r,i,a){if("active"===i){var o=r.data(e),s="next"===o?"left":"right",u=n.bind(this,r,s+" "+o,a);return r.addClass(o),t(r,{addClass:s}).start().done(u),function(){!0}}a()},beforeRemoveClass:function(r,i,a){if("active"===i){var o="next"===r.data(e)?"left":"right",s=n.bind(this,r,o,a);return t(r,{addClass:o}).start().done(s),function(){!0}}a()}}}]),angular.module("ui.bootstrap.dateparser",[]).service("uibDateParser",["$log","$locale","dateFilter","orderByFilter","filterFilter",function(t,e,n,r,i){var a,o,s=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;this.init=function(){a=e.id,this.parsers={},this.formatters={},o=[{key:"yyyy",regex:"\\d{4}",apply:function(t){this.year=+t},formatter:function(t){var e=new Date;return e.setFullYear(Math.abs(t.getFullYear())),n(e,"yyyy")}},{key:"yy",regex:"\\d{2}",apply:function(t){t=+t,this.year=t<69?t+2e3:t+1900},formatter:function(t){var e=new Date;return e.setFullYear(Math.abs(t.getFullYear())),n(e,"yy")}},{key:"y",regex:"\\d{1,4}",apply:function(t){this.year=+t},formatter:function(t){var e=new Date;return e.setFullYear(Math.abs(t.getFullYear())),n(e,"y")}},{key:"M!",regex:"0?[1-9]|1[0-2]",apply:function(t){this.month=t-1},formatter:function(t){var e=t.getMonth();return/^[0-9]$/.test(e)?n(t,"MM"):n(t,"M")}},{key:"MMMM",regex:e.DATETIME_FORMATS.MONTH.join("|"),apply:function(t){this.month=e.DATETIME_FORMATS.MONTH.indexOf(t)},formatter:function(t){return n(t,"MMMM")}},{key:"MMM",regex:e.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(t){this.month=e.DATETIME_FORMATS.SHORTMONTH.indexOf(t)},formatter:function(t){return n(t,"MMM")}},{key:"MM",regex:"0[1-9]|1[0-2]",apply:function(t){this.month=t-1},formatter:function(t){return n(t,"MM")}},{key:"M",regex:"[1-9]|1[0-2]",apply:function(t){this.month=t-1},formatter:function(t){return n(t,"M")}},{key:"d!",regex:"[0-2]?[0-9]{1}|3[0-1]{1}",apply:function(t){this.date=+t},formatter:function(t){var e=t.getDate();return/^[1-9]$/.test(e)?n(t,"dd"):n(t,"d")}},{key:"dd",regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(t){this.date=+t},formatter:function(t){return n(t,"dd")}},{key:"d",regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(t){this.date=+t},formatter:function(t){return n(t,"d")}},{key:"EEEE",regex:e.DATETIME_FORMATS.DAY.join("|"),formatter:function(t){return n(t,"EEEE")}},{key:"EEE",regex:e.DATETIME_FORMATS.SHORTDAY.join("|"),formatter:function(t){return n(t,"EEE")}},{key:"HH",regex:"(?:0|1)[0-9]|2[0-3]",apply:function(t){this.hours=+t},formatter:function(t){return n(t,"HH")}},{key:"hh",regex:"0[0-9]|1[0-2]",apply:function(t){this.hours=+t},formatter:function(t){return n(t,"hh")}},{key:"H",regex:"1?[0-9]|2[0-3]",apply:function(t){this.hours=+t},formatter:function(t){return n(t,"H")}},{key:"h",regex:"[0-9]|1[0-2]",apply:function(t){this.hours=+t},formatter:function(t){return n(t,"h")}},{key:"mm",regex:"[0-5][0-9]",apply:function(t){this.minutes=+t},formatter:function(t){return n(t,"mm")}},{key:"m",regex:"[0-9]|[1-5][0-9]",apply:function(t){this.minutes=+t},formatter:function(t){return n(t,"m")}},{key:"sss",regex:"[0-9][0-9][0-9]",apply:function(t){this.milliseconds=+t},formatter:function(t){return n(t,"sss")}},{key:"ss",regex:"[0-5][0-9]",apply:function(t){this.seconds=+t},formatter:function(t){return n(t,"ss")}},{key:"s",regex:"[0-9]|[1-5][0-9]",apply:function(t){this.seconds=+t},formatter:function(t){return n(t,"s")}},{key:"a",regex:e.DATETIME_FORMATS.AMPMS.join("|"),apply:function(t){12===this.hours&&(this.hours=0),"PM"===t&&(this.hours+=12)},formatter:function(t){return n(t,"a")}},{key:"Z",regex:"[+-]\\d{4}",apply:function(t){var e=t.match(/([+-])(\d{2})(\d{2})/),n=e[1],r=e[2],i=e[3];this.hours+=c(n+r),this.minutes+=c(n+i)},formatter:function(t){return n(t,"Z")}},{key:"ww",regex:"[0-4][0-9]|5[0-3]",formatter:function(t){return n(t,"ww")}},{key:"w",regex:"[0-9]|[1-4][0-9]|5[0-3]",formatter:function(t){return n(t,"w")}},{key:"GGGG",regex:e.DATETIME_FORMATS.ERANAMES.join("|").replace(/\s/g,"\\s"),formatter:function(t){return n(t,"GGGG")}},{key:"GGG",regex:e.DATETIME_FORMATS.ERAS.join("|"),formatter:function(t){return n(t,"GGG")}},{key:"GG",regex:e.DATETIME_FORMATS.ERAS.join("|"),formatter:function(t){return n(t,"GG")}},{key:"G",regex:e.DATETIME_FORMATS.ERAS.join("|"),formatter:function(t){return n(t,"G")}}],angular.version.major>=1&&angular.version.minor>4&&o.push({key:"LLLL",regex:e.DATETIME_FORMATS.STANDALONEMONTH.join("|"),apply:function(t){this.month=e.DATETIME_FORMATS.STANDALONEMONTH.indexOf(t)},formatter:function(t){return n(t,"LLLL")}})},this.init();function u(t){return i(o,{key:t},!0)[0]}this.getParser=function(t){var e=u(t);return e&&e.apply||null},this.overrideParser=function(t,e){var n=u(t);n&&angular.isFunction(e)&&(this.parsers={},n.apply=e)}.bind(this);function l(t,e){for(var n=t.substr(e),r=0;r=t.length||"'"!==t.charAt(i+1))&&(r.push((a=t,o=n,s=i,function(){return a.substr(o+1,s-o-1)})),n=null);else if(i===t.length)for(;n-1){var a=!1;t=t.split("");for(var s=i;s-1){t=t.split(""),n[i]="("+r.regex+")",t[i]="$";for(var a=i+1,o=i+r.key.length;a28)return 29===n&&(t%4==0&&t%100!=0||t%400==0);if(3===e||5===e||8===e||10===e)return n<31;return!0}(h.year,h.month,h.date)&&(!angular.isDate(u)||isNaN(u.getTime())||f?(g=new Date(0),b.call(g,h.year,h.month,h.date),w.call(g,h.hours||0,h.minutes||0,h.seconds||0,h.milliseconds||0)):(g=new Date(u),b.call(g,h.year,h.month,h.date),w.call(g,h.hours,h.minutes,h.seconds,h.milliseconds))),g}};function c(t){return parseInt(t,10)}this.toTimezone=function(t,e){return t&&e?f(t,e):t},this.fromTimezone=function(t,e){return t&&e?f(t,e,!0):t},this.timezoneToOffset=p,this.addDateMinutes=d,this.convertTimezoneToLocal=f;function p(t,e){t=t.replace(/:/g,"");var n=Date.parse("Jan 01, 1970 00:00:00 "+t)/6e4;return isNaN(n)?e:n}function d(t,e){return(t=new Date(t.getTime())).setMinutes(t.getMinutes()+e),t}function f(t,e,n){n=n?-1:1;var r=t.getTimezoneOffset();return d(t,n*(p(e,r)-r))}}]),angular.module("ui.bootstrap.isClass",[]).directive("uibIsClass",["$animate",function(t){var e=/^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/,n=/^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;return{restrict:"A",compile:function(r,i){var a=[],o=[],s={},u=i.uibIsClass.match(e),l=u[2],c=u[1].split(",");return function(e,r,i){a.push(e),o.push({scope:e,element:r}),c.forEach(function(r,i){!function(e,r){var i=e.match(n),a=r.$eval(i[1]),u=i[2],c=s[e];if(!c){var p=function(e){var n=null;o.some(function(t){var r=t.scope.$eval(l);if(r===e)return n=t,!0}),c.lastActivated!==n&&(c.lastActivated&&t.removeClass(c.lastActivated.element,a),n&&t.addClass(n.element,a),c.lastActivated=n)};s[e]=c={lastActivated:null,scope:r,watchFn:p,compareWithExp:u,watcher:r.$watch(u,p)}}c.watchFn(r.$eval(u))}(r,e)}),e.$on("$destroy",p)};function p(t){var e=t.targetScope,n=a.indexOf(e);if(a.splice(n,1),o.splice(n,1),a.length){var r=a[0];angular.forEach(s,function(t){t.scope===e&&(t.watcher=r.$watch(t.compareWithExp,t.watchFn),t.scope=r)})}else s={}}}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.isClass"]).value("$datepickerSuppressError",!1).value("$datepickerLiteralWarning",!0).constant("uibDatepickerConfig",{datepickerMode:"day",formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",maxDate:null,maxMode:"year",minDate:null,minMode:"day",monthColumns:3,ngModelOptions:{},shortcutPropagation:!1,showWeeks:!0,yearColumns:5,yearRows:4}).controller("UibDatepickerController",["$scope","$element","$attrs","$parse","$interpolate","$locale","$log","dateFilter","uibDatepickerConfig","$datepickerLiteralWarning","$datepickerSuppressError","uibDateParser",function(t,e,n,r,i,a,o,s,u,l,c,p){var d=this,f={$setViewValue:angular.noop},h={},g=[];e.addClass("uib-datepicker"),n.$set("role","application"),t.datepickerOptions||(t.datepickerOptions={}),this.modes=["day","month","year"],["customClass","dateDisabled","datepickerMode","formatDay","formatDayHeader","formatDayTitle","formatMonth","formatMonthTitle","formatYear","maxDate","maxMode","minDate","minMode","monthColumns","showWeeks","shortcutPropagation","startingDay","yearColumns","yearRows"].forEach(function(e){switch(e){case"customClass":case"dateDisabled":t[e]=t.datepickerOptions[e]||angular.noop;break;case"datepickerMode":t.datepickerMode=angular.isDefined(t.datepickerOptions.datepickerMode)?t.datepickerOptions.datepickerMode:u.datepickerMode;break;case"formatDay":case"formatDayHeader":case"formatDayTitle":case"formatMonth":case"formatMonthTitle":case"formatYear":d[e]=angular.isDefined(t.datepickerOptions[e])?i(t.datepickerOptions[e])(t.$parent):u[e];break;case"monthColumns":case"showWeeks":case"shortcutPropagation":case"yearColumns":case"yearRows":d[e]=angular.isDefined(t.datepickerOptions[e])?t.datepickerOptions[e]:u[e];break;case"startingDay":angular.isDefined(t.datepickerOptions.startingDay)?d.startingDay=t.datepickerOptions.startingDay:angular.isNumber(u.startingDay)?d.startingDay=u.startingDay:d.startingDay=(a.DATETIME_FORMATS.FIRSTDAYOFWEEK+8)%7;break;case"maxDate":case"minDate":t.$watch("datepickerOptions."+e,function(t){t?angular.isDate(t)?d[e]=p.fromTimezone(new Date(t),h.getOption("timezone")):(l&&o.warn("Literal date support has been deprecated, please switch to date object usage"),d[e]=new Date(s(t,"medium"))):d[e]=u[e]?p.fromTimezone(new Date(u[e]),h.getOption("timezone")):null,d.refreshView()});break;case"maxMode":case"minMode":t.datepickerOptions[e]?t.$watch(function(){return t.datepickerOptions[e]},function(n){d[e]=t[e]=angular.isDefined(n)?n:t.datepickerOptions[e],("minMode"===e&&d.modes.indexOf(t.datepickerOptions.datepickerMode)d.modes.indexOf(d[e]))&&(t.datepickerMode=d[e],t.datepickerOptions.datepickerMode=d[e])}):d[e]=t[e]=u[e]||null}}),t.uniqueId="datepicker-"+t.$id+"-"+Math.floor(1e4*Math.random()),t.disabled=angular.isDefined(n.disabled)||!1,angular.isDefined(n.ngDisabled)&&g.push(t.$parent.$watch(n.ngDisabled,function(e){t.disabled=e,d.refreshView()})),t.isActive=function(e){return 0===d.compare(e.date,d.activeDate)&&(t.activeDateId=e.uid,!0)},this.init=function(e){h=function(e){var n;if(angular.version.minor<6)(n=e.$options||t.datepickerOptions.ngModelOptions||u.ngModelOptions||{}).getOption=function(t){return n[t]};else{var r=e.$options.getOption("timezone")||(t.datepickerOptions.ngModelOptions?t.datepickerOptions.ngModelOptions.timezone:null)||(u.ngModelOptions?u.ngModelOptions.timezone:null);n=e.$options.createChild(u.ngModelOptions).createChild(t.datepickerOptions.ngModelOptions).createChild(e.$options).createChild({timezone:r})}return n}(f=e),t.datepickerOptions.initDate?(d.activeDate=p.fromTimezone(t.datepickerOptions.initDate,h.getOption("timezone"))||new Date,t.$watch("datepickerOptions.initDate",function(t){t&&(f.$isEmpty(f.$modelValue)||f.$invalid)&&(d.activeDate=p.fromTimezone(t,h.getOption("timezone")),d.refreshView())})):d.activeDate=new Date;var n=f.$modelValue?new Date(f.$modelValue):new Date;this.activeDate=isNaN(n)?p.fromTimezone(new Date,h.getOption("timezone")):p.fromTimezone(n,h.getOption("timezone")),f.$render=function(){d.render()}},this.render=function(){if(f.$viewValue){var t=new Date(f.$viewValue);!isNaN(t)?this.activeDate=p.fromTimezone(t,h.getOption("timezone")):c||o.error('Datepicker directive: "ng-model" value must be a Date object')}this.refreshView()},this.refreshView=function(){if(this.element){t.selectedDt=null,this._refreshView(),t.activeDt&&(t.activeDateId=t.activeDt.uid);var e=f.$viewValue?new Date(f.$viewValue):null;e=p.fromTimezone(e,h.getOption("timezone")),f.$setValidity("dateDisabled",!e||this.element&&!this.isDisabled(e))}},this.createDateObject=function(e,n){var r=f.$viewValue?new Date(f.$viewValue):null;r=p.fromTimezone(r,h.getOption("timezone"));var i=new Date;i=p.fromTimezone(i,h.getOption("timezone"));var a=this.compare(e,i),o={date:e,label:p.filter(e,n),selected:r&&0===this.compare(e,r),disabled:this.isDisabled(e),past:a<0,current:0===a,future:a>0,customClass:this.customClass(e)||null};return r&&0===this.compare(e,r)&&(t.selectedDt=o),d.activeDate&&0===this.compare(o.date,d.activeDate)&&(t.activeDt=o),o},this.isDisabled=function(e){return t.disabled||this.minDate&&this.compare(e,this.minDate)<0||this.maxDate&&this.compare(e,this.maxDate)>0||t.dateDisabled&&t.dateDisabled({date:e,mode:t.datepickerMode})},this.customClass=function(e){return t.customClass({date:e,mode:t.datepickerMode})},this.split=function(t,e){for(var n=[];t.length>0;)n.push(t.splice(0,e));return n},t.select=function(e){if(t.datepickerMode===d.minMode){var n=f.$viewValue?p.fromTimezone(new Date(f.$viewValue),h.getOption("timezone")):new Date(0,0,0,0,0,0,0);n.setFullYear(e.getFullYear(),e.getMonth(),e.getDate()),n=p.toTimezone(n,h.getOption("timezone")),f.$setViewValue(n),f.$render()}else d.activeDate=e,m(d.modes[d.modes.indexOf(t.datepickerMode)-1]),t.$emit("uib:datepicker.mode");t.$broadcast("uib:datepicker.focus")},t.move=function(t){var e=d.activeDate.getFullYear()+t*(d.step.years||0),n=d.activeDate.getMonth()+t*(d.step.months||0);d.activeDate.setFullYear(e,n,1),d.refreshView()},t.toggleMode=function(e){e=e||1,t.datepickerMode===d.maxMode&&1===e||t.datepickerMode===d.minMode&&-1===e||(m(d.modes[d.modes.indexOf(t.datepickerMode)+e]),t.$emit("uib:datepicker.mode"))},t.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};t.$on("uib:datepicker.focus",function(){d.element[0].focus()}),t.keydown=function(e){var n=t.keys[e.which];if(n&&!e.shiftKey&&!e.altKey&&!t.disabled)if(e.preventDefault(),d.shortcutPropagation||e.stopPropagation(),"enter"===n||"space"===n){if(d.isDisabled(d.activeDate))return;t.select(d.activeDate)}else!e.ctrlKey||"up"!==n&&"down"!==n?(d.handleKeyDown(n,e),d.refreshView()):t.toggleMode("up"===n?1:-1)},e.on("keydown",function(e){t.$apply(function(){t.keydown(e)})}),t.$on("$destroy",function(){for(;g.length;)g.shift()()});function m(e){t.datepickerMode=e,t.datepickerOptions.datepickerMode=e}}]).controller("UibDaypickerController",["$scope","$element","dateFilter",function(t,e,n){var r=[31,28,31,30,31,30,31,31,30,31,30,31];this.step={months:1},this.element=e;function i(t,e){return 1!==e||t%4!=0||t%100==0&&t%400!=0?r[e]:29}this.init=function(e){angular.extend(e,this),t.showWeeks=e.showWeeks,e.refreshView()},this.getDates=function(t,e){for(var n,r=new Array(e),i=new Date(t),a=0;a0?7-o:-o,u=new Date(i);s>0&&u.setDate(1-s);for(var l=this.getDates(u,42),c=0;c<42;c++)l[c]=angular.extend(this.createDateObject(l[c],this.formatDay),{secondary:l[c].getMonth()!==r,uid:t.uniqueId+"-"+c});t.labels=new Array(7);for(var p=0;p<7;p++)t.labels[p]={abbr:n(l[p].date,this.formatDayHeader),full:n(l[p].date,"EEEE")};if(t.title=n(this.activeDate,this.formatDayTitle),t.rows=this.split(l,7),t.showWeeks){t.weekNumbers=[];for(var d=(11-this.startingDay)%7,f=t.rows.length,h=0;h3?this.monthColumns-2:1},this.compare=function(t,e){var n=new Date(t.getFullYear(),t.getMonth()),r=new Date(e.getFullYear(),e.getMonth());return n.setFullYear(t.getFullYear()),r.setFullYear(e.getFullYear()),n-r},this.handleKeyDown=function(t,e){var n=this.activeDate.getMonth();if("left"===t)n-=1;else if("up"===t)n-=this.monthColumns;else if("right"===t)n+=1;else if("down"===t)n+=this.monthColumns;else if("pageup"===t||"pagedown"===t){var r=this.activeDate.getFullYear()+("pageup"===t?-1:1);this.activeDate.setFullYear(r)}else"home"===t?n=0:"end"===t&&(n=11);this.activeDate.setMonth(n)}}]).controller("UibYearpickerController",["$scope","$element","dateFilter",function(t,e,n){var r,i;this.element=e;function a(t){return parseInt((t-1)/i,10)*i+1}this.yearpickerInit=function(){r=this.yearColumns,i=this.yearRows*r,this.step={years:i}},this._refreshView=function(){for(var e,n=new Array(i),o=0,s=a(this.activeDate.getFullYear());o');t.find("body").append(o),n=o[0].offsetWidth-o[0].clientWidth,n=isFinite(n)?n:0,o.remove()}return n},scrollbarPadding:function(t){t=this.getRawNode(t);var n=e.getComputedStyle(t),r=this.parseStyle(n.paddingRight),i=this.parseStyle(n.paddingBottom),a=this.scrollParent(t,!1,!0),s=this.scrollbarWidth(o.test(a.tagName));return{scrollbarWidth:s,widthOverflow:a.scrollWidth>a.clientWidth,right:r+s,originalRight:r,heightOverflow:a.scrollHeight>a.clientHeight,bottom:i+s,originalBottom:i}},isScrollable:function(t,n){t=this.getRawNode(t);var r=n?i.hidden:i.normal,a=e.getComputedStyle(t);return r.test(a.overflow+a.overflowY+a.overflowX)},scrollParent:function(n,r,a){n=this.getRawNode(n);var o=r?i.hidden:i.normal,s=t[0].documentElement,u=e.getComputedStyle(n);if(a&&o.test(u.overflow+u.overflowY+u.overflowX))return n;var l="absolute"===u.position,c=n.parentElement||s;if(c===s||"fixed"===u.position)return s;for(;c.parentElement&&c!==s;){var p=e.getComputedStyle(c);if(l&&"static"!==p.position&&(l=!1),!l&&o.test(p.overflow+p.overflowY+p.overflowX))break;c=c.parentElement}return c},position:function(n,r){n=this.getRawNode(n);var i=this.offset(n);if(r){var a=e.getComputedStyle(n);i.top-=this.parseStyle(a.marginTop),i.left-=this.parseStyle(a.marginLeft)}var o=this.offsetParent(n),s={top:0,left:0};return o!==t[0].documentElement&&((s=this.offset(o)).top+=o.clientTop-o.scrollTop,s.left+=o.clientLeft-o.scrollLeft),{width:Math.round(angular.isNumber(i.width)?i.width:n.offsetWidth),height:Math.round(angular.isNumber(i.height)?i.height:n.offsetHeight),top:Math.round(i.top-s.top),left:Math.round(i.left-s.left)}},offset:function(n){var r=(n=this.getRawNode(n)).getBoundingClientRect();return{width:Math.round(angular.isNumber(r.width)?r.width:n.offsetWidth),height:Math.round(angular.isNumber(r.height)?r.height:n.offsetHeight),top:Math.round(r.top+(e.pageYOffset||t[0].documentElement.scrollTop)),left:Math.round(r.left+(e.pageXOffset||t[0].documentElement.scrollLeft))}},viewportOffset:function(n,r,i){i=!1!==i;var a=(n=this.getRawNode(n)).getBoundingClientRect(),o={top:0,left:0,bottom:0,right:0},s=r?t[0].documentElement:this.scrollParent(n),u=s.getBoundingClientRect();if(o.top=u.top+s.clientTop,o.left=u.left+s.clientLeft,s===t[0].documentElement&&(o.top+=e.pageYOffset,o.left+=e.pageXOffset),o.bottom=o.top+s.clientHeight,o.right=o.left+s.clientWidth,i){var l=e.getComputedStyle(s);o.top+=this.parseStyle(l.paddingTop),o.bottom-=this.parseStyle(l.paddingBottom),o.left+=this.parseStyle(l.paddingLeft),o.right-=this.parseStyle(l.paddingRight)}return{top:Math.round(a.top-o.top),bottom:Math.round(o.bottom-a.bottom),left:Math.round(a.left-o.left),right:Math.round(o.right-a.right)}},parsePlacement:function(t){var e=a.auto.test(t);return e&&(t=t.replace(a.auto,"")),(t=t.split("-"))[0]=t[0]||"top",a.primary.test(t[0])||(t[0]="top"),t[1]=t[1]||"center",a.secondary.test(t[1])||(t[1]="center"),t[2]=!!e,t},positionElements:function(t,n,r,i){t=this.getRawNode(t),n=this.getRawNode(n);var o=angular.isDefined(n.offsetWidth)?n.offsetWidth:n.prop("offsetWidth"),s=angular.isDefined(n.offsetHeight)?n.offsetHeight:n.prop("offsetHeight");r=this.parsePlacement(r);var u=i?this.offset(t):this.position(t),l={top:0,left:0,placement:""};if(r[2]){var c=this.viewportOffset(t,i),p=e.getComputedStyle(n),d=o+Math.round(Math.abs(this.parseStyle(p.marginLeft)+this.parseStyle(p.marginRight))),f=s+Math.round(Math.abs(this.parseStyle(p.marginTop)+this.parseStyle(p.marginBottom)));if(r[0]="top"===r[0]&&f>c.top&&f<=c.bottom?"bottom":"bottom"===r[0]&&f>c.bottom&&f<=c.top?"top":"left"===r[0]&&d>c.left&&d<=c.right?"right":"right"===r[0]&&d>c.right&&d<=c.left?"left":r[0],r[1]="top"===r[1]&&f-u.height>c.bottom&&f-u.height<=c.top?"bottom":"bottom"===r[1]&&f-u.height>c.top&&f-u.height<=c.bottom?"top":"left"===r[1]&&d-u.width>c.right&&d-u.width<=c.left?"right":"right"===r[1]&&d-u.width>c.left&&d-u.width<=c.right?"left":r[1],"center"===r[1])if(a.vertical.test(r[0])){var h=u.width/2-o/2;c.left+h<0&&d-u.width<=c.right?r[1]="left":c.right+h<0&&d-u.width<=c.left&&(r[1]="right")}else{var g=u.height/2-f/2;c.top+g<0&&f-u.height<=c.bottom?r[1]="top":c.bottom+g<0&&f-u.height<=c.top&&(r[1]="bottom")}}switch(r[0]){case"top":l.top=u.top-s;break;case"bottom":l.top=u.top+u.height;break;case"left":l.left=u.left-o;break;case"right":l.left=u.left+u.width}switch(r[1]){case"top":l.top=u.top;break;case"bottom":l.top=u.top+u.height-s;break;case"left":l.left=u.left;break;case"right":l.left=u.left+u.width-o;break;case"center":a.vertical.test(r[0])?l.left=u.left+u.width/2-o/2:l.top=u.top+u.height/2-s/2}return l.top=Math.round(l.top),l.left=Math.round(l.left),l.placement="center"===r[1]?r[0]:r[0]+"-"+r[1],l},adjustTop:function(t,e,n,r){if(-1!==t.indexOf("top")&&n!==r)return{top:e.top-r+"px"}},positionArrow:function(t,n){var r=(t=this.getRawNode(t)).querySelector(".tooltip-inner, .popover-inner");if(r){var i=angular.element(r).hasClass("tooltip-inner"),o=i?t.querySelector(".tooltip-arrow"):t.querySelector(".arrow");if(o){var s={top:"",bottom:"",left:"",right:""};if("center"!==(n=this.parsePlacement(n))[1]){var u="border-"+n[0]+"-width",l=e.getComputedStyle(o)[u],c="border-";a.vertical.test(n[0])?c+=n[0]+"-"+n[1]:c+=n[1]+"-"+n[0],c+="-radius";var p=e.getComputedStyle(i?r:t)[c];switch(n[0]){case"top":s.bottom=i?"0":"-"+l;break;case"bottom":s.top=i?"0":"-"+l;break;case"left":s.right=i?"0":"-"+l;break;case"right":s.left=i?"0":"-"+l}s[n[1]]=p,angular.element(o).css(s)}else angular.element(o).css(s)}}}}}]),angular.module("ui.bootstrap.datepickerPopup",["ui.bootstrap.datepicker","ui.bootstrap.position"]).value("$datepickerPopupLiteralWarning",!0).constant("uibDatepickerPopupConfig",{altInputFormats:[],appendToBody:!1,clearText:"Clear",closeOnDateSelection:!0,closeText:"Done",currentText:"Today",datepickerPopup:"yyyy-MM-dd",datepickerPopupTemplateUrl:"uib/template/datepickerPopup/popup.html",datepickerTemplateUrl:"uib/template/datepicker/datepicker.html",html5Types:{date:"yyyy-MM-dd","datetime-local":"yyyy-MM-ddTHH:mm:ss.sss",month:"yyyy-MM"},onOpenFocus:!0,showButtonBar:!0,placement:"auto bottom-left"}).controller("UibDatepickerPopupController",["$scope","$element","$attrs","$compile","$log","$parse","$window","$document","$rootScope","$uibPosition","dateFilter","uibDateParser","uibDatepickerPopupConfig","$timeout","uibDatepickerConfig","$datepickerPopupLiteralWarning",function(t,e,n,r,i,a,o,s,u,l,c,p,d,f,h,g){var m,v,y,b,w,$,k,C,x,T,S,E,D,A=!1,B=[];this.init=function(i){if(S=function(t){var e;angular.version.minor<6?(e=angular.isObject(t.$options)?t.$options:{timezone:null}).getOption=function(t){return e[t]}:e=t.$options;return e}(T=i),v=angular.isDefined(n.closeOnDateSelection)?t.$parent.$eval(n.closeOnDateSelection):d.closeOnDateSelection,y=angular.isDefined(n.datepickerAppendToBody)?t.$parent.$eval(n.datepickerAppendToBody):d.appendToBody,b=angular.isDefined(n.onOpenFocus)?t.$parent.$eval(n.onOpenFocus):d.onOpenFocus,w=angular.isDefined(n.datepickerPopupTemplateUrl)?n.datepickerPopupTemplateUrl:d.datepickerPopupTemplateUrl,$=angular.isDefined(n.datepickerTemplateUrl)?n.datepickerTemplateUrl:d.datepickerTemplateUrl,D=angular.isDefined(n.altInputFormats)?t.$parent.$eval(n.altInputFormats):d.altInputFormats,t.showButtonBar=angular.isDefined(n.showButtonBar)?t.$parent.$eval(n.showButtonBar):d.showButtonBar,d.html5Types[n.type]?(m=d.html5Types[n.type],A=!0):(m=n.uibDatepickerPopup||d.datepickerPopup,n.$observe("uibDatepickerPopup",function(t,e){var n=t||d.datepickerPopup;if(n!==m&&(m=n,T.$modelValue=null,!m))throw new Error("uibDatepickerPopup must have a date format specified.")})),!m)throw new Error("uibDatepickerPopup must have a date format specified.");if(A&&n.uibDatepickerPopup)throw new Error("HTML5 date input types do not support custom formats.");(k=angular.element("
")).attr({"ng-model":"date","ng-change":"dateSelection(date)","template-url":w}),(C=angular.element(k.children()[0])).attr("template-url",$),t.datepickerOptions||(t.datepickerOptions={}),A&&"month"===n.type&&(t.datepickerOptions.datepickerMode="month",t.datepickerOptions.minMode="month"),C.attr("datepicker-options","datepickerOptions"),A?T.$formatters.push(function(e){return t.date=p.fromTimezone(e,S.getOption("timezone")),e}):(T.$$parserName="date",T.$validators.date=M,T.$parsers.unshift(O),T.$formatters.push(function(e){return T.$isEmpty(e)?(t.date=e,e):(angular.isNumber(e)&&(e=new Date(e)),t.date=p.fromTimezone(e,S.getOption("timezone")),p.filter(t.date,m))})),T.$viewChangeListeners.push(function(){t.date=I(T.$viewValue)}),e.on("keydown",_),E=r(k)(t),k.remove(),y?s.find("body").append(E):e.after(E),t.$on("$destroy",function(){for(!0===t.isOpen&&(u.$$phase||t.$apply(function(){t.isOpen=!1})),E.remove(),e.off("keydown",_),s.off("click",P),x&&x.off("scroll",R),angular.element(o).off("resize",R);B.length;)B.shift()()})},t.getText=function(e){return t[e+"Text"]||d[e+"Text"]},t.isDisabled=function(e){"today"===e&&(e=p.fromTimezone(new Date,S.getOption("timezone")));var n={};return angular.forEach(["minDate","maxDate"],function(e){t.datepickerOptions[e]?angular.isDate(t.datepickerOptions[e])?n[e]=new Date(t.datepickerOptions[e]):(g&&i.warn("Literal date support has been deprecated, please switch to date object usage"),n[e]=new Date(c(t.datepickerOptions[e],"medium"))):n[e]=null}),t.datepickerOptions&&n.minDate&&t.compare(e,n.minDate)<0||n.maxDate&&t.compare(e,n.maxDate)>0},t.compare=function(t,e){return new Date(t.getFullYear(),t.getMonth(),t.getDate())-new Date(e.getFullYear(),e.getMonth(),e.getDate())},t.dateSelection=function(n){t.date=n;var r=t.date?p.filter(t.date,m):null;e.val(r),T.$setViewValue(r),v&&(t.isOpen=!1,e[0].focus())},t.keydown=function(n){27===n.which&&(n.stopPropagation(),t.isOpen=!1,e[0].focus())},t.select=function(e,n){if(n.stopPropagation(),"today"===e){var r=new Date;angular.isDate(t.date)?(e=new Date(t.date)).setFullYear(r.getFullYear(),r.getMonth(),r.getDate()):(e=p.fromTimezone(r,S.getOption("timezone"))).setHours(0,0,0,0)}t.dateSelection(e)},t.close=function(n){n.stopPropagation(),t.isOpen=!1,e[0].focus()},t.disabled=angular.isDefined(n.disabled)||!1,n.ngDisabled&&B.push(t.$parent.$watch(a(n.ngDisabled),function(e){t.disabled=e})),t.$watch("isOpen",function(r){r?t.disabled?t.isOpen=!1:f(function(){R(),b&&t.$broadcast("uib:datepicker.focus"),s.on("click",P);var r=n.popupPlacement?n.popupPlacement:d.placement;y||l.parsePlacement(r)[2]?(x=x||angular.element(l.scrollParent(e)))&&x.on("scroll",R):x=null,angular.element(o).on("resize",R)},0,!1):(s.off("click",P),x&&x.off("scroll",R),angular.element(o).off("resize",R))});function I(e){var n=p.parse(e,m,t.date);if(isNaN(n))for(var r=0;r');f.dropdownMenu.replaceWith(P),f.dropdownMenu=P}f.selectedOption=null}angular.isFunction(v)&&v(t,i)})}]).directive("uibDropdown",function(){return{controller:"UibDropdownController",link:function(t,e,n,r){r.init()}}}).directive("uibDropdownMenu",function(){return{restrict:"A",require:"?^uibDropdown",link:function(t,e,n,r){if(r&&!angular.isDefined(n.dropdownNested)){e.addClass("dropdown-menu");var i=n.templateUrl;i&&(r.dropdownMenuTemplateUrl=i),r.dropdownMenu||(r.dropdownMenu=e)}}}}).directive("uibDropdownToggle",function(){return{require:"?^uibDropdown",link:function(t,e,n,r){if(r){e.addClass("dropdown-toggle"),r.toggleElement=e;var i=function(i){i.preventDefault(),e.hasClass("disabled")||n.disabled||t.$apply(function(){r.toggle()})};e.on("click",i),e.attr({"aria-haspopup":!0,"aria-expanded":!1}),t.$watch(r.isOpen,function(t){e.attr("aria-expanded",!!t)}),t.$on("$destroy",function(){e.off("click",i)})}}}}),angular.module("ui.bootstrap.stackedMap",[]).factory("$$stackedMap",function(){return{createNew:function(){var t=[];return{add:function(e,n){t.push({key:e,value:n})},get:function(e){for(var n=0;n-1&&t0&&(e=f.top().value).modalDomEl.toggleClass(e.windowTopClass||"",t)}function C(e,n,r,i){var o,s=null;return n.$broadcast(g.NOW_CLOSING_EVENT,function(){return o||(o=a.defer(),s=o.promise),function(){o.resolve()}}),a.when(s).then(u);function u(){u.done||(u.done=!0,t.leave(e).then(function(){r&&r(),e.remove(),i&&i.resolve()}),n.$destroy())}}n.on("keydown",x),i.$on("$destroy",function(){n.off("keydown",x)});function x(t){if(t.isDefaultPrevented())return t;var e=f.top();if(e)switch(t.which){case 27:e.value.keyboard&&(t.preventDefault(),i.$apply(function(){g.dismiss(e.key,"escape key press")}));break;case 9:var n=g.loadFocusElementList(e),r=!1;t.shiftKey?(g.isFocusInFirstItem(t,n)||g.isModalFocused(t,e))&&(r=g.focusLastFocusableElement(n)):g.isFocusInLastItem(t,n)&&(r=g.focusFirstFocusableElement(n)),r&&(t.preventDefault(),t.stopPropagation())}}g.open=function(e,a){var o=n[0].activeElement,s=a.openedClass||d;k(!1),v=f.top(),f.add(e,{deferred:a.deferred,renderDeferred:a.renderDeferred,closedDeferred:a.closedDeferred,modalScope:a.scope,backdrop:a.backdrop,keyboard:a.keyboard,openedClass:a.openedClass,windowTopClass:a.windowTopClass,animation:a.animation,appendTo:a.appendTo}),h.put(s,e);var g=a.appendTo,$=w();$>=0&&!l&&((c=i.$new(!0)).modalOptions=a,c.index=$,(l=angular.element('
')).attr({class:"modal-backdrop","ng-style":"{'z-index': 1040 + (index && 1 || 0) + index*10}","uib-modal-animation-class":"fade","modal-in-class":"in"}),a.backdropClass&&l.addClass(a.backdropClass),a.animation&&l.attr("modal-animation","true"),r(l)(c),t.enter(l,g),u.isScrollable(g)&&(p=u.scrollbarPadding(g)).heightOverflow&&p.scrollbarWidth&&g.css({paddingRight:p.right+"px"}));var C;a.component?(C=document.createElement((x=a.component.name,x.replace(b,function(t,e){return(e?"-":"")+t.toLowerCase()}))),(C=angular.element(C)).attr({resolve:"$resolve","modal-instance":"$uibModalInstance",close:"$close($value)",dismiss:"$dismiss($value)"})):C=a.content;var x;m=v?parseInt(v.value.modalDomEl.attr("index"),10)+1:0;var T=angular.element('
');T.attr({class:"modal","template-url":a.windowTemplateUrl,"window-top-class":a.windowTopClass,role:"dialog","aria-labelledby":a.ariaLabelledBy,"aria-describedby":a.ariaDescribedBy,size:a.size,index:m,animate:"animate","ng-style":"{'z-index': 1050 + $$topModalIndex*10, display: 'block'}",tabindex:-1,"uib-modal-animation-class":"fade","modal-in-class":"in"}).append(C),a.windowClass&&T.addClass(a.windowClass),a.animation&&T.attr("modal-animation","true"),g.addClass(s),a.scope&&(a.scope.$$topModalIndex=m),t.enter(r(T)(a.scope),g),f.top().value.modalDomEl=T,f.top().value.modalOpener=o,S(T);function S(t){if(t&&"BODY"!==t[0].tagName)return function(t){var e=t.parent()?t.parent().children():[];return Array.prototype.filter.call(e,function(e){return e!==t[0]})}(t).forEach(function(t){var e="true"===t.getAttribute("aria-hidden"),n=parseInt(t.getAttribute(y),10);n||(n=e?1:0),t.setAttribute(y,n+1),t.setAttribute("aria-hidden","true")}),S(t.parent())}};function T(t,e,n){return!t.value.modalScope.$broadcast("modal.closing",e,n).defaultPrevented}function S(){Array.prototype.forEach.call(document.querySelectorAll("["+y+"]"),function(t){var e=parseInt(t.getAttribute(y),10)-1;t.setAttribute(y,e),e||(t.removeAttribute(y),t.removeAttribute("aria-hidden"))})}return g.close=function(t,e){var n=f.get(t);return S(),n&&T(n,e,!0)?(n.value.modalScope.$$uibDestructionScheduled=!0,n.value.deferred.resolve(e),$(t,n.value.modalOpener),!0):!n},g.dismiss=function(t,e){var n=f.get(t);return S(),n&&T(n,e,!1)?(n.value.modalScope.$$uibDestructionScheduled=!0,n.value.deferred.reject(e),$(t,n.value.modalOpener),!0):!n},g.dismissAll=function(t){for(var e=this.getTop();e&&this.dismiss(e.key,t);)e=this.getTop()},g.getTop=function(){return f.top()},g.modalRendered=function(t){var e=f.get(t);e&&e.value.renderDeferred.resolve()},g.focusFirstFocusableElement=function(t){return t.length>0&&(t[0].focus(),!0)},g.focusLastFocusableElement=function(t){return t.length>0&&(t[t.length-1].focus(),!0)},g.isModalFocused=function(t,e){if(t&&e){var n=e.value.modalDomEl;if(n&&n.length)return(t.target||t.srcElement)===n[0]}return!1},g.isFocusInFirstItem=function(t,e){return e.length>0&&(t.target||t.srcElement)===e[0]},g.isFocusInLastItem=function(t,e){return e.length>0&&(t.target||t.srcElement)===e[e.length-1]},g.loadFocusElementList=function(t){if(t){var e=t.value.modalDomEl;if(e&&e.length){var n=e[0].querySelectorAll("a[href], area[href], input:not([disabled]):not([tabindex='-1']), button:not([disabled]):not([tabindex='-1']),select:not([disabled]):not([tabindex='-1']), textarea:not([disabled]):not([tabindex='-1']), iframe, object, embed, *[tabindex]:not([tabindex='-1']), *[contenteditable=true]");return n?Array.prototype.filter.call(n,function(t){return!!((e=t).offsetWidth||e.offsetHeight||e.getClientRects().length);var e}):n}}},g}]).provider("$uibModal",function(){var t={options:{animation:!0,backdrop:!0,keyboard:!0},$get:["$rootScope","$q","$document","$templateRequest","$controller","$uibResolve","$uibModalStack",function(e,n,r,i,a,o,s){var u={};var l=null;return u.getPromiseChain=function(){return l},u.open=function(u){var c=n.defer(),p=n.defer(),d=n.defer(),f=n.defer(),h={result:c.promise,opened:p.promise,closed:d.promise,rendered:f.promise,close:function(t){return s.close(h,t)},dismiss:function(t){return s.dismiss(h,t)}};if((u=angular.extend({},t.options,u)).resolve=u.resolve||{},u.appendTo=u.appendTo||r.find("body").eq(0),!u.appendTo.length)throw new Error("appendTo element not found. Make sure that the element passed is in DOM.");if(!u.component&&!u.template&&!u.templateUrl)throw new Error("One of component or template or templateUrl options is required.");var g;g=u.component?n.when(o.resolve(u.resolve,{},null,null)):n.all([(m=u,m.template?n.when(m.template):i(angular.isFunction(m.templateUrl)?m.templateUrl():m.templateUrl)),o.resolve(u.resolve,{},null,null)]);var m;function v(){return g}var y;return y=l=n.all([l]).then(v,v).then(function(t){var n=u.scope||e,r=n.$new();r.$close=h.close,r.$dismiss=h.dismiss,r.$on("$destroy",function(){r.$$uibDestructionScheduled||r.$dismiss("$uibUnscheduledDestruction")});var i,o,l={scope:r,deferred:c,renderDeferred:f,closedDeferred:d,animation:u.animation,backdrop:u.backdrop,keyboard:u.keyboard,backdropClass:u.backdropClass,windowTopClass:u.windowTopClass,windowClass:u.windowClass,windowTemplateUrl:u.windowTemplateUrl,ariaLabelledBy:u.ariaLabelledBy,ariaDescribedBy:u.ariaDescribedBy,size:u.size,openedClass:u.openedClass,appendTo:u.appendTo},g={},m={};u.component?(v(g,!1,!0,!1),g.name=u.component,l.component=g):u.controller&&(v(m,!0,!1,!0),o=a(u.controller,m,!0,u.controllerAs),u.controllerAs&&u.bindToController&&((i=o.instance).$close=r.$close,i.$dismiss=r.$dismiss,angular.extend(i,{$resolve:m.$scope.$resolve},n)),i=o(),angular.isFunction(i.$onInit)&&i.$onInit()),u.component||(l.content=t[0]),s.open(h,l),p.resolve(!0);function v(e,n,i,a){e.$scope=r,e.$scope.$resolve={},i?e.$scope.$uibModalInstance=h:e.$uibModalInstance=h;var o=n?t[1]:t;angular.forEach(o,function(t,n){a&&(e[n]=t),e.$scope.$resolve[n]=t})}},function(t){p.reject(t),c.reject(t)}).finally(function(){l===y&&(l=null)}),h},u}]};return t}),angular.module("ui.bootstrap.paging",[]).factory("uibPaging",["$parse",function(t){return{create:function(e,n,r){e.setNumPages=r.numPages?t(r.numPages).assign:angular.noop,e.ngModelCtrl={$setViewValue:angular.noop},e._watchers=[],e.init=function(t,i){e.ngModelCtrl=t,e.config=i,t.$render=function(){e.render()},r.itemsPerPage?e._watchers.push(n.$parent.$watch(r.itemsPerPage,function(t){e.itemsPerPage=parseInt(t,10),n.totalPages=e.calculateTotalPages(),e.updatePage()})):e.itemsPerPage=i.itemsPerPage,n.$watch("totalItems",function(t,r){(angular.isDefined(t)||t!==r)&&(n.totalPages=e.calculateTotalPages(),e.updatePage())})},e.calculateTotalPages=function(){var t=e.itemsPerPage<1?1:Math.ceil(n.totalItems/e.itemsPerPage);return Math.max(t||0,1)},e.render=function(){n.page=parseInt(e.ngModelCtrl.$viewValue,10)||1},n.selectPage=function(t,r){r&&r.preventDefault();(!n.ngDisabled||!r)&&n.page!==t&&t>0&&t<=n.totalPages&&(r&&r.target&&r.target.blur(),e.ngModelCtrl.$setViewValue(t),e.ngModelCtrl.$render())},n.getText=function(t){return n[t+"Text"]||e.config[t+"Text"]},n.noPrevious=function(){return 1===n.page},n.noNext=function(){return n.page===n.totalPages},e.updatePage=function(){e.setNumPages(n.$parent,n.totalPages),n.page>n.totalPages?n.selectPage(n.totalPages):e.ngModelCtrl.$render()},n.$on("$destroy",function(){for(;e._watchers.length;)e._watchers.shift()()})}}}]),angular.module("ui.bootstrap.pager",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPagerController",["$scope","$attrs","uibPaging","uibPagerConfig",function(t,e,n,r){t.align=angular.isDefined(e.align)?t.$parent.$eval(e.align):r.align,n.create(this,t,e)}]).constant("uibPagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("uibPager",["uibPagerConfig",function(t){return{scope:{totalItems:"=",previousText:"@",nextText:"@",ngDisabled:"="},require:["uibPager","?ngModel"],restrict:"A",controller:"UibPagerController",controllerAs:"pager",templateUrl:function(t,e){return e.templateUrl||"uib/template/pager/pager.html"},link:function(e,n,r,i){n.addClass("pager");var a=i[0],o=i[1];o&&a.init(o,t)}}}]),angular.module("ui.bootstrap.pagination",["ui.bootstrap.paging","ui.bootstrap.tabindex"]).controller("UibPaginationController",["$scope","$attrs","$parse","uibPaging","uibPaginationConfig",function(t,e,n,r,i){var a=this,o=angular.isDefined(e.maxSize)?t.$parent.$eval(e.maxSize):i.maxSize,s=angular.isDefined(e.rotate)?t.$parent.$eval(e.rotate):i.rotate,u=angular.isDefined(e.forceEllipses)?t.$parent.$eval(e.forceEllipses):i.forceEllipses,l=angular.isDefined(e.boundaryLinkNumbers)?t.$parent.$eval(e.boundaryLinkNumbers):i.boundaryLinkNumbers,c=angular.isDefined(e.pageLabel)?function(n){return t.$parent.$eval(e.pageLabel,{$page:n})}:angular.identity;t.boundaryLinks=angular.isDefined(e.boundaryLinks)?t.$parent.$eval(e.boundaryLinks):i.boundaryLinks,t.directionLinks=angular.isDefined(e.directionLinks)?t.$parent.$eval(e.directionLinks):i.directionLinks,e.$set("role","menu"),r.create(this,t,e),e.maxSize&&a._watchers.push(t.$parent.$watch(n(e.maxSize),function(t){o=parseInt(t,10),a.render()}));function p(t,e,n){return{number:t,text:e,active:n}}var d=this.render;this.render=function(){d(),t.page>0&&t.page<=t.totalPages&&(t.pages=function(t,e){var n=[],r=1,i=e,a=angular.isDefined(o)&&oe&&(r=(i=e)-o+1):(r=(Math.ceil(t/o)-1)*o+1,i=Math.min(r+o-1,e)));for(var d=r;d<=i;d++){var f=p(d,c(d),d===t);n.push(f)}if(a&&o>0&&(!s||u||l)){if(r>1){if(!l||r>3){var h=p(r-1,"...",!1);n.unshift(h)}if(l){if(3===r){var g=p(2,"2",!1);n.unshift(g)}var m=p(1,"1",!1);n.unshift(m)}}if(i';return{compile:function(t,e){var n=i(b);return function(t,e,i,u){var p,g,m,v,y,b,w,$,k=!!angular.isDefined(f.appendToBody)&&f.appendToBody,C=h(void 0),x=angular.isDefined(i[l+"Enable"]),T=t.$new(!0),S=!1,E=!!angular.isDefined(i[l+"IsOpen"])&&c(i[l+"IsOpen"]),D=!!f.useContentExp&&c(i[r]),A=[],B=function(){p&&p.html()&&(b||(b=a(function(){var t=s.positionElements(e,p,T.placement,k),n=angular.isDefined(p.offsetHeight)?p.offsetHeight:p.prop("offsetHeight"),r=k?s.offset(e):s.position(e);p.css({top:t.top+"px",left:t.left+"px"});var i=t.placement.split("-");p.hasClass(i[0])||(p.removeClass($.split("-")[0]),p.addClass(i[0])),p.hasClass(f.placementClassPrefix+t.placement)||(p.removeClass(f.placementClassPrefix+$),p.addClass(f.placementClassPrefix+t.placement)),w=a(function(){var t=angular.isDefined(p.offsetHeight)?p.offsetHeight:p.prop("offsetHeight"),e=s.adjustTop(i,r,n,t);e&&p.css(e),w=null},0,!1),p.hasClass("uib-position-measure")?(s.positionArrow(p,t.placement),p.removeClass("uib-position-measure")):$!==t.placement&&s.positionArrow(p,t.placement),$=t.placement,b=null},0,!1)))};T.origScope=t,T.isOpen=!1;function I(){T.isOpen?M():O()}function O(){x&&!t.$eval(i[l+"Enable"])||(L(),function(){T.title=i[l+"Title"],T.content=D?D(t):i[r];T.popupClass=i[l+"Class"],T.placement=angular.isDefined(i[l+"Placement"])?i[l+"Placement"]:f.placement;var e=s.parsePlacement(T.placement);$=e[1]?e[0]+"-"+e[1]:e[0];var n=parseInt(i[l+"PopupDelay"],10),a=parseInt(i[l+"PopupCloseDelay"],10);T.popupDelay=isNaN(n)?f.popupDelay:n,T.popupCloseDelay=isNaN(a)?f.popupCloseDelay:a}(),T.popupDelay?v||(v=a(P,T.popupDelay,!1)):P())}function M(){_(),T.popupCloseDelay?y||(y=a(R,T.popupCloseDelay,!1)):R()}function P(){if(_(),L(),!T.content)return angular.noop;!function(){if(p)return;g=T.$new(),p=n(g,function(t){k?o.find("body").append(t):e.after(t)}),d.add(T,{close:R}),function(){A.length=0,D?(A.push(t.$watch(D,function(t){T.content=t,!t&&T.isOpen&&R()})),A.push(g.$watch(function(){S||(S=!0,g.$$postDigest(function(){S=!1,T&&T.isOpen&&B()}))}))):A.push(i.$observe(r,function(t){T.content=t,!t&&T.isOpen?R():B()}));A.push(i.$observe(l+"Title",function(t){T.title=t,T.isOpen&&B()})),A.push(i.$observe(l+"Placement",function(t){T.placement=t||f.placement,T.isOpen&&B()}))}()}(),T.$evalAsync(function(){T.isOpen=!0,U(!0),B()})}function _(){v&&(a.cancel(v),v=null),b&&(a.cancel(b),b=null)}function R(){T&&T.$evalAsync(function(){T&&(T.isOpen=!1,U(!1),T.animation?m||(m=a(N,150,!1)):N())})}function L(){y&&(a.cancel(y),y=null),m&&(a.cancel(m),m=null)}function N(){_(),L(),A.length&&(angular.forEach(A,function(t){t()}),A.length=0),p&&(p.remove(),p=null,w&&a.cancel(w)),d.remove(T),g&&(g.$destroy(),g=null)}function U(e){E&&angular.isFunction(E.assign)&&E.assign(t,e)}T.contentExp=function(){return T.content},i.$observe("disabled",function(t){t&&_(),t&&T.isOpen&&R()}),E&&t.$watch(E,function(t){T&&!t===T.isOpen&&I()});function q(t){T&&T.isOpen&&p&&(e[0].contains(t.target)||p[0].contains(t.target)||M())}function j(t){27===t.which&&M()}var F=function(){C.show.forEach(function(t){"outsideClick"===t?e.off("click",I):(e.off(t,O),e.off(t,I)),e.off("keypress",j)}),C.hide.forEach(function(t){"outsideClick"===t?o.off("click",q):e.off(t,M)})};!function(){var n=[],r=[],a=t.$eval(i[l+"Trigger"]);F(),angular.isObject(a)?(Object.keys(a).forEach(function(t){n.push(t),r.push(a[t])}),C={show:n,hide:r}):C=h(a),"none"!==C.show&&C.show.forEach(function(t,n){"outsideClick"===t?(e.on("click",I),o.on("click",q)):t===C.hide[n]?e.on(t,I):t&&(e.on(t,O),e.on(C.hide[n],M)),e.on("keypress",j)})}();var V=t.$eval(i[l+"Animation"]);T.animation=angular.isDefined(V)?!!V:f.animation;var z,H=l+"AppendToBody";z=H in i&&void 0===i[H]||t.$eval(i[H]),k=angular.isDefined(z)?z:k,t.$on("$destroy",function(){F(),N(),T=null})}}}}}]}).directive("uibTooltipTemplateTransclude",["$animate","$sce","$compile","$templateRequest",function(t,e,n,r){return{link:function(i,a,o){var s,u,l,c=i.$eval(o.tooltipTemplateTranscludeScope),p=0,d=function(){u&&(u.remove(),u=null),s&&(s.$destroy(),s=null),l&&(t.leave(l).then(function(){u=null}),u=l,l=null)};i.$watch(e.parseAsResourceUrl(o.uibTooltipTemplateTransclude),function(e){var o=++p;e?(r(e,!0).then(function(r){if(o===p){var i=c.$new(),u=n(r)(i,function(e){d(),t.enter(e,a)});l=u,(s=i).$emit("$includeContentLoaded",e)}},function(){o===p&&(d(),i.$emit("$includeContentError",e))}),i.$emit("$includeContentRequested",e)):d()}),i.$on("$destroy",d)}}}]).directive("uibTooltipClasses",["$uibPosition",function(t){return{restrict:"A",link:function(e,n,r){if(e.placement){var i=t.parsePlacement(e.placement);n.addClass(i[0])}e.popupClass&&n.addClass(e.popupClass),e.animation&&n.addClass(r.tooltipAnimationClass)}}}]).directive("uibTooltipPopup",function(){return{restrict:"A",scope:{content:"@"},templateUrl:"uib/template/tooltip/tooltip-popup.html"}}).directive("uibTooltip",["$uibTooltip",function(t){return t("uibTooltip","tooltip","mouseenter")}]).directive("uibTooltipTemplatePopup",function(){return{restrict:"A",scope:{contentExp:"&",originScope:"&"},templateUrl:"uib/template/tooltip/tooltip-template-popup.html"}}).directive("uibTooltipTemplate",["$uibTooltip",function(t){return t("uibTooltipTemplate","tooltip","mouseenter",{useContentExp:!0})}]).directive("uibTooltipHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&"},templateUrl:"uib/template/tooltip/tooltip-html-popup.html"}}).directive("uibTooltipHtml",["$uibTooltip",function(t){return t("uibTooltipHtml","tooltip","mouseenter",{useContentExp:!0})}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("uibPopoverTemplatePopup",function(){return{restrict:"A",scope:{uibTitle:"@",contentExp:"&",originScope:"&"},templateUrl:"uib/template/popover/popover-template.html"}}).directive("uibPopoverTemplate",["$uibTooltip",function(t){return t("uibPopoverTemplate","popover","click",{useContentExp:!0})}]).directive("uibPopoverHtmlPopup",function(){return{restrict:"A",scope:{contentExp:"&",uibTitle:"@"},templateUrl:"uib/template/popover/popover-html.html"}}).directive("uibPopoverHtml",["$uibTooltip",function(t){return t("uibPopoverHtml","popover","click",{useContentExp:!0})}]).directive("uibPopoverPopup",function(){return{restrict:"A",scope:{uibTitle:"@",content:"@"},templateUrl:"uib/template/popover/popover.html"}}).directive("uibPopover",["$uibTooltip",function(t){return t("uibPopover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("uibProgressConfig",{animate:!0,max:100}).controller("UibProgressController",["$scope","$attrs","uibProgressConfig",function(t,e,n){var r=this,i=angular.isDefined(e.animate)?t.$parent.$eval(e.animate):n.animate;this.bars=[],t.max=a(),this.addBar=function(t,e,n){i||e.css({transition:"none"}),this.bars.push(t),t.max=a(),t.title=n&&angular.isDefined(n.title)?n.title:"progressbar",t.$watch("value",function(e){t.recalculatePercentage()}),t.recalculatePercentage=function(){var e=r.bars.reduce(function(t,e){return e.percent=+(100*e.value/e.max).toFixed(2),t+e.percent},0);e>100&&(t.percent-=e-100)},t.$on("$destroy",function(){e=null,r.removeBar(t)})},this.removeBar=function(t){this.bars.splice(this.bars.indexOf(t),1),this.bars.forEach(function(t){t.recalculatePercentage()})},t.$watch("maxParam",function(t){r.bars.forEach(function(t){t.max=a(),t.recalculatePercentage()})});function a(){return angular.isDefined(t.maxParam)?t.maxParam:n.max}}]).directive("uibProgress",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",require:"uibProgress",scope:{maxParam:"=?max"},templateUrl:"uib/template/progressbar/progress.html"}}).directive("uibBar",function(){return{replace:!0,transclude:!0,require:"^uibProgress",scope:{value:"=",type:"@"},templateUrl:"uib/template/progressbar/bar.html",link:function(t,e,n,r){r.addBar(t,e,n)}}}).directive("uibProgressbar",function(){return{replace:!0,transclude:!0,controller:"UibProgressController",scope:{value:"=",maxParam:"=?max",type:"@"},templateUrl:"uib/template/progressbar/progressbar.html",link:function(t,e,n,r){r.addBar(t,angular.element(e.children()[0]),{title:n.title})}}}),angular.module("ui.bootstrap.rating",[]).constant("uibRatingConfig",{max:5,stateOn:null,stateOff:null,enableReset:!0,titles:["one","two","three","four","five"]}).controller("UibRatingController",["$scope","$attrs","uibRatingConfig",function(t,e,n){var r={$setViewValue:angular.noop},i=this;this.init=function(i){(r=i).$render=this.render,r.$formatters.push(function(t){return angular.isNumber(t)&&t<<0!==t&&(t=Math.round(t)),t}),this.stateOn=angular.isDefined(e.stateOn)?t.$parent.$eval(e.stateOn):n.stateOn,this.stateOff=angular.isDefined(e.stateOff)?t.$parent.$eval(e.stateOff):n.stateOff,this.enableReset=angular.isDefined(e.enableReset)?t.$parent.$eval(e.enableReset):n.enableReset;var a=angular.isDefined(e.titles)?t.$parent.$eval(e.titles):n.titles;this.titles=angular.isArray(a)&&a.length>0?a:n.titles;var o=angular.isDefined(e.ratingStates)?t.$parent.$eval(e.ratingStates):new Array(angular.isDefined(e.max)?t.$parent.$eval(e.max):n.max);t.range=this.buildTemplateObjects(o)},this.buildTemplateObjects=function(t){for(var e=0,n=t.length;e=this.titles.length?t+1:this.titles[t]},t.rate=function(e){if(!t.readonly&&e>=0&&e<=t.range.length){var n=i.enableReset&&r.$viewValue===e?0:e;r.$setViewValue(n),r.$render()}},t.enter=function(e){t.readonly||(t.value=e),t.onHover({value:e})},t.reset=function(){t.value=r.$viewValue,t.onLeave()},t.onKeydown=function(e){/(37|38|39|40)/.test(e.which)&&(e.preventDefault(),e.stopPropagation(),t.rate(t.value+(38===e.which||39===e.which?1:-1)))},this.render=function(){t.value=r.$viewValue,t.title=i.getTitle(t.value-1)}}]).directive("uibRating",function(){return{require:["uibRating","ngModel"],restrict:"A",scope:{readonly:"=?readOnly",onHover:"&",onLeave:"&"},controller:"UibRatingController",templateUrl:"uib/template/rating/rating.html",link:function(t,e,n,r){var i=r[0],a=r[1];i.init(a)}}}),angular.module("ui.bootstrap.tabs",[]).controller("UibTabsetController",["$scope",function(t){var e,n=this;n.tabs=[],n.select=function(t,a){if(!r){var o=i(e),s=n.tabs[o];if(s){if(s.tab.onDeselect({$event:a,$selectedIndex:t}),a&&a.isDefaultPrevented())return;s.tab.active=!1}var u=n.tabs[t];u?(u.tab.onSelect({$event:a}),u.tab.active=!0,n.active=u.index,e=u.index):!u&&angular.isDefined(e)&&(n.active=null,e=null)}},n.addTab=function(t){if(n.tabs.push({tab:t,index:t.index}),n.tabs.sort(function(t,e){return t.index>e.index?1:t.indexy||tc&&t>y},t.noIncrementMinutes=function(){var t=D(c,m);return b||t>y||tc&&t>y},t.noIncrementSeconds=function(){var t=A(c,w);return b||t>y||tc&&t>y},t.noToggleMeridian=function(){return c.getHours()<12?b||D(c,720)>y:b||D(c,-720)0&&e<13:e>=0&&e<24)&&""!==t.hours)return t.showMeridian&&(12===e&&(e=0),t.meridian===f[1]&&(e+=12)),e}function k(){var e=+t.minutes;if(e>=0&&e<60&&""!==t.minutes)return e}function C(t,e){return null===t?"":angular.isDefined(t)&&t.toString().length<2&&!e?"0"+t:t.toString()}this.setupMousewheelEvents=function(e,n,r){var i=function(t){t.originalEvent&&(t=t.originalEvent);var e=t.wheelDelta?t.wheelDelta:-t.deltaY;return t.detail||e>0};e.on("mousewheel wheel",function(e){b||t.$apply(i(e)?t.incrementHours():t.decrementHours()),e.preventDefault()}),n.on("mousewheel wheel",function(e){b||t.$apply(i(e)?t.incrementMinutes():t.decrementMinutes()),e.preventDefault()}),r.on("mousewheel wheel",function(e){b||t.$apply(i(e)?t.incrementSeconds():t.decrementSeconds()),e.preventDefault()})},this.setupArrowkeyEvents=function(e,n,r){e.on("keydown",function(e){b||(38===e.which?(e.preventDefault(),t.incrementHours(),t.$apply()):40===e.which&&(e.preventDefault(),t.decrementHours(),t.$apply()))}),n.on("keydown",function(e){b||(38===e.which?(e.preventDefault(),t.incrementMinutes(),t.$apply()):40===e.which&&(e.preventDefault(),t.decrementMinutes(),t.$apply()))}),r.on("keydown",function(e){b||(38===e.which?(e.preventDefault(),t.incrementSeconds(),t.$apply()):40===e.which&&(e.preventDefault(),t.decrementSeconds(),t.$apply()))})},this.setupInputEvents=function(e,n,r){if(t.readonlyInput)return t.updateHours=angular.noop,t.updateMinutes=angular.noop,void(t.updateSeconds=angular.noop);var i=function(e,n,r){d.$setViewValue(null),d.$setValidity("time",!1),angular.isDefined(e)&&(t.invalidHours=e,s&&s.$setValidity("hours",!1)),angular.isDefined(n)&&(t.invalidMinutes=n,u&&u.$setValidity("minutes",!1)),angular.isDefined(r)&&(t.invalidSeconds=r,l&&l.$setValidity("seconds",!1))};t.updateHours=function(){var t=$(),e=k();d.$setDirty(),angular.isDefined(t)&&angular.isDefined(e)?(c.setHours(t),c.setMinutes(e),cy?i(!0):x("h")):i(!0)},e.on("blur",function(e){d.$setTouched(),B()?T():null===t.hours||""===t.hours?i(!0):!t.invalidHours&&t.hours<10&&t.$apply(function(){t.hours=C(t.hours,!h)})}),t.updateMinutes=function(){var t=k(),e=$();d.$setDirty(),angular.isDefined(t)&&angular.isDefined(e)?(c.setHours(e),c.setMinutes(t),cy?i(void 0,!0):x("m")):i(void 0,!0)},n.on("blur",function(e){d.$setTouched(),B()?T():null===t.minutes?i(void 0,!0):!t.invalidMinutes&&t.minutes<10&&t.$apply(function(){t.minutes=C(t.minutes)})}),t.updateSeconds=function(){var e=function(){var e=+t.seconds;return e>=0&&e<60?e:void 0}();d.$setDirty(),angular.isDefined(e)?(c.setSeconds(e),x("s")):i(void 0,void 0,!0)},r.on("blur",function(e){B()?T():!t.invalidSeconds&&t.seconds<10&&t.$apply(function(){t.seconds=C(t.seconds)})})},this.render=function(){var e=d.$viewValue;isNaN(e)?(d.$setValidity("time",!1),i.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.')):(e&&(c=e),cy?(d.$setValidity("time",!1),t.invalidHours=!0,t.invalidMinutes=!0):T(),S())};function x(t){T(),d.$setViewValue(new Date(c)),S(t)}function T(){s&&s.$setValidity("hours",!0),u&&u.$setValidity("minutes",!0),l&&l.$setValidity("seconds",!0),d.$setValidity("time",!0),t.invalidHours=!1,t.invalidMinutes=!1,t.invalidSeconds=!1}function S(e){if(d.$modelValue){var n=c.getHours(),r=c.getMinutes(),i=c.getSeconds();t.showMeridian&&(n=0===n||12===n?12:n%12),t.hours="h"===e?n:C(n,!h),"m"!==e&&(t.minutes=C(r)),t.meridian=c.getHours()<12?f[0]:f[1],"s"!==e&&(t.seconds=C(i)),t.meridian=c.getHours()<12?f[0]:f[1]}else t.hours=null,t.minutes=null,t.seconds=null,t.meridian=f[0]}function E(t){c=A(c,t),x()}function D(t,e){return A(t,60*e)}function A(t,e){var n=new Date(t.getTime()+1e3*e),r=new Date(t);return r.setHours(n.getHours(),n.getMinutes(),n.getSeconds()),r}function B(){return(null===t.hours||""===t.hours)&&(null===t.minutes||""===t.minutes)&&(!t.showSeconds||t.showSeconds&&(null===t.seconds||""===t.seconds))}t.showSpinners=angular.isDefined(n.showSpinners)?t.$parent.$eval(n.showSpinners):o.showSpinners,t.incrementHours=function(){t.noIncrementHours()||E(60*g*60)},t.decrementHours=function(){t.noDecrementHours()||E(60*-g*60)},t.incrementMinutes=function(){t.noIncrementMinutes()||E(60*m)},t.decrementMinutes=function(){t.noDecrementMinutes()||E(60*-m)},t.incrementSeconds=function(){t.noIncrementSeconds()||E(w)},t.decrementSeconds=function(){t.noDecrementSeconds()||E(-w)},t.toggleMeridian=function(){var e=k(),n=$();t.noToggleMeridian()||(angular.isDefined(e)&&angular.isDefined(n)?E(720*(c.getHours()<12?60:-60)):t.meridian=t.meridian===f[0]?f[1]:f[0])},t.blur=function(){d.$setTouched()},t.$on("$destroy",function(){for(;p.length;)p.shift()()})}]).directive("uibTimepicker",["uibTimepickerConfig",function(t){return{require:["uibTimepicker","?^ngModel"],restrict:"A",controller:"UibTimepickerController",controllerAs:"timepicker",scope:{},templateUrl:function(e,n){return n.templateUrl||t.templateUrl},link:function(t,e,n,r){var i=r[0],a=r[1];a&&i.init(a,e.find("input"))}}}]),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.debounce","ui.bootstrap.position"]).factory("uibTypeaheadParser",["$parse",function(t){var e=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(n){var r=n.match(e);if(!r)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+n+'".');return{itemName:r[3],source:t(r[4]),viewMapper:t(r[2]||r[1]),modelMapper:t(r[1])}}}}]).controller("UibTypeaheadController",["$scope","$element","$attrs","$compile","$parse","$q","$timeout","$document","$window","$rootScope","$$debounce","$uibPosition","uibTypeaheadParser",function(t,e,n,r,i,a,o,s,u,l,c,p,d){var f,h,g=[9,13,27,38,40],m=t.$eval(n.typeaheadMinLength);m||0===m||(m=1),t.$watch(n.typeaheadMinLength,function(t){m=t||0===t?t:1});var v=t.$eval(n.typeaheadWaitMs)||0,y=!1!==t.$eval(n.typeaheadEditable);t.$watch(n.typeaheadEditable,function(t){y=!1!==t});var b,w,$=i(n.typeaheadLoading).assign||angular.noop,k=n.typeaheadShouldSelect?i(n.typeaheadShouldSelect):function(t,e){var n=e.$event;return 13===n.which||9===n.which},C=i(n.typeaheadOnSelect),x=!!angular.isDefined(n.typeaheadSelectOnBlur)&&t.$eval(n.typeaheadSelectOnBlur),T=i(n.typeaheadNoResults).assign||angular.noop,S=n.typeaheadInputFormatter?i(n.typeaheadInputFormatter):void 0,E=!!n.typeaheadAppendToBody&&t.$eval(n.typeaheadAppendToBody),D=n.typeaheadAppendTo?t.$eval(n.typeaheadAppendTo):null,A=!1!==t.$eval(n.typeaheadFocusFirst),B=!!n.typeaheadSelectOnExact&&t.$eval(n.typeaheadSelectOnExact),I=i(n.typeaheadIsOpen).assign||angular.noop,O=t.$eval(n.typeaheadShowHint)||!1,M=i(n.ngModel),P=i(n.ngModel+"($$$p)"),_=d.parse(n.uibTypeahead),R=t.$new(),L=t.$on("$destroy",function(){R.$destroy()});R.$on("$destroy",L);var N="typeahead-"+R.$id+"-"+Math.floor(1e4*Math.random());e.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":N});var U,q;O&&((U=angular.element("
")).css("position","relative"),e.after(U),(q=e.clone()).attr("placeholder",""),q.attr("tabindex","-1"),q.val(""),q.css({position:"absolute",top:"0px",left:"0px","border-color":"transparent","box-shadow":"none",opacity:1,background:"none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)",color:"#999"}),e.css({position:"relative","vertical-align":"top","background-color":"transparent"}),q.attr("id")&&q.removeAttr("id"),U.append(q),q.after(e));var j=angular.element("
");j.attr({id:N,matches:"matches",active:"activeIdx",select:"select(activeIdx, evt)","move-in-progress":"moveInProgress",query:"query",position:"position","assign-is-open":"assignIsOpen(isOpen)",debounce:"debounceUpdate"}),angular.isDefined(n.typeaheadTemplateUrl)&&j.attr("template-url",n.typeaheadTemplateUrl),angular.isDefined(n.typeaheadPopupTemplateUrl)&&j.attr("popup-template-url",n.typeaheadPopupTemplateUrl);var F=function(){R.matches=[],R.activeIdx=-1,e.attr("aria-expanded",!1),O&&q.val("")},V=function(t){return N+"-option-"+t};R.$watch("activeIdx",function(t){t<0?e.removeAttr("aria-activedescendant"):e.attr("aria-activedescendant",V(t))});var z=function(n,r){var i={$viewValue:n};$(t,!0),T(t,!1),a.when(_.source(t,i)).then(function(a){var o=n===f.$viewValue;if(o&&b)if(a&&a.length>0){R.activeIdx=A?0:-1,T(t,!1),R.matches.length=0;for(var s=0;sp&&l&&l.toUpperCase()===R.matches[p].label.toUpperCase())&&(angular.isNumber(R.debounceUpdate)||angular.isObject(R.debounceUpdate)?c(function(){R.select(0,r)},angular.isNumber(R.debounceUpdate)?R.debounceUpdate:R.debounceUpdate.default):R.select(0,r)),O){var u=R.matches[0].label;angular.isString(n)&&n.length>0&&u.slice(0,n.length).toUpperCase()===n.toUpperCase()?q.val(n+u.slice(n.length)):q.val("")}}else F(),T(t,!0);var l,p;o&&$(t,!1)},function(){F(),$(t,!1),T(t,!0)})};E&&(angular.element(u).on("resize",K),s.find("body").on("scroll",K));var H=c(function(){R.matches.length&&W(),R.moveInProgress=!1},200);R.moveInProgress=!1;function K(){R.moveInProgress||(R.moveInProgress=!0,R.$digest()),H()}function W(){R.position=E?p.offset(e):p.position(e),R.position.top+=e.prop("offsetHeight")}R.query=void 0;var G,Y=function(){G&&o.cancel(G)};F(),R.assignIsOpen=function(e){I(t,e)},R.select=function(r,i){var a,s,u={};w=!0,u[_.itemName]=s=R.matches[r].model,a=_.modelMapper(t,u),l=t,c=a,angular.isFunction(M(t))&&h.getOption("getterSetter")?P(l,{$$$p:c}):M.assign(l,c);var l,c;f.$setValidity("editable",!0),f.$setValidity("parse",!0),C(t,{$item:s,$model:a,$label:_.viewMapper(t,u),$event:i}),F(),!1!==R.$eval(n.typeaheadFocusOnSelect)&&o(function(){e[0].focus()},0,!1)},e.on("keydown",function(e){if(0!==R.matches.length&&-1!==g.indexOf(e.which)){var n=k(t,{$event:e});if(-1===R.activeIdx&&n||9===e.which&&e.shiftKey)return F(),void R.$digest();e.preventDefault();var r;switch(e.which){case 27:e.stopPropagation(),F(),t.$digest();break;case 38:R.activeIdx=(R.activeIdx>0?R.activeIdx:R.matches.length)-1,R.$digest(),(r=j[0].querySelectorAll(".uib-typeahead-match")[R.activeIdx]).parentNode.scrollTop=r.offsetTop;break;case 40:R.activeIdx=(R.activeIdx+1)%R.matches.length,R.$digest(),(r=j[0].querySelectorAll(".uib-typeahead-match")[R.activeIdx]).parentNode.scrollTop=r.offsetTop;break;default:n&&R.$apply(function(){angular.isNumber(R.debounceUpdate)||angular.isObject(R.debounceUpdate)?c(function(){R.select(R.activeIdx,e)},angular.isNumber(R.debounceUpdate)?R.debounceUpdate:R.debounceUpdate.default):R.select(R.activeIdx,e)})}}}),e.on("focus",function(t){b=!0,0!==m||f.$viewValue||o(function(){z(f.$viewValue,t)},0)}),e.on("blur",function(t){x&&R.matches.length&&-1!==R.activeIdx&&!w&&(w=!0,R.$apply(function(){angular.isObject(R.debounceUpdate)&&angular.isNumber(R.debounceUpdate.blur)?c(function(){R.select(R.activeIdx,t)},R.debounceUpdate.blur):R.select(R.activeIdx,t)})),!y&&f.$error.editable&&(f.$setViewValue(),R.$apply(function(){f.$setValidity("editable",!0),f.$setValidity("parse",!0)}),e.val("")),b=!1,w=!1});var Q=function(n){e[0]!==n.target&&3!==n.which&&0!==R.matches.length&&(F(),l.$$phase||t.$digest())};s.on("click",Q),t.$on("$destroy",function(){s.off("click",Q),(E||D)&&Z.remove(),E&&(angular.element(u).off("resize",K),s.find("body").off("scroll",K)),j.remove(),O&&U.remove()});var Z=r(j)(R);E?s.find("body").append(Z):D?angular.element(D).eq(0).append(Z):e.after(Z),this.init=function(e){h=function(t){var e;angular.version.minor<6?(e=t.$options||{}).getOption=function(t){return e[t]}:e=t.$options;return e}(f=e),R.debounceUpdate=i(h.getOption("debounce"))(t),f.$parsers.unshift(function(e){b=!0,0===m||e&&e.length>=m?v>0?(Y(),n=e,G=o(function(){z(n)},v)):z(e):($(t,!1),Y(),F());var n;return y?e:e?void f.$setValidity("editable",!1):(f.$setValidity("editable",!0),null)}),f.$formatters.push(function(e){var n,r={};return y||f.$setValidity("editable",!0),S?(r.$model=e,S(t,r)):(r[_.itemName]=e,n=_.viewMapper(t,r),r[_.itemName]=void 0,n!==_.viewMapper(t,r)?n:e)})}}]).directive("uibTypeahead",function(){return{controller:"UibTypeaheadController",require:["ngModel","uibTypeahead"],link:function(t,e,n,r){r[1].init(r[0])}}}).directive("uibTypeaheadPopup",["$$debounce",function(t){return{scope:{matches:"=",query:"=",active:"=",position:"&",moveInProgress:"=",select:"&",assignIsOpen:"&",debounce:"&"},replace:!0,templateUrl:function(t,e){return e.popupTemplateUrl||"uib/template/typeahead/typeahead-popup.html"},link:function(e,n,r){e.templateUrl=r.templateUrl,e.isOpen=function(){var t=e.matches.length>0;return e.assignIsOpen({isOpen:t}),t},e.isActive=function(t){return e.active===t},e.selectActive=function(t){e.active=t},e.selectMatch=function(n,r){var i=e.debounce();angular.isNumber(i)||angular.isObject(i)?t(function(){e.select({activeIdx:n,evt:r})},angular.isNumber(i)?i:i.default):e.select({activeIdx:n,evt:r})}}}}]).directive("uibTypeaheadMatch",["$templateRequest","$compile","$parse",function(t,e,n){return{scope:{index:"=",match:"=",query:"="},link:function(r,i,a){var o=n(a.templateUrl)(r.$parent)||"uib/template/typeahead/typeahead-match.html";t(o).then(function(t){var n=angular.element(t.trim());i.replaceWith(n),e(n)(r)})}}}]).filter("uibTypeaheadHighlight",["$sce","$injector","$log",function(t,e,n){var r;r=e.has("$sanitize");return function(e,i){!r&&(a=e,/<.*>/g.test(a))&&n.warn("Unsafe use of typeahead please use ngSanitize");var a;e=i?(""+e).replace(new RegExp((o=i,o.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")),"gi"),"$&"):e;var o;return r||(e=t.trustAsHtml(e)),e}}]),angular.module("uib/template/accordion/accordion-group.html",[]).run(["$templateCache",function(t){t.put("uib/template/accordion/accordion-group.html",'\n
\n
\n
\n')}]),angular.module("uib/template/accordion/accordion.html",[]).run(["$templateCache",function(t){t.put("uib/template/accordion/accordion.html",'
')}]),angular.module("uib/template/alert/alert.html",[]).run(["$templateCache",function(t){t.put("uib/template/alert/alert.html",'\n
\n')}]),angular.module("uib/template/carousel/carousel.html",[]).run(["$templateCache",function(t){t.put("uib/template/carousel/carousel.html",'\n\n \n previous\n\n\n \n next\n\n\n')}]),angular.module("uib/template/carousel/slide.html",[]).run(["$templateCache",function(t){t.put("uib/template/carousel/slide.html",'
\n')}]),angular.module("uib/template/datepicker/datepicker.html",[]).run(["$templateCache",function(t){t.put("uib/template/datepicker/datepicker.html",'
\n
\n
\n
\n
\n')}]),angular.module("uib/template/datepicker/day.html",[]).run(["$templateCache",function(t){t.put("uib/template/datepicker/day.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
{{::label.abbr}}
{{ weekNumbers[$index] }}\n \n
\n')}]),angular.module("uib/template/datepicker/month.html",[]).run(["$templateCache",function(t){t.put("uib/template/datepicker/month.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n
\n')}]),angular.module("uib/template/datepicker/popup.html",[]).run(["$templateCache",function(t){t.put("uib/template/datepicker/popup.html",'
\n \n
\n')}]),angular.module("uib/template/datepicker/year.html",[]).run(["$templateCache",function(t){t.put("uib/template/datepicker/year.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n
\n \n
\n')}]),angular.module("uib/template/datepickerPopup/popup.html",[]).run(["$templateCache",function(t){t.put("uib/template/datepickerPopup/popup.html",'\n')}]),angular.module("uib/template/modal/backdrop.html",[]).run(["$templateCache",function(t){t.put("uib/template/modal/backdrop.html",'\n')}]),angular.module("uib/template/modal/window.html",[]).run(["$templateCache",function(t){t.put("uib/template/modal/window.html","
\n")}]),angular.module("uib/template/pager/pager.html",[]).run(["$templateCache",function(t){t.put("uib/template/pager/pager.html",'
  • {{::getText(\'previous\')}}
  • \n
  • {{::getText(\'next\')}}
  • \n')}]),angular.module("uib/template/pagination/pagination.html",[]).run(["$templateCache",function(t){t.put("uib/template/pagination/pagination.html",'\n\n\n\n\n')}]),angular.module("uib/template/tooltip/tooltip-html-popup.html",[]).run(["$templateCache",function(t){t.put("uib/template/tooltip/tooltip-html-popup.html",'
    \n
    \n')}]),angular.module("uib/template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(t){t.put("uib/template/tooltip/tooltip-popup.html",'
    \n
    \n')}]),angular.module("uib/template/tooltip/tooltip-template-popup.html",[]).run(["$templateCache",function(t){t.put("uib/template/tooltip/tooltip-template-popup.html",'
    \n
    \n')}]),angular.module("uib/template/popover/popover-html.html",[]).run(["$templateCache",function(t){t.put("uib/template/popover/popover-html.html",'
    \n\n
    \n

    \n
    \n
    \n')}]),angular.module("uib/template/popover/popover-template.html",[]).run(["$templateCache",function(t){t.put("uib/template/popover/popover-template.html",'
    \n\n
    \n

    \n
    \n
    \n')}]),angular.module("uib/template/popover/popover.html",[]).run(["$templateCache",function(t){t.put("uib/template/popover/popover.html",'
    \n\n
    \n

    \n
    \n
    \n')}]),angular.module("uib/template/progressbar/bar.html",[]).run(["$templateCache",function(t){t.put("uib/template/progressbar/bar.html",'
    \n')}]),angular.module("uib/template/progressbar/progress.html",[]).run(["$templateCache",function(t){t.put("uib/template/progressbar/progress.html",'
    ')}]),angular.module("uib/template/progressbar/progressbar.html",[]).run(["$templateCache",function(t){t.put("uib/template/progressbar/progressbar.html",'
    \n
    \n
    \n')}]),angular.module("uib/template/rating/rating.html",[]).run(["$templateCache",function(t){t.put("uib/template/rating/rating.html",'\n ({{ $index < value ? \'*\' : \' \' }})\n \n\n')}]),angular.module("uib/template/tabs/tab.html",[]).run(["$templateCache",function(t){t.put("uib/template/tabs/tab.html",'\n')}]),angular.module("uib/template/tabs/tabset.html",[]).run(["$templateCache",function(t){t.put("uib/template/tabs/tabset.html",'
    \n \n
    \n
    \n
    \n
    \n
    \n')}]),angular.module("uib/template/timepicker/timepicker.html",[]).run(["$templateCache",function(t){t.put("uib/template/timepicker/timepicker.html",'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
      
    \n \n :\n \n :\n \n
      
    \n')}]),angular.module("uib/template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(t){t.put("uib/template/typeahead/typeahead-match.html",'\n')}]),angular.module("uib/template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(t){t.put("uib/template/typeahead/typeahead-popup.html",'\n')}]),angular.module("ui.bootstrap.carousel").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibCarouselCss&&angular.element(document).find("head").prepend(''),angular.$$uibCarouselCss=!0}),angular.module("ui.bootstrap.datepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerCss=!0}),angular.module("ui.bootstrap.position").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibPositionCss&&angular.element(document).find("head").prepend(''),angular.$$uibPositionCss=!0}),angular.module("ui.bootstrap.datepickerPopup").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibDatepickerpopupCss&&angular.element(document).find("head").prepend(''),angular.$$uibDatepickerpopupCss=!0}),angular.module("ui.bootstrap.tooltip").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTooltipCss&&angular.element(document).find("head").prepend(''),angular.$$uibTooltipCss=!0}),angular.module("ui.bootstrap.timepicker").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTimepickerCss&&angular.element(document).find("head").prepend(''),angular.$$uibTimepickerCss=!0}),angular.module("ui.bootstrap.typeahead").run(function(){!angular.$$csp().noInlineStyle&&!angular.$$uibTypeaheadCss&&angular.element(document).find("head").prepend(''),angular.$$uibTypeaheadCss=!0}),function(t,e,n){"use strict";e.module("angulartics.google.analytics",["angulartics"]).config(["$analyticsProvider",function(n){n.settings.pageTracking.trackRelativePath=!0,n.settings.ga={additionalAccountNames:void 0,additionalAccountHitTypes:{pageview:!0,event:!0,exception:!1,ecommerce:!1,userTiming:!1,setUserProperties:!1,userId:!1},disableEventTracking:null,disablePageTracking:null,enhancedEcommerce:!1,transport:null,userId:null},n.registerPageTrack(function(t,i){i=i||{},n.settings.ga.disablePageTracking||r("pageview","send",e.extend({},i,{hitType:"pageview",page:t}))}),n.registerEventTrack(function(i,a){if(!n.settings.ga.disableEventTracking&&(i||i+""=="0")){if((a=a||{}).category=a.category||"Event",a.value){var o=parseInt(a.value,10);a.value=isNaN(o)?0:o}e.isFunction(a.hitCallback)||(a.hitCallback=null),a.nonInteraction=a.nonInteraction||a.noninteraction,r("event","send",e.extend({},a,{hitType:"event",eventCategory:a.category,eventAction:i,eventLabel:a.label,eventValue:a.value,nonInteraction:a.nonInteraction,page:a.page||t.location.hash.substring(1)||t.location.pathname,hitCallback:a.hitCallback}))}}),n.registerExceptionTrack(function(e,n){r("exception","send",{hitType:"event",eventCategory:"Exceptions",eventAction:e.toString(),eventLabel:e.stack,nonInteraction:!0,page:t.location.hash.substring(1)||t.location.pathname,isException:!0})}),n.registerSetUsername(function(t){n.settings.ga.userId=t}),n.registerSetUserProperties(function(t){t&&r("setUserProperties","set",function(t){var e,n={};for(e in t)e.indexOf("dimension")&&e.indexOf("metric")||(n[e]=t[e]);return n}(t))}),n.registerUserTimings(function(n){if(!e.isObject(n)||e.isArray(n))return console.log("Required argument properties is missing or not an object");e.forEach(["timingCategory","timingVar","timingValue"],function(t){if(e.isUndefined(n[t]))return console.log("Argument properties missing required property "+t)}),r("userTiming","send",{hitType:"timing",timingCategory:n.timingCategory,timingVar:n.timingVar,timingValue:n.timingValue,timingLabel:n.timingLabel,optSampleRate:n.optSampleRate,page:n.page||t.location.hash.substring(1)||t.location.pathname})}),n.registerTransactionTrack(function(t){var n,i;if(r("ecommerce","require","ecommerce"),r("ecommerce","ecommerce:addTransaction",t),t.products)for(i=0;i-1&&n.settings.ga.enhancedEcommerce)switch(t){case"ecommerce:addTransaction":t=["ec:setAction","purchase"];break;case"ecommerce:addItem":t="ec:addProduct",r.id=r.sku;break;case"ecommerce:send":t="send",r.hitType="event",r.eventCategory="Angulartics Enhanced Ecommerce",r.eventAction="Purchase",r.nonInteraction=!0}i(t instanceof Array?t.concat(r):[t,r],a)}),r?function(t,e,i){var a=n.settings.ga.additionalAccountHitTypes[t];r(e,i,a)}:e.noop;function i(r,i){var a,o=n.settings.ga.userId,s=t.GoogleAnalyticsObject;t[s].apply(this,r),i&&(a=e.copy(r),o&&!n.settings.ga.additionalAccountHitTypes.userId&&a[2]&&"object"==typeof a[2]&&delete a[2].userId,e.forEach(n.settings.ga.additionalAccountNames,function(e){a[0]=e+"."+a[0],t[s].apply(this,a)}))}}()}])}(window,window.angular),"undefined"!=typeof module&&"undefined"!=typeof exports&&module.exports===exports&&(module.exports="ui.router"),function(t,e,n){"use strict";var r=e.isDefined,i=e.isFunction,a=e.isString,o=e.isObject,s=e.isArray,u=e.forEach,l=e.extend,c=e.copy,p=e.toJson;function d(t,e){return l(new(l(function(){},{prototype:t})),e)}function f(t){return u(arguments,function(e){e!==t&&u(e,function(e,n){t.hasOwnProperty(n)||(t[n]=e)})}),t}function h(t){if(Object.keys)return Object.keys(t);var e=[];return u(t,function(t,n){e.push(n)}),e}function g(t,e){if(Array.prototype.indexOf)return t.indexOf(e,Number(arguments[2])||0);var n=t.length>>>0,r=Number(arguments[2])||0;for((r=r<0?Math.ceil(r):Math.floor(r))<0&&(r+=n);r=0||(s.push(i[c]),o[i[c]]=t[i[c]]);return l({},o,e)}function v(t,e){var n={};return u(t,function(t){n[t]=e[t]}),n}function y(t){var e={},n=Array.prototype.concat.apply(Array.prototype,Array.prototype.slice.call(arguments,1));return u(n,function(n){n in t&&(e[n]=t[n])}),e}function b(t){var e={},n=Array.prototype.concat.apply(Array.prototype,Array.prototype.slice.call(arguments,1));for(var r in t)-1==g(n,r)&&(e[r]=t[r]);return e}function w(t,e){var n=s(t),r=n?[]:{};return u(t,function(t,i){e(t,i)&&(r[n?r.length:i]=t)}),r}function $(t,e){var n=s(t)?[]:{};return u(t,function(t,r){n[r]=e(t,r)}),n}function k(t){return t.then(n,function(){})&&t}e.module("ui.router.util",["ng"]),e.module("ui.router.router",["ui.router.util"]),e.module("ui.router.state",["ui.router.router","ui.router.util"]),e.module("ui.router",["ui.router.state"]),e.module("ui.router.compat",["ui.router"]),C.$inject=["$q","$injector"];function C(t,e){var i=1,s=2,c={},p=[],d=c,m=l(t.when(c),{$$promises:c,$$values:c});this.study=function(c){if(!o(c))throw new Error("'invocables' must be an object");var v=h(c||{}),y=[],w=[],$={};function C(t,n){if($[n]!==s){if(w.push(n),$[n]===i)throw w.splice(0,g(w,n)),new Error("Cyclic dependency: "+w.join(" -> "));if($[n]=i,a(t))y.push(n,[function(){return e.get(t)}],p);else{var r=e.annotate(t);u(r,function(t){t!==n&&c.hasOwnProperty(t)&&C(c[t],t)}),y.push(n,t,r)}w.pop(),$[n]=s}}u(c,C),c=w=$=null;function x(t){return o(t)&&t.then&&t.$$promises}return function(i,a,s){if(x(i)&&s===n&&(s=a,a=i,i=null),i){if(!o(i))throw new Error("'locals' must be an object")}else i=d;if(a){if(!x(a))throw new Error("'parent' must be a promise returned by $resolve.resolve()")}else a=m;var c=t.defer(),p=k(c.promise),h=p.$$promises={},g=l({},i),w=1+y.length/3,$=!1;k(p);function C(){--w||($||f(g,a.$$values),p.$$values=g,p.$$promises=p.$$promises||!0,delete p.$$inheritedValues,c.resolve(g))}function T(t){p.$$failure=t,c.reject(t)}if(r(a.$$failure))return T(a.$$failure),p;a.$$inheritedValues&&f(g,b(a.$$inheritedValues,v)),l(h,a.$$promises),a.$$values?($=f(g,b(a.$$values,v)),p.$$inheritedValues=b(a.$$values,v),C()):(a.$$inheritedValues&&(p.$$inheritedValues=b(a.$$inheritedValues,v)),a.then(C,T));for(var S=0,E=y.length;S=0);)w=m(b.id,b.type,b.cfg,"path"),u+=v(b.segment,w.type.pattern.source,w.squash,w.isOptional),p.push(b.segment),c=a.lastIndex;var k=($=t.substring(c)).indexOf("?");if(k>=0){var C=this.sourceSearch=$.substring(k);if($=$.substring(0,k),this.sourcePath=t.substring(0,c+k),C.length>0)for(c=0;i=s.exec(C);)w=m((b=y(i,!0)).id,b.type,b.cfg,"search"),c=a.lastIndex}else this.sourcePath=t,this.sourceSearch="";u+=v($)+(!1===e.strict?"/?":"")+"$",p.push($),this.regexp=new RegExp(u,e.caseInsensitive?"i":n),this.prefix=p[0],this.$$paramNames=g}T.prototype.concat=function(t,e){var n={caseInsensitive:x.caseInsensitive(),strict:x.strictMode(),squash:x.defaultSquashPolicy()};return new T(this.sourcePath+t+this.sourceSearch,l(n,e),this)},T.prototype.toString=function(){return this.source},T.prototype.exec=function(t,e){var n=this.regexp.exec(t);if(!n)return null;e=e||{};var i,a,o,s=this.parameters(),u=s.length,l=this.segments.length-1,c={};if(l!==n.length-1)throw new Error("Unbalanced capture group in route '"+this.source+"'");function p(t){function e(t){return t.split("").reverse().join("")}return $($(e(t).split(/-(?!\\)/),e),function(t){return t.replace(/\\-/g,"-")}).reverse()}var d,f;for(i=0;i=0)throw new Error("State must have a valid name");if(w.hasOwnProperty(n))throw new Error("State '"+n+"' is already defined");var r=-1!==n.indexOf(".")?n.substring(0,n.lastIndexOf(".")):a(e.parent)?e.parent:o(e.parent)&&a(e.parent.name)?e.parent.name:"";if(r&&!w[r])return s=r,u=e.self,C[s]||(C[s]=[]),void C[s].push(u);var s,u;for(var l in S)i(S[l])&&(e[l]=S[l](e,S.$delegates[l]));return w[n]=e,!e[T]&&e.url&&t.when(e.url,["$match","$stateParams",function(t,n){b.$current.navigable==e&&function(t,e,n){if(!n){n=[];for(var r in t)n.push(r)}for(var i=0;i=I;r--)(o=h[r]).self.onExit&&S.invoke(o.self.onExit,o.self,o.locals.globals),o.locals=null;for(r=I;r-1)){if(!function(t){for(var e=t.split("."),n=b.$current.name.split("."),r=0,i=e.length;r2?u.enter(t,null,n).then(r):u.enter(t,null,n,r)},leave:function(t,n){e.version.minor>2?u.leave(t).then(n):u.leave(t,n)}};if(s){var r=s&&s(n,t);return{enter:function(t,e,n){r.enter(t,null,e),n()},leave:function(t,e){r.leave(t),e()}}}return{enter:function(t,e,n){e.after(t),n()},leave:function(t,e){t.remove(),e()}}}(c,n);o.inheritedData("$uiView");n.$on("$stateChangeSuccess",function(){y(!1)}),y(!0);function y(s){var u,y=M(n,c,o,i),b=y&&t.$current&&t.$current.locals[y];if(s||b!==h){u=n.$new(),h=t.$current.locals[y],u.$emit("$viewContentLoading",y);var w=l(u,function(t){var i=a.defer(),s=a.defer(),u={$animEnter:i.promise,$animLeave:s.promise,$$animLeave:s};t.data("$uiViewAnim",u),v.enter(t,o,function(){i.resolve(),f&&f.$emit("$viewContentAnimationEnded"),(e.isDefined(m)&&!m||n.$eval(m))&&r(t)}),function(){if(p&&(p.remove(),p=null),f&&(f.$destroy(),f=null),d){var t=d.data("$uiViewAnim");v.leave(d,function(){t.$$animLeave.resolve(),p=null}),p=d,d=null}}()});d=w,(f=u).$emit("$viewContentLoaded",y),f.$eval(g)}}}}}}O.$inject=["$compile","$controller","$state","$interpolate"];function O(t,n,r,a){return{restrict:"ECA",priority:-400,compile:function(o){var s=o.html();return o.empty?o.empty():o[0].innerHTML=null,function(o,u,l){var c=r.$current,p=M(o,l,u,a),d=c&&c.locals[p];if(!d)return u.html(s),void t(u.contents())(o);u.data("$uiView",{name:p,state:d.$$state}),u.html(d.$template?d.$template:s);var f=e.extend({},d);o[d.$$resolveAs]=f;var h=t(u.contents());if(d.$$controller){d.$scope=o,d.$element=u;var g=n(d.$$controller,d);d.$$controllerAs&&(o[d.$$controllerAs]=g,o[d.$$controllerAs][d.$$resolveAs]=f),i(g.$onInit)&&g.$onInit(),u.data("$ngControllerController",g),u.children().data("$ngControllerController",g)}h(o)}}}}function M(t,e,n,r){var i=r(e.uiView||e.name||"")(t),a=n.inheritedData("$uiView");return i.indexOf("@")>=0?i:i+"@"+(a?a.state.name:"")}e.module("ui.router.state").directive("uiView",I),e.module("ui.router.state").directive("uiView",O);function P(t,e){var n,r=t.match(/^\s*({[^}]*})\s*$/);if(r&&(t=e+"("+r[1]+")"),!(n=t.replace(/\n/g," ").match(/^([^(]+?)\s*(\((.*)\))?$/))||4!==n.length)throw new Error("Invalid state ref '"+t+"'");return{state:n[1],paramExpr:n[3]||null}}function _(t){var e=t.parent().inheritedData("$uiView");if(e&&e.state&&e.state.name)return e.state}function R(t){var e="[object SVGAnimatedString]"===Object.prototype.toString.call(t.prop("href")),n="FORM"===t[0].nodeName;return{attr:n?"action":e?"xlink:href":"href",isAnchor:"A"===t.prop("tagName").toUpperCase(),clickable:!n}}function L(t,e,n,r,i){return function(a){var o=a.which||a.button,s=i();if(!(o>1||a.ctrlKey||a.metaKey||a.shiftKey||t.attr("target"))){var u=n(function(){e.go(s.state,s.params,s.options)});a.preventDefault();var l=r.isAnchor&&!s.href?1:0;a.preventDefault=function(){l--<=0&&n.cancel(u)}}}}N.$inject=["$state","$timeout"];function N(t,n){return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(r,i,a,o){var s,u=P(a.uiSref,t.current.name),c={state:u.state,href:null,params:null},p=R(i),d=o[1]||o[0],f=null;c.options=l((h=i,g=t,{relative:_(h)||g.$current,inherit:!0}),a.uiSrefOpts?r.$eval(a.uiSrefOpts):{});var h,g,m=function(n){n&&(c.params=e.copy(n)),c.href=t.href(u.state,c.params,c.options),f&&f(),d&&(f=d.$$addStateInfo(u.state,c.params)),null!==c.href&&a.$set(p.attr,c.href)};u.paramExpr&&(r.$watch(u.paramExpr,function(t){t!==c.params&&m(t)},!0),c.params=e.copy(r.$eval(u.paramExpr))),m(),p.clickable&&(s=L(i,t,n,p,function(){return c}),i[i.on?"on":"bind"]("click",s),r.$on("$destroy",function(){i[i.off?"off":"unbind"]("click",s)}))}}}U.$inject=["$state","$timeout"];function U(t,e){return{restrict:"A",require:["?^uiSrefActive","?^uiSrefActiveEq"],link:function(n,r,i,a){var o,s=R(r),u=a[1]||a[0],l="["+[i.uiState,i.uiStateParams||null,i.uiStateOpts||null].map(function(t){return t||"null"}).join(", ")+"]",c={state:null,params:null,options:null,href:null},p=null;function d(e){c.state=e[0],c.params=e[1],c.options=e[2],c.href=t.href(c.state,c.params,c.options),p&&p(),u&&(p=u.$$addStateInfo(c.state,c.params)),c.href&&i.$set(s.attr,c.href)}n.$watch(l,d,!0),d(n.$eval(l)),s.clickable&&(o=L(r,t,e,s,function(){return c}),r[r.on?"on":"bind"]("click",o),n.$on("$destroy",function(){r[r.off?"off":"unbind"]("click",o)}))}}}q.$inject=["$state","$stateParams","$interpolate"];function q(t,e,n){return{restrict:"A",controller:["$scope","$element","$attrs","$timeout",function(e,r,i,s){var l,c,d=[],f={};l=n(i.uiSrefActiveEq||"",!1)(e);try{c=e.$eval(i.uiSrefActive)}catch(t){}c=c||n(i.uiSrefActive||"",!1)(e),o(c)&&u(c,function(n,r){if(a(n)){var i=P(n,t.current.name);h(i.state,e.$eval(i.paramExpr),r)}}),this.$$addStateInfo=function(t,e){if(!(o(c)&&d.length>0)){var n=h(t,e,c);return g(),n}},e.$on("$stateChangeSuccess",g);function h(n,i,s){var u=t.get(n,_(r)),l=function(t,n){if(!a(t))throw new Error("state should be a string");if(o(n))return t+p(n);if(n=e.$eval(n),o(n))return t+p(n);return t}(n,i),c={state:u||{name:n},params:i,hash:l};return d.push(c),f[l]=s,function(){var t=d.indexOf(c);-1!==t&&d.splice(t,1)}}function g(){for(var e=0;e0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,r.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,r.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":i(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=o})},{select:5}],8:[function(t,e,n){!function(r,i){if(void 0!==n)i(e,t("./clipboard-action"),t("tiny-emitter"),t("good-listener"));else{var a={exports:{}};i(a,r.clipboardAction,r.tinyEmitter,r.goodListener),r.clipboard=a.exports}}(this,function(t,e,n,r){"use strict";var i=s(e),a=s(n),o=s(r);function s(t){return t&&t.__esModule?t:{default:t}}var u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};var l=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===u(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,o.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new i.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return p("action",t)}},{key:"defaultTarget",value:function(t){var e=p("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return p("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}();function p(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}t.exports=c})},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)}),function(t,e){if("function"==typeof define&&define.amd)define([],e);else if("object"==typeof module&&module.exports)module.exports=e();else{var n=e();n._onReady(n.init),t.Duo=n}}(this,function(){var t,e,n,r,i,a=/^(?:AUTH|ENROLL)+\|[A-Za-z0-9\+\/=]+\|[A-Za-z0-9\+\/=]+$/,o=/^ERR\|[\w\s\.\(\)]+$/,s=/^DUO_OPEN_WINDOW\|/,u=["duo.com","duosecurity.com","duomobile.s3-us-west-1.amazonaws.com"],l="duo_iframe",c="",p="sig_response";function d(t,e){throw new Error("Duo Web SDK error: "+t+(e?"\nSee "+e+" for more information":""))}function f(t,e){return"dataset"in t?t.dataset[e]:t.getAttribute("data-"+(n=e,n.replace(/([a-z])([A-Z])/,"$1-$2").toLowerCase()));var n}function h(t,e,n,r){"addEventListener"in window?t.addEventListener(e,r,!1):t.attachEvent(n,r)}function g(t,e,n,r){"removeEventListener"in window?t.removeEventListener(e,r,!1):t.detachEvent(n,r)}function m(t){h(document,"DOMContentLoaded","onreadystatechange",t)}function v(t){g(document,"DOMContentLoaded","onreadystatechange",t)}function y(t){if(t){0===t.indexOf("ERR|")&&d(t.split("|")[1]),-1!==t.indexOf(":")&&2===t.split(":").length||d("Duo was given a bad token. This might indicate a configuration problem with one of Duo's client libraries.","https://www.duosecurity.com/docs/duoweb#first-steps");var r=t.split(":");return t,e=r[0],n=r[1],{sigRequest:t,duoSig:r[0],appSig:r[1]}}}function b(){if(!(r=document.getElementById(l)))throw new Error('This page does not contain an iframe for Duo to use.Add an element like to this page. See https://www.duosecurity.com/docs/duoweb#3.-show-the-iframe for more information.');C(),v(b)}function w(e){return Boolean(e.origin==="https://"+t&&"string"==typeof e.data&&(e.data.match(a)||e.data.match(o)||e.data.match(s)))}function $(e){e&&(e.host&&(t=e.host),e.sig_request&&y(e.sig_request),e.post_action&&(c=e.post_action),e.post_argument&&(p=e.post_argument),e.iframe&&(e.iframe.tagName?r=e.iframe:"string"==typeof e.iframe&&(l=e.iframe)),"function"==typeof e.submit_callback&&(i=e.submit_callback)),(r=document.getElementById(l))?C():m(b),v($)}function k(t){if(w(t))if(t.data.match(s)){var e=t.data.substring("DUO_OPEN_WINDOW|".length);(function(t){if(!t)return!1;var e=document.createElement("a");{if(e.href=t,"duotrustedendpoints:"===e.protocol)return!0;if("https:"!==e.protocol)return!1}for(var n=0;n15?(n=Date.now(),o(t)):(e.push(t),1===e.length&&a.setAttribute("a",r=!r))}}i.nextTick=i.setImmediate}(),i.isNodejs="undefined"!=typeof process&&process.versions&&process.versions.node,i.isArray=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},i.isArrayBuffer=function(t){return"undefined"!=typeof ArrayBuffer&&t instanceof ArrayBuffer},i.isArrayBufferView=function(t){return t&&i.isArrayBuffer(t.buffer)&&void 0!==t.byteLength};function a(t){if(8!==t&&16!==t&&24!==t&&32!==t)throw new Error("Only 8, 16, 24, or 32 bits supported: "+t)}i.ByteBuffer=o;function o(t){if(this.data="",this.read=0,"string"==typeof t)this.data=t;else if(i.isArrayBuffer(t)||i.isArrayBufferView(t)){var e=new Uint8Array(t);try{this.data=String.fromCharCode.apply(null,e)}catch(t){for(var n=0;n4096&&(this.data.substr(0,1),this._constructedStringLength=0)},i.ByteStringBuffer.prototype.length=function(){return this.data.length-this.read},i.ByteStringBuffer.prototype.isEmpty=function(){return this.length()<=0},i.ByteStringBuffer.prototype.putByte=function(t){return this.putBytes(String.fromCharCode(t))},i.ByteStringBuffer.prototype.fillWithByte=function(t,e){t=String.fromCharCode(t);for(var n=this.data;e>0;)1&e&&(n+=t),(e>>>=1)>0&&(t+=t);return this.data=n,this._optimizeConstructedString(e),this},i.ByteStringBuffer.prototype.putBytes=function(t){return this.data+=t,this._optimizeConstructedString(t.length),this},i.ByteStringBuffer.prototype.putString=function(t){return this.putBytes(i.encodeUtf8(t))},i.ByteStringBuffer.prototype.putInt16=function(t){return this.putBytes(String.fromCharCode(t>>8&255)+String.fromCharCode(255&t))},i.ByteStringBuffer.prototype.putInt24=function(t){return this.putBytes(String.fromCharCode(t>>16&255)+String.fromCharCode(t>>8&255)+String.fromCharCode(255&t))},i.ByteStringBuffer.prototype.putInt32=function(t){return this.putBytes(String.fromCharCode(t>>24&255)+String.fromCharCode(t>>16&255)+String.fromCharCode(t>>8&255)+String.fromCharCode(255&t))},i.ByteStringBuffer.prototype.putInt16Le=function(t){return this.putBytes(String.fromCharCode(255&t)+String.fromCharCode(t>>8&255))},i.ByteStringBuffer.prototype.putInt24Le=function(t){return this.putBytes(String.fromCharCode(255&t)+String.fromCharCode(t>>8&255)+String.fromCharCode(t>>16&255))},i.ByteStringBuffer.prototype.putInt32Le=function(t){return this.putBytes(String.fromCharCode(255&t)+String.fromCharCode(t>>8&255)+String.fromCharCode(t>>16&255)+String.fromCharCode(t>>24&255))},i.ByteStringBuffer.prototype.putInt=function(t,e){a(e);var n="";do{e-=8,n+=String.fromCharCode(t>>e&255)}while(e>0);return this.putBytes(n)},i.ByteStringBuffer.prototype.putSignedInt=function(t,e){return t<0&&(t+=2<0);return e},i.ByteStringBuffer.prototype.getSignedInt=function(t){var e=this.getInt(t),n=2<=n&&(e-=n<<1),e},i.ByteStringBuffer.prototype.getBytes=function(t){var e;return t?(t=Math.min(this.length(),t),e=this.data.slice(this.read,this.read+t),this.read+=t):0===t?e="":(e=0===this.read?this.data:this.data.slice(this.read),this.clear()),e},i.ByteStringBuffer.prototype.bytes=function(t){return void 0===t?this.data.slice(this.read):this.data.slice(this.read,this.read+t)},i.ByteStringBuffer.prototype.at=function(t){return this.data.charCodeAt(this.read+t)},i.ByteStringBuffer.prototype.setAt=function(t,e){return this.data=this.data.substr(0,this.read+t)+String.fromCharCode(e)+this.data.substr(this.read+t+1),this},i.ByteStringBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)},i.ByteStringBuffer.prototype.copy=function(){var t=i.createBuffer(this.data);return t.read=this.read,t},i.ByteStringBuffer.prototype.compact=function(){return this.read>0&&(this.data=this.data.slice(this.read),this.read=0),this},i.ByteStringBuffer.prototype.clear=function(){return this.data="",this.read=0,this},i.ByteStringBuffer.prototype.truncate=function(t){var e=Math.max(0,this.length()-t);return this.data=this.data.substr(this.read,e),this.read=0,this},i.ByteStringBuffer.prototype.toHex=function(){for(var t="",e=this.read;e=t)return this;e=Math.max(e||this.growSize,t);var n=new Uint8Array(this.data.buffer,this.data.byteOffset,this.data.byteLength),r=new Uint8Array(this.length()+e);return r.set(n),this.data=new DataView(r.buffer),this},i.DataBuffer.prototype.putByte=function(t){return this.accommodate(1),this.data.setUint8(this.write++,t),this},i.DataBuffer.prototype.fillWithByte=function(t,e){this.accommodate(e);for(var n=0;n>8&65535),this.data.setInt8(this.write,t>>16&255),this.write+=3,this},i.DataBuffer.prototype.putInt32=function(t){return this.accommodate(4),this.data.setInt32(this.write,t),this.write+=4,this},i.DataBuffer.prototype.putInt16Le=function(t){return this.accommodate(2),this.data.setInt16(this.write,t,!0),this.write+=2,this},i.DataBuffer.prototype.putInt24Le=function(t){return this.accommodate(3),this.data.setInt8(this.write,t>>16&255),this.data.setInt16(this.write,t>>8&65535,!0),this.write+=3,this},i.DataBuffer.prototype.putInt32Le=function(t){return this.accommodate(4),this.data.setInt32(this.write,t,!0),this.write+=4,this},i.DataBuffer.prototype.putInt=function(t,e){a(e),this.accommodate(e/8);do{e-=8,this.data.setInt8(this.write++,t>>e&255)}while(e>0);return this},i.DataBuffer.prototype.putSignedInt=function(t,e){return a(e),this.accommodate(e/8),t<0&&(t+=2<0);return e},i.DataBuffer.prototype.getSignedInt=function(t){var e=this.getInt(t),n=2<=n&&(e-=n<<1),e},i.DataBuffer.prototype.getBytes=function(t){var e;return t?(t=Math.min(this.length(),t),e=this.data.slice(this.read,this.read+t),this.read+=t):0===t?e="":(e=0===this.read?this.data:this.data.slice(this.read),this.clear()),e},i.DataBuffer.prototype.bytes=function(t){return void 0===t?this.data.slice(this.read):this.data.slice(this.read,this.read+t)},i.DataBuffer.prototype.at=function(t){return this.data.getUint8(this.read+t)},i.DataBuffer.prototype.setAt=function(t,e){return this.data.setUint8(t,e),this},i.DataBuffer.prototype.last=function(){return this.data.getUint8(this.write-1)},i.DataBuffer.prototype.copy=function(){return new i.DataBuffer(this)},i.DataBuffer.prototype.compact=function(){if(this.read>0){var t=new Uint8Array(this.data.buffer,this.read),e=new Uint8Array(t.byteLength);e.set(t),this.data=new DataView(e),this.write-=this.read,this.read=0}return this},i.DataBuffer.prototype.clear=function(){return this.data=new DataView(new ArrayBuffer(0)),this.read=this.write=0,this},i.DataBuffer.prototype.truncate=function(t){return this.write=Math.max(0,this.length()-t),this.read=Math.min(this.read,this.write),this},i.DataBuffer.prototype.toHex=function(){for(var t="",e=this.read;e0;)1&e&&(n+=t),(e>>>=1)>0&&(t+=t);return n},i.xorBytes=function(t,e,n){for(var r="",i="",a="",o=0,s=0;n>0;--n,++o)i=t.charCodeAt(o)^e.charCodeAt(o),s>=10&&(r+=a,a="",s=0),a+=String.fromCharCode(i),++s;return r+=a},i.hexToBytes=function(t){var e="",n=0;for(!0&t.length&&(n=1,e+=String.fromCharCode(parseInt(t[0],16)));n>24&255)+String.fromCharCode(t>>16&255)+String.fromCharCode(t>>8&255)+String.fromCharCode(255&t)};var s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",u=[62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];i.encode64=function(t,e){for(var n,r,i,a="",o="",u=0;u>2),a+=s.charAt((3&n)<<4|r>>4),isNaN(r)?a+="==":(a+=s.charAt((15&r)<<2|i>>6),a+=isNaN(i)?"=":s.charAt(63&i)),e&&a.length>e&&(o+=a.substr(0,e)+"\r\n",a=a.substr(e));return o+=a},i.decode64=function(t){t=t.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var e,n,r,i,a="",o=0;o>4),64!==r&&(a+=String.fromCharCode((15&n)<<4|r>>2),64!==i&&(a+=String.fromCharCode((3&r)<<6|i)));return a},i.encodeUtf8=function(t){return unescape(encodeURIComponent(t))},i.decodeUtf8=function(t){return decodeURIComponent(escape(t))},i.binary={raw:{},hex:{},base64:{}},i.binary.raw.encode=function(t){return String.fromCharCode.apply(null,t)},i.binary.raw.decode=function(t,e,n){var r=e;r||(r=new Uint8Array(t.length));for(var i=n=n||0,a=0;a>2),a+=s.charAt((3&n)<<4|r>>4),isNaN(r)?a+="==":(a+=s.charAt((15&r)<<2|i>>6),a+=isNaN(i)?"=":s.charAt(63&i)),e&&a.length>e&&(o+=a.substr(0,e)+"\r\n",a=a.substr(e));return o+=a},i.binary.base64.decode=function(t,e,n){var r=e;r||(r=new Uint8Array(3*Math.ceil(t.length/4))),t=t.replace(/[^A-Za-z0-9\+\/\=]/g,"");for(var i,a,o,s,l=0,c=n=n||0;l>4,64!==o&&(r[c++]=(15&a)<<4|o>>2,64!==s&&(r[c++]=(3&o)<<6|s));return e?c-n:r.subarray(0,c)},i.text={utf8:{},utf16:{}},i.text.utf8.encode=function(t,e,n){t=i.encodeUtf8(t);var r=e;r||(r=new Uint8Array(t.length));for(var a=n=n||0,o=0;o0?(i=n[r].substring(0,o),a=n[r].substring(o+1)):(i=n[r],a=null),i in e||(e[i]=[]),i in Object.prototype||null===a||e[i].push(unescape(a))}return e};return void 0===t?(null===m&&(m="undefined"!=typeof window&&window.location&&window.location.search?n(window.location.search.substring(1)):{}),e=m):e=n(t),e},i.parseFragment=function(t){var e=t,n="",r=t.indexOf("?");r>0&&(e=t.substring(0,r),n=t.substring(r+1));var a=e.split("/");a.length>0&&""===a[0]&&a.shift();return{pathString:e,queryString:n,path:a,query:""===n?{}:i.getQueryVariables(n)}},i.makeRequest=function(t){var e=i.parseFragment(t),n={path:e.pathString,query:e.queryString,getPath:function(t){return void 0===t?e.path:e.path[t]},getQuery:function(t,n){var r;return void 0===t?r=e.query:(r=e.query[t])&&void 0!==n&&(r=r[n]),r},getQueryLast:function(t,e){var r=n.getQuery(t);return r?r[r.length-1]:e}};return n},i.makeLink=function(t,e,n){t=jQuery.isArray(t)?t.join("/"):t;var r=jQuery.param(e||{});return n=n||"",t+(r.length>0?"?"+r:"")+(n.length>0?"#"+n:"")},i.setPath=function(t,e,n){if("object"==typeof t&&null!==t)for(var r=0,i=e.length;r0&&a.push(n),o=r.lastIndex;var s=e[0][1];switch(s){case"s":case"o":i");break;case"%":a.push("%");break;default:a.push("<%"+s+"?>")}}return a.push(t.substring(o)),a.join("")},i.formatNumber=function(t,e,n,r){var i=t,a=isNaN(e=Math.abs(e))?2:e,o=void 0===n?",":n,s=void 0===r?".":r,u=i<0?"-":"",l=parseInt(i=Math.abs(+i||0).toFixed(a),10)+"",c=l.length>3?l.length%3:0;return u+(c?l.substr(0,c)+s:"")+l.substr(c).replace(/(\d{3})(?=\d)/g,"$1"+s)+(a?o+Math.abs(i-l).toFixed(a).slice(2):"")},i.formatSize=function(t){return t=t>=1073741824?i.formatNumber(t/1073741824,2,".","")+" GiB":t>=1048576?i.formatNumber(t/1048576,2,".","")+" MiB":t>=1024?i.formatNumber(t/1024,0)+" KiB":i.formatNumber(t,0)+" bytes"},i.bytesFromIP=function(t){return-1!==t.indexOf(".")?i.bytesFromIPv4(t):-1!==t.indexOf(":")?i.bytesFromIPv6(t):null},i.bytesFromIPv4=function(t){if(4!==(t=t.split(".")).length)return null;for(var e=i.createBuffer(),n=0;nn[r].end-n[r].start&&(r=n.length-1)):n.push({start:u,end:u})}e.push(o)}if(n.length>0){var l=n[r];l.end-l.start>0&&(e.splice(l.start,l.end-l.start+1,""),0===l.start&&e.unshift(""),7===l.end&&e.push(""))}return e.join(":")},i.estimateCores=function(t,e){if("function"==typeof t&&(e=t,t={}),t=t||{},"cores"in i&&!t.update)return e(null,i.cores);if("undefined"!=typeof navigator&&"hardwareConcurrency"in navigator&&navigator.hardwareConcurrency>0)return i.cores=navigator.hardwareConcurrency,e(null,i.cores);if("undefined"==typeof Worker)return i.cores=1,e(null,i.cores);if("undefined"==typeof Blob)return i.cores=2,e(null,i.cores);var n=URL.createObjectURL(new Blob(["(",function(){self.addEventListener("message",function(t){for(var e=Date.now(),n=e+4;Date.now()s.st&&i.sti.st&&s.ste.blockLength&&(e.start(),e.update(o.bytes()),o=e.digest()),n=r.util.createBuffer(),i=r.util.createBuffer(),l=o.length();for(u=0;u>>=2;for(i=0;i>8^255&p^99,a[g]=p,o[p]=g,f=(d=t[p])<<24^p<<16^p<<8^p^d,h=((n=t[g])^(r=t[n])^(i=t[r]))<<24^(g^i)<<16^(g^r^i)<<8^g^n^i;for(var v=0;v<4;++v)u[v][g]=f,l[v][p]=h,f=f<<24|f>>>8,h=h<<24|h>>>8;0===g?g=m=1:(g=n^t[t[t[n^i]]],m^=t[t[m]])}}function f(t,e){for(var n,r=t.slice(0),i=1,o=r.length,u=p*(o+6+1),c=o;c>>16&255]<<24^a[n>>>8&255]<<16^a[255&n]<<8^a[n>>>24]^s[i]<<24,i++):o>6&&c%o==4&&(n=a[n>>>24]<<24^a[n>>>16&255]<<16^a[n>>>8&255]<<8^a[255&n]),r[c]=r[c-o]^n;if(e){for(var d,f=l[0],h=l[1],g=l[2],m=l[3],v=r.slice(0),y=(c=0,(u=r.length)-p);c>>24]]^h[a[d>>>16&255]]^g[a[d>>>8&255]]^m[a[255&d]];r=v}return r}function h(t,e,n,r){var i,s,c,p,d,f=t.length/4-1;r?(i=l[0],s=l[1],c=l[2],p=l[3],d=o):(i=u[0],s=u[1],c=u[2],p=u[3],d=a);var h,g,m,v,y,b,w;h=e[0]^t[0],g=e[r?3:1]^t[1],m=e[2]^t[2],v=e[r?1:3]^t[3];for(var $=3,k=1;k>>24]^s[g>>>16&255]^c[m>>>8&255]^p[255&v]^t[++$],b=i[g>>>24]^s[m>>>16&255]^c[v>>>8&255]^p[255&h]^t[++$],w=i[m>>>24]^s[v>>>16&255]^c[h>>>8&255]^p[255&g]^t[++$],v=i[v>>>24]^s[h>>>16&255]^c[g>>>8&255]^p[255&m]^t[++$],h=y,g=b,m=w;n[0]=d[h>>>24]<<24^d[g>>>16&255]<<16^d[m>>>8&255]<<8^d[255&v]^t[++$],n[r?3:1]=d[g>>>24]<<24^d[m>>>16&255]<<16^d[v>>>8&255]<<8^d[255&h]^t[++$],n[2]=d[m>>>24]<<24^d[v>>>16&255]<<16^d[h>>>8&255]<<8^d[255&g]^t[++$],n[r?1:3]=d[v>>>24]<<24^d[h>>>16&255]<<16^d[g>>>8&255]<<8^d[255&m]^t[++$]}function g(t){var e,n="AES-"+((t=t||{}).mode||"CBC").toUpperCase(),i=(e=t.decrypt?r.cipher.createDecipher(n,t.key):r.cipher.createCipher(n,t.key)).start;return e.start=function(t,n){var a=null;n instanceof r.util.ByteBuffer&&(a=n,n={}),(n=n||{}).output=a,n.iv=t,i.call(e,n)},e}},function(t,e,n){var r=n(0);r.pki=r.pki||{};var i=t.exports=r.pki.oids=r.oids=r.oids||{};function a(t,e){i[t]=e,i[e]=t}function o(t,e){i[t]=e}a("1.2.840.113549.1.1.1","rsaEncryption"),a("1.2.840.113549.1.1.4","md5WithRSAEncryption"),a("1.2.840.113549.1.1.5","sha1WithRSAEncryption"),a("1.2.840.113549.1.1.7","RSAES-OAEP"),a("1.2.840.113549.1.1.8","mgf1"),a("1.2.840.113549.1.1.9","pSpecified"),a("1.2.840.113549.1.1.10","RSASSA-PSS"),a("1.2.840.113549.1.1.11","sha256WithRSAEncryption"),a("1.2.840.113549.1.1.12","sha384WithRSAEncryption"),a("1.2.840.113549.1.1.13","sha512WithRSAEncryption"),a("1.3.14.3.2.7","desCBC"),a("1.3.14.3.2.26","sha1"),a("2.16.840.1.101.3.4.2.1","sha256"),a("2.16.840.1.101.3.4.2.2","sha384"),a("2.16.840.1.101.3.4.2.3","sha512"),a("1.2.840.113549.2.5","md5"),a("1.2.840.113549.1.7.1","data"),a("1.2.840.113549.1.7.2","signedData"),a("1.2.840.113549.1.7.3","envelopedData"),a("1.2.840.113549.1.7.4","signedAndEnvelopedData"),a("1.2.840.113549.1.7.5","digestedData"),a("1.2.840.113549.1.7.6","encryptedData"),a("1.2.840.113549.1.9.1","emailAddress"),a("1.2.840.113549.1.9.2","unstructuredName"),a("1.2.840.113549.1.9.3","contentType"),a("1.2.840.113549.1.9.4","messageDigest"),a("1.2.840.113549.1.9.5","signingTime"),a("1.2.840.113549.1.9.6","counterSignature"),a("1.2.840.113549.1.9.7","challengePassword"),a("1.2.840.113549.1.9.8","unstructuredAddress"),a("1.2.840.113549.1.9.14","extensionRequest"),a("1.2.840.113549.1.9.20","friendlyName"),a("1.2.840.113549.1.9.21","localKeyId"),a("1.2.840.113549.1.9.22.1","x509Certificate"),a("1.2.840.113549.1.12.10.1.1","keyBag"),a("1.2.840.113549.1.12.10.1.2","pkcs8ShroudedKeyBag"),a("1.2.840.113549.1.12.10.1.3","certBag"),a("1.2.840.113549.1.12.10.1.4","crlBag"),a("1.2.840.113549.1.12.10.1.5","secretBag"),a("1.2.840.113549.1.12.10.1.6","safeContentsBag"),a("1.2.840.113549.1.5.13","pkcs5PBES2"),a("1.2.840.113549.1.5.12","pkcs5PBKDF2"),a("1.2.840.113549.1.12.1.1","pbeWithSHAAnd128BitRC4"),a("1.2.840.113549.1.12.1.2","pbeWithSHAAnd40BitRC4"),a("1.2.840.113549.1.12.1.3","pbeWithSHAAnd3-KeyTripleDES-CBC"),a("1.2.840.113549.1.12.1.4","pbeWithSHAAnd2-KeyTripleDES-CBC"),a("1.2.840.113549.1.12.1.5","pbeWithSHAAnd128BitRC2-CBC"),a("1.2.840.113549.1.12.1.6","pbewithSHAAnd40BitRC2-CBC"),a("1.2.840.113549.2.7","hmacWithSHA1"),a("1.2.840.113549.2.8","hmacWithSHA224"),a("1.2.840.113549.2.9","hmacWithSHA256"),a("1.2.840.113549.2.10","hmacWithSHA384"),a("1.2.840.113549.2.11","hmacWithSHA512"),a("1.2.840.113549.3.7","des-EDE3-CBC"),a("2.16.840.1.101.3.4.1.2","aes128-CBC"),a("2.16.840.1.101.3.4.1.22","aes192-CBC"),a("2.16.840.1.101.3.4.1.42","aes256-CBC"),a("2.5.4.3","commonName"),a("2.5.4.5","serialName"),a("2.5.4.6","countryName"),a("2.5.4.7","localityName"),a("2.5.4.8","stateOrProvinceName"),a("2.5.4.10","organizationName"),a("2.5.4.11","organizationalUnitName"),a("2.16.840.1.113730.1.1","nsCertType"),o("2.5.29.1","authorityKeyIdentifier"),o("2.5.29.2","keyAttributes"),o("2.5.29.3","certificatePolicies"),o("2.5.29.4","keyUsageRestriction"),o("2.5.29.5","policyMapping"),o("2.5.29.6","subtreesConstraint"),o("2.5.29.7","subjectAltName"),o("2.5.29.8","issuerAltName"),o("2.5.29.9","subjectDirectoryAttributes"),o("2.5.29.10","basicConstraints"),o("2.5.29.11","nameConstraints"),o("2.5.29.12","policyConstraints"),o("2.5.29.13","basicConstraints"),a("2.5.29.14","subjectKeyIdentifier"),a("2.5.29.15","keyUsage"),o("2.5.29.16","privateKeyUsagePeriod"),a("2.5.29.17","subjectAltName"),a("2.5.29.18","issuerAltName"),a("2.5.29.19","basicConstraints"),o("2.5.29.20","cRLNumber"),o("2.5.29.21","cRLReason"),o("2.5.29.22","expirationDate"),o("2.5.29.23","instructionCode"),o("2.5.29.24","invalidityDate"),o("2.5.29.25","cRLDistributionPoints"),o("2.5.29.26","issuingDistributionPoint"),o("2.5.29.27","deltaCRLIndicator"),o("2.5.29.28","issuingDistributionPoint"),o("2.5.29.29","certificateIssuer"),o("2.5.29.30","nameConstraints"),a("2.5.29.31","cRLDistributionPoints"),a("2.5.29.32","certificatePolicies"),o("2.5.29.33","policyMappings"),o("2.5.29.34","policyConstraints"),a("2.5.29.35","authorityKeyIdentifier"),o("2.5.29.36","policyConstraints"),a("2.5.29.37","extKeyUsage"),o("2.5.29.46","freshestCRL"),o("2.5.29.54","inhibitAnyPolicy"),a("1.3.6.1.4.1.11129.2.4.2","timestampList"),a("1.3.6.1.5.5.7.1.1","authorityInfoAccess"),a("1.3.6.1.5.5.7.3.1","serverAuth"),a("1.3.6.1.5.5.7.3.2","clientAuth"),a("1.3.6.1.5.5.7.3.3","codeSigning"),a("1.3.6.1.5.5.7.3.4","emailProtection"),a("1.3.6.1.5.5.7.3.8","timeStamping")},function(t,e,n){var r=n(0);t.exports=r.jsbn=r.jsbn||{};var i;function a(t,e,n){this.data=[],null!=t&&("number"==typeof t?this.fromNumber(t,e,n):null==e&&"string"!=typeof t?this.fromString(t,256):this.fromString(t,e))}r.jsbn.BigInteger=a;function o(){return new a(null)}function s(t,e,n,r,i,a){for(var o=16383&e,s=e>>14;--a>=0;){var u=16383&this.data[t],l=this.data[t++]>>14,c=s*u+l*o;i=((u=o*u+((16383&c)<<14)+n.data[r]+i)>>28)+(c>>14)+s*l,n.data[r++]=268435455&u}return i}"undefined"==typeof navigator?(a.prototype.am=s,i=28):"Microsoft Internet Explorer"==navigator.appName?(a.prototype.am=function(t,e,n,r,i,a){for(var o=32767&e,s=e>>15;--a>=0;){var u=32767&this.data[t],l=this.data[t++]>>15,c=s*u+l*o;i=((u=o*u+((32767&c)<<15)+n.data[r]+(1073741823&i))>>>30)+(c>>>15)+s*l+(i>>>30),n.data[r++]=1073741823&u}return i},i=30):"Netscape"!=navigator.appName?(a.prototype.am=function(t,e,n,r,i,a){for(;--a>=0;){var o=e*this.data[t++]+n.data[r]+i;i=Math.floor(o/67108864),n.data[r++]=67108863&o}return i},i=26):(a.prototype.am=s,i=28),a.prototype.DB=i,a.prototype.DM=(1<>>16)&&(t=e,n+=16),0!=(e=t>>8)&&(t=e,n+=8),0!=(e=t>>4)&&(t=e,n+=4),0!=(e=t>>2)&&(t=e,n+=2),0!=(e=t>>1)&&(t=e,n+=1),n}function m(t){this.m=t}m.prototype.convert=function(t){return t.s<0||t.compareTo(this.m)>=0?t.mod(this.m):t},m.prototype.revert=function(t){return t},m.prototype.reduce=function(t){t.divRemTo(this.m,null,t)},m.prototype.mulTo=function(t,e,n){t.multiplyTo(e,n),this.reduce(n)},m.prototype.sqrTo=function(t,e){t.squareTo(e),this.reduce(e)};function v(t){this.m=t,this.mp=t.invDigit(),this.mpl=32767&this.mp,this.mph=this.mp>>15,this.um=(1<0&&this.m.subTo(e,e),e},v.prototype.revert=function(t){var e=o();return t.copyTo(e),this.reduce(e),e},v.prototype.reduce=function(t){for(;t.t<=this.mt2;)t.data[t.t++]=0;for(var e=0;e>15)*this.mpl&this.um)<<15)&t.DM;for(n=e+this.m.t,t.data[n]+=this.m.am(0,r,t,e,0,this.m.t);t.data[n]>=t.DV;)t.data[n]-=t.DV,t.data[++n]++}t.clamp(),t.drShiftTo(this.m.t,t),t.compareTo(this.m)>=0&&t.subTo(this.m,t)},v.prototype.mulTo=function(t,e,n){t.multiplyTo(e,n),this.reduce(n)},v.prototype.sqrTo=function(t,e){t.squareTo(e),this.reduce(e)};a.prototype.copyTo=function(t){for(var e=this.t-1;e>=0;--e)t.data[e]=this.data[e];t.t=this.t,t.s=this.s},a.prototype.fromInt=function(t){this.t=1,this.s=t<0?-1:0,t>0?this.data[0]=t:t<-1?this.data[0]=t+this.DV:this.t=0},a.prototype.fromString=function(t,e){var n;if(16==e)n=4;else if(8==e)n=3;else if(256==e)n=8;else if(2==e)n=1;else if(32==e)n=5;else{if(4!=e)return void this.fromRadix(t,e);n=2}this.t=0,this.s=0;for(var r=t.length,i=!1,o=0;--r>=0;){var s=8==n?255&t[r]:f(t,r);s<0?"-"==t.charAt(r)&&(i=!0):(i=!1,0==o?this.data[this.t++]=s:o+n>this.DB?(this.data[this.t-1]|=(s&(1<>this.DB-o):this.data[this.t-1]|=s<=this.DB&&(o-=this.DB))}8==n&&0!=(128&t[0])&&(this.s=-1,o>0&&(this.data[this.t-1]|=(1<0&&this.data[this.t-1]==t;)--this.t},a.prototype.dlShiftTo=function(t,e){var n;for(n=this.t-1;n>=0;--n)e.data[n+t]=this.data[n];for(n=t-1;n>=0;--n)e.data[n]=0;e.t=this.t+t,e.s=this.s},a.prototype.drShiftTo=function(t,e){for(var n=t;n=0;--n)e.data[n+o+1]=this.data[n]>>i|s,s=(this.data[n]&a)<=0;--n)e.data[n]=0;e.data[o]=s,e.t=this.t+o+1,e.s=this.s,e.clamp()},a.prototype.rShiftTo=function(t,e){e.s=this.s;var n=Math.floor(t/this.DB);if(n>=this.t)e.t=0;else{var r=t%this.DB,i=this.DB-r,a=(1<>r;for(var o=n+1;o>r;r>0&&(e.data[this.t-n-1]|=(this.s&a)<>=this.DB;if(t.t>=this.DB;r+=this.s}else{for(r+=this.s;n>=this.DB;r-=t.s}e.s=r<0?-1:0,r<-1?e.data[n++]=this.DV+r:r>0&&(e.data[n++]=r),e.t=n,e.clamp()},a.prototype.multiplyTo=function(t,e){var n=this.abs(),r=t.abs(),i=n.t;for(e.t=i+r.t;--i>=0;)e.data[i]=0;for(i=0;i=0;)t.data[n]=0;for(n=0;n=e.DV&&(t.data[n+e.t]-=e.DV,t.data[n+e.t+1]=1)}t.t>0&&(t.data[t.t-1]+=e.am(n,e.data[n],t,2*n,0,1)),t.s=0,t.clamp()},a.prototype.divRemTo=function(t,e,n){var r=t.abs();if(!(r.t<=0)){var i=this.abs();if(i.t0?(r.lShiftTo(c,s),i.lShiftTo(c,n)):(r.copyTo(s),i.copyTo(n));var p=s.t,d=s.data[p-1];if(0!=d){var f=d*(1<1?s.data[p-2]>>this.F2:0),h=this.FV/f,m=(1<=0&&(n.data[n.t++]=1,n.subTo(w,n)),a.ONE.dlShiftTo(p,w),w.subTo(s,s);s.t=0;){var $=n.data[--y]==d?this.DM:Math.floor(n.data[y]*h+(n.data[y-1]+v)*m);if((n.data[y]+=s.am(0,$,n,b,0,p))<$)for(s.dlShiftTo(b,w),n.subTo(w,n);n.data[y]<--$;)n.subTo(w,n)}null!=e&&(n.drShiftTo(p,e),u!=l&&a.ZERO.subTo(e,e)),n.t=p,n.clamp(),c>0&&n.rShiftTo(c,n),u<0&&a.ZERO.subTo(n,n)}}},a.prototype.invDigit=function(){if(this.t<1)return 0;var t=this.data[0];if(0==(1&t))return 0;var e=3&t;return(e=(e=(e=(e=e*(2-(15&t)*e)&15)*(2-(255&t)*e)&255)*(2-((65535&t)*e&65535))&65535)*(2-t*e%this.DV)%this.DV)>0?this.DV-e:-e},a.prototype.isEven=function(){return 0==(this.t>0?1&this.data[0]:this.s)},a.prototype.exp=function(t,e){if(t>4294967295||t<1)return a.ONE;var n=o(),r=o(),i=e.convert(this),s=g(t)-1;for(i.copyTo(n);--s>=0;)if(e.sqrTo(n,r),(t&1<0)e.mulTo(r,i,n);else{var u=n;n=r,r=u}return e.revert(n)},a.prototype.toString=function(t){if(this.s<0)return"-"+this.negate().toString(t);var e;if(16==t)e=4;else if(8==t)e=3;else if(2==t)e=1;else if(32==t)e=5;else{if(4!=t)return this.toRadix(t);e=2}var n,r=(1<0)for(s>s)>0&&(i=!0,a=d(n));o>=0;)s>(s+=this.DB-e)):(n=this.data[o]>>(s-=e)&r,s<=0&&(s+=this.DB,--o)),n>0&&(i=!0),i&&(a+=d(n));return i?a:"0"},a.prototype.negate=function(){var t=o();return a.ZERO.subTo(this,t),t},a.prototype.abs=function(){return this.s<0?this.negate():this},a.prototype.compareTo=function(t){var e=this.s-t.s;if(0!=e)return e;var n=this.t;if(0!=(e=n-t.t))return this.s<0?-e:e;for(;--n>=0;)if(0!=(e=this.data[n]-t.data[n]))return e;return 0},a.prototype.bitLength=function(){return this.t<=0?0:this.DB*(this.t-1)+g(this.data[this.t-1]^this.s&this.DM)},a.prototype.mod=function(t){var e=o();return this.abs().divRemTo(t,null,e),this.s<0&&e.compareTo(a.ZERO)>0&&t.subTo(e,e),e},a.prototype.modPowInt=function(t,e){var n;return n=t<256||e.isEven()?new m(e):new v(e),this.exp(t,n)},a.ZERO=h(0),a.ONE=h(1);function y(t,e){return t&e}function b(t,e){return t|e}function w(t,e){return t^e}function $(t,e){return t&~e}function k(t){if(0==t)return-1;var e=0;return 0==(65535&t)&&(t>>=16,e+=16),0==(255&t)&&(t>>=8,e+=8),0==(15&t)&&(t>>=4,e+=4),0==(3&t)&&(t>>=2,e+=2),0==(1&t)&&++e,e}function C(t){for(var e=0;0!=t;)t&=t-1,++e;return e}function x(){}function T(t){return t}x.prototype.convert=T,x.prototype.revert=T,x.prototype.mulTo=function(t,e,n){t.multiplyTo(e,n)},x.prototype.sqrTo=function(t,e){t.squareTo(e)};function S(t){this.r2=o(),this.q3=o(),a.ONE.dlShiftTo(2*t.t,this.r2),this.mu=this.r2.divide(t),this.m=t}S.prototype.convert=function(t){if(t.s<0||t.t>2*this.m.t)return t.mod(this.m);if(t.compareTo(this.m)<0)return t;var e=o();return t.copyTo(e),this.reduce(e),e},S.prototype.revert=function(t){return t},S.prototype.reduce=function(t){for(t.drShiftTo(this.m.t-1,this.r2),t.t>this.m.t+1&&(t.t=this.m.t+1,t.clamp()),this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3),this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);t.compareTo(this.r2)<0;)t.dAddOffset(1,this.m.t+1);for(t.subTo(this.r2,t);t.compareTo(this.m)>=0;)t.subTo(this.m,t)},S.prototype.mulTo=function(t,e,n){t.multiplyTo(e,n),this.reduce(n)},S.prototype.sqrTo=function(t,e){t.squareTo(e),this.reduce(e)};var E=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509],D=(1<<26)/E[E.length-1];a.prototype.chunkSize=function(t){return Math.floor(Math.LN2*this.DB/Math.log(t))},a.prototype.toRadix=function(t){if(null==t&&(t=10),0==this.signum()||t<2||t>36)return"0";var e=this.chunkSize(t),n=Math.pow(t,e),r=h(n),i=o(),a=o(),s="";for(this.divRemTo(r,i,a);i.signum()>0;)s=(n+a.intValue()).toString(t).substr(1)+s,i.divRemTo(r,i,a);return a.intValue().toString(t)+s},a.prototype.fromRadix=function(t,e){this.fromInt(0),null==e&&(e=10);for(var n=this.chunkSize(e),r=Math.pow(e,n),i=!1,o=0,s=0,u=0;u=n&&(this.dMultiply(r),this.dAddOffset(s,0),o=0,s=0))}o>0&&(this.dMultiply(Math.pow(e,o)),this.dAddOffset(s,0)),i&&a.ZERO.subTo(this,this)},a.prototype.fromNumber=function(t,e,n){if("number"==typeof e)if(t<2)this.fromInt(1);else for(this.fromNumber(t,n),this.testBit(t-1)||this.bitwiseTo(a.ONE.shiftLeft(t-1),b,this),this.isEven()&&this.dAddOffset(1,0);!this.isProbablePrime(e);)this.dAddOffset(2,0),this.bitLength()>t&&this.subTo(a.ONE.shiftLeft(t-1),this);else{var r=new Array,i=7&t;r.length=1+(t>>3),e.nextBytes(r),i>0?r[0]&=(1<>=this.DB;if(t.t>=this.DB;r+=this.s}else{for(r+=this.s;n>=this.DB;r+=t.s}e.s=r<0?-1:0,r>0?e.data[n++]=r:r<-1&&(e.data[n++]=this.DV+r),e.t=n,e.clamp()},a.prototype.dMultiply=function(t){this.data[this.t]=this.am(0,t-1,this,0,0,this.t),++this.t,this.clamp()},a.prototype.dAddOffset=function(t,e){if(0!=t){for(;this.t<=e;)this.data[this.t++]=0;for(this.data[e]+=t;this.data[e]>=this.DV;)this.data[e]-=this.DV,++e>=this.t&&(this.data[this.t++]=0),++this.data[e]}},a.prototype.multiplyLowerTo=function(t,e,n){var r=Math.min(this.t+t.t,e);for(n.s=0,n.t=r;r>0;)n.data[--r]=0;var i;for(i=n.t-this.t;r=0;)n.data[r]=0;for(r=Math.max(e-this.t,0);r0)if(0==e)n=this.data[0]%t;else for(var r=this.t-1;r>=0;--r)n=(e*n+this.data[r])%t;return n},a.prototype.millerRabin=function(t){var e=this.subtract(a.ONE),n=e.getLowestSetBit();if(n<=0)return!1;for(var r,i=e.shiftRight(n),o={nextBytes:function(t){for(var e=0;e=0);var u=r.modPow(i,this);if(0!=u.compareTo(a.ONE)&&0!=u.compareTo(e)){for(var l=1;l++>24},a.prototype.shortValue=function(){return 0==this.t?this.s:this.data[0]<<16>>16},a.prototype.signum=function(){return this.s<0?-1:this.t<=0||1==this.t&&this.data[0]<=0?0:1},a.prototype.toByteArray=function(){var t=this.t,e=new Array;e[0]=this.s;var n,r=this.DB-t*this.DB%8,i=0;if(t-- >0)for(r>r)!=(this.s&this.DM)>>r&&(e[i++]=n|this.s<=0;)r<8?(n=(this.data[t]&(1<>(r+=this.DB-8)):(n=this.data[t]>>(r-=8)&255,r<=0&&(r+=this.DB,--t)),0!=(128&n)&&(n|=-256),0==i&&(128&this.s)!=(128&n)&&++i,(i>0||n!=this.s)&&(e[i++]=n);return e},a.prototype.equals=function(t){return 0==this.compareTo(t)},a.prototype.min=function(t){return this.compareTo(t)<0?this:t},a.prototype.max=function(t){return this.compareTo(t)>0?this:t},a.prototype.and=function(t){var e=o();return this.bitwiseTo(t,y,e),e},a.prototype.or=function(t){var e=o();return this.bitwiseTo(t,b,e),e},a.prototype.xor=function(t){var e=o();return this.bitwiseTo(t,w,e),e},a.prototype.andNot=function(t){var e=o();return this.bitwiseTo(t,$,e),e},a.prototype.not=function(){for(var t=o(),e=0;e=this.t?0!=this.s:0!=(this.data[e]&1<1){var p=o();for(r.sqrTo(s[1],p);u<=c;)s[u]=o(),r.mulTo(p,s[u-2],s[u]),u+=2}var d,f,y=t.t-1,b=!0,w=o();for(i=g(t.data[y])-1;y>=0;){for(i>=l?d=t.data[y]>>i-l&c:(d=(t.data[y]&(1<0&&(d|=t.data[y-1]>>this.DB+i-l)),u=n;0==(1&d);)d>>=1,--u;if((i-=u)<0&&(i+=this.DB,--y),b)s[d].copyTo(a),b=!1;else{for(;u>1;)r.sqrTo(a,w),r.sqrTo(w,a),u-=2;u>0?r.sqrTo(a,w):(f=a,a=w,w=f),r.mulTo(w,s[d],a)}for(;y>=0&&0==(t.data[y]&1<=0?(n.subTo(r,n),e&&i.subTo(s,i),o.subTo(u,o)):(r.subTo(n,r),e&&s.subTo(i,s),u.subTo(o,u))}return 0!=r.compareTo(a.ONE)?a.ZERO:u.compareTo(t)>=0?u.subtract(t):u.signum()<0?(u.addTo(t,u),u.signum()<0?u.add(t):u):u},a.prototype.pow=function(t){return this.exp(t,new x)},a.prototype.gcd=function(t){var e=this.s<0?this.negate():this.clone(),n=t.s<0?t.negate():t.clone();if(e.compareTo(n)<0){var r=e;e=n,n=r}var i=e.getLowestSetBit(),a=n.getLowestSetBit();if(a<0)return e;for(i0&&(e.rShiftTo(a,e),n.rShiftTo(a,n));e.signum()>0;)(i=e.getLowestSetBit())>0&&e.rShiftTo(i,e),(i=n.getLowestSetBit())>0&&n.rShiftTo(i,n),e.compareTo(n)>=0?(e.subTo(n,e),e.rShiftTo(1,e)):(n.subTo(e,n),n.rShiftTo(1,n));return a>0&&n.lShiftTo(a,n),n},a.prototype.isProbablePrime=function(t){var e,n=this.abs();if(1==n.t&&n.data[0]<=E[E.length-1]){for(e=0;e>>0,s>>>0];for(var l=i.fullMessageLength.length-1;l>=0;--l)i.fullMessageLength[l]+=s[1],s[1]=s[0]+(i.fullMessageLength[l]/4294967296>>>0),i.fullMessageLength[l]=i.fullMessageLength[l]>>>0,s[0]=s[1]/4294967296>>>0;return e.putBytes(a),u(t,n,e),(e.read>2048||0===e.length())&&e.compact(),i},i.digest=function(){var o=r.util.createBuffer();o.putBytes(e.bytes());var s=i.fullMessageLength[i.fullMessageLength.length-1]+i.messageLengthSize&i.blockLength-1;o.putBytes(a.substr(0,i.blockLength-s));for(var l,c=8*i.fullMessageLength[0],p=0;p>>0,o.putInt32(c>>>0),c=l>>>0;o.putInt32(c);var d={h0:t.h0,h1:t.h1,h2:t.h2,h3:t.h3,h4:t.h4,h5:t.h5,h6:t.h6,h7:t.h7};u(d,n,o);var f=r.util.createBuffer();return f.putInt32(d.h0),f.putInt32(d.h1),f.putInt32(d.h2),f.putInt32(d.h3),f.putInt32(d.h4),f.putInt32(d.h5),f.putInt32(d.h6),f.putInt32(d.h7),f},i};var a=null,o=!1,s=null;function u(t,e,n){for(var r,i,a,o,u,l,c,p,d,f,h,g,m,v=n.length();v>=64;){for(u=0;u<16;++u)e[u]=n.getInt32();for(;u<64;++u)r=((r=e[u-2])>>>17|r<<15)^(r>>>19|r<<13)^r>>>10,i=((i=e[u-15])>>>7|i<<25)^(i>>>18|i<<14)^i>>>3,e[u]=r+e[u-7]+i+e[u-16]|0;for(l=t.h0,c=t.h1,p=t.h2,d=t.h3,f=t.h4,h=t.h5,g=t.h6,m=t.h7,u=0;u<64;++u)a=(l>>>2|l<<30)^(l>>>13|l<<19)^(l>>>22|l<<10),o=l&c|p&(l^c),r=m+((f>>>6|f<<26)^(f>>>11|f<<21)^(f>>>25|f<<7))+(g^f&(h^g))+s[u]+e[u],m=g,g=h,h=f,f=d+r>>>0,d=p,p=c,c=l,l=r+(i=a+o)>>>0;t.h0=t.h0+l|0,t.h1=t.h1+c|0,t.h2=t.h2+p|0,t.h3=t.h3+d|0,t.h4=t.h4+f|0,t.h5=t.h5+h|0,t.h6=t.h6+g|0,t.h7=t.h7+m|0,v-=64}}},function(t,e,n){n(11),n(6),n(14),n(4),n(9),n(3),t.exports=n(0)},function(t,e,n){var r=n(0);n(4),n(2),n(1);var i,a=r.pkcs5=r.pkcs5||{};r.util.isNodejs&&!r.options.usePureJavaScript&&(i=n(5)),t.exports=r.pbkdf2=a.pbkdf2=function(t,e,n,a,o,s){if("function"==typeof o&&(s=o,o=null),r.util.isNodejs&&!r.options.usePureJavaScript&&i.pbkdf2&&(null===o||"object"!=typeof o)&&(i.pbkdf2Sync.length>4||!o||"sha1"===o))return"string"!=typeof o&&(o="sha1"),t=new Buffer(t,"binary"),e=new Buffer(e,"binary"),s?4===i.pbkdf2Sync.length?i.pbkdf2(t,e,n,a,function(t,e){if(t)return s(t);s(null,e.toString("binary"))}):i.pbkdf2(t,e,n,a,o,function(t,e){if(t)return s(t);s(null,e.toString("binary"))}):4===i.pbkdf2Sync.length?i.pbkdf2Sync(t,e,n,a).toString("binary"):i.pbkdf2Sync(t,e,n,a,o).toString("binary");if(void 0!==o&&null!==o||(o="sha1"),"string"==typeof o){if(!(o in r.md.algorithms))throw new Error("Unknown hash algorithm: "+o);o=r.md[o].create()}var u=o.digestLength;if(a>4294967295*u){var l=new Error("Derived key is too long.");if(s)return s(l);throw l}var c=Math.ceil(a/u),p=a-(c-1)*u,d=r.hmac.create();d.start(o,t);var f,h,g,m="";if(!s){for(var v=1;v<=c;++v){d.start(null,null),d.update(e),d.update(r.util.int32ToBytes(v)),f=g=d.digest().getBytes();for(var y=2;y<=n;++y)d.start(null,null),d.update(g),h=d.digest().getBytes(),f=r.util.xorBytes(f,h,u),g=h;m+=vc)return s(null,m);d.start(null,null),d.update(e),d.update(r.util.int32ToBytes(v)),f=g=d.digest().getBytes(),y=2,w()}function w(){if(y<=n)return d.start(null,null),d.update(g),h=d.digest().getBytes(),f=r.util.xorBytes(f,h,u),g=h,++y,r.util.setImmediate(w);m+=v0))return!0;for(var r=0;r0))return!0;for(var r=0;r0)return!1;var n=t.length(),r=t.at(n-1);return!(r>this.blockSize<<2)&&(t.truncate(r),!0)},i.cbc=function(t){t=t||{},this.name="CBC",this.cipher=t.cipher,this.blockSize=t.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints)},i.cbc.prototype.start=function(t){if(null===t.iv){if(!this._prev)throw new Error("Invalid IV parameter.");this._iv=this._prev.slice(0)}else{if(!("iv"in t))throw new Error("Invalid IV parameter.");this._iv=a(t.iv),this._prev=this._iv.slice(0)}},i.cbc.prototype.encrypt=function(t,e,n){if(t.length()0))return!0;for(var r=0;r0))return!0;for(var r=0;r0)return!1;var n=t.length(),r=t.at(n-1);return!(r>this.blockSize<<2)&&(t.truncate(r),!0)},i.cfb=function(t){t=t||{},this.name="CFB",this.cipher=t.cipher,this.blockSize=t.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialBlock=new Array(this._ints),this._partialOutput=r.util.createBuffer(),this._partialBytes=0},i.cfb.prototype.start=function(t){if(!("iv"in t))throw new Error("Invalid IV parameter.");this._iv=a(t.iv),this._inBlock=this._iv.slice(0),this._partialBytes=0},i.cfb.prototype.encrypt=function(t,e,n){var r=t.length();if(0===r)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&r>=this.blockSize)for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(i=0;i0)t.read-=this.blockSize;else for(i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!n)return e.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;e.putBytes(this._partialOutput.getBytes(r-this._partialBytes)),this._partialBytes=0}},i.cfb.prototype.decrypt=function(t,e,n){var r=t.length();if(0===r)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&r>=this.blockSize)for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(i=0;i0)t.read-=this.blockSize;else for(i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!n)return e.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;e.putBytes(this._partialOutput.getBytes(r-this._partialBytes)),this._partialBytes=0}},i.ofb=function(t){t=t||{},this.name="OFB",this.cipher=t.cipher,this.blockSize=t.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=r.util.createBuffer(),this._partialBytes=0},i.ofb.prototype.start=function(t){if(!("iv"in t))throw new Error("Invalid IV parameter.");this._iv=a(t.iv),this._inBlock=this._iv.slice(0),this._partialBytes=0},i.ofb.prototype.encrypt=function(t,e,n){var r=t.length();if(0===t.length())return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&r>=this.blockSize)for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(i=0;i0)t.read-=this.blockSize;else for(i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!n)return e.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;e.putBytes(this._partialOutput.getBytes(r-this._partialBytes)),this._partialBytes=0}},i.ofb.prototype.decrypt=i.ofb.prototype.encrypt,i.ctr=function(t){t=t||{},this.name="CTR",this.cipher=t.cipher,this.blockSize=t.blockSize||16,this._ints=this.blockSize/4,this._inBlock=null,this._outBlock=new Array(this._ints),this._partialOutput=r.util.createBuffer(),this._partialBytes=0},i.ctr.prototype.start=function(t){if(!("iv"in t))throw new Error("Invalid IV parameter.");this._iv=a(t.iv),this._inBlock=this._iv.slice(0),this._partialBytes=0},i.ctr.prototype.encrypt=function(t,e,n){var r=t.length();if(0===r)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&r>=this.blockSize)for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(i=0;i0&&(t.read-=this.blockSize),this._partialBytes>0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!n)return e.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;e.putBytes(this._partialOutput.getBytes(r-this._partialBytes)),this._partialBytes=0}o(this._inBlock)},i.ctr.prototype.decrypt=i.ctr.prototype.encrypt,i.gcm=function(t){t=t||{},this.name="GCM",this.cipher=t.cipher,this.blockSize=t.blockSize||16,this._ints=this.blockSize/4,this._inBlock=new Array(this._ints),this._outBlock=new Array(this._ints),this._partialOutput=r.util.createBuffer(),this._partialBytes=0,this._R=3774873600},i.gcm.prototype.start=function(t){if(!("iv"in t))throw new Error("Invalid IV parameter.");var e=r.util.createBuffer(t.iv);this._cipherLength=0;var n;if(n="additionalData"in t?r.util.createBuffer(t.additionalData):r.util.createBuffer(),this._tagLength="tagLength"in t?t.tagLength:128,this._tag=null,t.decrypt&&(this._tag=r.util.createBuffer(t.tag).getBytes(),this._tag.length!==this._tagLength/8))throw new Error("Authentication tag does not match tag length.");this._hashBlock=new Array(this._ints),this.tag=null,this._hashSubkey=new Array(this._ints),this.cipher.encrypt([0,0,0,0],this._hashSubkey),this.componentBits=4,this._m=this.generateHashTable(this._hashSubkey,this.componentBits);var i=e.length();if(12===i)this._j0=[e.getInt32(),e.getInt32(),e.getInt32(),1];else{for(this._j0=[0,0,0,0];e.length()>0;)this._j0=this.ghash(this._hashSubkey,this._j0,[e.getInt32(),e.getInt32(),e.getInt32(),e.getInt32()]);this._j0=this.ghash(this._hashSubkey,this._j0,[0,0].concat(s(8*i)))}this._inBlock=this._j0.slice(0),o(this._inBlock),this._partialBytes=0,n=r.util.createBuffer(n),this._aDataLength=s(8*n.length());var a=n.length()%this.blockSize;for(a&&n.fillWithByte(0,this.blockSize-a),this._s=[0,0,0,0];n.length()>0;)this._s=this.ghash(this._hashSubkey,this._s,[n.getInt32(),n.getInt32(),n.getInt32(),n.getInt32()])},i.gcm.prototype.encrypt=function(t,e,n){var r=t.length();if(0===r)return!0;if(this.cipher.encrypt(this._inBlock,this._outBlock),0===this._partialBytes&&r>=this.blockSize){for(var i=0;i0&&(a=this.blockSize-a),this._partialOutput.clear();for(i=0;i0&&this._partialOutput.getBytes(this._partialBytes),a>0&&!n)return t.read-=this.blockSize,e.putBytes(this._partialOutput.getBytes(a-this._partialBytes)),this._partialBytes=a,!0;e.putBytes(this._partialOutput.getBytes(r-this._partialBytes)),this._partialBytes=0}this._s=this.ghash(this._hashSubkey,this._s,this._outBlock),o(this._inBlock)},i.gcm.prototype.decrypt=function(t,e,n){var r=t.length();if(r0))return!0;this.cipher.encrypt(this._inBlock,this._outBlock),o(this._inBlock),this._hashBlock[0]=t.getInt32(),this._hashBlock[1]=t.getInt32(),this._hashBlock[2]=t.getInt32(),this._hashBlock[3]=t.getInt32(),this._s=this.ghash(this._hashSubkey,this._s,this._hashBlock);for(var i=0;i0;--r)e[r]=t[r]>>>1|(1&t[r-1])<<31;e[0]=t[0]>>>1,n&&(e[0]^=this._R)},i.gcm.prototype.tableMultiply=function(t){for(var e=[0,0,0,0],n=0;n<32;++n){var r=t[n/8|0]>>>4*(7-n%8)&15,i=this._m[n][r];e[0]^=i[0],e[1]^=i[1],e[2]^=i[2],e[3]^=i[3]}return e},i.gcm.prototype.ghash=function(t,e,n){return e[0]^=n[0],e[1]^=n[1],e[2]^=n[2],e[3]^=n[3],this.tableMultiply(e)},i.gcm.prototype.generateHashTable=function(t,e){for(var n=8/e,r=4*n,i=16*n,a=new Array(i),o=0;o>>1,i=new Array(n);i[r]=t.slice(0);for(var a=r>>>1;a>0;)this.pow(i[2*a],i[a]=[]),a>>=1;for(a=2;a4){var e=t;t=r.util.createBuffer();for(var n=0;n=0||!a.gcd(e.n).equals(i.ONE));for(var o=(t=t.multiply(a.modPow(e.e,e.n)).mod(e.n)).mod(e.p).modPow(e.dP,e.p),s=t.mod(e.q).modPow(e.dQ,e.q);o.compareTo(s)<0;)o=o.add(e.p);var u=o.subtract(s).multiply(e.qInv).mod(e.p).multiply(e.q).add(s);return u=u.multiply(a.modInverse(e.n)).mod(e.n)};o.rsa.encrypt=function(t,e,n){var a,o=n,s=Math.ceil(e.n.bitLength()/8);!1!==n&&!0!==n?(o=2===n,a=h(t,e,n)):(a=r.util.createBuffer()).putBytes(t);for(var u=new i(a.toHex(),16),l=f(u,e,o).toString(16),c=r.util.createBuffer(),p=s-Math.ceil(l.length/2);p>0;)c.putByte(0),--p;return c.putBytes(r.util.hexToBytes(l)),c.getBytes()},o.rsa.decrypt=function(t,e,n,a){var o=Math.ceil(e.n.bitLength()/8);if(t.length!==o){var s=new Error("Encrypted message length is invalid.");throw s.length=t.length,s.expected=o,s}var u=new i(r.util.createBuffer(t).toHex(),16);if(u.compareTo(e.n)>=0)throw new Error("Encrypted message is invalid.");for(var l=f(u,e,n).toString(16),c=r.util.createBuffer(),p=o-Math.ceil(l.length/2);p>0;)c.putByte(0),--p;return c.putBytes(r.util.hexToBytes(l)),!1!==a?g(c.getBytes(),e,n):c.getBytes()},o.rsa.createKeyPairGenerationState=function(t,e,n){"string"==typeof t&&(t=parseInt(t,10)),t=t||2048;var a,o=(n=n||{}).prng||r.random,s={nextBytes:function(t){for(var e=o.getBytesSync(t.length),n=0;n>1,pBits:t-(t>>1),pqState:0,num:null,keys:null}).e.fromInt(a.eInt),a},o.rsa.stepKeyPairGenerationState=function(t,e){"algorithm"in t||(t.algorithm="PRIMEINC");var n=new i(null);n.fromInt(30);for(var r,a=0,u=function(t,e){return t|e},l=+new Date,c=0;null===t.keys&&(e<=0||cp?t.pqState=0:t.num.isProbablePrime(v(t.num.bitLength()))?++t.pqState:t.num.dAddOffset(s[a++%8],0):2===t.pqState?t.pqState=0===t.num.subtract(i.ONE).gcd(t.e).compareTo(i.ONE)?3:0:3===t.pqState&&(t.pqState=0,null===t.p?t.p=t.num:t.q=t.num,null!==t.p&&null!==t.q&&++t.state,t.num=null)}else if(1===t.state)t.p.compareTo(t.q)<0&&(t.num=t.p,t.p=t.q,t.q=t.num),++t.state;else if(2===t.state)t.p1=t.p.subtract(i.ONE),t.q1=t.q.subtract(i.ONE),t.phi=t.p1.multiply(t.q1),++t.state;else if(3===t.state)0===t.phi.gcd(t.e).compareTo(i.ONE)?++t.state:(t.p=null,t.q=null,t.state=0);else if(4===t.state)t.n=t.p.multiply(t.q),t.n.bitLength()===t.bits?++t.state:(t.q=null,t.state=0);else if(5===t.state){var f=t.e.modInverse(t.phi);t.keys={privateKey:o.rsa.setPrivateKey(t.n,t.e,f,t.p,t.q,f.mod(t.p1),f.mod(t.q1),t.q.modInverse(t.p)),publicKey:o.rsa.setPublicKey(t.n,t.e)}}c+=(r=+new Date)-l,l=r}return null!==t.keys},o.rsa.generateKeyPair=function(t,e,n,s){if(1===arguments.length?"object"==typeof t?(n=t,t=void 0):"function"==typeof t&&(s=t,t=void 0):2===arguments.length?"number"==typeof t?"function"==typeof e?(s=e,e=void 0):"number"!=typeof e&&(n=e,e=void 0):(n=t,s=e,t=void 0,e=void 0):3===arguments.length&&("number"==typeof e?"function"==typeof n&&(s=n,n=void 0):(s=n,n=e,e=void 0)),n=n||{},void 0===t&&(t=n.bits||2048),void 0===e&&(e=n.e||65537),!r.options.usePureJavaScript&&s&&t>=256&&t<=16384&&(65537===e||3===e)){if(y("generateKey")&&y("exportKey"))return window.crypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:t,publicExponent:w(e),hash:{name:"SHA-256"}},!0,["sign","verify"]).then(function(t){return window.crypto.subtle.exportKey("pkcs8",t.privateKey)}).then(void 0,function(t){s(t)}).then(function(t){if(t){var e=o.privateKeyFromAsn1(a.fromDer(r.util.createBuffer(t)));s(null,{privateKey:e,publicKey:o.setRsaPublicKey(e.n,e.e)})}});if(b("generateKey")&&b("exportKey")){var u=window.msCrypto.subtle.generateKey({name:"RSASSA-PKCS1-v1_5",modulusLength:t,publicExponent:w(e),hash:{name:"SHA-256"}},!0,["sign","verify"]);return u.oncomplete=function(t){var e=t.target.result,n=window.msCrypto.subtle.exportKey("pkcs8",e.privateKey);n.oncomplete=function(t){var e=t.target.result,n=o.privateKeyFromAsn1(a.fromDer(r.util.createBuffer(e)));s(null,{privateKey:n,publicKey:o.setRsaPublicKey(n.n,n.e)})},n.onerror=function(t){s(t)}},void(u.onerror=function(t){s(t)})}}var l=o.rsa.createKeyPairGenerationState(t,e,n);if(!s)return o.rsa.stepKeyPairGenerationState(l,0),l.keys;!function(t,e,n){"function"==typeof e&&(n=e,e={});var a={algorithm:{name:(e=e||{}).algorithm||"PRIMEINC",options:{workers:e.workers||2,workLoad:e.workLoad||100,workerScript:e.workerScript}}};"prng"in e&&(a.prng=e.prng);s();function s(){u(t.pBits,function(e,r){return e?n(e):(t.p=r,null!==t.q?l(e,t.q):void u(t.qBits,l))})}function u(t,e){r.prime.generateProbablePrime(t,a,e)}function l(e,r){if(e)return n(e);if(t.q=r,t.p.compareTo(t.q)<0){var a=t.p;t.p=t.q,t.q=a}if(0!==t.p.subtract(i.ONE).gcd(t.e).compareTo(i.ONE))return t.p=null,void s();if(0!==t.q.subtract(i.ONE).gcd(t.e).compareTo(i.ONE))return t.q=null,void u(t.qBits,l);if(t.p1=t.p.subtract(i.ONE),t.q1=t.q.subtract(i.ONE),t.phi=t.p1.multiply(t.q1),0!==t.phi.gcd(t.e).compareTo(i.ONE))return t.p=t.q=null,void s();if(t.n=t.p.multiply(t.q),t.n.bitLength()!==t.bits)return t.q=null,void u(t.qBits,l);var c=t.e.modInverse(t.phi);t.keys={privateKey:o.rsa.setPrivateKey(t.n,t.e,c,t.p,t.q,c.mod(t.p1),c.mod(t.q1),t.q.modInverse(t.p)),publicKey:o.rsa.setPublicKey(t.n,t.e)},n(null,t.keys)}}(l,n,s)},o.setRsaPublicKey=o.rsa.setPublicKey=function(t,e){var n={n:t,e:e};return n.encrypt=function(t,e,i){if("string"==typeof e?e=e.toUpperCase():void 0===e&&(e="RSAES-PKCS1-V1_5"),"RSAES-PKCS1-V1_5"===e)e={encode:function(t,e,n){return h(t,e,2).getBytes()}};else if("RSA-OAEP"===e||"RSAES-OAEP"===e)e={encode:function(t,e){return r.pkcs1.encode_rsa_oaep(e,t,i)}};else if(-1!==["RAW","NONE","NULL",null].indexOf(e))e={encode:function(t){return t}};else if("string"==typeof e)throw new Error('Unsupported encryption scheme: "'+e+'".');var a=e.encode(t,n,!0);return o.rsa.encrypt(a,n,!0)},n.verify=function(t,e,r){"string"==typeof r?r=r.toUpperCase():void 0===r&&(r="RSASSA-PKCS1-V1_5"),"RSASSA-PKCS1-V1_5"===r?r={verify:function(t,e){e=g(e,n,!0);return t===a.fromDer(e).value[1].value}}:"NONE"!==r&&"NULL"!==r&&null!==r||(r={verify:function(t,e){return t===(e=g(e,n,!0))}});var i=o.rsa.decrypt(e,n,!0,!1);return r.verify(t,i,n.n.bitLength())},n},o.setRsaPrivateKey=o.rsa.setPrivateKey=function(t,e,n,i,a,s,u,l){var c={n:t,e:e,d:n,p:i,q:a,dP:s,dQ:u,qInv:l};return c.decrypt=function(t,e,n){"string"==typeof e?e=e.toUpperCase():void 0===e&&(e="RSAES-PKCS1-V1_5");var i=o.rsa.decrypt(t,c,!1,!1);if("RSAES-PKCS1-V1_5"===e)e={decode:g};else if("RSA-OAEP"===e||"RSAES-OAEP"===e)e={decode:function(t,e){return r.pkcs1.decode_rsa_oaep(e,t,n)}};else{if(-1===["RAW","NONE","NULL",null].indexOf(e))throw new Error('Unsupported encryption scheme: "'+e+'".');e={decode:function(t){return t}}}return e.decode(i,c,!1)},c.sign=function(t,e){var n=!1;"string"==typeof e&&(e=e.toUpperCase()),void 0===e||"RSASSA-PKCS1-V1_5"===e?(e={encode:d},n=1):"NONE"!==e&&"NULL"!==e&&null!==e||(e={encode:function(){return t}},n=1);var r=e.encode(t,c.n.bitLength());return o.rsa.encrypt(r,c,n)},c},o.wrapRsaPrivateKey=function(t){return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(0).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(o.oids.rsaEncryption).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")]),a.create(a.Class.UNIVERSAL,a.Type.OCTETSTRING,!1,a.toDer(t).getBytes())])},o.privateKeyFromAsn1=function(t){var e={},n=[];if(a.validate(t,u,e,n)&&(t=a.fromDer(r.util.createBuffer(e.privateKey))),e={},n=[],!a.validate(t,l,e,n)){var s=new Error("Cannot read private key. ASN.1 object does not contain an RSAPrivateKey.");throw s.errors=n,s}var c,p,d,f,h,g,m,v;return c=r.util.createBuffer(e.privateKeyModulus).toHex(),p=r.util.createBuffer(e.privateKeyPublicExponent).toHex(),d=r.util.createBuffer(e.privateKeyPrivateExponent).toHex(),f=r.util.createBuffer(e.privateKeyPrime1).toHex(),h=r.util.createBuffer(e.privateKeyPrime2).toHex(),g=r.util.createBuffer(e.privateKeyExponent1).toHex(),m=r.util.createBuffer(e.privateKeyExponent2).toHex(),v=r.util.createBuffer(e.privateKeyCoefficient).toHex(),o.setRsaPrivateKey(new i(c,16),new i(p,16),new i(d,16),new i(f,16),new i(h,16),new i(g,16),new i(m,16),new i(v,16))},o.privateKeyToAsn1=o.privateKeyToRSAPrivateKey=function(t){return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,a.integerToDer(0).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.n)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.e)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.d)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.p)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.q)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.dP)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.dQ)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.qInv))])},o.publicKeyFromAsn1=function(t){var e={},n=[];if(a.validate(t,p,e,n)){var s=a.derToOid(e.publicKeyOid);if(s!==o.oids.rsaEncryption){throw(u=new Error("Cannot read public key. Unknown OID.")).oid=s,u}t=e.rsaPublicKey}if(n=[],!a.validate(t,c,e,n)){var u;throw(u=new Error("Cannot read public key. ASN.1 object does not contain an RSAPublicKey.")).errors=n,u}var l=r.util.createBuffer(e.publicKeyModulus).toHex(),d=r.util.createBuffer(e.publicKeyExponent).toHex();return o.setRsaPublicKey(new i(l,16),new i(d,16))},o.publicKeyToAsn1=o.publicKeyToSubjectPublicKeyInfo=function(t){return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.OID,!1,a.oidToDer(o.oids.rsaEncryption).getBytes()),a.create(a.Class.UNIVERSAL,a.Type.NULL,!1,"")]),a.create(a.Class.UNIVERSAL,a.Type.BITSTRING,!1,[o.publicKeyToRSAPublicKey(t)])])},o.publicKeyToRSAPublicKey=function(t){return a.create(a.Class.UNIVERSAL,a.Type.SEQUENCE,!0,[a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.n)),a.create(a.Class.UNIVERSAL,a.Type.INTEGER,!1,m(t.e))])};function h(t,e,n){var i=r.util.createBuffer(),a=Math.ceil(e.n.bitLength()/8);if(t.length>a-11){var o=new Error("Message is too long for PKCS#1 v1.5 padding.");throw o.length=t.length,o.max=a-11,o}i.putByte(0),i.putByte(n);var s,u=a-3-t.length;if(0===n||1===n){s=0===n?0:255;for(var l=0;l0;){var c=0,p=r.random.getBytes(u);for(l=0;l1;){if(255!==o.getByte()){--o.read;break}++l}else if(2===u)for(l=0;o.length()>1;){if(0===o.getByte()){--o.read;break}++l}if(0!==o.getByte()||l!==a-3-o.length())throw new Error("Encryption block is invalid.");return o.getBytes()}function m(t){var e=t.toString(16);e[0]>="8"&&(e="00"+e);var n=r.util.hexToBytes(e);return n.length>1&&(0===n.charCodeAt(0)&&0==(128&n.charCodeAt(1))||255===n.charCodeAt(0)&&128==(128&n.charCodeAt(1)))?n.substr(1):n}function v(t){return t<=100?27:t<=150?18:t<=200?15:t<=250?12:t<=300?9:t<=350?8:t<=400?7:t<=500?6:t<=600?5:t<=800?4:t<=1250?3:2}function y(t){return"undefined"!=typeof window&&"object"==typeof window.crypto&&"object"==typeof window.crypto.subtle&&"function"==typeof window.crypto.subtle[t]}function b(t){return"undefined"!=typeof window&&"object"==typeof window.msCrypto&&"object"==typeof window.msCrypto.subtle&&"function"==typeof window.msCrypto.subtle[t]}function w(t){for(var e=r.util.hexToBytes(t.toString(16)),n=new Uint8Array(e.length),i=0;ie){var r=new Error("Too few bytes to parse DER.");throw r.available=t.length(),r.remaining=e,r.requested=n,r}}var o=function(t,e){var n=t.getByte();if(e--,128!==n){var r;if(128&n){var i=127&n;a(t,e,i),r=t.getInt(i<<3)}else r=n;if(r<0)throw new Error("Negative length: "+r);return r}};i.fromDer=function(t,e){return void 0===e&&(e={strict:!0,decodeBitStrings:!0}),"boolean"==typeof e&&(e={strict:e,decodeBitStrings:!0}),"strict"in e||(e.strict=!0),"decodeBitStrings"in e||(e.decodeBitStrings=!0),"string"==typeof t&&(t=r.util.createBuffer(t)),s(t,t.length(),0,e)};function s(t,e,n,r){var u;a(t,e,2);var l=t.getByte();e--;var c=192&l,p=31&l;u=t.length();var d=o(t,e);if(e-=u-t.length(),void 0!==d&&d>e){if(r.strict){var f=new Error("Too few bytes to read ASN.1 value.");throw f.available=t.length(),f.remaining=e,f.requested=d,f}d=e}var h,g,m=32==(32&l);if(m)if(h=[],void 0===d)for(;;){if(a(t,e,2),t.bytes(2)===String.fromCharCode(0,0)){t.getBytes(2),e-=2;break}u=t.length(),h.push(s(t,e,n+1,r)),e-=u-t.length()}else for(;d>0;)u=t.length(),h.push(s(t,d,n+1,r)),e-=u-t.length(),d-=u-t.length();if(void 0===h&&c===i.Class.UNIVERSAL&&p===i.Type.BITSTRING&&(g=t.bytes(d)),void 0===h&&r.decodeBitStrings&&c===i.Class.UNIVERSAL&&p===i.Type.BITSTRING&&d>1){var v=t.read,y=e,b=0;if(p===i.Type.BITSTRING&&(a(t,e,1),b=t.getByte(),e--),0===b)try{u=t.length();var w=s(t,e,n+1,{verbose:r.verbose,strict:!0,decodeBitStrings:!0}),$=u-t.length();e-=$,p==i.Type.BITSTRING&&$++;var k=w.tagClass;$!==d||k!==i.Class.UNIVERSAL&&k!==i.Class.CONTEXT_SPECIFIC||(h=[w])}catch(t){}void 0===h&&(t.read=v,e=y)}if(void 0===h){if(void 0===d){if(r.strict)throw new Error("Non-constructed ASN.1 object of indefinite length.");d=e}if(p===i.Type.BMPSTRING)for(h="";d>0;d-=2)a(t,e,2),h+=String.fromCharCode(t.getInt16()),e-=2;else h=t.getBytes(d)}var C=void 0===g?null:{bitStringContents:g};return i.create(c,p,m,h,C)}i.toDer=function(t){var e=r.util.createBuffer(),n=t.tagClass|t.type,a=r.util.createBuffer(),o=!1;if("bitStringContents"in t&&(o=!0,t.original&&(o=i.equals(t,t.original))),o)a.putBytes(t.bitStringContents);else if(t.composed){t.constructed?n|=32:a.putByte(0);for(var s=0;s1&&(0===t.value.charCodeAt(0)&&0==(128&t.value.charCodeAt(1))||255===t.value.charCodeAt(0)&&128==(128&t.value.charCodeAt(1)))?a.putBytes(t.value.substr(1)):a.putBytes(t.value);if(e.putByte(n),a.length()<=127)e.putByte(127&a.length());else{var u=a.length(),l="";do{l+=String.fromCharCode(255&u),u>>>=8}while(u>0);e.putByte(128|l.length);for(s=l.length-1;s>=0;--s)e.putByte(l.charCodeAt(s))}return e.putBuffer(a),e},i.oidToDer=function(t){var e=t.split("."),n=r.util.createBuffer();n.putByte(40*parseInt(e[0],10)+parseInt(e[1],10));for(var i,a,o,s,u=2;u>>=7,i||(s|=128),a.push(s),i=!1}while(o>0);for(var l=a.length-1;l>=0;--l)n.putByte(a[l])}return n},i.derToOid=function(t){var e;"string"==typeof t&&(t=r.util.createBuffer(t));var n=t.getByte();e=Math.floor(n/40)+"."+n%40;for(var i=0;t.length()>0;)i<<=7,128&(n=t.getByte())?i+=127&n:(e+="."+(i+n),i=0);return e},i.utcTimeToDate=function(t){var e=new Date,n=parseInt(t.substr(0,2),10);n=n>=50?1900+n:2e3+n;var r=parseInt(t.substr(2,2),10)-1,i=parseInt(t.substr(4,2),10),a=parseInt(t.substr(6,2),10),o=parseInt(t.substr(8,2),10),s=0;if(t.length>11){var u=t.charAt(10),l=10;"+"!==u&&"-"!==u&&(s=parseInt(t.substr(10,2),10),l+=2)}if(e.setUTCFullYear(n,r,i),e.setUTCHours(a,o,s,0),l&&("+"===(u=t.charAt(l))||"-"===u)){var c=60*parseInt(t.substr(l+1,2),10)+parseInt(t.substr(l+4,2),10);c*=6e4,"+"===u?e.setTime(+e-c):e.setTime(+e+c)}return e},i.generalizedTimeToDate=function(t){var e=new Date,n=parseInt(t.substr(0,4),10),r=parseInt(t.substr(4,2),10)-1,i=parseInt(t.substr(6,2),10),a=parseInt(t.substr(8,2),10),o=parseInt(t.substr(10,2),10),s=parseInt(t.substr(12,2),10),u=0,l=0,c=!1;"Z"===t.charAt(t.length-1)&&(c=!0);var p=t.length-5,d=t.charAt(p);if("+"===d||"-"===d){l=60*parseInt(t.substr(p+1,2),10)+parseInt(t.substr(p+4,2),10),l*=6e4,"+"===d&&(l*=-1),c=!0}return"."===t.charAt(14)&&(u=1e3*parseFloat(t.substr(14),10)),c?(e.setUTCFullYear(n,r,i),e.setUTCHours(a,o,s,u),e.setTime(+e+l)):(e.setFullYear(n,r,i),e.setHours(a,o,s,u)),e},i.dateToUtcTime=function(t){if("string"==typeof t)return t;var e="",n=[];n.push((""+t.getUTCFullYear()).substr(2)),n.push(""+(t.getUTCMonth()+1)),n.push(""+t.getUTCDate()),n.push(""+t.getUTCHours()),n.push(""+t.getUTCMinutes()),n.push(""+t.getUTCSeconds());for(var r=0;r=-128&&t<128)return e.putSignedInt(t,8);if(t>=-32768&&t<32768)return e.putSignedInt(t,16);if(t>=-8388608&&t<8388608)return e.putSignedInt(t,24);if(t>=-2147483648&&t<2147483648)return e.putSignedInt(t,32);var n=new Error("Integer too large; max is 32-bits.");throw n.integer=t,n},i.derToInteger=function(t){"string"==typeof t&&(t=r.util.createBuffer(t));var e=8*t.length();if(e>32)throw new Error("Integer too large; max is 32-bits.");return t.getSignedInt(e)},i.validate=function(t,e,n,a){var o=!1;if(t.tagClass!==e.tagClass&&void 0!==e.tagClass||t.type!==e.type&&void 0!==e.type)a&&(t.tagClass!==e.tagClass&&a.push("["+e.name+'] Expected tag class "'+e.tagClass+'", got "'+t.tagClass+'"'),t.type!==e.type&&a.push("["+e.name+'] Expected type "'+e.type+'", got "'+t.type+'"'));else if(t.constructed===e.constructed||void 0===e.constructed){if(o=!0,e.value&&r.util.isArray(e.value))for(var s=0,u=0;o&&u0&&(a+="\n");for(var o="",s=0;s1?a+="0x"+r.util.bytesToHex(t.value.slice(1)):a+="(none)",t.value.length>0){var d=t.value.charCodeAt(0);1==d?a+=" (1 unused bit shown)":d>1&&(a+=" ("+d+" unused bits shown)")}}else t.type===i.Type.OCTETSTRING?(u.test(t.value)||(a+="("+t.value+") "),a+="0x"+r.util.bytesToHex(t.value)):t.type===i.Type.UTF8?a+=r.util.decodeUtf8(t.value):t.type===i.Type.PRINTABLESTRING||t.type===i.Type.IA5String?a+=t.value:u.test(t.value)?a+="0x"+r.util.bytesToHex(t.value):0===t.value.length?a+="[null]":a+=t.value}return a}},function(t,e,n){var r=n(0);n(1),n(3),n(18);var i=t.exports=r.pkcs1=r.pkcs1||{};i.encode_rsa_oaep=function(t,e,n){var i,o,s,u;"string"==typeof n?(i=n,o=arguments[3]||void 0,s=arguments[4]||void 0):n&&(i=n.label||void 0,o=n.seed||void 0,s=n.md||void 0,n.mgf1&&n.mgf1.md&&(u=n.mgf1.md)),s?s.start():s=r.md.sha1.create(),u||(u=s);var l=Math.ceil(t.n.bitLength()/8),c=l-2*s.digestLength-2;if(e.length>c){throw(m=new Error("RSAES-OAEP input message length is too long.")).length=e.length,m.maxLength=c,m}i||(i=""),s.update(i,"raw");for(var p=s.digest(),d="",f=c-e.length,h=0;h>24&255,o>>16&255,o>>8&255,255&o);n.start(),n.update(t+s),i+=n.digest().getBytes()}return i.substring(0,e)}},function(t,e,n){var r=n(0);n(1);var i=null;!r.util.isNodejs||r.options.usePureJavaScript||process.versions["node-webkit"]||(i=n(5));(t.exports=r.prng=r.prng||{}).create=function(t){for(var e={plugin:t,key:null,seed:null,time:null,reseeds:0,generated:0},n=t.md,a=new Array(32),o=0;o<32;++o)a[o]=n.create();e.pools=a,e.pool=0,e.generate=function(t,n){if(!n)return e.generateSync(t);var i=e.plugin.cipher,a=e.plugin.increment,o=e.plugin.formatKey,s=e.plugin.formatSeed,l=r.util.createBuffer();e.key=null,c();function c(p){if(p)return n(p);if(l.length()>=t)return n(null,l.getBytes(t));if(e.generated>1048575&&(e.key=null),null===e.key)return r.util.nextTick(function(){!function(t){if(e.pools[0].messageLength>=32)return u(),t();var n=32-e.pools[0].messageLength<<5;e.seedFile(n,function(n,r){if(n)return t(n);e.collect(r),u(),t()})}(c)});var d=i(e.key,e.seed);e.generated+=d.length,l.putBytes(d),e.key=o(i(e.key,a(e.seed))),e.seed=s(i(e.key,e.seed)),r.util.setImmediate(c)}},e.generateSync=function(t){var n=e.plugin.cipher,i=e.plugin.increment,a=e.plugin.formatKey,o=e.plugin.formatSeed;e.key=null;for(var u=r.util.createBuffer();u.length()1048575&&(e.key=null),null===e.key&&s();var l=n(e.key,e.seed);e.generated+=l.length,u.putBytes(l),e.key=a(n(e.key,i(e.seed))),e.seed=o(n(e.key,e.seed))}return u.getBytes(t)};function s(){if(e.pools[0].messageLength>=32)return u();var t=32-e.pools[0].messageLength<<5;e.collect(e.seedFileSync(t)),u()}function u(){var t=e.plugin.md.create();t.update(e.pools[0].digest().getBytes()),e.pools[0].start();for(var n=1,r=1;r<32;++r)(n=31===n?2147483648:n<<2)%e.reseeds==0&&(t.update(e.pools[r].digest().getBytes()),e.pools[r].start());var i=t.digest().getBytes();t.start(),t.update(i);var a=t.digest().getBytes();e.key=e.plugin.formatKey(i),e.seed=e.plugin.formatSeed(a),e.reseeds=4294967295===e.reseeds?0:e.reseeds+1,e.generated=0}function l(t){var e=null;if("undefined"!=typeof window){var n=window.crypto||window.msCrypto;n&&n.getRandomValues&&(e=function(t){return n.getRandomValues(t)})}var i=r.util.createBuffer();if(e)for(;i.length()>16)))<<16,p=4294967295&(l=(2147483647&(l+=u>>15))+(l>>31));for(s=0;s<3;++s)c=p>>>(s<<3),c^=Math.floor(256*Math.random()),i.putByte(String.fromCharCode(255&c))}return i.getBytes(t)}return i?(e.seedFile=function(t,e){i.randomBytes(t,function(t,n){if(t)return e(t);e(null,n.toString())})},e.seedFileSync=function(t){return i.randomBytes(t).toString()}):(e.seedFile=function(t,e){try{e(null,l(t))}catch(t){e(t)}},e.seedFileSync=l),e.collect=function(t){for(var n=t.length,r=0;r>i&255);e.collect(r)},e.registerWorker=function(t){if(t===self)e.seedFile=function(t,e){function n(t){var r=t.data;r.forge&&r.forge.prng&&(self.removeEventListener("message",n),e(r.forge.prng.err,r.forge.prng.bytes))}self.addEventListener("message",n),self.postMessage({forge:{prng:{needed:t}}})};else{t.addEventListener("message",function(n){var r=n.data;r.forge&&r.forge.prng&&e.seedFile(r.forge.prng.needed,function(e,n){t.postMessage({forge:{prng:{err:e,bytes:n}}})})})}},e}},function(t,e,n){var r=n(0);n(2),n(1);var i=t.exports=r.sha1=r.sha1||{};r.md.sha1=r.md.algorithms.sha1=i,i.create=function(){o||(a=String.fromCharCode(128),a+=r.util.fillString(String.fromCharCode(0),64),o=!0);var t=null,e=r.util.createBuffer(),n=new Array(80),i={algorithm:"sha1",blockLength:64,digestLength:20,messageLength:0,fullMessageLength:null,messageLengthSize:8};return i.start=function(){i.messageLength=0,i.fullMessageLength=i.messageLength64=[];for(var n=i.messageLengthSize/4,a=0;a>>0,u>>>0];for(var l=i.fullMessageLength.length-1;l>=0;--l)i.fullMessageLength[l]+=u[1],u[1]=u[0]+(i.fullMessageLength[l]/4294967296>>>0),i.fullMessageLength[l]=i.fullMessageLength[l]>>>0,u[0]=u[1]/4294967296>>>0;return e.putBytes(a),s(t,n,e),(e.read>2048||0===e.length())&&e.compact(),i},i.digest=function(){var o=r.util.createBuffer();o.putBytes(e.bytes());var u=i.fullMessageLength[i.fullMessageLength.length-1]+i.messageLengthSize&i.blockLength-1;o.putBytes(a.substr(0,i.blockLength-u));for(var l,c=8*i.fullMessageLength[0],p=0;p>>0,o.putInt32(c>>>0),c=l>>>0;o.putInt32(c);var d={h0:t.h0,h1:t.h1,h2:t.h2,h3:t.h3,h4:t.h4};s(d,n,o);var f=r.util.createBuffer();return f.putInt32(d.h0),f.putInt32(d.h1),f.putInt32(d.h2),f.putInt32(d.h3),f.putInt32(d.h4),f},i};var a=null,o=!1;function s(t,e,n){for(var r,i,a,o,s,u,l,c=n.length();c>=64;){for(i=t.h0,a=t.h1,o=t.h2,s=t.h3,u=t.h4,l=0;l<16;++l)r=n.getInt32(),e[l]=r,r=(i<<5|i>>>27)+(s^a&(o^s))+u+1518500249+r,u=s,s=o,o=(a<<30|a>>>2)>>>0,a=i,i=r;for(;l<20;++l)r=(r=e[l-3]^e[l-8]^e[l-14]^e[l-16])<<1|r>>>31,e[l]=r,r=(i<<5|i>>>27)+(s^a&(o^s))+u+1518500249+r,u=s,s=o,o=(a<<30|a>>>2)>>>0,a=i,i=r;for(;l<32;++l)r=(r=e[l-3]^e[l-8]^e[l-14]^e[l-16])<<1|r>>>31,e[l]=r,r=(i<<5|i>>>27)+(a^o^s)+u+1859775393+r,u=s,s=o,o=(a<<30|a>>>2)>>>0,a=i,i=r;for(;l<40;++l)r=(r=e[l-6]^e[l-16]^e[l-28]^e[l-32])<<2|r>>>30,e[l]=r,r=(i<<5|i>>>27)+(a^o^s)+u+1859775393+r,u=s,s=o,o=(a<<30|a>>>2)>>>0,a=i,i=r;for(;l<60;++l)r=(r=e[l-6]^e[l-16]^e[l-28]^e[l-32])<<2|r>>>30,e[l]=r,r=(i<<5|i>>>27)+(a&o|s&(a^o))+u+2400959708+r,u=s,s=o,o=(a<<30|a>>>2)>>>0,a=i,i=r;for(;l<80;++l)r=(r=e[l-6]^e[l-16]^e[l-28]^e[l-32])<<2|r>>>30,e[l]=r,r=(i<<5|i>>>27)+(a^o^s)+u+3395469782+r,u=s,s=o,o=(a<<30|a>>>2)>>>0,a=i,i=r;t.h0=t.h0+i|0,t.h1=t.h1+a|0,t.h2=t.h2+o|0,t.h3=t.h3+s|0,t.h4=t.h4+u|0,c-=64}}},function(t,e,n){var r=n(0);n(1),n(8),n(3),function(){if(r.prime)t.exports=r.prime;else{var e=t.exports=r.prime=r.prime||{},n=r.jsbn.BigInteger,i=[6,4,2,4,2,4,6,2],a=new n(null);a.fromInt(30);var o=function(t,e){return t|e};e.generateProbablePrime=function(t,e,i){"function"==typeof e&&(i=e,e={});var a=(e=e||{}).algorithm||"PRIMEINC";"string"==typeof a&&(a={name:a}),a.options=a.options||{};var o=e.prng||r.random,u={nextBytes:function(t){for(var e=o.getBytesSync(t.length),n=0;nt&&(o=l(t,e));var f=o.toString(16);i.target.postMessage({hex:f,workLoad:c}),o.dAddOffset(p,0)}}}}(t,e,i,a);return s(t,e,i,a)}(t,u,a.options,i);throw new Error("Invalid prime generation algorithm: "+a.name)}}function s(t,e,n,r){var i,a=l(t,e),o=(i=a.bitLength(),i<=100?27:i<=150?18:i<=200?15:i<=250?12:i<=300?9:i<=350?8:i<=400?7:i<=500?6:i<=600?5:i<=800?4:i<=1250?3:2);"millerRabinTests"in n&&(o=n.millerRabinTests);var s=10;"maxBlockTime"in n&&(s=n.maxBlockTime),u(a,t,e,0,o,s,r)}function u(t,e,n,a,o,s,c){var p=+new Date;do{if(t.bitLength()>e&&(t=l(e,n)),t.isProbablePrime(o))return c(null,t);t.dAddOffset(i[a++%8],0)}while(s<0||+new Date-p0,s=!(e[0]instanceof Array);if(o&&r){for(var u=0;u0&&(n+=i),n+=p(t[u],u);e.length>0&&(n+=a)}for(var l=0;l0&&(n+=i);var f=o&&s?t[d]:d;n+=p(e[l][f],d)}l-1)return!0;return!1}(t,s.BAD_DELIMITERS)||t.indexOf(i)>-1||" "===t.charAt(0)||" "===t.charAt(t.length-1);return r?o+t+o:t}},s.RECORD_SEP=String.fromCharCode(30),s.UNIT_SEP=String.fromCharCode(31),s.BYTE_ORDER_MARK="\ufeff",s.BAD_DELIMITERS=["\r","\n",'"',s.BYTE_ORDER_MARK],s.WORKERS_SUPPORTED=!n&&!!e.Worker,s.SCRIPT_PATH=null,s.LocalChunkSize=10485760,s.RemoteChunkSize=5242880,s.DefaultDelimiter=",",s.Parser=g,s.ParserHandle=h,s.NetworkStreamer=c,s.FileStreamer=p,s.StringStreamer=d,s.ReadableStreamStreamer=f,e.jQuery){var u=e.jQuery;u.fn.parse=function(t){var n=t.config||{},r=[];return this.each(function(t){if(!("INPUT"===u(this).prop("tagName").toUpperCase()&&"file"===u(this).attr("type").toLowerCase()&&e.FileReader)||!this.files||0===this.files.length)return!0;for(var i=0;i=this._config.preview;if(r)e.postMessage({results:a,workerId:s.WORKER_ID,finished:u});else if($(this._config.chunk)){if(this._config.chunk(a,this._handle),this._paused)return;a=void 0,this._completeResults=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(a.data),this._completeResults.errors=this._completeResults.errors.concat(a.errors),this._completeResults.meta=a.meta),!u||!$(this._config.complete)||a&&a.meta.aborted||this._config.complete(this._completeResults,this._input),u||a&&a.meta.paused||this._nextChunk(),a}},this._sendError=function(t){$(this._config.error)?this._config.error(t):r&&this._config.error&&e.postMessage({workerId:s.WORKER_ID,error:t,finished:!1})}}function c(t){(t=t||{}).chunkSize||(t.chunkSize=s.RemoteChunkSize),l.call(this,t);var e;this._nextChunk=n?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(t){this._input=t,this._nextChunk()},this._readChunk=function(){if(this._finished)this._chunkLoaded();else{if(e=new XMLHttpRequest,this._config.withCredentials&&(e.withCredentials=this._config.withCredentials),n||(e.onload=w(this._chunkLoaded,this),e.onerror=w(this._chunkError,this)),e.open("GET",this._input,!n),this._config.downloadRequestHeaders){var t=this._config.downloadRequestHeaders;for(var r in t)e.setRequestHeader(r,t[r])}if(this._config.chunkSize){var i=this._start+this._config.chunkSize-1;e.setRequestHeader("Range","bytes="+this._start+"-"+i),e.setRequestHeader("If-None-Match","webkit-no-cache")}try{e.send()}catch(t){this._chunkError(t.message)}n&&0===e.status?this._chunkError():this._start+=this._config.chunkSize}},this._chunkLoaded=function(){4==e.readyState&&(e.status<200||e.status>=400?this._chunkError():(this._finished=!this._config.chunkSize||this._start>function(t){var e=t.getResponseHeader("Content-Range");if(null===e)return-1;return parseInt(e.substr(e.lastIndexOf("/")+1))}(e),this.parseChunk(e.responseText)))},this._chunkError=function(t){var n=e.statusText||t;this._sendError(n)}}(c.prototype=Object.create(l.prototype)).constructor=c;function p(t){(t=t||{}).chunkSize||(t.chunkSize=s.LocalChunkSize),l.call(this,t);var e,n,r="undefined"!=typeof FileReader;this.stream=function(t){this._input=t,n=t.slice||t.webkitSlice||t.mozSlice,r?((e=new FileReader).onload=w(this._chunkLoaded,this),e.onerror=w(this._chunkError,this)):e=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount=this._input.size,this.parseChunk(t.target.result)},this._chunkError=function(){this._sendError(e.error)}}(p.prototype=Object.create(l.prototype)).constructor=p;function d(t){l.call(this,t=t||{});var e;this.stream=function(t){return t,e=t,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var t=this._config.chunkSize,n=t?e.substr(0,t):e;return e=t?e.substr(t):"",this._finished=!e,this.parseChunk(n)}}}(d.prototype=Object.create(d.prototype)).constructor=d;function f(t){l.call(this,t=t||{});var e=[],n=!0;this.stream=function(t){this._input=t,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError)},this._nextChunk=function(){e.length?this.parseChunk(e.shift()):n=!0},this._streamData=w(function(t){try{e.push("string"==typeof t?t:t.toString(this._config.encoding)),n&&(n=!1,this.parseChunk(e.shift()))}catch(t){this._streamError(t)}},this),this._streamError=w(function(t){this._streamCleanUp(),this._sendError(t.message)},this),this._streamEnd=w(function(){this._streamCleanUp(),this._finished=!0,this._streamData("")},this),this._streamCleanUp=w(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError)},this)}(f.prototype=Object.create(l.prototype)).constructor=f;function h(t){var e,n,r,i=/^\s*-?(\d*\.?\d+|\d+\.?\d*)(e[-+]?\d+)?\s*$/i,a=this,o=0,u=!1,l=!1,c=[],p={data:[],errors:[],meta:{}};if($(t.step)){var d=t.step;t.step=function(e){if(p=e,h())f();else{if(f(),0===p.data.length)return;o+=e.data.length,t.preview&&o>t.preview?n.abort():d(p,a)}}}this.parse=function(i,a,o){if(t.newline||(t.newline=function(t){var e=(t=t.substr(0,1048576)).split("\r"),n=t.split("\n"),r=n.length>1&&n[0].length=e.length/2?"\r\n":"\r"}(i)),r=!1,t.delimiter)$(t.delimiter)&&(t.delimiter=t.delimiter(i),p.meta.delimiter=t.delimiter);else{var l=function(e,n,r){for(var i,a,o,u=[",","\t","|",";",s.RECORD_SEP,s.UNIT_SEP],l=0;l1&&(p+=Math.abs(v-o),o=v):o=v}h.data.length>0&&(d/=h.data.length-f),(void 0===a||p1.99&&(a=p,i=c)}return t.delimiter=i,{successful:!!i,bestDelimiter:i}}(i,t.newline,t.skipEmptyLines);l.successful?t.delimiter=l.bestDelimiter:(r=!0,t.delimiter=s.DefaultDelimiter),p.meta.delimiter=t.delimiter}var c=b(t);return t.preview&&t.header&&c.preview++,e=i,n=new g(c),p=n.parse(e,a,o),f(),u?{meta:{paused:!0}}:p||{meta:{paused:!1}}},this.paused=function(){return u},this.pause=function(){u=!0,n.abort(),e=e.substr(n.getCharIndex())},this.resume=function(){u=!1,a.streamer.parseChunk(e)},this.aborted=function(){return l},this.abort=function(){l=!0,n.abort(),p.meta.aborted=!0,$(t.complete)&&t.complete(p),e=""};function f(){if(p&&r&&(v("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+s.DefaultDelimiter+"'"),r=!1),t.skipEmptyLines)for(var e=0;e=c.length?"__parsed_extra":c[r]),a=m(i,a),"__parsed_extra"===i?(n[i]=n[i]||[],n[i].push(a)):n[i]=a}p.data[e]=n,t.header&&(r>c.length?v("FieldMismatch","TooManyFields","Too many fields: expected "+c.length+" fields but parsed "+r,e):r-1)&&(e=","),r===e)throw"Comment character same as delimiter";!0===r?r="#":("string"!=typeof r||s.BAD_DELIMITERS.indexOf(r)>-1)&&(r=!1),"\n"!=n&&"\r"!=n&&"\r\n"!=n&&(n="\n");var l=0,c=!1;this.parse=function(t,s,p){if("string"!=typeof t)throw"Input must be a string";var d=t.length,f=e.length,h=n.length,g=r.length,m=$(i);l=0;var v=[],y=[],b=[],w=0;if(!t)return I();if(o||!1!==o&&-1===t.indexOf(u)){for(var k=t.split(n),C=0;C=a)return v=v.slice(0,a),I(!0)}}return I()}for(var x=t.indexOf(e,l),T=t.indexOf(n,l),S=new RegExp(u+u,"g");;)if(t[l]!==u)if(r&&0===b.length&&t.substr(l,g)===r){if(-1===T)return I();l=T+h,T=t.indexOf(n,l),x=t.indexOf(e,l)}else if(-1!==x&&(x=a)return I(!0)}else{var E=l;for(l++;;){if(-1===(E=t.indexOf(u,E+1)))return p||y.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:v.length,index:l}),A();if(E===d-1){return A(t.substring(l,E).replace(S,u))}if(t[E+1]!==u){if(t[E+1]===e){b.push(t.substring(l,E).replace(S,u)),l=E+1+f,x=t.indexOf(e,l),T=t.indexOf(n,l);break}if(t.substr(E+1,h)===n){if(b.push(t.substring(l,E).replace(S,u)),B(E+1+h),x=t.indexOf(e,l),m&&(O(),c))return I();if(a&&v.length>=a)return I(!0);break}y.push({type:"Quotes",code:"InvalidQuotes",message:"Trailing quote on quoted field is malformed",row:v.length,index:l}),E++}else E++}}return A();function D(t){v.push(t),w=l}function A(e){return p?I():(void 0===e&&(e=t.substr(l)),b.push(e),l=d,D(b),m&&O(),I())}function B(e){l=e,D(b),b=[],T=t.indexOf(n,l)}function I(t){return{data:v,errors:y,meta:{delimiter:e,linebreak:n,aborted:c,truncated:!!t,cursor:w+(s||0)}}}function O(){i(I()),v=[],y=[]}},this.abort=function(){c=!0},this.getCharIndex=function(){return l}}function m(t){var e=t.data,n=a[e.workerId],r=!1;if(e.error)n.userError(e.error,e.file);else if(e.results&&e.results.data){var i={abort:function(){r=!0,v(e.workerId,{data:[],errors:[],meta:{aborted:!0}})},pause:y,resume:y};if($(n.userStep)){for(var o=0;o .wrapper").css("overflow","hidden");var t=$(".main-footer").outerHeight()||0,e=$(".main-header").outerHeight()+t,n=$(window).height(),r=$(".sidebar").height()||0;if($("body").hasClass("fixed"))$(".content-wrapper, .right-side").css("min-height",n-t);else{var i;n>=r?($(".content-wrapper, .right-side").css("min-height",n-e),i=n-e):($(".content-wrapper, .right-side").css("min-height",r),i=r);var a=$($.AdminLTE.options.controlSidebarOptions.selector);void 0!==a&&a.height()>i&&$(".content-wrapper, .right-side").css("min-height",a.height())}},fixSidebar:function(){$("body").hasClass("fixed")?(void 0===$.fn.slimScroll&&window.console&&window.console.error("Error: the fixed layout requires the slimscroll plugin!"),$.AdminLTE.options.sidebarSlimScroll&&void 0!==$.fn.slimScroll&&($(".sidebar").slimScroll({destroy:!0}).height("auto"),$(".sidebar").slimScroll({height:$(window).height()-$(".main-header").height()+"px",color:"rgba(0,0,0,0.2)",size:"3px"}))):void 0!==$.fn.slimScroll&&$(".sidebar").slimScroll({destroy:!0}).height("auto")}},$.AdminLTE.pushMenu={activate:function(t){var e=$.AdminLTE.options.screenSizes;$(document).on("click",t,function(t){t.preventDefault(),$(window).width()>e.sm-1?$("body").hasClass("sidebar-collapse")?$("body").removeClass("sidebar-collapse").trigger("expanded.pushMenu"):$("body").addClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").hasClass("sidebar-open")?$("body").removeClass("sidebar-open").removeClass("sidebar-collapse").trigger("collapsed.pushMenu"):$("body").addClass("sidebar-open").trigger("expanded.pushMenu")}),$(".content-wrapper").click(function(){$(window).width()<=e.sm-1&&$("body").hasClass("sidebar-open")&&$("body").removeClass("sidebar-open")}),($.AdminLTE.options.sidebarExpandOnHover||$("body").hasClass("fixed")&&$("body").hasClass("sidebar-mini"))&&this.expandOnHover()},expandOnHover:function(){var t=this,e=$.AdminLTE.options.screenSizes.sm-1;$(".main-sidebar").hover(function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-collapse")&&$(window).width()>e&&t.expand()},function(){$("body").hasClass("sidebar-mini")&&$("body").hasClass("sidebar-expanded-on-hover")&&$(window).width()>e&&t.collapse()})},expand:function(){$("body").removeClass("sidebar-collapse").addClass("sidebar-expanded-on-hover")},collapse:function(){$("body").hasClass("sidebar-expanded-on-hover")&&$("body").removeClass("sidebar-expanded-on-hover").addClass("sidebar-collapse")}},$.AdminLTE.tree=function(t){var e=this,n=$.AdminLTE.options.animationSpeed;$(document).off("click",t+" li a").on("click",t+" li a",function(t){var r=$(this),i=r.next();if(i.is(".treeview-menu")&&i.is(":visible")&&!$("body").hasClass("sidebar-collapse"))i.slideUp(n,function(){i.removeClass("menu-open")}),i.parent("li").removeClass("active");else if(i.is(".treeview-menu")&&!i.is(":visible")){var a=r.parents("ul").first();a.find("ul:visible").slideUp(n).removeClass("menu-open");var o=r.parent("li");i.slideDown(n,function(){i.addClass("menu-open"),a.find("li.active").removeClass("active"),o.addClass("active"),e.layout.fix()})}i.is(".treeview-menu")&&t.preventDefault()})},$.AdminLTE.controlSidebar={activate:function(){var t=this,e=$.AdminLTE.options.controlSidebarOptions,n=$(e.selector);$(e.toggleBtnSelector).on("click",function(r){r.preventDefault(),n.hasClass("control-sidebar-open")||$("body").hasClass("control-sidebar-open")?t.close(n,e.slide):t.open(n,e.slide)});var r=$(".control-sidebar-bg");t._fix(r),$("body").hasClass("fixed")?t._fixForFixed(n):$(".content-wrapper, .right-side").height() .box-body, > .box-footer, > form >.box-body, > form > .box-footer");e.hasClass("collapsed-box")?(t.children(":first").removeClass(this.icons.open).addClass(this.icons.collapse),n.slideDown(this.animationSpeed,function(){e.removeClass("collapsed-box")})):(t.children(":first").removeClass(this.icons.collapse).addClass(this.icons.open),n.slideUp(this.animationSpeed,function(){e.addClass("collapsed-box")}))},remove:function(t){t.parents(".box").first().slideUp(this.animationSpeed)}}}!function(t){"use strict";t.fn.boxRefresh=function(e){var n=t.extend({trigger:".refresh-btn",source:"",onLoadStart:function(t){return t},onLoadDone:function(t){return t}},e),r=t('
    ');return this.each(function(){if(""!==n.source){var e=t(this);e.find(n.trigger).first().on("click",function(t){t.preventDefault(),(i=e).append(r),n.onLoadStart.call(i);var i;e.find(".box-body").load(n.source,function(){(t=e).find(r).remove(),n.onLoadDone.call(t);var t})})}else window.console&&window.console.log("Please specify a source first - boxRefresh()")})}}(jQuery),function(t){"use strict";t.fn.activateBox=function(){t.AdminLTE.boxWidget.activate(this)},t.fn.toggleBox=function(){var e=t(t.AdminLTE.boxWidget.selectors.collapse,this);t.AdminLTE.boxWidget.collapse(e)},t.fn.removeBox=function(){var e=t(t.AdminLTE.boxWidget.selectors.remove,this);t.AdminLTE.boxWidget.remove(e)}}(jQuery),function(t){"use strict";t.fn.todolist=function(e){var n=t.extend({onCheck:function(t){return t},onUncheck:function(t){return t}},e);return this.each(function(){void 0!==t.fn.iCheck?(t("input",this).on("ifChecked",function(){var e=t(this).parents("li").first();e.toggleClass("done"),n.onCheck.call(e)}),t("input",this).on("ifUnchecked",function(){var e=t(this).parents("li").first();e.toggleClass("done"),n.onUncheck.call(e)})):t("input",this).on("change",function(){var e=t(this).parents("li").first();e.toggleClass("done"),t("input",e).is(":checked")?n.onCheck.call(e):n.onUncheck.call(e)})})}}(jQuery); \ No newline at end of file +/** + * @license Angulartics + * (c) 2013 Luis Farzati http://angulartics.github.io/ + * License: MIT + */ +(function(angular, analytics) { +'use strict'; + +var angulartics = window.angulartics || (window.angulartics = {}); +angulartics.waitForVendorCount = 0; +angulartics.waitForVendorApi = function (objectName, delay, containsField, registerFn, onTimeout) { + if (!onTimeout) { angulartics.waitForVendorCount++; } + if (!registerFn) { registerFn = containsField; containsField = undefined; } + if (!Object.prototype.hasOwnProperty.call(window, objectName) || (containsField !== undefined && window[objectName][containsField] === undefined)) { + setTimeout(function () { angulartics.waitForVendorApi(objectName, delay, containsField, registerFn, true); }, delay); + } + else { + angulartics.waitForVendorCount--; + registerFn(window[objectName]); + } +}; + +/** + * @ngdoc overview + * @name angulartics + */ +angular.module('angulartics', []) +.provider('$analytics', $analytics) +.run(['$rootScope', '$window', '$analytics', '$injector', $analyticsRun]) +.directive('analyticsOn', ['$analytics', analyticsOn]) +.config(['$provide', exceptionTrack]); + +function $analytics() { + var vm = this; + + var settings = { + pageTracking: { + autoTrackFirstPage: true, + autoTrackVirtualPages: true, + trackRelativePath: false, + trackRoutes: true, + trackStates: true, + autoBasePath: false, + basePath: '', + excludedRoutes: [], + queryKeysWhitelisted: [], + queryKeysBlacklisted: [] + }, + eventTracking: {}, + bufferFlushDelay: 1000, // Support only one configuration for buffer flush delay to simplify buffering + trackExceptions: false, + optOut: false, + developerMode: false // Prevent sending data in local/development environment + }; + + // List of known handlers that plugins can register themselves for + var knownHandlers = [ + 'pageTrack', + 'eventTrack', + 'exceptionTrack', + 'transactionTrack', + 'setAlias', + 'setUsername', + 'setUserProperties', + 'setUserPropertiesOnce', + 'setSuperProperties', + 'setSuperPropertiesOnce', + 'incrementProperty', + 'userTimings', + 'clearCookies' + ]; + // Cache and handler properties will match values in 'knownHandlers' as the buffering functons are installed. + var cache = {}; + var handlers = {}; + var handlerOptions = {}; + + // General buffering handler + function bufferedHandler(handlerName){ + return function(){ + if(angulartics.waitForVendorCount){ + if(!cache[handlerName]){ cache[handlerName] = []; } + cache[handlerName].push(arguments); + } + }; + } + + // As handlers are installed by plugins, they get pushed into a list and invoked in order. + function updateHandlers(handlerName, fn, options){ + if(!handlers[handlerName]){ + handlers[handlerName] = []; + } + handlers[handlerName].push(fn); + handlerOptions[fn] = options; + return function(){ + if(!this.settings.optOut) { + var handlerArgs = Array.prototype.slice.apply(arguments); + return this.$inject(['$q', angular.bind(this, function($q) { + return $q.all(handlers[handlerName].map(function(handlerFn) { + var options = handlerOptions[handlerFn] || {}; + if (options.async) { + var deferred = $q.defer(); + var currentArgs = angular.copy(handlerArgs); + currentArgs.unshift(deferred.resolve); + handlerFn.apply(this, currentArgs); + return deferred.promise; + } else{ + return $q.when(handlerFn.apply(this, handlerArgs)); + } + }, this)); + })]); + } + }; + } + + // The api (returned by this provider) gets populated with handlers below. + var api = { + settings: settings + }; + + // Opt in and opt out functions + api.setOptOut = function(optOut) { + this.settings.optOut = optOut; + triggerRegister(); + }; + + api.getOptOut = function() { + return this.settings.optOut; + }; + + + // Will run setTimeout if delay is > 0 + // Runs immediately if no delay to make sure cache/buffer is flushed before anything else. + // Plugins should take care to register handlers by order of precedence. + function onTimeout(fn, delay){ + if(delay){ + setTimeout(fn, delay); + } else { + fn(); + } + } + + var provider = { + $get: ['$injector', function($injector) { + return apiWithInjector($injector); + }], + api: api, + settings: settings, + virtualPageviews: function (value) { this.settings.pageTracking.autoTrackVirtualPages = value; }, + trackStates: function (value) { this.settings.pageTracking.trackStates = value; }, + trackRoutes: function (value) { this.settings.pageTracking.trackRoutes = value; }, + excludeRoutes: function(routes) { this.settings.pageTracking.excludedRoutes = routes; }, + queryKeysWhitelist: function(keys) { this.settings.pageTracking.queryKeysWhitelisted = keys; }, + queryKeysBlacklist: function(keys) { this.settings.pageTracking.queryKeysBlacklisted = keys; }, + firstPageview: function (value) { this.settings.pageTracking.autoTrackFirstPage = value; }, + withBase: function (value) { + this.settings.pageTracking.basePath = (value) ? angular.element(document).find('base').attr('href') : ''; + }, + withAutoBase: function (value) { this.settings.pageTracking.autoBasePath = value; }, + trackExceptions: function (value) { this.settings.trackExceptions = value; }, + developerMode: function(value) { this.settings.developerMode = value; } + }; + + // General function to register plugin handlers. Flushes buffers immediately upon registration according to the specified delay. + function register(handlerName, fn, options){ + // Do not add a handler if developerMode is true + if (settings.developerMode) { + return; + } + api[handlerName] = updateHandlers(handlerName, fn, options); + var handlerSettings = settings[handlerName]; + var handlerDelay = (handlerSettings) ? handlerSettings.bufferFlushDelay : null; + var delay = (handlerDelay !== null) ? handlerDelay : settings.bufferFlushDelay; + angular.forEach(cache[handlerName], function (args, index) { + onTimeout(function () { fn.apply(this, args); }, index * delay); + }); + } + + function capitalize(input) { + return input.replace(/^./, function (match) { + return match.toUpperCase(); + }); + } + + //provide a method to inject services into handlers + var apiWithInjector = function(injector) { + return angular.extend(api, { + '$inject': injector.invoke + }); + }; + + // Adds to the provider a 'register#{handlerName}' function that manages multiple plugins and buffer flushing. + function installHandlerRegisterFunction(handlerName){ + var registerName = 'register'+capitalize(handlerName); + provider[registerName] = function(fn, options){ + register(handlerName, fn, options); + }; + api[handlerName] = updateHandlers(handlerName, bufferedHandler(handlerName)); + } + + function startRegistering(_provider, _knownHandlers, _installHandlerRegisterFunction) { + angular.forEach(_knownHandlers, _installHandlerRegisterFunction); + + for (var key in _provider) { + vm[key] = _provider[key]; + } + } + + // Allow $angulartics to trigger the register to update opt in/out + var triggerRegister = function() { + startRegistering(provider, knownHandlers, installHandlerRegisterFunction); + }; + + // Initial register + startRegistering(provider, knownHandlers, installHandlerRegisterFunction); + +} + +function $analyticsRun($rootScope, $window, $analytics, $injector) { + + function matchesExcludedRoute(url) { + for (var i = 0; i < $analytics.settings.pageTracking.excludedRoutes.length; i++) { + var excludedRoute = $analytics.settings.pageTracking.excludedRoutes[i]; + if ((excludedRoute instanceof RegExp && excludedRoute.test(url)) || url.indexOf(excludedRoute) > -1) { + return true; + } + } + return false; + } + + function arrayDifference(a1, a2) { + var result = []; + for (var i = 0; i < a1.length; i++) { + if (a2.indexOf(a1[i]) === -1) { + result.push(a1[i]); + } + } + return result; + } + + function filterQueryString(url, keysMatchArr, thisType){ + if (/\?/.test(url) && keysMatchArr.length > 0) { + var urlArr = url.split('?'); + var urlBase = urlArr[0]; + var pairs = urlArr[1].split('&'); + var matchedPairs = []; + + for (var i = 0; i < keysMatchArr.length; i++) { + var listedKey = keysMatchArr[i]; + for (var j = 0; j < pairs.length; j++) { + if ((listedKey instanceof RegExp && listedKey.test(pairs[j])) || pairs[j].indexOf(listedKey) > -1) matchedPairs.push(pairs[j]); + } + } + + var matchedPairsArr = (thisType == 'white' ? matchedPairs : arrayDifference(pairs,matchedPairs)); + if(matchedPairsArr.length > 0){ + return urlBase + '?' + matchedPairsArr.join('&'); + }else{ + return urlBase; + } + } else { + return url; + } + } + + function whitelistQueryString(url){ + return filterQueryString(url, $analytics.settings.pageTracking.queryKeysWhitelisted, 'white'); + } + + function blacklistQueryString(url){ + return filterQueryString(url, $analytics.settings.pageTracking.queryKeysBlacklisted, 'black'); + } + + function pageTrack(url, $location) { + if (!matchesExcludedRoute(url)) { + url = whitelistQueryString(url); + url = blacklistQueryString(url); + $analytics.pageTrack(url, $location); + } + } + + if ($analytics.settings.pageTracking.autoTrackFirstPage) { + $injector.invoke(['$location', function ($location) { + /* Only track the 'first page' if there are no routes or states on the page */ + var noRoutesOrStates = true; + if ($injector.has('$route')) { + var $route = $injector.get('$route'); + if ($route) { + for (var route in $route.routes) { + noRoutesOrStates = false; + break; + } + } else if ($route === null){ + noRoutesOrStates = false; + } + } else if ($injector.has('$state')) { + var $state = $injector.get('$state'); + if ($state.get().length > 1) noRoutesOrStates = false; + } + if (noRoutesOrStates) { + if ($analytics.settings.pageTracking.autoBasePath) { + $analytics.settings.pageTracking.basePath = $window.location.pathname; + } + if ($analytics.settings.pageTracking.trackRelativePath) { + var url = $analytics.settings.pageTracking.basePath + $location.url(); + pageTrack(url, $location); + } else { + pageTrack($location.absUrl(), $location); + } + } + }]); + } + + if ($analytics.settings.pageTracking.autoTrackVirtualPages) { + $injector.invoke(['$location', function ($location) { + if ($analytics.settings.pageTracking.autoBasePath) { + /* Add the full route to the base. */ + $analytics.settings.pageTracking.basePath = $window.location.pathname + "#"; + } + var noRoutesOrStates = true; + + if ($analytics.settings.pageTracking.trackRoutes) { + if ($injector.has('$route')) { + var $route = $injector.get('$route'); + if ($route) { + for (var route in $route.routes) { + noRoutesOrStates = false; + break; + } + } else if ($route === null){ + noRoutesOrStates = false; + } + $rootScope.$on('$routeChangeSuccess', function (event, current) { + if (current && (current.$$route||current).redirectTo) return; + var url = $analytics.settings.pageTracking.basePath + $location.url(); + pageTrack(url, $location); + }); + } + } + + if ($analytics.settings.pageTracking.trackStates) { + if ($injector.has('$state') && !$injector.has('$transitions')) { + noRoutesOrStates = false; + $rootScope.$on('$stateChangeSuccess', function (event, current) { + var url = $analytics.settings.pageTracking.basePath + $location.url(); + pageTrack(url, $location); + }); + } + if ($injector.has('$state') && $injector.has('$transitions')) { + noRoutesOrStates = false; + $injector.invoke(['$transitions', function($transitions) { + $transitions.onSuccess({}, function($transition$) { + var transitionOptions = $transition$.options(); + + // only track for transitions that would have triggered $stateChangeSuccess + if (transitionOptions.notify) { + var url = $analytics.settings.pageTracking.basePath + $location.url(); + pageTrack(url, $location); + } + }); + }]); + } + } + + if (noRoutesOrStates) { + $rootScope.$on('$locationChangeSuccess', function (event, current) { + if (current && (current.$$route || current).redirectTo) return; + if ($analytics.settings.pageTracking.trackRelativePath) { + var url = $analytics.settings.pageTracking.basePath + $location.url(); + pageTrack(url, $location); + } else { + pageTrack($location.absUrl(), $location); + } + }); + } + }]); + } + if ($analytics.settings.developerMode) { + angular.forEach($analytics, function(attr, name) { + if (typeof attr === 'function') { + $analytics[name] = function(){}; + } + }); + } +} + +function analyticsOn($analytics) { + return { + restrict: 'A', + link: function ($scope, $element, $attrs) { + var eventType = $attrs.analyticsOn || 'click'; + var trackingData = {}; + + angular.forEach($attrs.$attr, function(attr, name) { + if (isProperty(name)) { + trackingData[propertyName(name)] = $attrs[name]; + $attrs.$observe(name, function(value){ + trackingData[propertyName(name)] = value; + }); + } + }); + + angular.element($element[0]).on(eventType, function ($event) { + var eventName = $attrs.analyticsEvent || inferEventName($element[0]); + trackingData.eventType = $event.type; + + if($attrs.analyticsIf){ + if(! $scope.$eval($attrs.analyticsIf)){ + return; // Cancel this event if we don't pass the analytics-if condition + } + } + // Allow components to pass through an expression that gets merged on to the event properties + // eg. analytics-properites='myComponentScope.someConfigExpression.$analyticsProperties' + if($attrs.analyticsProperties){ + angular.extend(trackingData, $scope.$eval($attrs.analyticsProperties)); + } + $analytics.eventTrack(eventName, trackingData); + }); + } + }; +} + +function exceptionTrack($provide) { + $provide.decorator('$exceptionHandler', ['$delegate', '$injector', function ($delegate, $injector) { + return function (error, cause) { + var result = $delegate(error, cause); + var $analytics = $injector.get('$analytics'); + if ($analytics.settings.trackExceptions) { + $analytics.exceptionTrack(error, cause); + } + return result; + }; + }]); +} + +function isCommand(element) { + return ['a:','button:','button:button','button:submit','input:button','input:submit'].indexOf( + element.tagName.toLowerCase()+':'+(element.type||'')) >= 0; +} + +function inferEventType(element) { + if (isCommand(element)) return 'click'; + return 'click'; +} + +function inferEventName(element) { + if (isCommand(element)) return element.innerText || element.value; + return element.id || element.name || element.tagName; +} + +function isProperty(name) { + return name.substr(0, 9) === 'analytics' && ['On', 'Event', 'If', 'Properties', 'EventType'].indexOf(name.substr(9)) === -1; +} + +function propertyName(name) { + var s = name.slice(9); // slice off the 'analytics' prefix + if (typeof s !== 'undefined' && s!==null && s.length > 0) { + return s.substring(0, 1).toLowerCase() + s.substring(1); + } + else { + return s; + } +} +})(angular); + +(function() { + var showErrorsModule; + + showErrorsModule = angular.module('ui.bootstrap.showErrors', []); + + showErrorsModule.directive('showErrors', [ + '$timeout', 'showErrorsConfig', '$interpolate', function($timeout, showErrorsConfig, $interpolate) { + var getShowSuccess, getTrigger, linkFn; + getTrigger = function(options) { + var trigger; + trigger = showErrorsConfig.trigger; + if (options && (options.trigger != null)) { + trigger = options.trigger; + } + return trigger; + }; + getShowSuccess = function(options) { + var showSuccess; + showSuccess = showErrorsConfig.showSuccess; + if (options && (options.showSuccess != null)) { + showSuccess = options.showSuccess; + } + return showSuccess; + }; + linkFn = function(scope, el, attrs, formCtrl) { + var blurred, inputEl, inputName, inputNgEl, options, showSuccess, toggleClasses, trigger; + blurred = false; + options = scope.$eval(attrs.showErrors); + showSuccess = getShowSuccess(options); + trigger = getTrigger(options); + inputEl = el[0].querySelector('.form-control[name]'); + inputNgEl = angular.element(inputEl); + inputName = $interpolate(inputNgEl.attr('name') || '')(scope); + if (!inputName) { + throw "show-errors element has no child input elements with a 'name' attribute and a 'form-control' class"; + } + inputNgEl.bind(trigger, function() { + blurred = true; + return toggleClasses(formCtrl[inputName].$invalid); + }); + scope.$watch(function() { + return formCtrl[inputName] && formCtrl[inputName].$invalid; + }, function(invalid) { + if (!blurred) { + return; + } + return toggleClasses(invalid); + }); + scope.$on('show-errors-check-validity', function() { + return toggleClasses(formCtrl[inputName].$invalid); + }); + scope.$on('show-errors-reset', function() { + return $timeout(function() { + el.removeClass('has-error'); + el.removeClass('has-success'); + return blurred = false; + }, 0, false); + }); + return toggleClasses = function(invalid) { + el.toggleClass('has-error', invalid); + if (showSuccess) { + return el.toggleClass('has-success', !invalid); + } + }; + }; + return { + restrict: 'A', + require: '^form', + compile: function(elem, attrs) { + if (attrs['showErrors'].indexOf('skipFormGroupCheck') === -1) { + if (!(elem.hasClass('form-group') || elem.hasClass('input-group'))) { + throw "show-errors element does not have the 'form-group' or 'input-group' class"; + } + } + return linkFn; + } + }; + } + ]); + + showErrorsModule.provider('showErrorsConfig', function() { + var _showSuccess, _trigger; + _showSuccess = false; + _trigger = 'blur'; + this.showSuccess = function(showSuccess) { + return _showSuccess = showSuccess; + }; + this.trigger = function(trigger) { + return _trigger = trigger; + }; + this.$get = function() { + return { + showSuccess: _showSuccess, + trigger: _trigger + }; + }; + }); + +}).call(this); + +/** + * @license AngularJS v1.6.7 + * (c) 2010-2017 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +/** + * @ngdoc module + * @name ngCookies + * @description + * + * The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies. + * + * See {@link ngCookies.$cookies `$cookies`} for usage. + */ + + +angular.module('ngCookies', ['ng']). + info({ angularVersion: '1.6.7' }). + /** + * @ngdoc provider + * @name $cookiesProvider + * @description + * Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service. + * */ + provider('$cookies', [/** @this */function $CookiesProvider() { + /** + * @ngdoc property + * @name $cookiesProvider#defaults + * @description + * + * Object containing default options to pass when setting cookies. + * + * The object may have following properties: + * + * - **path** - `{string}` - The cookie will be available only for this path and its + * sub-paths. By default, this is the URL that appears in your `` tag. + * - **domain** - `{string}` - The cookie will be available only for this domain and + * its sub-domains. For security reasons the user agent will not accept the cookie + * if the current domain is not a sub-domain of this domain or equal to it. + * - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT" + * or a Date object indicating the exact date/time this cookie will expire. + * - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a + * secured connection. + * + * Note: By default, the address that appears in your `` tag will be used as the path. + * This is important so that cookies will be visible for all routes when html5mode is enabled. + * + * @example + * + * ```js + * angular.module('cookiesProviderExample', ['ngCookies']) + * .config(['$cookiesProvider', function($cookiesProvider) { + * // Setting default options + * $cookiesProvider.defaults.domain = 'foo.com'; + * $cookiesProvider.defaults.secure = true; + * }]); + * ``` + **/ + var defaults = this.defaults = {}; + + function calcOptions(options) { + return options ? angular.extend({}, defaults, options) : defaults; + } + + /** + * @ngdoc service + * @name $cookies + * + * @description + * Provides read/write access to browser's cookies. + * + *
    + * Up until Angular 1.3, `$cookies` exposed properties that represented the + * current browser cookie values. In version 1.4, this behavior has changed, and + * `$cookies` now provides a standard api of getters, setters etc. + *
    + * + * Requires the {@link ngCookies `ngCookies`} module to be installed. + * + * @example + * + * ```js + * angular.module('cookiesExample', ['ngCookies']) + * .controller('ExampleController', ['$cookies', function($cookies) { + * // Retrieving a cookie + * var favoriteCookie = $cookies.get('myFavorite'); + * // Setting a cookie + * $cookies.put('myFavorite', 'oatmeal'); + * }]); + * ``` + */ + this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) { + return { + /** + * @ngdoc method + * @name $cookies#get + * + * @description + * Returns the value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {string} Raw cookie value. + */ + get: function(key) { + return $$cookieReader()[key]; + }, + + /** + * @ngdoc method + * @name $cookies#getObject + * + * @description + * Returns the deserialized value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {Object} Deserialized cookie value. + */ + getObject: function(key) { + var value = this.get(key); + return value ? angular.fromJson(value) : value; + }, + + /** + * @ngdoc method + * @name $cookies#getAll + * + * @description + * Returns a key value object with all the cookies + * + * @returns {Object} All cookies + */ + getAll: function() { + return $$cookieReader(); + }, + + /** + * @ngdoc method + * @name $cookies#put + * + * @description + * Sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {string} value Raw value to be stored. + * @param {Object=} options Options object. + * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} + */ + put: function(key, value, options) { + $$cookieWriter(key, value, calcOptions(options)); + }, + + /** + * @ngdoc method + * @name $cookies#putObject + * + * @description + * Serializes and sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {Object} value Value to be stored. + * @param {Object=} options Options object. + * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} + */ + putObject: function(key, value, options) { + this.put(key, angular.toJson(value), options); + }, + + /** + * @ngdoc method + * @name $cookies#remove + * + * @description + * Remove given cookie + * + * @param {string} key Id of the key-value pair to delete. + * @param {Object=} options Options object. + * See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults} + */ + remove: function(key, options) { + $$cookieWriter(key, undefined, calcOptions(options)); + } + }; + }]; + }]); + +angular.module('ngCookies'). +/** + * @ngdoc service + * @name $cookieStore + * @deprecated + * sinceVersion="v1.4.0" + * Please use the {@link ngCookies.$cookies `$cookies`} service instead. + * + * @requires $cookies + * + * @description + * Provides a key-value (string-object) storage, that is backed by session cookies. + * Objects put or retrieved from this storage are automatically serialized or + * deserialized by angular's toJson/fromJson. + * + * Requires the {@link ngCookies `ngCookies`} module to be installed. + * + * @example + * + * ```js + * angular.module('cookieStoreExample', ['ngCookies']) + * .controller('ExampleController', ['$cookieStore', function($cookieStore) { + * // Put cookie + * $cookieStore.put('myFavorite','oatmeal'); + * // Get cookie + * var favoriteCookie = $cookieStore.get('myFavorite'); + * // Removing a cookie + * $cookieStore.remove('myFavorite'); + * }]); + * ``` + */ + factory('$cookieStore', ['$cookies', function($cookies) { + + return { + /** + * @ngdoc method + * @name $cookieStore#get + * + * @description + * Returns the value of given cookie key + * + * @param {string} key Id to use for lookup. + * @returns {Object} Deserialized cookie value, undefined if the cookie does not exist. + */ + get: function(key) { + return $cookies.getObject(key); + }, + + /** + * @ngdoc method + * @name $cookieStore#put + * + * @description + * Sets a value for given cookie key + * + * @param {string} key Id for the `value`. + * @param {Object} value Value to be stored. + */ + put: function(key, value) { + $cookies.putObject(key, value); + }, + + /** + * @ngdoc method + * @name $cookieStore#remove + * + * @description + * Remove given cookie + * + * @param {string} key Id of the key-value pair to delete. + */ + remove: function(key) { + $cookies.remove(key); + } + }; + + }]); + +/** + * @name $$cookieWriter + * @requires $document + * + * @description + * This is a private service for writing cookies + * + * @param {string} name Cookie name + * @param {string=} value Cookie value (if undefined, cookie will be deleted) + * @param {Object=} options Object with options that need to be stored for the cookie. + */ +function $$CookieWriter($document, $log, $browser) { + var cookiePath = $browser.baseHref(); + var rawDocument = $document[0]; + + function buildCookieString(name, value, options) { + var path, expires; + options = options || {}; + expires = options.expires; + path = angular.isDefined(options.path) ? options.path : cookiePath; + if (angular.isUndefined(value)) { + expires = 'Thu, 01 Jan 1970 00:00:00 GMT'; + value = ''; + } + if (angular.isString(expires)) { + expires = new Date(expires); + } + + var str = encodeURIComponent(name) + '=' + encodeURIComponent(value); + str += path ? ';path=' + path : ''; + str += options.domain ? ';domain=' + options.domain : ''; + str += expires ? ';expires=' + expires.toUTCString() : ''; + str += options.secure ? ';secure' : ''; + + // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: + // - 300 cookies + // - 20 cookies per unique domain + // - 4096 bytes per cookie + var cookieLength = str.length + 1; + if (cookieLength > 4096) { + $log.warn('Cookie \'' + name + + '\' possibly not set or overflowed because it was too large (' + + cookieLength + ' > 4096 bytes)!'); + } + + return str; + } + + return function(name, value, options) { + rawDocument.cookie = buildCookieString(name, value, options); + }; +} + +$$CookieWriter.$inject = ['$document', '$log', '$browser']; + +angular.module('ngCookies').provider('$$cookieWriter', /** @this */ function $$CookieWriterProvider() { + this.$get = $$CookieWriter; +}); + + +})(window, window.angular); + +(function() { + + +// Create all modules and define dependencies to make sure they exist +// and are loaded in the correct order to satisfy dependency injection +// before all nested files are concatenated by Grunt + +// Modules +angular.module('angular-jwt', + [ + 'angular-jwt.options', + 'angular-jwt.interceptor', + 'angular-jwt.jwt', + 'angular-jwt.authManager' + ]); + +angular.module('angular-jwt.authManager', []) + .provider('authManager', function () { + + this.$get = ["$rootScope", "$injector", "$location", "jwtHelper", "jwtInterceptor", "jwtOptions", function ($rootScope, $injector, $location, jwtHelper, jwtInterceptor, jwtOptions) { + + var config = jwtOptions.getConfig(); + + function invokeToken(tokenGetter) { + var token = null; + if (Array.isArray(tokenGetter)) { + token = $injector.invoke(tokenGetter, this, {options: null}); + } else { + token = tokenGetter(); + } + return token; + } + + function invokeRedirector(redirector) { + if (Array.isArray(redirector) || angular.isFunction(redirector)) { + return $injector.invoke(redirector, config, {}); + } else { + throw new Error('unauthenticatedRedirector must be a function'); + } + } + + function isAuthenticated() { + var token = invokeToken(config.tokenGetter); + if (token) { + return !jwtHelper.isTokenExpired(token); + } + } + + $rootScope.isAuthenticated = false; + + function authenticate() { + $rootScope.isAuthenticated = true; + } + + function unauthenticate() { + $rootScope.isAuthenticated = false; + } + + function checkAuthOnRefresh() { + $rootScope.$on('$locationChangeStart', function () { + var token = invokeToken(config.tokenGetter); + if (token) { + if (!jwtHelper.isTokenExpired(token)) { + authenticate(); + } else { + $rootScope.$broadcast('tokenHasExpired', token); + } + } + }); + } + + function redirectWhenUnauthenticated() { + $rootScope.$on('unauthenticated', function () { + invokeRedirector(config.unauthenticatedRedirector); + unauthenticate(); + }); + } + + function verifyRoute(event, next) { + if (!next) { + return false; + } + + var routeData = (next.$$route) ? next.$$route : next.data; + + if (routeData && routeData.requiresLogin === true) { + var token = invokeToken(config.tokenGetter); + if (!token || jwtHelper.isTokenExpired(token)) { + event.preventDefault(); + invokeRedirector(config.unauthenticatedRedirector); + } + } + } + + var eventName = ($injector.has('$state')) ? '$stateChangeStart' : '$routeChangeStart'; + $rootScope.$on(eventName, verifyRoute); + + return { + authenticate: authenticate, + unauthenticate: unauthenticate, + getToken: function(){ return invokeToken(config.tokenGetter); }, + redirect: function() { return invokeRedirector(config.unauthenticatedRedirector); }, + checkAuthOnRefresh: checkAuthOnRefresh, + redirectWhenUnauthenticated: redirectWhenUnauthenticated, + isAuthenticated: isAuthenticated + } + }] + }); + +angular.module('angular-jwt.interceptor', []) + .provider('jwtInterceptor', function() { + + this.urlParam; + this.authHeader; + this.authPrefix; + this.whiteListedDomains; + this.tokenGetter; + + var config = this; + + this.$get = ["$q", "$injector", "$rootScope", "urlUtils", "jwtOptions", function($q, $injector, $rootScope, urlUtils, jwtOptions) { + + var options = angular.extend({}, jwtOptions.getConfig(), config); + + function isSafe (url) { + if (!urlUtils.isSameOrigin(url) && !options.whiteListedDomains.length) { + throw new Error('As of v0.1.0, requests to domains other than the application\'s origin must be white listed. Use jwtOptionsProvider.config({ whiteListedDomains: [] }); to whitelist.') + } + var hostname = urlUtils.urlResolve(url).hostname.toLowerCase(); + for (var i = 0; i < options.whiteListedDomains.length; i++) { + var domain = options.whiteListedDomains[i]; + var regexp = domain instanceof RegExp ? domain : new RegExp(domain, 'i'); + if (hostname.match(regexp)) { + return true; + } + } + + if (urlUtils.isSameOrigin(url)) { + return true; + } + + return false; + } + + return { + request: function (request) { + if (request.skipAuthorization || !isSafe(request.url)) { + return request; + } + + if (options.urlParam) { + request.params = request.params || {}; + // Already has the token in the url itself + if (request.params[options.urlParam]) { + return request; + } + } else { + request.headers = request.headers || {}; + // Already has an Authorization header + if (request.headers[options.authHeader]) { + return request; + } + } + + var tokenPromise = $q.when($injector.invoke(options.tokenGetter, this, { + options: request + })); + + return tokenPromise.then(function(token) { + if (token) { + if (options.urlParam) { + request.params[options.urlParam] = token; + } else { + request.headers[options.authHeader] = options.authPrefix + token; + } + } + return request; + }); + }, + responseError: function (response) { + // handle the case where the user is not authenticated + if (response.status === 401) { + $rootScope.$broadcast('unauthenticated', response); + } + return $q.reject(response); + } + }; + }] + }); + + angular.module('angular-jwt.jwt', []) + .service('jwtHelper', ["$window", function($window) { + + this.urlBase64Decode = function(str) { + var output = str.replace(/-/g, '+').replace(/_/g, '/'); + switch (output.length % 4) { + case 0: { break; } + case 2: { output += '=='; break; } + case 3: { output += '='; break; } + default: { + throw 'Illegal base64url string!'; + } + } + return $window.decodeURIComponent(escape($window.atob(output))); //polyfill https://github.com/davidchambers/Base64.js + }; + + + this.decodeToken = function(token) { + var parts = token.split('.'); + + if (parts.length !== 3) { + throw new Error('JWT must have 3 parts'); + } + + var decoded = this.urlBase64Decode(parts[1]); + if (!decoded) { + throw new Error('Cannot decode the token'); + } + + return angular.fromJson(decoded); + }; + + this.getTokenExpirationDate = function(token) { + var decoded = this.decodeToken(token); + + if(typeof decoded.exp === "undefined") { + return null; + } + + var d = new Date(0); // The 0 here is the key, which sets the date to the epoch + d.setUTCSeconds(decoded.exp); + + return d; + }; + + this.isTokenExpired = function(token, offsetSeconds) { + var d = this.getTokenExpirationDate(token); + offsetSeconds = offsetSeconds || 0; + if (d === null) { + return false; + } + + // Token expired? + return !(d.valueOf() > (new Date().valueOf() + (offsetSeconds * 1000))); + }; + }]); + +angular.module('angular-jwt.options', []) + .provider('jwtOptions', function() { + var globalConfig = {}; + this.config = function(value) { + globalConfig = value; + }; + this.$get = function() { + + var options = { + urlParam: null, + authHeader: 'Authorization', + authPrefix: 'Bearer ', + whiteListedDomains: [], + tokenGetter: function() { + return null; + }, + loginPath: '/', + unauthenticatedRedirectPath: '/', + unauthenticatedRedirector: ['$location', function($location) { + $location.path(this.unauthenticatedRedirectPath); + }] + }; + + function JwtOptions() { + var config = this.config = angular.extend({}, options, globalConfig); + } + + JwtOptions.prototype.getConfig = function() { + return this.config; + }; + + return new JwtOptions(); + } + }); + + /** + * The content from this file was directly lifted from Angular. It is + * unfortunately not a public API, so the best we can do is copy it. + * + * Angular References: + * https://github.com/angular/angular.js/issues/3299 + * https://github.com/angular/angular.js/blob/d077966ff1ac18262f4615ff1a533db24d4432a7/src/ng/urlUtils.js + */ + + angular.module('angular-jwt.interceptor') + .service('urlUtils', function () { + + // 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 <= 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. + * + * 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; + + // 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 = (angular.isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; + return (parsed.protocol === originUrl.protocol && + parsed.host === originUrl.host); + } + + return { + urlResolve: urlResolve, + isSameOrigin: urlIsSameOrigin + }; + + }); + +}()); +/** + * @license AngularJS v1.6.7 + * (c) 2010-2017 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +var forEach; +var isArray; +var isString; +var jqLite; + +/** + * @ngdoc module + * @name ngMessages + * @description + * + * The `ngMessages` module provides enhanced support for displaying messages within templates + * (typically within forms or when rendering message objects that return key/value data). + * Instead of relying on JavaScript code and/or complex ng-if statements within your form template to + * show and hide error messages specific to the state of an input field, the `ngMessages` and + * `ngMessage` directives are designed to handle the complexity, inheritance and priority + * sequencing based on the order of how the messages are defined in the template. + * + * Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude` + * `ngMessage` and `ngMessageExp` directives. + * + * ## Usage + * The `ngMessages` directive allows keys in a key/value collection to be associated with a child element + * (or 'message') that will show or hide based on the truthiness of that key's value in the collection. A common use + * case for `ngMessages` is to display error messages for inputs using the `$error` object exposed by the + * {@link ngModel ngModel} directive. + * + * The child elements of the `ngMessages` directive are matched to the collection keys by a `ngMessage` or + * `ngMessageExp` directive. The value of these attributes must match a key in the collection that is provided by + * the `ngMessages` directive. + * + * Consider the following example, which illustrates a typical use case of `ngMessages`. Within the form `myForm` we + * have a text input named `myField` which is bound to the scope variable `field` using the {@link ngModel ngModel} + * directive. + * + * The `myField` field is a required input of type `email` with a maximum length of 15 characters. + * + * ```html + *
    + * + *
    + *
    Please enter a value for this field.
    + *
    This field must be a valid email address.
    + *
    This field can be at most 15 characters long.
    + *
    + *
    + * ``` + * + * In order to show error messages corresponding to `myField` we first create an element with an `ngMessages` attribute + * set to the `$error` object owned by the `myField` input in our `myForm` form. + * + * Within this element we then create separate elements for each of the possible errors that `myField` could have. + * The `ngMessage` attribute is used to declare which element(s) will appear for which error - for example, + * setting `ng-message="required"` specifies that this particular element should be displayed when there + * is no value present for the required field `myField` (because the key `required` will be `true` in the object + * `myForm.myField.$error`). + * + * ### Message order + * + * By default, `ngMessages` will only display one message for a particular key/value collection at any time. If more + * than one message (or error) key is currently true, then which message is shown is determined by the order of messages + * in the HTML template code (messages declared first are prioritised). This mechanism means the developer does not have + * to prioritize messages using custom JavaScript code. + * + * Given the following error object for our example (which informs us that the field `myField` currently has both the + * `required` and `email` errors): + * + * ```javascript + * + * myField.$error = { required : true, email: true, maxlength: false }; + * ``` + * The `required` message will be displayed to the user since it appears before the `email` message in the DOM. + * Once the user types a single character, the `required` message will disappear (since the field now has a value) + * but the `email` message will be visible because it is still applicable. + * + * ### Displaying multiple messages at the same time + * + * While `ngMessages` will by default only display one error element at a time, the `ng-messages-multiple` attribute can + * be applied to the `ngMessages` container element to cause it to display all applicable error messages at once: + * + * ```html + * + *
    ...
    + * + * + * ... + * ``` + * + * ## Reusing and Overriding Messages + * In addition to prioritization, ngMessages also allows for including messages from a remote or an inline + * template. This allows for generic collection of messages to be reused across multiple parts of an + * application. + * + * ```html + * + * + *
    + *
    + *
    + * ``` + * + * However, including generic messages may not be useful enough to match all input fields, therefore, + * `ngMessages` provides the ability to override messages defined in the remote template by redefining + * them within the directive container. + * + * ```html + * + * + * + *
    + * + * + *
    + * + *
    You did not enter your email address
    + * + * + *
    Your email address is invalid
    + * + * + *
    + *
    + *
    + * ``` + * + * In the example HTML code above the message that is set on required will override the corresponding + * required message defined within the remote template. Therefore, with particular input fields (such + * email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied + * while more generic messages can be used to handle other, more general input errors. + * + * ## Dynamic Messaging + * ngMessages also supports using expressions to dynamically change key values. Using arrays and + * repeaters to list messages is also supported. This means that the code below will be able to + * fully adapt itself and display the appropriate message when any of the expression data changes: + * + * ```html + *
    + * + *
    + *
    You did not enter your email address
    + *
    + * + *
    {{ errorMessage.text }}
    + *
    + *
    + *
    + * ``` + * + * The `errorMessage.type` expression can be a string value or it can be an array so + * that multiple errors can be associated with a single error message: + * + * ```html + * + *
    + *
    You did not enter your email address
    + *
    + * Your email must be between 5 and 100 characters long + *
    + *
    + * ``` + * + * Feel free to use other structural directives such as ng-if and ng-switch to further control + * what messages are active and when. Be careful, if you place ng-message on the same element + * as these structural directives, Angular may not be able to determine if a message is active + * or not. Therefore it is best to place the ng-message on a child element of the structural + * directive. + * + * ```html + *
    + *
    + *
    Please enter something
    + *
    + *
    + * ``` + * + * ## Animations + * If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and + * `ngMessageExp` directives will trigger animations whenever any messages are added and removed from + * the DOM by the `ngMessages` directive. + * + * Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS + * class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no + * messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can + * hook into the animations whenever these classes are added/removed. + * + * Let's say that our HTML code for our messages container looks like so: + * + * ```html + * + * ``` + * + * Then the CSS animation code for the message container looks like so: + * + * ```css + * .my-messages { + * transition:1s linear all; + * } + * .my-messages.ng-active { + * // messages are visible + * } + * .my-messages.ng-inactive { + * // messages are hidden + * } + * ``` + * + * Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter + * and leave animation is triggered for each particular element bound to the `ngMessage` directive. + * + * Therefore, the CSS code for the inner messages looks like so: + * + * ```css + * .some-message { + * transition:1s linear all; + * } + * + * .some-message.ng-enter {} + * .some-message.ng-enter.ng-enter-active {} + * + * .some-message.ng-leave {} + * .some-message.ng-leave.ng-leave-active {} + * ``` + * + * {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate. + */ +angular.module('ngMessages', [], function initAngularHelpers() { + // Access helpers from angular core. + // Do it inside a `config` block to ensure `window.angular` is available. + forEach = angular.forEach; + isArray = angular.isArray; + isString = angular.isString; + jqLite = angular.element; +}) + .info({ angularVersion: '1.6.7' }) + + /** + * @ngdoc directive + * @module ngMessages + * @name ngMessages + * @restrict AE + * + * @description + * `ngMessages` is a directive that is designed to show and hide messages based on the state + * of a key/value object that it listens on. The directive itself complements error message + * reporting with the `ngModel` $error object (which stores a key/value state of validation errors). + * + * `ngMessages` manages the state of internal messages within its container element. The internal + * messages use the `ngMessage` directive and will be inserted/removed from the page depending + * on if they're present within the key/value object. By default, only one message will be displayed + * at a time and this depends on the prioritization of the messages within the template. (This can + * be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.) + * + * A remote template can also be used to promote message reusability and messages can also be + * overridden. + * + * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`. + * + * @usage + * ```html + * + * + * ... + * ... + * ... + * + * + * + * + * ... + * ... + * ... + * + * ``` + * + * @param {string} ngMessages an angular expression evaluating to a key/value object + * (this is typically the $error object on an ngModel instance). + * @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true + * + * @example + * + * + *
    + * + *
    myForm.myName.$error = {{ myForm.myName.$error | json }}
    + * + *
    + *
    You did not enter a field
    + *
    Your field is too short
    + *
    Your field is too long
    + *
    + *
    + *
    + * + * angular.module('ngMessagesExample', ['ngMessages']); + * + *
    + */ + .directive('ngMessages', ['$animate', function($animate) { + var ACTIVE_CLASS = 'ng-active'; + var INACTIVE_CLASS = 'ng-inactive'; + + return { + require: 'ngMessages', + restrict: 'AE', + controller: ['$element', '$scope', '$attrs', function NgMessagesCtrl($element, $scope, $attrs) { + var ctrl = this; + var latestKey = 0; + var nextAttachId = 0; + + this.getAttachId = function getAttachId() { return nextAttachId++; }; + + var messages = this.messages = {}; + var renderLater, cachedCollection; + + this.render = function(collection) { + collection = collection || {}; + + renderLater = false; + cachedCollection = collection; + + // this is true if the attribute is empty or if the attribute value is truthy + var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) || + isAttrTruthy($scope, $attrs.multiple); + + var unmatchedMessages = []; + var matchedKeys = {}; + var messageItem = ctrl.head; + var messageFound = false; + var totalMessages = 0; + + // we use != instead of !== to allow for both undefined and null values + while (messageItem != null) { + totalMessages++; + var messageCtrl = messageItem.message; + + var messageUsed = false; + if (!messageFound) { + forEach(collection, function(value, key) { + if (!messageUsed && truthy(value) && messageCtrl.test(key)) { + // this is to prevent the same error name from showing up twice + if (matchedKeys[key]) return; + matchedKeys[key] = true; + + messageUsed = true; + messageCtrl.attach(); + } + }); + } + + if (messageUsed) { + // unless we want to display multiple messages then we should + // set a flag here to avoid displaying the next message in the list + messageFound = !multiple; + } else { + unmatchedMessages.push(messageCtrl); + } + + messageItem = messageItem.next; + } + + forEach(unmatchedMessages, function(messageCtrl) { + messageCtrl.detach(); + }); + + if (unmatchedMessages.length !== totalMessages) { + $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS); + } else { + $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS); + } + }; + + $scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render); + + // If the element is destroyed, proactively destroy all the currently visible messages + $element.on('$destroy', function() { + forEach(messages, function(item) { + item.message.detach(); + }); + }); + + this.reRender = function() { + if (!renderLater) { + renderLater = true; + $scope.$evalAsync(function() { + if (renderLater && cachedCollection) { + ctrl.render(cachedCollection); + } + }); + } + }; + + this.register = function(comment, messageCtrl) { + var nextKey = latestKey.toString(); + messages[nextKey] = { + message: messageCtrl + }; + insertMessageNode($element[0], comment, nextKey); + comment.$$ngMessageNode = nextKey; + latestKey++; + + ctrl.reRender(); + }; + + this.deregister = function(comment) { + var key = comment.$$ngMessageNode; + delete comment.$$ngMessageNode; + removeMessageNode($element[0], comment, key); + delete messages[key]; + ctrl.reRender(); + }; + + function findPreviousMessage(parent, comment) { + var prevNode = comment; + var parentLookup = []; + + while (prevNode && prevNode !== parent) { + var prevKey = prevNode.$$ngMessageNode; + if (prevKey && prevKey.length) { + return messages[prevKey]; + } + + // dive deeper into the DOM and examine its children for any ngMessage + // comments that may be in an element that appears deeper in the list + if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) === -1) { + parentLookup.push(prevNode); + prevNode = prevNode.childNodes[prevNode.childNodes.length - 1]; + } else if (prevNode.previousSibling) { + prevNode = prevNode.previousSibling; + } else { + prevNode = prevNode.parentNode; + parentLookup.push(prevNode); + } + } + } + + function insertMessageNode(parent, comment, key) { + var messageNode = messages[key]; + if (!ctrl.head) { + ctrl.head = messageNode; + } else { + var match = findPreviousMessage(parent, comment); + if (match) { + messageNode.next = match.next; + match.next = messageNode; + } else { + messageNode.next = ctrl.head; + ctrl.head = messageNode; + } + } + } + + function removeMessageNode(parent, comment, key) { + var messageNode = messages[key]; + + var match = findPreviousMessage(parent, comment); + if (match) { + match.next = messageNode.next; + } else { + ctrl.head = messageNode.next; + } + } + }] + }; + + function isAttrTruthy(scope, attr) { + return (isString(attr) && attr.length === 0) || //empty attribute + truthy(scope.$eval(attr)); + } + + function truthy(val) { + return isString(val) ? val.length : !!val; + } + }]) + + /** + * @ngdoc directive + * @name ngMessagesInclude + * @restrict AE + * @scope + * + * @description + * `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template + * code from a remote template and place the downloaded template code into the exact spot + * that the ngMessagesInclude directive is placed within the ngMessages container. This allows + * for a series of pre-defined messages to be reused and also allows for the developer to + * determine what messages are overridden due to the placement of the ngMessagesInclude directive. + * + * @usage + * ```html + * + * + * ... + * + * + * + * + * ... + * + * ``` + * + * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`. + * + * @param {string} ngMessagesInclude|src a string value corresponding to the remote template. + */ + .directive('ngMessagesInclude', + ['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) { + + return { + restrict: 'AE', + require: '^^ngMessages', // we only require this for validation sake + link: function($scope, element, attrs) { + var src = attrs.ngMessagesInclude || attrs.src; + $templateRequest(src).then(function(html) { + if ($scope.$$destroyed) return; + + if (isString(html) && !html.trim()) { + // Empty template - nothing to compile + replaceElementWithMarker(element, src); + } else { + // Non-empty template - compile and link + $compile(html)($scope, function(contents) { + element.after(contents); + replaceElementWithMarker(element, src); + }); + } + }); + } + }; + + // Helpers + function replaceElementWithMarker(element, src) { + // A comment marker is placed for debugging purposes + var comment = $compile.$$createComment ? + $compile.$$createComment('ngMessagesInclude', src) : + $document[0].createComment(' ngMessagesInclude: ' + src + ' '); + var marker = jqLite(comment); + element.after(marker); + + // Don't pollute the DOM anymore by keeping an empty directive element + element.remove(); + } + }]) + + /** + * @ngdoc directive + * @name ngMessage + * @restrict AE + * @scope + * @priority 1 + * + * @description + * `ngMessage` is a directive with the purpose to show and hide a particular message. + * For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element + * must be situated since it determines which messages are visible based on the state + * of the provided key/value map that `ngMessages` listens on. + * + * More information about using `ngMessage` can be found in the + * {@link module:ngMessages `ngMessages` module documentation}. + * + * @usage + * ```html + * + * + * ... + * ... + * + * + * + * + * ... + * ... + * + * ``` + * + * @param {expression} ngMessage|when a string value corresponding to the message key. + */ + .directive('ngMessage', ngMessageDirectiveFactory()) + + + /** + * @ngdoc directive + * @name ngMessageExp + * @restrict AE + * @priority 1 + * @scope + * + * @description + * `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static + * value, it accepts an expression to be evaluated for the message key. + * + * @usage + * ```html + * + * + * ... + * + * + * + * + * ... + * + * ``` + * + * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`. + * + * @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key. + */ + .directive('ngMessageExp', ngMessageDirectiveFactory()); + +function ngMessageDirectiveFactory() { + return ['$animate', function($animate) { + return { + restrict: 'AE', + transclude: 'element', + priority: 1, // must run before ngBind, otherwise the text is set on the comment + terminal: true, + require: '^^ngMessages', + link: function(scope, element, attrs, ngMessagesCtrl, $transclude) { + var commentNode = element[0]; + + var records; + var staticExp = attrs.ngMessage || attrs.when; + var dynamicExp = attrs.ngMessageExp || attrs.whenExp; + var assignRecords = function(items) { + records = items + ? (isArray(items) + ? items + : items.split(/[\s,]+/)) + : null; + ngMessagesCtrl.reRender(); + }; + + if (dynamicExp) { + assignRecords(scope.$eval(dynamicExp)); + scope.$watchCollection(dynamicExp, assignRecords); + } else { + assignRecords(staticExp); + } + + var currentElement, messageCtrl; + ngMessagesCtrl.register(commentNode, messageCtrl = { + test: function(name) { + return contains(records, name); + }, + attach: function() { + if (!currentElement) { + $transclude(function(elm, newScope) { + $animate.enter(elm, null, element); + currentElement = elm; + + // Each time we attach this node to a message we get a new id that we can match + // when we are destroying the node later. + var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId(); + + // in the event that the element or a parent element is destroyed + // by another structural directive then it's time + // to deregister the message from the controller + currentElement.on('$destroy', function() { + if (currentElement && currentElement.$$attachId === $$attachId) { + ngMessagesCtrl.deregister(commentNode); + messageCtrl.detach(); + } + newScope.$destroy(); + }); + }); + } + }, + detach: function() { + if (currentElement) { + var elm = currentElement; + currentElement = null; + $animate.leave(elm); + } + } + }); + } + }; + }]; + + function contains(collection, key) { + if (collection) { + return isArray(collection) + ? collection.indexOf(key) >= 0 + : collection.hasOwnProperty(key); + } + } +} + + +})(window, window.angular); + +'use strict'; + +var app = angular + .module('angular-promise-polyfill', []) + .run(['$q', '$window', function($q, $window) { + $window.Promise = function(executor) { + return $q(executor); + }; + + $window.Promise.all = $q.all.bind($q); + $window.Promise.reject = $q.reject.bind($q); + $window.Promise.resolve = $q.when.bind($q); + + $window.Promise.race = function(promises) { + var promiseMgr = $q.defer(); + + for(var i = 0; i < promises.length; i++) { + promises[i].then(function(result) { + if(promiseMgr) { + promiseMgr.resolve(result); + promiseMgr = null; + } + }); + + promises[i].catch(function(result) { + if(promiseMgr) { + promiseMgr.reject(result); + promiseMgr = null; + } + }); + } + + return promiseMgr.promise; + }; + }]); + +if (typeof module === 'object') { + module.exports = app.name; +} + +/** + * @license AngularJS v1.6.7 + * (c) 2010-2017 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +var $resourceMinErr = angular.$$minErr('$resource'); + +// Helper functions and regex to lookup a dotted path on an object +// stopping at undefined/null. The path must be composed of ASCII +// identifiers (just like $parse) +var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/; + +function isValidDottedPath(path) { + return (path != null && path !== '' && path !== 'hasOwnProperty' && + MEMBER_NAME_REGEX.test('.' + path)); +} + +function lookupDottedPath(obj, path) { + if (!isValidDottedPath(path)) { + throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path); + } + var keys = path.split('.'); + for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) { + var key = keys[i]; + obj = (obj !== null) ? obj[key] : undefined; + } + return obj; +} + +/** + * Create a shallow copy of an object and clear other fields from the destination + */ +function shallowClearAndCopy(src, dst) { + dst = dst || {}; + + angular.forEach(dst, function(value, key) { + delete dst[key]; + }); + + for (var key in src) { + if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { + dst[key] = src[key]; + } + } + + return dst; +} + +/** + * @ngdoc module + * @name ngResource + * @description + * + * The `ngResource` module provides interaction support with RESTful services + * via the $resource service. + * + * See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage. + */ + +/** + * @ngdoc provider + * @name $resourceProvider + * + * @description + * + * Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource} + * service. + * + * ## Dependencies + * Requires the {@link ngResource } module to be installed. + * + */ + +/** + * @ngdoc service + * @name $resource + * @requires $http + * @requires ng.$log + * @requires $q + * @requires ng.$timeout + * + * @description + * A factory which creates a resource object that lets you interact with + * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. + * + * The returned resource object has action methods which provide high-level behaviors without + * the need to interact with the low level {@link ng.$http $http} service. + * + * Requires the {@link ngResource `ngResource`} module to be installed. + * + * By default, trailing slashes will be stripped from the calculated URLs, + * which can pose problems with server backends that do not expect that + * behavior. This can be disabled by configuring the `$resourceProvider` like + * this: + * + * ```js + app.config(['$resourceProvider', function($resourceProvider) { + // Don't strip trailing slashes from calculated URLs + $resourceProvider.defaults.stripTrailingSlashes = false; + }]); + * ``` + * + * @param {string} url A parameterized URL template with parameters prefixed by `:` as in + * `/user/:username`. If you are using a URL with a port number (e.g. + * `http://example.com:8080/api`), it will be respected. + * + * If you are using a url with a suffix, just add the suffix, like this: + * `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')` + * or even `$resource('http://example.com/resource/:resource_id.:format')` + * If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be + * collapsed down to a single `.`. If you need this sequence to appear and not collapse then you + * can escape it with `/\.`. + * + * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in + * `actions` methods. If a parameter value is a function, it will be called every time + * a param value needs to be obtained for a request (unless the param was overridden). The function + * will be passed the current data value as an argument. + * + * Each key value in the parameter object is first bound to url template if present and then any + * excess keys are appended to the url search query after the `?`. + * + * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in + * URL `/path/greet?salutation=Hello`. + * + * If the parameter value is prefixed with `@`, then the value for that parameter will be + * extracted from the corresponding property on the `data` object (provided when calling actions + * with a request body). + * For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of + * `someParam` will be `data.someProp`. + * Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action + * method that does not accept a request body) + * + * @param {Object.=} actions Hash with declaration of custom actions that will be available + * in addition to the default set of resource actions (see below). If a custom action has the same + * key as a default action (e.g. `save`), then the default action will be *overwritten*, and not + * extended. + * + * The declaration should be created in the format of {@link ng.$http#usage $http.config}: + * + * {action1: {method:?, params:?, isArray:?, headers:?, ...}, + * action2: {method:?, params:?, isArray:?, headers:?, ...}, + * ...} + * + * Where: + * + * - **`action`** – {string} – The name of action. This name becomes the name of the method on + * your resource object. + * - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`, + * `DELETE`, `JSONP`, etc). + * - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of + * the parameter value is a function, it will be called every time when a param value needs to + * be obtained for a request (unless the param was overridden). The function will be passed the + * current data value as an argument. + * - **`url`** – {string} – action specific `url` override. The url templating is supported just + * like for the resource-level urls. + * - **`isArray`** – {boolean=} – If true then the returned object for this action is an array, + * see `returns` section. + * - **`transformRequest`** – + * `{function(data, headersGetter)|Array.}` – + * 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 it using `angular.toJson`. To prevent this behavior, set + * `transformRequest` to an empty array: `transformRequest: []` + * - **`transformResponse`** – + * `{function(data, headersGetter, status)|Array.}` – + * 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. + * 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} is supplied, this cache will be used for + * caching. + * - **`timeout`** – `{number}` – timeout in milliseconds.
    + * **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are + * **not** supported in $resource, because the same value would be used for multiple requests. + * If you are looking for a way to cancel requests, you should use the `cancellable` option. + * - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call + * will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's + * return value. Calling `$cancelRequest()` for a non-cancellable or an already + * completed/cancelled request will have no effect.
    + * - **`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}. In addition, the + * resource instance or array object is accessible by the `resource` property of the + * `http response` object. + * Keep in mind that the associated promise will be resolved with the value returned by the + * response interceptor, if one is specified. The default response interceptor returns + * `response.resource` (i.e. the resource instance or array). + * - **`hasBody`** - `{boolean}` - allows to specify if a request body should be included or not. + * If not specified only POST, PUT and PATCH requests will have a body. + * + * @param {Object} options Hash with custom settings that should extend the + * default `$resourceProvider` behavior. The supported options are: + * + * - **`stripTrailingSlashes`** – {boolean} – If true then the trailing + * slashes from any calculated URL will be stripped. (Defaults to true.) + * - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be + * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value. + * This can be overwritten per action. (Defaults to false.) + * + * @returns {Object} A resource "class" object with methods for the default set of resource actions + * optionally extended with custom `actions`. The default set contains these actions: + * ```js + * { 'get': {method:'GET'}, + * 'save': {method:'POST'}, + * 'query': {method:'GET', isArray:true}, + * 'remove': {method:'DELETE'}, + * 'delete': {method:'DELETE'} }; + * ``` + * + * Calling these methods invoke an {@link ng.$http} with the specified http method, + * destination and parameters. When the data is returned from the server then the object is an + * instance of the resource class. The actions `save`, `remove` and `delete` are available on it + * as methods with the `$` prefix. This allows you to easily perform CRUD operations (create, + * read, update, delete) on server-side data like this: + * ```js + * var User = $resource('/user/:userId', {userId:'@id'}); + * var user = User.get({userId:123}, function() { + * user.abc = true; + * user.$save(); + * }); + * ``` + * + * It is important to realize that invoking a $resource object method immediately returns an + * empty reference (object or array depending on `isArray`). Once the data is returned from the + * server the existing reference is populated with the actual data. This is a useful trick since + * usually the resource is assigned to a model which is then rendered by the view. Having an empty + * object results in no rendering, once the data arrives from the server then the object is + * populated with the data and the view automatically re-renders itself showing the new data. This + * means that in most cases one never has to write a callback function for the action methods. + * + * The action methods on the class object or instance object can be invoked with the following + * parameters: + * + * - "class" actions without a body: `Resource.action([parameters], [success], [error])` + * - "class" actions with a body: `Resource.action([parameters], postData, [success], [error])` + * - instance actions: `instance.$action([parameters], [success], [error])` + * + * + * When calling instance methods, the instance itself is used as the request body (if the action + * should have a body). By default, only actions using `POST`, `PUT` or `PATCH` have request + * bodies, but you can use the `hasBody` configuration option to specify whether an action + * should have a body or not (regardless of its HTTP method). + * + * + * Success callback is called with (value (Object|Array), responseHeaders (Function), + * status (number), statusText (string)) arguments, where the value is the populated resource + * instance or collection object. The error callback is called with (httpResponse) argument. + * + * Class actions return empty instance (with additional properties below). + * Instance actions return promise of the action. + * + * The Resource instances and collections have these additional properties: + * + * - `$promise`: the {@link ng.$q promise} of the original server interaction that created this + * instance or collection. + * + * On success, the promise is resolved with the same resource instance or collection object, + * updated with data from server. This makes it easy to use in + * {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view + * rendering until the resource(s) are loaded. + * + * On failure, the promise is rejected with the {@link ng.$http http response} object. + * + * If an interceptor object was provided, the promise will instead be resolved with the value + * returned by the interceptor. + * + * - `$resolved`: `true` after first server interaction is completed (either with success or + * rejection), `false` before that. Knowing if the Resource has been resolved is useful in + * data-binding. + * + * The Resource instances and collections have these additional methods: + * + * - `$cancelRequest`: If there is a cancellable, pending request related to the instance or + * collection, calling this method will abort the request. + * + * The Resource instances have these additional methods: + * + * - `toJSON`: It returns a simple object without any of the extra properties added as part of + * the Resource API. This object can be serialized through {@link angular.toJson} safely + * without attaching Angular-specific fields. Notice that `JSON.stringify` (and + * `angular.toJson`) automatically use this method when serializing a Resource instance + * (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON%28%29_behavior)). + * + * @example + * + * ### Credit card resource + * + * ```js + // Define CreditCard class + var CreditCard = $resource('/user/:userId/card/:cardId', + {userId:123, cardId:'@id'}, { + charge: {method:'POST', params:{charge:true}} + }); + + // We can retrieve a collection from the server + var cards = CreditCard.query(function() { + // GET: /user/123/card + // server returns: [ {id:456, number:'1234', name:'Smith'} ]; + + var card = cards[0]; + // each item is an instance of CreditCard + expect(card instanceof CreditCard).toEqual(true); + card.name = "J. Smith"; + // non GET methods are mapped onto the instances + card.$save(); + // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'} + // server returns: {id:456, number:'1234', name: 'J. Smith'}; + + // our custom method is mapped as well. + card.$charge({amount:9.99}); + // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'} + }); + + // we can create an instance as well + var newCard = new CreditCard({number:'0123'}); + newCard.name = "Mike Smith"; + newCard.$save(); + // POST: /user/123/card {number:'0123', name:'Mike Smith'} + // server returns: {id:789, number:'0123', name: 'Mike Smith'}; + expect(newCard.id).toEqual(789); + * ``` + * + * The object returned from this function execution is a resource "class" which has "static" method + * for each action in the definition. + * + * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and + * `headers`. + * + * @example + * + * ### User resource + * + * When the data is returned from the server then the object is an instance of the resource type and + * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD + * operations (create, read, update, delete) on server-side data. + + ```js + var User = $resource('/user/:userId', {userId:'@id'}); + User.get({userId:123}, function(user) { + user.abc = true; + user.$save(); + }); + ``` + * + * It's worth noting that the success callback for `get`, `query` and other methods gets passed + * in the response that came from the server as well as $http header getter function, so one + * could rewrite the above example and get access to http headers as: + * + ```js + var User = $resource('/user/:userId', {userId:'@id'}); + User.get({userId:123}, function(user, getResponseHeaders){ + user.abc = true; + user.$save(function(user, putResponseHeaders) { + //user => saved user object + //putResponseHeaders => $http header getter + }); + }); + ``` + * + * You can also access the raw `$http` promise via the `$promise` property on the object returned + * + ``` + var User = $resource('/user/:userId', {userId:'@id'}); + User.get({userId:123}) + .$promise.then(function(user) { + $scope.user = user; + }); + ``` + * + * @example + * + * ### Creating a custom 'PUT' request + * + * In this example we create a custom method on our resource to make a PUT request + * ```js + * var app = angular.module('app', ['ngResource', 'ngRoute']); + * + * // Some APIs expect a PUT request in the format URL/object/ID + * // Here we are creating an 'update' method + * app.factory('Notes', ['$resource', function($resource) { + * return $resource('/notes/:id', null, + * { + * 'update': { method:'PUT' } + * }); + * }]); + * + * // In our controller we get the ID from the URL using ngRoute and $routeParams + * // We pass in $routeParams and our Notes factory along with $scope + * app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes', + function($scope, $routeParams, Notes) { + * // First get a note object from the factory + * var note = Notes.get({ id:$routeParams.id }); + * $id = note.id; + * + * // Now call update passing in the ID first then the object you are updating + * Notes.update({ id:$id }, note); + * + * // This will PUT /notes/ID with the note object in the request payload + * }]); + * ``` + * + * @example + * + * ### Cancelling requests + * + * If an action's configuration specifies that it is cancellable, you can cancel the request related + * to an instance or collection (as long as it is a result of a "non-instance" call): + * + ```js + // ...defining the `Hotel` resource... + var Hotel = $resource('/api/hotel/:id', {id: '@id'}, { + // Let's make the `query()` method cancellable + query: {method: 'get', isArray: true, cancellable: true} + }); + + // ...somewhere in the PlanVacationController... + ... + this.onDestinationChanged = function onDestinationChanged(destination) { + // We don't care about any pending request for hotels + // in a different destination any more + this.availableHotels.$cancelRequest(); + + // Let's query for hotels in '' + // (calls: /api/hotel?location=) + this.availableHotels = Hotel.query({location: destination}); + }; + ``` + * + */ +angular.module('ngResource', ['ng']). + info({ angularVersion: '1.6.7' }). + provider('$resource', function ResourceProvider() { + var PROTOCOL_AND_IPV6_REGEX = /^https?:\/\/\[[^\]]*][^/]*/; + + var provider = this; + + /** + * @ngdoc property + * @name $resourceProvider#defaults + * @description + * Object containing default options used when creating `$resource` instances. + * + * The default values satisfy a wide range of usecases, but you may choose to overwrite any of + * them to further customize your instances. The available properties are: + * + * - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any + * calculated URL will be stripped.
    + * (Defaults to true.) + * - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be + * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return + * value. For more details, see {@link ngResource.$resource}. This can be overwritten per + * resource class or action.
    + * (Defaults to false.) + * - **actions** - `{Object.}` - A hash with default actions declarations. Actions are + * high-level methods corresponding to RESTful actions/methods on resources. An action may + * specify what HTTP method to use, what URL to hit, if the return value will be a single + * object or a collection (array) of objects etc. For more details, see + * {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource + * class.
    + * The default actions are: + * ```js + * { + * get: {method: 'GET'}, + * save: {method: 'POST'}, + * query: {method: 'GET', isArray: true}, + * remove: {method: 'DELETE'}, + * delete: {method: 'DELETE'} + * } + * ``` + * + * #### Example + * + * For example, you can specify a new `update` action that uses the `PUT` HTTP verb: + * + * ```js + * angular. + * module('myApp'). + * config(['$resourceProvider', function ($resourceProvider) { + * $resourceProvider.defaults.actions.update = { + * method: 'PUT' + * }; + * }]); + * ``` + * + * Or you can even overwrite the whole `actions` list and specify your own: + * + * ```js + * angular. + * module('myApp'). + * config(['$resourceProvider', function ($resourceProvider) { + * $resourceProvider.defaults.actions = { + * create: {method: 'POST'}, + * get: {method: 'GET'}, + * getAll: {method: 'GET', isArray:true}, + * update: {method: 'PUT'}, + * delete: {method: 'DELETE'} + * }; + * }); + * ``` + * + */ + this.defaults = { + // Strip slashes by default + stripTrailingSlashes: true, + + // Make non-instance requests cancellable (via `$cancelRequest()`) + cancellable: false, + + // Default actions configuration + actions: { + 'get': {method: 'GET'}, + 'save': {method: 'POST'}, + 'query': {method: 'GET', isArray: true}, + 'remove': {method: 'DELETE'}, + 'delete': {method: 'DELETE'} + } + }; + + this.$get = ['$http', '$log', '$q', '$timeout', function($http, $log, $q, $timeout) { + + var noop = angular.noop, + forEach = angular.forEach, + extend = angular.extend, + copy = angular.copy, + isArray = angular.isArray, + isDefined = angular.isDefined, + isFunction = angular.isFunction, + isNumber = angular.isNumber, + encodeUriQuery = angular.$$encodeUriQuery, + encodeUriSegment = angular.$$encodeUriSegment; + + 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, + protocolAndIpv6 = ''; + + var urlParams = self.urlParams = Object.create(null); + forEach(url.split(/\W/), function(param) { + if (param === 'hasOwnProperty') { + throw $resourceMinErr('badname', 'hasOwnProperty is not a valid parameter name.'); + } + if (!(new RegExp('^\\d+$').test(param)) && param && + (new RegExp('(^|[^\\\\]):' + param + '(\\W|$)').test(url))) { + urlParams[param] = { + isQueryParamValue: (new RegExp('\\?.*=:' + param + '(?:\\W|$)')).test(url) + }; + } + }); + url = url.replace(/\\:/g, ':'); + url = url.replace(PROTOCOL_AND_IPV6_REGEX, function(match) { + protocolAndIpv6 = match; + return ''; + }); + + params = params || {}; + forEach(self.urlParams, function(paramInfo, urlParam) { + val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; + if (isDefined(val) && val !== null) { + if (paramInfo.isQueryParamValue) { + encodedVal = encodeUriQuery(val, true); + } else { + encodedVal = encodeUriSegment(val); + } + url = url.replace(new RegExp(':' + urlParam + '(\\W|$)', 'g'), function(match, p1) { + return encodedVal + p1; + }); + } else { + url = url.replace(new RegExp('(/?):' + urlParam + '(\\W|$)', 'g'), function(match, + leadingSlashes, tail) { + if (tail.charAt(0) === '/') { + return tail; + } else { + return leadingSlashes + tail; + } + }); + } + }); + + // strip trailing slashes and set the url (unless this behavior is specifically disabled) + if (self.defaults.stripTrailingSlashes) { + url = url.replace(/\/+$/, '') || '/'; + } + + // 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 `/.`. + // (If `\.` comes from a param value, it will be encoded as `%5C.`.) + config.url = protocolAndIpv6 + url.replace(/\/(\\|%5C)\./, '/.'); + + + // set params - delegate param encoding to $http + forEach(params, function(value, key) { + if (!self.urlParams[key]) { + config.params = config.params || {}; + config.params[key] = value; + } + }); + } + }; + + + function resourceFactory(url, paramDefaults, actions, options) { + var route = new Route(url, options); + + actions = extend({}, provider.defaults.actions, actions); + + function extractParams(data, actionParams) { + var ids = {}; + actionParams = extend({}, paramDefaults, actionParams); + forEach(actionParams, function(value, key) { + if (isFunction(value)) { value = value(data); } + ids[key] = value && value.charAt && value.charAt(0) === '@' ? + lookupDottedPath(data, value.substr(1)) : value; + }); + return ids; + } + + function defaultResponseInterceptor(response) { + return response.resource; + } + + function Resource(value) { + shallowClearAndCopy(value || {}, this); + } + + Resource.prototype.toJSON = function() { + var data = extend({}, this); + delete data.$promise; + delete data.$resolved; + delete data.$cancelRequest; + return data; + }; + + forEach(actions, function(action, name) { + var hasBody = action.hasBody === true || (action.hasBody !== false && /^(POST|PUT|PATCH)$/i.test(action.method)); + var numericTimeout = action.timeout; + var cancellable = isDefined(action.cancellable) ? + action.cancellable : route.defaults.cancellable; + + if (numericTimeout && !isNumber(numericTimeout)) { + $log.debug('ngResource:\n' + + ' Only numeric values are allowed as `timeout`.\n' + + ' Promises are not supported in $resource, because the same value would ' + + 'be used for multiple requests. If you are looking for a way to cancel ' + + 'requests, you should use the `cancellable` option.'); + delete action.timeout; + numericTimeout = null; + } + + Resource[name] = function(a1, a2, a3, a4) { + var params = {}, data, success, error; + + switch (arguments.length) { + case 4: + error = a4; + success = a3; + // falls through + case 3: + case 2: + if (isFunction(a2)) { + if (isFunction(a1)) { + success = a1; + error = a2; + break; + } + + success = a2; + error = a3; + // falls through + } else { + params = a1; + data = a2; + success = a3; + break; + } + // falls through + 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); + } + + var isInstanceCall = this instanceof Resource; + var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data)); + var httpConfig = {}; + var responseInterceptor = action.interceptor && action.interceptor.response || + defaultResponseInterceptor; + var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || + undefined; + var hasError = !!error; + var hasResponseErrorInterceptor = !!responseErrorInterceptor; + var timeoutDeferred; + var numericTimeoutPromise; + + forEach(action, function(value, key) { + switch (key) { + default: + httpConfig[key] = copy(value); + break; + case 'params': + case 'isArray': + case 'interceptor': + case 'cancellable': + break; + } + }); + + if (!isInstanceCall && cancellable) { + timeoutDeferred = $q.defer(); + httpConfig.timeout = timeoutDeferred.promise; + + if (numericTimeout) { + numericTimeoutPromise = $timeout(timeoutDeferred.resolve, numericTimeout); + } + } + + if (hasBody) httpConfig.data = data; + route.setUrlParams(httpConfig, + extend({}, extractParams(data, action.params || {}), params), + action.url); + + var promise = $http(httpConfig).then(function(response) { + var data = response.data; + + if (data) { + // Need to convert action.isArray to boolean in case it is undefined + if (isArray(data) !== (!!action.isArray)) { + throw $resourceMinErr('badcfg', + 'Error in resource configuration for action `{0}`. Expected response to ' + + 'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object', + isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url); + } + if (action.isArray) { + value.length = 0; + forEach(data, function(item) { + if (typeof item === 'object') { + value.push(new Resource(item)); + } else { + // Valid JSON values may be string literals, and these should not be converted + // into objects. These items will not have access to the Resource prototype + // methods, but unfortunately there + value.push(item); + } + }); + } else { + var promise = value.$promise; // Save the promise + shallowClearAndCopy(data, value); + value.$promise = promise; // Restore the promise + } + } + response.resource = value; + + return response; + }, function(response) { + response.resource = value; + return $q.reject(response); + }); + + promise = promise['finally'](function() { + value.$resolved = true; + if (!isInstanceCall && cancellable) { + value.$cancelRequest = noop; + $timeout.cancel(numericTimeoutPromise); + timeoutDeferred = numericTimeoutPromise = httpConfig.timeout = null; + } + }); + + promise = promise.then( + function(response) { + var value = responseInterceptor(response); + (success || noop)(value, response.headers, response.status, response.statusText); + return value; + }, + (hasError || hasResponseErrorInterceptor) ? + function(response) { + if (hasError && !hasResponseErrorInterceptor) { + // Avoid `Possibly Unhandled Rejection` error, + // but still fulfill the returned promise with a rejection + promise.catch(noop); + } + if (hasError) error(response); + return hasResponseErrorInterceptor ? + responseErrorInterceptor(response) : + $q.reject(response); + } : + undefined); + + if (!isInstanceCall) { + // we are creating instance / collection + // - set the initial promise + // - return the instance / collection + value.$promise = promise; + value.$resolved = false; + if (cancellable) value.$cancelRequest = cancelRequest; + + return value; + } + + // instance call + return promise; + + function cancelRequest(value) { + promise.catch(noop); + if (timeoutDeferred !== null) { + timeoutDeferred.resolve(value); + } + } + }; + + + 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; + }; + }); + + return Resource; + } + + return resourceFactory; + }]; + }); + + +})(window, window.angular); + +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.angularCreditCards = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= new Date(year, month) +} + +function parseMonth (month) { + return parseIntStrict(month) +} + +function formatExpYear (year, strip) { + year = year.toString() + return strip ? year.substr(2, 4) : year +} + +function isExpYearValid (year) { + if (typeof year !== 'number') return false + year = parseIntStrict(year) + return year > 0 +} + +function isExpYearPast (year) { + return new Date().getFullYear() > year +} + +},{"is-valid-month":21,"parse-int":24,"parse-year":25}],13:[function(_dereq_,module,exports){ +'use strict' + +module.exports = { + card: _dereq_('./card'), + cvc: _dereq_('./cvc'), + expiration: _dereq_('./expiration') +} + +},{"./card":10,"./cvc":11,"./expiration":12}],14:[function(_dereq_,module,exports){ +'use strict' + +var ccTypes = _dereq_('creditcards-types') +var camel = _dereq_('to-camel-case') +var extend = _dereq_('xtend') + +module.exports = extend(ccTypes, { + get: function getTypeByName (name) { + return ccTypes.types[camel(name)] + } +}) + +},{"creditcards-types":7,"to-camel-case":26,"xtend":29}],15:[function(_dereq_,module,exports){ +'use strict' + +var zeroFill = _dereq_('zero-fill') +var parseIntStrict = _dereq_('parse-int') + +var pad = zeroFill(2) + +module.exports = function expandYear (year, now) { + now = now || new Date() + var base = now.getFullYear().toString().substr(0, 2) + year = parseIntStrict(year) + return parseIntStrict(base + pad(year)) +} + +},{"parse-int":24,"zero-fill":31}],16:[function(_dereq_,module,exports){ +'use strict' + +module.exports = (function (array) { + return function luhn (number) { + if (typeof number !== 'string') throw new TypeError('Expected string input') + if (!number) return false + var length = number.length + var bit = 1 + var sum = 0 + var value + + while (length) { + value = parseInt(number.charAt(--length), 10) + sum += (bit ^= 1) ? array[value] : value + } + + return !!sum && sum % 10 === 0 + } +}([0, 2, 4, 6, 8, 1, 3, 5, 7, 9])) + +},{}],17:[function(_dereq_,module,exports){ +'use strict'; + +/* eslint no-invalid-this: 1 */ + +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var funcType = '[object Function]'; + +module.exports = function bind(that) { + var target = this; + if (typeof target !== 'function' || toStr.call(target) !== funcType) { + throw new TypeError(ERROR_MESSAGE + target); + } + var args = slice.call(arguments, 1); + + var bound; + var binder = function () { + if (this instanceof bound) { + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + } + }; + + var boundLength = Math.max(0, target.length - args.length); + var boundArgs = []; + for (var i = 0; i < boundLength; i++) { + boundArgs.push('$' + i); + } + + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + + if (target.prototype) { + var Empty = function Empty() {}; + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + + return bound; +}; + +},{}],18:[function(_dereq_,module,exports){ +'use strict'; + +var implementation = _dereq_('./implementation'); + +module.exports = Function.prototype.bind || implementation; + +},{"./implementation":17}],19:[function(_dereq_,module,exports){ +'use strict'; +var numberIsNan = _dereq_('number-is-nan'); + +module.exports = Number.isFinite || function (val) { + return !(typeof val !== 'number' || numberIsNan(val) || val === Infinity || val === -Infinity); +}; + +},{"number-is-nan":23}],20:[function(_dereq_,module,exports){ +// https://github.com/paulmillr/es6-shim +// http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isinteger +var isFinite = _dereq_("is-finite"); +module.exports = Number.isInteger || function(val) { + return typeof val === "number" && + isFinite(val) && + Math.floor(val) === val; +}; + +},{"is-finite":19}],21:[function(_dereq_,module,exports){ +'use strict' + +var isInteger = _dereq_('is-integer') + +module.exports = function isValidMonth (month) { + if (typeof month !== 'number' || !isInteger(month)) return false + return month >= 1 && month <= 12 +} + +},{"is-integer":20}],22:[function(_dereq_,module,exports){ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; + +},{}],23:[function(_dereq_,module,exports){ +'use strict'; +module.exports = Number.isNaN || function (x) { + return x !== x; +}; + +},{}],24:[function(_dereq_,module,exports){ +'use strict' + +var isInteger = _dereq_('is-integer') + +module.exports = function parseIntStrict (integer) { + if (typeof integer === 'number') { + return isInteger(integer) ? integer : undefined + } + if (typeof integer === 'string') { + return /^-?\d+$/.test(integer) ? parseInt(integer, 10) : undefined + } +} + +},{"is-integer":20}],25:[function(_dereq_,module,exports){ +'use strict' + +var parseIntStrict = _dereq_('parse-int') +var expandYear = _dereq_('expand-year') + +module.exports = function parseYear (year, expand, now) { + year = parseIntStrict(year) + if (year == null) return + if (!expand) return year + return expandYear(year, now) +} + +},{"expand-year":15,"parse-int":24}],26:[function(_dereq_,module,exports){ + +var space = _dereq_('to-space-case') + +/** + * Export. + */ + +module.exports = toCamelCase + +/** + * Convert a `string` to camel case. + * + * @param {String} string + * @return {String} + */ + +function toCamelCase(string) { + return space(string).replace(/\s(\w)/g, function (matches, letter) { + return letter.toUpperCase() + }) +} + +},{"to-space-case":28}],27:[function(_dereq_,module,exports){ + +/** + * Export. + */ + +module.exports = toNoCase + +/** + * Test whether a string is camel-case. + */ + +var hasSpace = /\s/ +var hasSeparator = /(_|-|\.|:)/ +var hasCamel = /([a-z][A-Z]|[A-Z][a-z])/ + +/** + * Remove any starting case from a `string`, like camel or snake, but keep + * spaces and punctuation that may be important otherwise. + * + * @param {String} string + * @return {String} + */ + +function toNoCase(string) { + if (hasSpace.test(string)) return string.toLowerCase() + if (hasSeparator.test(string)) return (unseparate(string) || string).toLowerCase() + if (hasCamel.test(string)) return uncamelize(string).toLowerCase() + return string.toLowerCase() +} + +/** + * Separator splitter. + */ + +var separatorSplitter = /[\W_]+(.|$)/g + +/** + * Un-separate a `string`. + * + * @param {String} string + * @return {String} + */ + +function unseparate(string) { + return string.replace(separatorSplitter, function (m, next) { + return next ? ' ' + next : '' + }) +} + +/** + * Camelcase splitter. + */ + +var camelSplitter = /(.)([A-Z]+)/g + +/** + * Un-camelcase a `string`. + * + * @param {String} string + * @return {String} + */ + +function uncamelize(string) { + return string.replace(camelSplitter, function (m, previous, uppers) { + return previous + ' ' + uppers.toLowerCase().split('').join(' ') + }) +} + +},{}],28:[function(_dereq_,module,exports){ + +var clean = _dereq_('to-no-case') + +/** + * Export. + */ + +module.exports = toSpaceCase + +/** + * Convert a `string` to space case. + * + * @param {String} string + * @return {String} + */ + +function toSpaceCase(string) { + return clean(string).replace(/[\W_]+(.|$)/g, function (matches, match) { + return match ? ' ' + match : '' + }).trim() +} + +},{"to-no-case":27}],29:[function(_dereq_,module,exports){ +module.exports = extend + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function extend() { + var target = {} + + for (var i = 0; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target +} + +},{}],30:[function(_dereq_,module,exports){ +module.exports = extend + +var hasOwnProperty = Object.prototype.hasOwnProperty; + +function extend(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] + + for (var key in source) { + if (hasOwnProperty.call(source, key)) { + target[key] = source[key] + } + } + } + + return target +} + +},{}],31:[function(_dereq_,module,exports){ +/** + * Given a number, return a zero-filled string. + * From http://stackoverflow.com/questions/1267283/ + * @param {number} width + * @param {number} number + * @return {string} + */ +module.exports = function zeroFill (width, number, pad) { + if (number === undefined) { + return function (number, pad) { + return zeroFill(width, number, pad) + } + } + if (pad === undefined) pad = '0' + width -= number.toString().length + if (width > 0) return new Array(width + (/\./.test(number) ? 2 : 1)).join(pad) + number + return number + '' +} + +},{}]},{},[3])(3) +}); +/** + * @license AngularJS v1.6.7 + * (c) 2010-2017 Google, Inc. http://angularjs.org + * License: MIT + */ +(function(window, angular) {'use strict'; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Any commits to this file should be reviewed with security in mind. * + * Changes to this file can potentially create security vulnerabilities. * + * An approval from 2 Core members with history of modifying * + * this file is required. * + * * + * Does the change somehow allow for arbitrary javascript to be executed? * + * Or allows for someone to change the prototype of built-in objects? * + * Or gives undesired access to variables likes document or window? * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +var $sanitizeMinErr = angular.$$minErr('$sanitize'); +var bind; +var extend; +var forEach; +var isDefined; +var lowercase; +var noop; +var nodeContains; +var htmlParser; +var htmlSanitizeWriter; + +/** + * @ngdoc module + * @name ngSanitize + * @description + * + * The `ngSanitize` module provides functionality to sanitize HTML. + * + * See {@link ngSanitize.$sanitize `$sanitize`} for usage. + */ + +/** + * @ngdoc service + * @name $sanitize + * @kind function + * + * @description + * Sanitizes an html string by stripping all potentially dangerous tokens. + * + * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are + * then serialized back to properly escaped html string. This means that no unsafe input can make + * it into the returned string. + * + * The whitelist for URL sanitization of attribute values is configured using the functions + * `aHrefSanitizationWhitelist` and `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider + * `$compileProvider`}. + * + * The input may also contain SVG markup if this is enabled via {@link $sanitizeProvider}. + * + * @param {string} html HTML input. + * @returns {string} Sanitized HTML. + * + * @example + + + +
    + Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + +
    DirectiveHowSourceRendered
    ng-bind-htmlAutomatically uses $sanitize
    <div ng-bind-html="snippet">
    </div>
    ng-bind-htmlBypass $sanitize by explicitly trusting the dangerous value +
    <div ng-bind-html="deliberatelyTrustDangerousSnippet()">
    +</div>
    +
    ng-bindAutomatically escapes
    <div ng-bind="snippet">
    </div>
    +
    +
    + + it('should sanitize the html snippet by default', function() { + expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). + toBe('

    an html\nclick here\nsnippet

    '); + }); + + it('should inline raw snippet if bound to a trusted value', function() { + expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')). + toBe("

    an html\n" + + "click here\n" + + "snippet

    "); + }); + + it('should escape snippet without any filter', function() { + expect(element(by.css('#bind-default div')).getAttribute('innerHTML')). + toBe("<p style=\"color:blue\">an html\n" + + "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" + + "snippet</p>"); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new text'); + expect(element(by.css('#bind-html-with-sanitize div')).getAttribute('innerHTML')). + toBe('new text'); + expect(element(by.css('#bind-html-with-trust div')).getAttribute('innerHTML')).toBe( + 'new text'); + expect(element(by.css('#bind-default div')).getAttribute('innerHTML')).toBe( + "new <b onclick=\"alert(1)\">text</b>"); + }); +
    +
    + */ + + +/** + * @ngdoc provider + * @name $sanitizeProvider + * @this + * + * @description + * Creates and configures {@link $sanitize} instance. + */ +function $SanitizeProvider() { + var svgEnabled = false; + + this.$get = ['$$sanitizeUri', function($$sanitizeUri) { + if (svgEnabled) { + extend(validElements, svgElements); + } + return function(html) { + var buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) { + return !/^unsafe:/.test($$sanitizeUri(uri, isImage)); + })); + return buf.join(''); + }; + }]; + + + /** + * @ngdoc method + * @name $sanitizeProvider#enableSvg + * @kind function + * + * @description + * Enables a subset of svg to be supported by the sanitizer. + * + *
    + *

    By enabling this setting without taking other precautions, you might expose your + * application to click-hijacking attacks. In these attacks, sanitized svg elements could be positioned + * outside of the containing element and be rendered over other elements on the page (e.g. a login + * link). Such behavior can then result in phishing incidents.

    + * + *

    To protect against these, explicitly setup `overflow: hidden` css rule for all potential svg + * tags within the sanitized content:

    + * + *
    + * + *
    
    +   *   .rootOfTheIncludedContent svg {
    +   *     overflow: hidden !important;
    +   *   }
    +   *   
    + *
    + * + * @param {boolean=} flag Enable or disable SVG support in the sanitizer. + * @returns {boolean|ng.$sanitizeProvider} Returns the currently configured value if called + * without an argument or self for chaining otherwise. + */ + this.enableSvg = function(enableSvg) { + if (isDefined(enableSvg)) { + svgEnabled = enableSvg; + return this; + } else { + return svgEnabled; + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + // Private stuff + ////////////////////////////////////////////////////////////////////////////////////////////////// + + bind = angular.bind; + extend = angular.extend; + forEach = angular.forEach; + isDefined = angular.isDefined; + lowercase = angular.lowercase; + noop = angular.noop; + + htmlParser = htmlParserImpl; + htmlSanitizeWriter = htmlSanitizeWriterImpl; + + nodeContains = window.Node.prototype.contains || /** @this */ function(arg) { + // eslint-disable-next-line no-bitwise + return !!(this.compareDocumentPosition(arg) & 16); + }; + + // Regular Expressions for parsing tags and attributes + var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^#-~ |!])/g; + + + // Good source of info about elements and attributes + // http://dev.w3.org/html5/spec/Overview.html#semantics + // http://simon.html5.org/html-elements + + // Safe Void Elements - HTML5 + // http://dev.w3.org/html5/spec/Overview.html#void-elements + var voidElements = toMap('area,br,col,hr,img,wbr'); + + // Elements that you can, intentionally, leave open (and which close themselves) + // http://dev.w3.org/html5/spec/Overview.html#optional-tags + var optionalEndTagBlockElements = toMap('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr'), + optionalEndTagInlineElements = toMap('rp,rt'), + optionalEndTagElements = extend({}, + optionalEndTagInlineElements, + optionalEndTagBlockElements); + + // Safe Block Elements - HTML5 + var blockElements = extend({}, optionalEndTagBlockElements, toMap('address,article,' + + 'aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' + + 'h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul')); + + // Inline Elements - HTML5 + var inlineElements = extend({}, optionalEndTagInlineElements, toMap('a,abbr,acronym,b,' + + 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,' + + 'samp,small,span,strike,strong,sub,sup,time,tt,u,var')); + + // SVG Elements + // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements + // Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. + // They can potentially allow for arbitrary javascript to be executed. See #11290 + var svgElements = toMap('circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,' + + 'hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,' + + 'radialGradient,rect,stop,svg,switch,text,title,tspan'); + + // Blocked Elements (will be stripped) + var blockedElements = toMap('script,style'); + + var validElements = extend({}, + voidElements, + blockElements, + inlineElements, + optionalEndTagElements); + + //Attributes that have href and hence need to be sanitized + var uriAttrs = toMap('background,cite,href,longdesc,src,xlink:href'); + + var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + + 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + + 'valign,value,vspace,width'); + + // SVG attributes (without "id" and "name" attributes) + // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes + var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + + 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + + 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + + 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + + 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + + 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + + 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + + 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + + 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + + 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + + 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + + 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + + 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + + 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + + 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); + + var validAttrs = extend({}, + uriAttrs, + svgAttrs, + htmlAttrs); + + function toMap(str, lowercaseKeys) { + var obj = {}, items = str.split(','), i; + for (i = 0; i < items.length; i++) { + obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true; + } + return obj; + } + + /** + * Create an inert document that contains the dirty HTML that needs sanitizing + * Depending upon browser support we use one of three strategies for doing this. + * Support: Safari 10.x -> XHR strategy + * Support: Firefox -> DomParser strategy + */ + var getInertBodyElement /* function(html: string): HTMLBodyElement */ = (function(window, document) { + var inertDocument; + if (document && document.implementation) { + inertDocument = document.implementation.createHTMLDocument('inert'); + } else { + throw $sanitizeMinErr('noinert', 'Can\'t create an inert html document'); + } + var inertBodyElement = (inertDocument.documentElement || inertDocument.getDocumentElement()).querySelector('body'); + + // Check for the Safari 10.1 bug - which allows JS to run inside the SVG G element + inertBodyElement.innerHTML = ''; + if (!inertBodyElement.querySelector('svg')) { + return getInertBodyElement_XHR; + } else { + // Check for the Firefox bug - which prevents the inner img JS from being sanitized + inertBodyElement.innerHTML = '

    '; + if (inertBodyElement.querySelector('svg img')) { + return getInertBodyElement_DOMParser; + } else { + return getInertBodyElement_InertDocument; + } + } + + function getInertBodyElement_XHR(html) { + // We add this dummy element to ensure that the rest of the content is parsed as expected + // e.g. leading whitespace is maintained and tags like `` do not get hoisted to the `` tag. + html = '' + html; + try { + html = encodeURI(html); + } catch (e) { + return undefined; + } + var xhr = new window.XMLHttpRequest(); + xhr.responseType = 'document'; + xhr.open('GET', 'data:text/html;charset=utf-8,' + html, false); + xhr.send(null); + var body = xhr.response.body; + body.firstChild.remove(); + return body; + } + + function getInertBodyElement_DOMParser(html) { + // We add this dummy element to ensure that the rest of the content is parsed as expected + // e.g. leading whitespace is maintained and tags like `` do not get hoisted to the `` tag. + html = '' + html; + try { + var body = new window.DOMParser().parseFromString(html, 'text/html').body; + body.firstChild.remove(); + return body; + } catch (e) { + return undefined; + } + } + + function getInertBodyElement_InertDocument(html) { + inertBodyElement.innerHTML = html; + + // Support: IE 9-11 only + // strip custom-namespaced attributes on IE<=11 + if (document.documentMode) { + stripCustomNsAttrs(inertBodyElement); + } + + return inertBodyElement; + } + })(window, window.document); + + /** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ + function htmlParserImpl(html, handler) { + if (html === null || html === undefined) { + html = ''; + } else if (typeof html !== 'string') { + html = '' + html; + } + + var inertBodyElement = getInertBodyElement(html); + if (!inertBodyElement) return ''; + + //mXSS protection + var mXSSAttempts = 5; + do { + if (mXSSAttempts === 0) { + throw $sanitizeMinErr('uinput', 'Failed to sanitize html because the input is unstable'); + } + mXSSAttempts--; + + // trigger mXSS if it is going to happen by reading and writing the innerHTML + html = inertBodyElement.innerHTML; + inertBodyElement = getInertBodyElement(html); + } while (html !== inertBodyElement.innerHTML); + + var node = inertBodyElement.firstChild; + while (node) { + switch (node.nodeType) { + case 1: // ELEMENT_NODE + handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes)); + break; + case 3: // TEXT NODE + handler.chars(node.textContent); + break; + } + + var nextNode; + if (!(nextNode = node.firstChild)) { + if (node.nodeType === 1) { + handler.end(node.nodeName.toLowerCase()); + } + nextNode = getNonDescendant('nextSibling', node); + if (!nextNode) { + while (nextNode == null) { + node = getNonDescendant('parentNode', node); + if (node === inertBodyElement) break; + nextNode = getNonDescendant('nextSibling', node); + if (node.nodeType === 1) { + handler.end(node.nodeName.toLowerCase()); + } + } + } + } + node = nextNode; + } + + while ((node = inertBodyElement.firstChild)) { + inertBodyElement.removeChild(node); + } + } + + function attrToMap(attrs) { + var map = {}; + for (var i = 0, ii = attrs.length; i < ii; i++) { + var attr = attrs[i]; + map[attr.name] = attr.value; + } + return map; + } + + + /** + * Escapes all potentially dangerous characters, so that the + * resulting string can be safely inserted into attribute or + * element text. + * @param value + * @returns {string} escaped text + */ + function encodeEntities(value) { + return value. + replace(/&/g, '&'). + replace(SURROGATE_PAIR_REGEXP, function(value) { + var hi = value.charCodeAt(0); + var low = value.charCodeAt(1); + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'; + }). + replace(NON_ALPHANUMERIC_REGEXP, function(value) { + return '&#' + value.charCodeAt(0) + ';'; + }). + replace(//g, '>'); + } + + /** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.join('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ + function htmlSanitizeWriterImpl(buf, uriValidator) { + var ignoreCurrentElement = false; + var out = bind(buf, buf.push); + return { + start: function(tag, attrs) { + tag = lowercase(tag); + if (!ignoreCurrentElement && blockedElements[tag]) { + ignoreCurrentElement = tag; + } + if (!ignoreCurrentElement && validElements[tag] === true) { + out('<'); + out(tag); + forEach(attrs, function(value, key) { + var lkey = lowercase(key); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out('>'); + } + }, + end: function(tag) { + tag = lowercase(tag); + if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) { + out(''); + } + // eslint-disable-next-line eqeqeq + if (tag == ignoreCurrentElement) { + ignoreCurrentElement = false; + } + }, + chars: function(chars) { + if (!ignoreCurrentElement) { + out(encodeEntities(chars)); + } + } + }; + } + + + /** + * When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare + * ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want + * to allow any of these custom attributes. This method strips them all. + * + * @param node Root element to process + */ + function stripCustomNsAttrs(node) { + while (node) { + if (node.nodeType === window.Node.ELEMENT_NODE) { + var attrs = node.attributes; + for (var i = 0, l = attrs.length; i < l; i++) { + var attrNode = attrs[i]; + var attrName = attrNode.name.toLowerCase(); + if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) { + node.removeAttributeNode(attrNode); + i--; + l--; + } + } + } + + var nextNode = node.firstChild; + if (nextNode) { + stripCustomNsAttrs(nextNode); + } + + node = getNonDescendant('nextSibling', node); + } + } + + function getNonDescendant(propName, node) { + // An element is clobbered if its `propName` property points to one of its descendants + var nextNode = node[propName]; + if (nextNode && nodeContains.call(node, nextNode)) { + throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText); + } + return nextNode; + } +} + +function sanitizeText(chars) { + var buf = []; + var writer = htmlSanitizeWriter(buf, noop); + writer.chars(chars); + return buf.join(''); +} + + +// define ngSanitize module and register $sanitize service +angular.module('ngSanitize', []) + .provider('$sanitize', $SanitizeProvider) + .info({ angularVersion: '1.6.7' }); + +/** + * @ngdoc filter + * @name linky + * @kind function + * + * @description + * Finds links in text input and turns them into html links. Supports `http/https/ftp/sftp/mailto` and + * plain email address links. + * + * Requires the {@link ngSanitize `ngSanitize`} module to be installed. + * + * @param {string} text Input text. + * @param {string} [target] Window (`_blank|_self|_parent|_top`) or named frame to open links in. + * @param {object|function(url)} [attributes] Add custom attributes to the link element. + * + * Can be one of: + * + * - `object`: A map of attributes + * - `function`: Takes the url as a parameter and returns a map of attributes + * + * If the map of attributes contains a value for `target`, it overrides the value of + * the target parameter. + * + * + * @returns {string} Html-linkified and {@link $sanitize sanitized} text. + * + * @usage + + * + * @example + + +

    + Snippet: + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FilterSourceRendered
    linky filter +
    <div ng-bind-html="snippet | linky">
    </div>
    +
    +
    +
    linky target +
    <div ng-bind-html="snippetWithSingleURL | linky:'_blank'">
    </div>
    +
    +
    +
    linky custom attributes +
    <div ng-bind-html="snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}">
    </div>
    +
    +
    +
    no filter
    <div ng-bind="snippet">
    </div>
    + + + angular.module('linkyExample', ['ngSanitize']) + .controller('ExampleController', ['$scope', function($scope) { + $scope.snippet = + 'Pretty text with some links:\n' + + 'http://angularjs.org/,\n' + + 'mailto:us@somewhere.org,\n' + + 'another@somewhere.org,\n' + + 'and one more: ftp://127.0.0.1/.'; + $scope.snippetWithSingleURL = 'http://angularjs.org/'; + }]); + + + it('should linkify the snippet with urls', function() { + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(4); + }); + + it('should not linkify snippet without the linky filter', function() { + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()). + toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' + + 'another@somewhere.org, and one more: ftp://127.0.0.1/.'); + expect(element.all(by.css('#escaped-html a')).count()).toEqual(0); + }); + + it('should update', function() { + element(by.model('snippet')).clear(); + element(by.model('snippet')).sendKeys('new http://link.'); + expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()). + toBe('new http://link.'); + expect(element.all(by.css('#linky-filter a')).count()).toEqual(1); + expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()) + .toBe('new http://link.'); + }); + + it('should work with the target property', function() { + expect(element(by.id('linky-target')). + element(by.binding("snippetWithSingleURL | linky:'_blank'")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank'); + }); + + it('should optionally add custom attributes', function() { + expect(element(by.id('linky-custom-attributes')). + element(by.binding("snippetWithSingleURL | linky:'_self':{rel: 'nofollow'}")).getText()). + toBe('http://angularjs.org/'); + expect(element(by.css('#linky-custom-attributes a')).getAttribute('rel')).toEqual('nofollow'); + }); + + + */ +angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) { + var LINKY_URL_REGEXP = + /((s?ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"\u201d\u2019]/i, + MAILTO_REGEXP = /^mailto:/i; + + var linkyMinErr = angular.$$minErr('linky'); + var isDefined = angular.isDefined; + var isFunction = angular.isFunction; + var isObject = angular.isObject; + var isString = angular.isString; + + return function(text, target, attributes) { + if (text == null || text === '') return text; + if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text); + + var attributesFn = + isFunction(attributes) ? attributes : + isObject(attributes) ? function getAttributesObject() {return attributes;} : + function getEmptyAttributesObject() {return {};}; + + var match; + var raw = text; + var html = []; + var url; + var i; + while ((match = raw.match(LINKY_URL_REGEXP))) { + // We can not end in these as they are sometimes found at the end of the sentence + url = match[0]; + // if we did not match ftp/http/www/mailto then assume mailto + if (!match[2] && !match[4]) { + url = (match[3] ? 'http://' : 'mailto:') + url; + } + i = match.index; + addText(raw.substr(0, i)); + addLink(url, match[0].replace(MAILTO_REGEXP, '')); + raw = raw.substring(i + match[0].length); + } + addText(raw); + return $sanitize(html.join('')); + + function addText(text) { + if (!text) { + return; + } + html.push(sanitizeText(text)); + } + + function addLink(url, text) { + var key, linkAttributes = attributesFn(url); + html.push(''); + addText(text); + html.push(''); + } + }; +}]); + + +})(window, window.angular); + +/* + * angular-ui-bootstrap + * http://angular-ui.github.io/bootstrap/ + + * Version: 2.5.6 - 2017-10-14 + * License: MIT + */angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.tabindex","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.datepicker","ui.bootstrap.position","ui.bootstrap.datepickerPopup","ui.bootstrap.debounce","ui.bootstrap.multiMap","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","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", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/year.html","uib/template/datepickerPopup/popup.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]); +angular.module('ui.bootstrap.collapse', []) + + .directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) { + var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; + return { + link: function(scope, element, attrs) { + var expandingExpr = $parse(attrs.expanding), + expandedExpr = $parse(attrs.expanded), + collapsingExpr = $parse(attrs.collapsing), + collapsedExpr = $parse(attrs.collapsed), + horizontal = false, + css = {}, + cssTo = {}; + + init(); + + function init() { + horizontal = !!('horizontal' in attrs); + if (horizontal) { + css = { + width: '' + }; + cssTo = {width: '0'}; + } else { + css = { + height: '' + }; + cssTo = {height: '0'}; + } + if (!scope.$eval(attrs.uibCollapse)) { + element.addClass('in') + .addClass('collapse') + .attr('aria-expanded', true) + .attr('aria-hidden', false) + .css(css); + } + } + + function getScrollFromElement(element) { + if (horizontal) { + return {width: element.scrollWidth + 'px'}; + } + return {height: element.scrollHeight + 'px'}; + } + + function expand() { + if (element.hasClass('collapse') && element.hasClass('in')) { + return; + } + + $q.resolve(expandingExpr(scope)) + .then(function() { + element.removeClass('collapse') + .addClass('collapsing') + .attr('aria-expanded', true) + .attr('aria-hidden', false); + + if ($animateCss) { + $animateCss(element, { + addClass: 'in', + easing: 'ease', + css: { + overflow: 'hidden' + }, + to: getScrollFromElement(element[0]) + }).start()['finally'](expandDone); + } else { + $animate.addClass(element, 'in', { + css: { + overflow: 'hidden' + }, + to: getScrollFromElement(element[0]) + }).then(expandDone); + } + }, angular.noop); + } + + function expandDone() { + element.removeClass('collapsing') + .addClass('collapse') + .css(css); + expandedExpr(scope); + } + + function collapse() { + if (!element.hasClass('collapse') && !element.hasClass('in')) { + return collapseDone(); + } + + $q.resolve(collapsingExpr(scope)) + .then(function() { + element + // IMPORTANT: The width must be set before adding "collapsing" class. + // Otherwise, the browser attempts to animate from width 0 (in + // collapsing class) to the given width here. + .css(getScrollFromElement(element[0])) + // 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: cssTo + }).start()['finally'](collapseDone); + } else { + $animate.removeClass(element, 'in', { + to: cssTo + }).then(collapseDone); + } + }, angular.noop); + } + + function collapseDone() { + element.css(cssTo); // Required so that collapse works when animation is disabled + element.removeClass('collapsing') + .addClass('collapse'); + collapsedExpr(scope); + } + + scope.$watch(attrs.uibCollapse, function(shouldCollapse) { + if (shouldCollapse) { + collapse(); + } else { + expand(); + } + }); + } + }; + }]); + +angular.module('ui.bootstrap.tabindex', []) + +.directive('uibTabindexToggle', function() { + return { + restrict: 'A', + link: function(scope, elem, attrs) { + attrs.$observe('disabled', function(disabled) { + attrs.$set('tabindex', disabled ? -1 : null); + }); + } + }; +}); + +angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex']) + +.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 || 'uib/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 + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/accordion/accordion-group.html'; + }, + scope: { + heading: '@', // Interpolate the heading attribute onto this scope + panelClass: '@?', // Ditto with panelClass + isOpen: '=?', + isDisabled: '=?' + }, + controller: function() { + this.setHeading = function(element) { + this.heading = element; + }; + }, + link: function(scope, element, attrs, accordionCtrl) { + element.addClass('panel'); + accordionCtrl.addGroup(scope); + + scope.openClass = attrs.openClass || 'panel-open'; + scope.panelClass = attrs.panelClass || 'panel-default'; + 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; + } + } + }; + + var id = 'accordiongroup-' + scope.$id + '-' + Math.floor(Math.random() * 10000); + scope.headingId = id + '-tab'; + scope.panelId = id + '-panel'; + } + }; +}) + +// 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', + link: function(scope, element, attrs, controller) { + scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) { + if (heading) { + var elem = angular.element(element[0].querySelector(getHeaderSelectors())); + elem.html(''); + elem.append(heading); + } + }); + } + }; + + function getHeaderSelectors() { + return 'uib-accordion-header,' + + 'data-uib-accordion-header,' + + 'x-uib-accordion-header,' + + 'uib\\:accordion-header,' + + '[uib-accordion-header],' + + '[data-uib-accordion-header],' + + '[x-uib-accordion-header]'; + } +}); + +angular.module('ui.bootstrap.alert', []) + +.controller('UibAlertController', ['$scope', '$element', '$attrs', '$interpolate', '$timeout', function($scope, $element, $attrs, $interpolate, $timeout) { + $scope.closeable = !!$attrs.close; + $element.addClass('alert'); + $attrs.$set('role', 'alert'); + if ($scope.closeable) { + $element.addClass('alert-dismissible'); + } + + 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', + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/alert/alert.html'; + }, + transclude: true, + scope: { + close: '&' + } + }; +}); + +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', ['$parse', function($parse) { + return { + require: ['uibBtnRadio', 'ngModel'], + controller: 'UibButtonsController', + controllerAs: 'buttons', + link: function(scope, element, attrs, ctrls) { + var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + var uncheckableExpr = $parse(attrs.uibUncheckable); + + 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(); + }); + } + }); + + if (attrs.uibUncheckable) { + scope.$watch(uncheckableExpr, function(uncheckable) { + attrs.$set('uncheckable', uncheckable ? '' : undefined); + }); + } + } + }; +}]) + +.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(); + }); + }); + } + }; +}); + +angular.module('ui.bootstrap.carousel', []) + +.controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) { + var self = this, + slides = self.slides = $scope.slides = [], + SLIDE_DIRECTION = 'uib-slideDirection', + currentIndex = $scope.active, + currentInterval, isPlaying; + + var destroyed = false; + $element.addClass('carousel'); + + self.addSlide = function(slide, element) { + slides.push({ + slide: slide, + element: element + }); + slides.sort(function(a, b) { + return +a.slide.index - +b.slide.index; + }); + //if this is the first slide or the slide is set to active, select it + if (slide.index === $scope.active || slides.length === 1 && !angular.isNumber($scope.active)) { + if ($scope.$currentTransition) { + $scope.$currentTransition = null; + } + + currentIndex = slide.index; + $scope.active = slide.index; + setActive(currentIndex); + self.select(slides[findSlideIndex(slide)]); + if (slides.length === 1) { + $scope.play(); + } + } + }; + + self.getCurrentIndex = function() { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide.index === currentIndex) { + return i; + } + } + }; + + self.next = $scope.next = function() { + var newIndex = (self.getCurrentIndex() + 1) % slides.length; + + if (newIndex === 0 && $scope.noWrap()) { + $scope.pause(); + return; + } + + return self.select(slides[newIndex], 'next'); + }; + + self.prev = $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(slides[newIndex], 'prev'); + }; + + self.removeSlide = function(slide) { + var index = findSlideIndex(slide); + + //get the index of the slide inside the carousel + slides.splice(index, 1); + if (slides.length > 0 && currentIndex === index) { + if (index >= slides.length) { + currentIndex = slides.length - 1; + $scope.active = currentIndex; + setActive(currentIndex); + self.select(slides[slides.length - 1]); + } else { + currentIndex = index; + $scope.active = currentIndex; + setActive(currentIndex); + self.select(slides[index]); + } + } else if (currentIndex > index) { + currentIndex--; + $scope.active = currentIndex; + } + + //clean the active value when no more slide + if (slides.length === 0) { + currentIndex = null; + $scope.active = null; + } + }; + + /* direction: "prev" or "next" */ + self.select = $scope.select = function(nextSlide, direction) { + var nextIndex = findSlideIndex(nextSlide.slide); + //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.slide.index !== currentIndex && + !$scope.$currentTransition) { + goNext(nextSlide.slide, nextIndex, direction); + } + }; + + /* Allow outside people to call indexOf on slides array */ + $scope.indexOfSlide = function(slide) { + return +slide.slide.index; + }; + + $scope.isActive = function(slide) { + return $scope.active === slide.slide.index; + }; + + $scope.isPrevDisabled = function() { + return $scope.active === 0 && $scope.noWrap(); + }; + + $scope.isNextDisabled = function() { + return $scope.active === slides.length - 1 && $scope.noWrap(); + }; + + $scope.pause = function() { + if (!$scope.noPause) { + isPlaying = false; + resetTimer(); + } + }; + + $scope.play = function() { + if (!isPlaying) { + isPlaying = true; + restartTimer(); + } + }; + + $element.on('mouseenter', $scope.pause); + $element.on('mouseleave', $scope.play); + + $scope.$on('$destroy', function() { + destroyed = true; + resetTimer(); + }); + + $scope.$watch('noTransition', function(noTransition) { + $animate.enabled($element, !noTransition); + }); + + $scope.$watch('interval', restartTimer); + + $scope.$watchCollection('slides', resetTransition); + + $scope.$watch('active', function(index) { + if (angular.isNumber(index) && currentIndex !== index) { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide.index === index) { + index = i; + break; + } + } + + var slide = slides[index]; + if (slide) { + setActive(index); + self.select(slides[index]); + currentIndex = index; + } + } + }); + + function getSlideByIndex(index) { + for (var i = 0, l = slides.length; i < l; ++i) { + if (slides[i].index === index) { + return slides[i]; + } + } + } + + function setActive(index) { + for (var i = 0; i < slides.length; i++) { + slides[i].slide.active = i === index; + } + } + + function goNext(slide, index, direction) { + if (destroyed) { + return; + } + + angular.extend(slide, {direction: direction}); + angular.extend(slides[currentIndex].slide || {}, {direction: direction}); + if ($animate.enabled($element) && !$scope.$currentTransition && + slides[index].element && self.slides.length > 1) { + slides[index].element.data(SLIDE_DIRECTION, slide.direction); + var currentIdx = self.getCurrentIndex(); + + if (angular.isNumber(currentIdx) && slides[currentIdx].element) { + slides[currentIdx].element.data(SLIDE_DIRECTION, slide.direction); + } + + $scope.$currentTransition = true; + $animate.on('addClass', slides[index].element, function(element, phase) { + if (phase === 'close') { + $scope.$currentTransition = null; + $animate.off('addClass', element); + } + }); + } + + $scope.active = slide.index; + currentIndex = slide.index; + setActive(index); + + //every time you change slides, reset the timer + restartTimer(); + } + + function findSlideIndex(slide) { + for (var i = 0; i < slides.length; i++) { + if (slides[i].slide === slide) { + return i; + } + } + } + + function resetTimer() { + if (currentInterval) { + $interval.cancel(currentInterval); + currentInterval = null; + } + } + + function resetTransition(slides) { + if (!slides.length) { + $scope.$currentTransition = null; + } + } + + function restartTimer() { + resetTimer(); + var interval = +$scope.interval; + if (!isNaN(interval) && interval > 0) { + currentInterval = $interval(timerFn, interval); + } + } + + function timerFn() { + var interval = +$scope.interval; + if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) { + $scope.next(); + } else { + $scope.pause(); + } + } +}]) + +.directive('uibCarousel', function() { + return { + transclude: true, + controller: 'UibCarouselController', + controllerAs: 'carousel', + restrict: 'A', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/carousel/carousel.html'; + }, + scope: { + active: '=', + interval: '=', + noTransition: '=', + noPause: '=', + noWrap: '&' + } + }; +}) + +.directive('uibSlide', ['$animate', function($animate) { + return { + require: '^uibCarousel', + restrict: 'A', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/carousel/slide.html'; + }, + scope: { + actual: '=?', + index: '=?' + }, + link: function (scope, element, attrs, carouselCtrl) { + element.addClass('item'); + 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) { + $animate[active ? 'addClass' : 'removeClass'](element, 'active'); + }); + } + }; +}]) + +.animation('.item', ['$animateCss', +function($animateCss) { + var SLIDE_DIRECTION = 'uib-slideDirection'; + + function removeClass(element, className, callback) { + element.removeClass(className); + if (callback) { + callback(); + } + } + + return { + beforeAddClass: function(element, className, done) { + if (className === 'active') { + 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); + + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + + return function() { + stopped = true; + }; + } + done(); + }, + beforeRemoveClass: function (element, className, done) { + if (className === 'active') { + var stopped = false; + var direction = element.data(SLIDE_DIRECTION); + var directionClass = direction === 'next' ? 'left' : 'right'; + var removeClassFn = removeClass.bind(this, element, directionClass, done); + + $animateCss(element, {addClass: directionClass}) + .start() + .done(removeClassFn); + + return function() { + stopped = true; + }; + } + done(); + } + }; +}]); + +angular.module('ui.bootstrap.dateparser', []) + +.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', 'filterFilter', function($log, $locale, dateFilter, orderByFilter, filterFilter) { + // 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 = {}; + this.formatters = {}; + + formatCodeToRegex = [ + { + key: 'yyyy', + regex: '\\d{4}', + apply: function(value) { this.year = +value; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'yyyy'); + } + }, + { + key: 'yy', + regex: '\\d{2}', + apply: function(value) { value = +value; this.year = value < 69 ? value + 2000 : value + 1900; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'yy'); + } + }, + { + key: 'y', + regex: '\\d{1,4}', + apply: function(value) { this.year = +value; }, + formatter: function(date) { + var _date = new Date(); + _date.setFullYear(Math.abs(date.getFullYear())); + return dateFilter(_date, 'y'); + } + }, + { + key: 'M!', + regex: '0?[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { + var value = date.getMonth(); + if (/^[0-9]$/.test(value)) { + return dateFilter(date, 'MM'); + } + + return dateFilter(date, 'M'); + } + }, + { + key: 'MMMM', + regex: $locale.DATETIME_FORMATS.MONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'MMMM'); } + }, + { + key: 'MMM', + regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'MMM'); } + }, + { + key: 'MM', + regex: '0[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { return dateFilter(date, 'MM'); } + }, + { + key: 'M', + regex: '[1-9]|1[0-2]', + apply: function(value) { this.month = value - 1; }, + formatter: function(date) { return dateFilter(date, 'M'); } + }, + { + key: 'd!', + regex: '[0-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { + var value = date.getDate(); + if (/^[1-9]$/.test(value)) { + return dateFilter(date, 'dd'); + } + + return dateFilter(date, 'd'); + } + }, + { + key: 'dd', + regex: '[0-2][0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { return dateFilter(date, 'dd'); } + }, + { + key: 'd', + regex: '[1-2]?[0-9]{1}|3[0-1]{1}', + apply: function(value) { this.date = +value; }, + formatter: function(date) { return dateFilter(date, 'd'); } + }, + { + key: 'EEEE', + regex: $locale.DATETIME_FORMATS.DAY.join('|'), + formatter: function(date) { return dateFilter(date, 'EEEE'); } + }, + { + key: 'EEE', + regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|'), + formatter: function(date) { return dateFilter(date, 'EEE'); } + }, + { + key: 'HH', + regex: '(?:0|1)[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'HH'); } + }, + { + key: 'hh', + regex: '0[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'hh'); } + }, + { + key: 'H', + regex: '1?[0-9]|2[0-3]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'H'); } + }, + { + key: 'h', + regex: '[0-9]|1[0-2]', + apply: function(value) { this.hours = +value; }, + formatter: function(date) { return dateFilter(date, 'h'); } + }, + { + key: 'mm', + regex: '[0-5][0-9]', + apply: function(value) { this.minutes = +value; }, + formatter: function(date) { return dateFilter(date, 'mm'); } + }, + { + key: 'm', + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.minutes = +value; }, + formatter: function(date) { return dateFilter(date, 'm'); } + }, + { + key: 'sss', + regex: '[0-9][0-9][0-9]', + apply: function(value) { this.milliseconds = +value; }, + formatter: function(date) { return dateFilter(date, 'sss'); } + }, + { + key: 'ss', + regex: '[0-5][0-9]', + apply: function(value) { this.seconds = +value; }, + formatter: function(date) { return dateFilter(date, 'ss'); } + }, + { + key: 's', + regex: '[0-9]|[1-5][0-9]', + apply: function(value) { this.seconds = +value; }, + formatter: function(date) { return dateFilter(date, 's'); } + }, + { + key: 'a', + regex: $locale.DATETIME_FORMATS.AMPMS.join('|'), + apply: function(value) { + if (this.hours === 12) { + this.hours = 0; + } + + if (value === 'PM') { + this.hours += 12; + } + }, + formatter: function(date) { return dateFilter(date, 'a'); } + }, + { + key: 'Z', + regex: '[+-]\\d{4}', + apply: function(value) { + var matches = value.match(/([+-])(\d{2})(\d{2})/), + sign = matches[1], + hours = matches[2], + minutes = matches[3]; + this.hours += toInt(sign + hours); + this.minutes += toInt(sign + minutes); + }, + formatter: function(date) { + return dateFilter(date, 'Z'); + } + }, + { + key: 'ww', + regex: '[0-4][0-9]|5[0-3]', + formatter: function(date) { return dateFilter(date, 'ww'); } + }, + { + key: 'w', + regex: '[0-9]|[1-4][0-9]|5[0-3]', + formatter: function(date) { return dateFilter(date, 'w'); } + }, + { + key: 'GGGG', + regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s'), + formatter: function(date) { return dateFilter(date, 'GGGG'); } + }, + { + key: 'GGG', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'GGG'); } + }, + { + key: 'GG', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'GG'); } + }, + { + key: 'G', + regex: $locale.DATETIME_FORMATS.ERAS.join('|'), + formatter: function(date) { return dateFilter(date, 'G'); } + } + ]; + + if (angular.version.major >= 1 && angular.version.minor > 4) { + formatCodeToRegex.push({ + key: 'LLLL', + regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'), + apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); }, + formatter: function(date) { return dateFilter(date, 'LLLL'); } + }); + } + }; + + this.init(); + + function getFormatCodeToRegex(key) { + return filterFilter(formatCodeToRegex, {key: key}, true)[0]; + } + + this.getParser = function (key) { + var f = getFormatCodeToRegex(key); + return f && f.apply || null; + }; + + this.overrideParser = function (key, parser) { + var f = getFormatCodeToRegex(key); + if (f && angular.isFunction(parser)) { + this.parsers = {}; + f.apply = parser; + } + }.bind(this); + + function createParser(format) { + var map = [], regex = format.split(''); + + // check for literal values + var quoteIndex = format.indexOf('\''); + if (quoteIndex > -1) { + var inLiteral = false; + format = format.split(''); + for (var i = quoteIndex; i < format.length; i++) { + if (inLiteral) { + if (format[i] === '\'') { + if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote + format[i+1] = '$'; + regex[i+1] = ''; + } else { // end of literal + regex[i] = ''; + inLiteral = false; + } + } + format[i] = '$'; + } else { + if (format[i] === '\'') { // start of literal + format[i] = '$'; + regex[i] = ''; + inLiteral = true; + } + } + } + + format = format.join(''); + } + + angular.forEach(formatCodeToRegex, function(data) { + var index = format.indexOf(data.key); + + 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 + data.key.length; i < n; i++) { + regex[i] = ''; + format[i] = '$'; + } + format = format.join(''); + + map.push({ + index: index, + key: data.key, + apply: data.apply, + matcher: data.regex + }); + } + }); + + return { + regex: new RegExp('^' + regex.join('') + '$'), + map: orderByFilter(map, 'index') + }; + } + + function createFormatter(format) { + var formatters = []; + var i = 0; + var formatter, literalIdx; + while (i < format.length) { + if (angular.isNumber(literalIdx)) { + if (format.charAt(i) === '\'') { + if (i + 1 >= format.length || format.charAt(i + 1) !== '\'') { + formatters.push(constructLiteralFormatter(format, literalIdx, i)); + literalIdx = null; + } + } else if (i === format.length) { + while (literalIdx < format.length) { + formatter = constructFormatterFromIdx(format, literalIdx); + formatters.push(formatter); + literalIdx = formatter.endIdx; + } + } + + i++; + continue; + } + + if (format.charAt(i) === '\'') { + literalIdx = i; + i++; + continue; + } + + formatter = constructFormatterFromIdx(format, i); + + formatters.push(formatter.parser); + i = formatter.endIdx; + } + + return formatters; + } + + function constructLiteralFormatter(format, literalIdx, endIdx) { + return function() { + return format.substr(literalIdx + 1, endIdx - literalIdx - 1); + }; + } + + function constructFormatterFromIdx(format, i) { + var currentPosStr = format.substr(i); + for (var j = 0; j < formatCodeToRegex.length; j++) { + if (new RegExp('^' + formatCodeToRegex[j].key).test(currentPosStr)) { + var data = formatCodeToRegex[j]; + return { + endIdx: i + data.key.length, + parser: data.formatter + }; + } + } + + return { + endIdx: i + 1, + parser: function() { + return currentPosStr.charAt(0); + } + }; + } + + this.filter = function(date, format) { + if (!angular.isDate(date) || isNaN(date) || !format) { + return ''; + } + + format = $locale.DATETIME_FORMATS[format] || format; + + if ($locale.id !== localeId) { + this.init(); + } + + if (!this.formatters[format]) { + this.formatters[format] = createFormatter(format); + } + + var formatters = this.formatters[format]; + + return formatters.reduce(function(str, formatter) { + return str + formatter(date); + }, ''); + }; + + 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, 'apply'); + } + + var parser = this.parsers[format], + regex = parser.regex, + map = parser.map, + results = input.match(regex), + tzOffset = false; + 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.matcher === 'Z') { + tzOffset = true; + } + + if (mapper.apply) { + mapper.apply.call(fields, results[i]); + } + } + + var datesetter = tzOffset ? Date.prototype.setUTCFullYear : + Date.prototype.setFullYear; + var timesetter = tzOffset ? Date.prototype.setUTCHours : + Date.prototype.setHours; + + if (isValid(fields.year, fields.month, fields.date)) { + if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) { + dt = new Date(baseDate); + datesetter.call(dt, fields.year, fields.month, fields.date); + timesetter.call(dt, fields.hours, fields.minutes, + fields.seconds, fields.milliseconds); + } else { + dt = new Date(0); + datesetter.call(dt, fields.year, fields.month, fields.date); + timesetter.call(dt, fields.hours || 0, fields.minutes || 0, + fields.seconds || 0, 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; + } + + function toInt(str) { + return parseInt(str, 10); + } + + this.toTimezone = toTimezone; + this.fromTimezone = fromTimezone; + this.timezoneToOffset = timezoneToOffset; + this.addDateMinutes = addDateMinutes; + this.convertTimezoneToLocal = convertTimezoneToLocal; + + function toTimezone(date, timezone) { + return date && timezone ? convertTimezoneToLocal(date, timezone) : date; + } + + function fromTimezone(date, timezone) { + return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date; + } + + //https://github.com/angular/angular.js/blob/622c42169699ec07fc6daaa19fe6d224e5d2f70e/src/Angular.js#L1207 + function timezoneToOffset(timezone, fallback) { + timezone = timezone.replace(/:/g, ''); + var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000; + return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset; + } + + function addDateMinutes(date, minutes) { + date = new Date(date.getTime()); + date.setMinutes(date.getMinutes() + minutes); + return date; + } + + function convertTimezoneToLocal(date, timezone, reverse) { + reverse = reverse ? -1 : 1; + var dateTimezoneOffset = date.getTimezoneOffset(); + var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset); + return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset)); + } +}]); + +// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to +// at most one element. +angular.module('ui.bootstrap.isClass', []) +.directive('uibIsClass', [ + '$animate', +function ($animate) { + // 11111111 22222222 + var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/; + // 11111111 22222222 + var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/; + + var dataPerTracked = {}; + + return { + restrict: 'A', + compile: function(tElement, tAttrs) { + var linkedScopes = []; + var instances = []; + var expToData = {}; + var lastActivated = null; + var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP); + var onExp = onExpMatches[2]; + var expsStr = onExpMatches[1]; + var exps = expsStr.split(','); + + return linkFn; + + function linkFn(scope, element, attrs) { + linkedScopes.push(scope); + instances.push({ + scope: scope, + element: element + }); + + exps.forEach(function(exp, k) { + addForExp(exp, scope); + }); + + scope.$on('$destroy', removeScope); + } + + function addForExp(exp, scope) { + var matches = exp.match(IS_REGEXP); + var clazz = scope.$eval(matches[1]); + var compareWithExp = matches[2]; + var data = expToData[exp]; + if (!data) { + var watchFn = function(compareWithVal) { + var newActivated = null; + instances.some(function(instance) { + var thisVal = instance.scope.$eval(onExp); + if (thisVal === compareWithVal) { + newActivated = instance; + return true; + } + }); + if (data.lastActivated !== newActivated) { + if (data.lastActivated) { + $animate.removeClass(data.lastActivated.element, clazz); + } + if (newActivated) { + $animate.addClass(newActivated.element, clazz); + } + data.lastActivated = newActivated; + } + }; + expToData[exp] = data = { + lastActivated: null, + scope: scope, + watchFn: watchFn, + compareWithExp: compareWithExp, + watcher: scope.$watch(compareWithExp, watchFn) + }; + } + data.watchFn(scope.$eval(compareWithExp)); + } + + function removeScope(e) { + var removedScope = e.targetScope; + var index = linkedScopes.indexOf(removedScope); + linkedScopes.splice(index, 1); + instances.splice(index, 1); + if (linkedScopes.length) { + var newWatchScope = linkedScopes[0]; + angular.forEach(expToData, function(data) { + if (data.scope === removedScope) { + data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn); + data.scope = newWatchScope; + } + }); + } else { + expToData = {}; + } + } + } + }; +}]); +angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass']) + +.value('$datepickerSuppressError', false) + +.value('$datepickerLiteralWarning', true) + +.constant('uibDatepickerConfig', { + datepickerMode: 'day', + formatDay: 'dd', + formatMonth: 'MMMM', + formatYear: 'yyyy', + formatDayHeader: 'EEE', + formatDayTitle: 'MMMM yyyy', + formatMonthTitle: 'yyyy', + maxDate: null, + maxMode: 'year', + minDate: null, + minMode: 'day', + monthColumns: 3, + ngModelOptions: {}, + shortcutPropagation: false, + showWeeks: true, + yearColumns: 5, + yearRows: 4 +}) + +.controller('UibDatepickerController', ['$scope', '$element', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser', + function($scope, $element, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) { + var self = this, + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl; + ngModelOptions = {}, + watchListeners = []; + + $element.addClass('uib-datepicker'); + $attrs.$set('role', 'application'); + + if (!$scope.datepickerOptions) { + $scope.datepickerOptions = {}; + } + + // Modes chain + this.modes = ['day', 'month', 'year']; + + [ + 'customClass', + 'dateDisabled', + 'datepickerMode', + 'formatDay', + 'formatDayHeader', + 'formatDayTitle', + 'formatMonth', + 'formatMonthTitle', + 'formatYear', + 'maxDate', + 'maxMode', + 'minDate', + 'minMode', + 'monthColumns', + 'showWeeks', + 'shortcutPropagation', + 'startingDay', + 'yearColumns', + 'yearRows' + ].forEach(function(key) { + switch (key) { + case 'customClass': + case 'dateDisabled': + $scope[key] = $scope.datepickerOptions[key] || angular.noop; + break; + case 'datepickerMode': + $scope.datepickerMode = angular.isDefined($scope.datepickerOptions.datepickerMode) ? + $scope.datepickerOptions.datepickerMode : datepickerConfig.datepickerMode; + break; + case 'formatDay': + case 'formatDayHeader': + case 'formatDayTitle': + case 'formatMonth': + case 'formatMonthTitle': + case 'formatYear': + self[key] = angular.isDefined($scope.datepickerOptions[key]) ? + $interpolate($scope.datepickerOptions[key])($scope.$parent) : + datepickerConfig[key]; + break; + case 'monthColumns': + case 'showWeeks': + case 'shortcutPropagation': + case 'yearColumns': + case 'yearRows': + self[key] = angular.isDefined($scope.datepickerOptions[key]) ? + $scope.datepickerOptions[key] : datepickerConfig[key]; + break; + case 'startingDay': + if (angular.isDefined($scope.datepickerOptions.startingDay)) { + self.startingDay = $scope.datepickerOptions.startingDay; + } else if (angular.isNumber(datepickerConfig.startingDay)) { + self.startingDay = datepickerConfig.startingDay; + } else { + self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7; + } + + break; + case 'maxDate': + case 'minDate': + $scope.$watch('datepickerOptions.' + key, function(value) { + if (value) { + if (angular.isDate(value)) { + self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.getOption('timezone')); + } else { + if ($datepickerLiteralWarning) { + $log.warn('Literal date support has been deprecated, please switch to date object usage'); + } + + self[key] = new Date(dateFilter(value, 'medium')); + } + } else { + self[key] = datepickerConfig[key] ? + dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.getOption('timezone')) : + null; + } + + self.refreshView(); + }); + + break; + case 'maxMode': + case 'minMode': + if ($scope.datepickerOptions[key]) { + $scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) { + self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key]; + if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) || + key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) { + $scope.datepickerMode = self[key]; + $scope.datepickerOptions.datepickerMode = self[key]; + } + }); + } else { + self[key] = $scope[key] = datepickerConfig[key] || null; + } + + break; + } + }); + + $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000); + + $scope.disabled = angular.isDefined($attrs.disabled) || false; + if (angular.isDefined($attrs.ngDisabled)) { + watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) { + $scope.disabled = disabled; + self.refreshView(); + })); + } + + $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_; + ngModelOptions = extractOptions(ngModelCtrl); + + if ($scope.datepickerOptions.initDate) { + self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.getOption('timezone')) || new Date(); + $scope.$watch('datepickerOptions.initDate', function(initDate) { + if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { + self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.getOption('timezone')); + self.refreshView(); + } + }); + } else { + self.activeDate = new Date(); + } + + var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date(); + this.activeDate = !isNaN(date) ? + dateParser.fromTimezone(date, ngModelOptions.getOption('timezone')) : + dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone')); + + ngModelCtrl.$render = function() { + self.render(); + }; + }; + + this.render = function() { + if (ngModelCtrl.$viewValue) { + var date = new Date(ngModelCtrl.$viewValue), + isValid = !isNaN(date); + + if (isValid) { + this.activeDate = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone')); + } else if (!$datepickerSuppressError) { + $log.error('Datepicker directive: "ng-model" value must be a Date object'); + } + } + this.refreshView(); + }; + + this.refreshView = function() { + if (this.element) { + $scope.selectedDt = null; + this._refreshView(); + if ($scope.activeDt) { + $scope.activeDateId = $scope.activeDt.uid; + } + + var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + date = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone')); + ngModelCtrl.$setValidity('dateDisabled', !date || + this.element && !this.isDisabled(date)); + } + }; + + this.createDateObject = function(date, format) { + var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null; + model = dateParser.fromTimezone(model, ngModelOptions.getOption('timezone')); + var today = new Date(); + today = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone')); + var time = this.compare(date, today); + var dt = { + date: date, + label: dateParser.filter(date, format), + selected: model && this.compare(date, model) === 0, + disabled: this.isDisabled(date), + past: time < 0, + current: time === 0, + future: time > 0, + customClass: this.customClass(date) || null + }; + + if (model && this.compare(date, model) === 0) { + $scope.selectedDt = dt; + } + + if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) { + $scope.activeDt = dt; + } + + return dt; + }; + + this.isDisabled = function(date) { + return $scope.disabled || + this.minDate && this.compare(date, this.minDate) < 0 || + this.maxDate && this.compare(date, this.maxDate) > 0 || + $scope.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 ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.getOption('timezone')) : new Date(0, 0, 0, 0, 0, 0, 0); + dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + dt = dateParser.toTimezone(dt, ngModelOptions.getOption('timezone')); + ngModelCtrl.$setViewValue(dt); + ngModelCtrl.$render(); + } else { + self.activeDate = date; + setMode(self.modes[self.modes.indexOf($scope.datepickerMode) - 1]); + + $scope.$emit('uib:datepicker.mode'); + } + + $scope.$broadcast('uib:datepicker.focus'); + }; + + $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; + } + + setMode(self.modes[self.modes.indexOf($scope.datepickerMode) + direction]); + + $scope.$emit('uib:datepicker.mode'); + }; + + // 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 || $scope.disabled) { + 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(); + } + }; + + $element.on('keydown', function(evt) { + $scope.$apply(function() { + $scope.keydown(evt); + }); + }); + + $scope.$on('$destroy', function() { + //Clear all watch listeners on destroy + while (watchListeners.length) { + watchListeners.shift()(); + } + }); + + function setMode(mode) { + $scope.datepickerMode = mode; + $scope.datepickerOptions.datepickerMode = mode; + } + + function extractOptions(ngModelCtrl) { + var ngModelOptions; + + if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing + // guarantee a value + ngModelOptions = ngModelCtrl.$options || + $scope.datepickerOptions.ngModelOptions || + datepickerConfig.ngModelOptions || + {}; + + // mimic 1.6+ api + ngModelOptions.getOption = function (key) { + return ngModelOptions[key]; + }; + } else { // in angular >=1.6 $options is always present + // ng-model-options defaults timezone to null; don't let its precedence squash a non-null value + var timezone = ngModelCtrl.$options.getOption('timezone') || + ($scope.datepickerOptions.ngModelOptions ? $scope.datepickerOptions.ngModelOptions.timezone : null) || + (datepickerConfig.ngModelOptions ? datepickerConfig.ngModelOptions.timezone : null); + + // values passed to createChild override existing values + ngModelOptions = ngModelCtrl.$options // start with a ModelOptions instance + .createChild(datepickerConfig.ngModelOptions) // lowest precedence + .createChild($scope.datepickerOptions.ngModelOptions) + .createChild(ngModelCtrl.$options) // highest precedence + .createChild({timezone: timezone}); // to keep from squashing a non-null value + } + + return ngModelOptions; + } +}]) + +.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-week 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) { + var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()); + var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()); + _date1.setFullYear(date1.getFullYear()); + _date2.setFullYear(date2.getFullYear()); + return _date1 - _date2; + }; + + 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; + } else if (key === 'up') { + date = date - 7; + } else if (key === 'right') { + date = date + 1; + } 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, this.monthColumns); + scope.yearHeaderColspan = this.monthColumns > 3 ? this.monthColumns - 2 : 1; + }; + + this.compare = function(date1, date2) { + var _date1 = new Date(date1.getFullYear(), date1.getMonth()); + var _date2 = new Date(date2.getFullYear(), date2.getMonth()); + _date1.setFullYear(date1.getFullYear()); + _date2.setFullYear(date2.getFullYear()); + return _date1 - _date2; + }; + + this.handleKeyDown = function(key, evt) { + var date = this.activeDate.getMonth(); + + if (key === 'left') { + date = date - 1; + } else if (key === 'up') { + date = date - this.monthColumns; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + this.monthColumns; + } 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 columns, range; + this.element = $element; + + function getStartingYear(year) { + return parseInt((year - 1) / range, 10) * range + 1; + } + + this.yearpickerInit = function() { + columns = this.yearColumns; + range = this.yearRows * columns; + 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, columns); + scope.columns = columns; + }; + + 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; + } else if (key === 'up') { + date = date - columns; + } else if (key === 'right') { + date = date + 1; + } else if (key === 'down') { + date = date + columns; + } else if (key === 'pageup' || key === 'pagedown') { + date += (key === 'pageup' ? - 1 : 1) * range; + } 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 { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/datepicker.html'; + }, + scope: { + datepickerOptions: '=?' + }, + require: ['uibDatepicker', '^ngModel'], + restrict: 'A', + controller: 'UibDatepickerController', + controllerAs: 'datepicker', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + datepickerCtrl.init(ngModelCtrl); + } + }; +}) + +.directive('uibDaypicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/day.html'; + }, + require: ['^uibDatepicker', 'uibDaypicker'], + restrict: 'A', + controller: 'UibDaypickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], + daypickerCtrl = ctrls[1]; + + daypickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibMonthpicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/month.html'; + }, + require: ['^uibDatepicker', 'uibMonthpicker'], + restrict: 'A', + controller: 'UibMonthpickerController', + link: function(scope, element, attrs, ctrls) { + var datepickerCtrl = ctrls[0], + monthpickerCtrl = ctrls[1]; + + monthpickerCtrl.init(datepickerCtrl); + } + }; +}) + +.directive('uibYearpicker', function() { + return { + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepicker/year.html'; + }, + require: ['^uibDatepicker', 'uibYearpicker'], + restrict: 'A', + controller: 'UibYearpickerController', + link: function(scope, element, attrs, ctrls) { + var ctrl = ctrls[0]; + angular.extend(ctrl, ctrls[1]); + ctrl.yearpickerInit(); + + ctrl.refreshView(); + } + }; +}); + +angular.module('ui.bootstrap.position', []) + +/** + * A set of utility methods for working with the DOM. + * It is meant to be used where we need to absolute-position elements in + * relation to another element (this is the case for tooltips, popovers, + * typeahead suggestions etc.). + */ + .factory('$uibPosition', ['$document', '$window', function($document, $window) { + /** + * Used by scrollbarWidth() function to cache scrollbar's width. + * Do not access this variable directly, use scrollbarWidth() instead. + */ + var SCROLLBAR_WIDTH; + /** + * scrollbar on body and html element in IE and Edge overlay + * content and should be considered 0 width. + */ + var BODY_SCROLLBAR_WIDTH; + var OVERFLOW_REGEX = { + normal: /(auto|scroll)/, + hidden: /(auto|scroll|hidden)/ + }; + var PLACEMENT_REGEX = { + auto: /\s?auto?\s?/i, + primary: /^(top|bottom|left|right)$/, + secondary: /^(top|bottom|left|right|center)$/, + vertical: /^(top|bottom)$/ + }; + var BODY_REGEX = /(HTML|BODY)/; + + return { + + /** + * Provides a raw DOM element from a jQuery/jQLite element. + * + * @param {element} elem - The element to convert. + * + * @returns {element} A HTML element. + */ + getRawNode: function(elem) { + return elem.nodeName ? elem : elem[0] || elem; + }, + + /** + * Provides a parsed number for a style property. Strips + * units and casts invalid numbers to 0. + * + * @param {string} value - The style value to parse. + * + * @returns {number} A valid number. + */ + parseStyle: function(value) { + value = parseFloat(value); + return isFinite(value) ? value : 0; + }, + + /** + * Provides the closest positioned ancestor. + * + * @param {element} element - The element to get the offest parent for. + * + * @returns {element} The closest positioned ancestor. + */ + offsetParent: function(elem) { + elem = this.getRawNode(elem); + + var offsetParent = elem.offsetParent || $document[0].documentElement; + + function isStaticPositioned(el) { + return ($window.getComputedStyle(el).position || 'static') === 'static'; + } + + while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || $document[0].documentElement; + }, + + /** + * Provides the scrollbar width, concept from TWBS measureScrollbar() + * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js + * In IE and Edge, scollbar on body and html element overlay and should + * return a width of 0. + * + * @returns {number} The width of the browser scollbar. + */ + scrollbarWidth: function(isBody) { + if (isBody) { + if (angular.isUndefined(BODY_SCROLLBAR_WIDTH)) { + var bodyElem = $document.find('body'); + bodyElem.addClass('uib-position-body-scrollbar-measure'); + BODY_SCROLLBAR_WIDTH = $window.innerWidth - bodyElem[0].clientWidth; + BODY_SCROLLBAR_WIDTH = isFinite(BODY_SCROLLBAR_WIDTH) ? BODY_SCROLLBAR_WIDTH : 0; + bodyElem.removeClass('uib-position-body-scrollbar-measure'); + } + return BODY_SCROLLBAR_WIDTH; + } + + if (angular.isUndefined(SCROLLBAR_WIDTH)) { + var scrollElem = angular.element('
    '); + $document.find('body').append(scrollElem); + SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth; + SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0; + scrollElem.remove(); + } + + return SCROLLBAR_WIDTH; + }, + + /** + * Provides the padding required on an element to replace the scrollbar. + * + * @returns {object} An object with the following properties: + *
      + *
    • **scrollbarWidth**: the width of the scrollbar
    • + *
    • **widthOverflow**: whether the the width is overflowing
    • + *
    • **right**: the amount of right padding on the element needed to replace the scrollbar
    • + *
    • **rightOriginal**: the amount of right padding currently on the element
    • + *
    • **heightOverflow**: whether the the height is overflowing
    • + *
    • **bottom**: the amount of bottom padding on the element needed to replace the scrollbar
    • + *
    • **bottomOriginal**: the amount of bottom padding currently on the element
    • + *
    + */ + scrollbarPadding: function(elem) { + elem = this.getRawNode(elem); + + var elemStyle = $window.getComputedStyle(elem); + var paddingRight = this.parseStyle(elemStyle.paddingRight); + var paddingBottom = this.parseStyle(elemStyle.paddingBottom); + var scrollParent = this.scrollParent(elem, false, true); + var scrollbarWidth = this.scrollbarWidth(BODY_REGEX.test(scrollParent.tagName)); + + return { + scrollbarWidth: scrollbarWidth, + widthOverflow: scrollParent.scrollWidth > scrollParent.clientWidth, + right: paddingRight + scrollbarWidth, + originalRight: paddingRight, + heightOverflow: scrollParent.scrollHeight > scrollParent.clientHeight, + bottom: paddingBottom + scrollbarWidth, + originalBottom: paddingBottom + }; + }, + + /** + * Checks to see if the element is scrollable. + * + * @param {element} elem - The element to check. + * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, + * default is false. + * + * @returns {boolean} Whether the element is scrollable. + */ + isScrollable: function(elem, includeHidden) { + elem = this.getRawNode(elem); + + var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; + var elemStyle = $window.getComputedStyle(elem); + return overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX); + }, + + /** + * Provides the closest scrollable ancestor. + * A port of the jQuery UI scrollParent method: + * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js + * + * @param {element} elem - The element to find the scroll parent of. + * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered, + * default is false. + * @param {boolean=} [includeSelf=false] - Should the element being passed be + * included in the scrollable llokup. + * + * @returns {element} A HTML element. + */ + scrollParent: function(elem, includeHidden, includeSelf) { + elem = this.getRawNode(elem); + + var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal; + var documentEl = $document[0].documentElement; + var elemStyle = $window.getComputedStyle(elem); + if (includeSelf && overflowRegex.test(elemStyle.overflow + elemStyle.overflowY + elemStyle.overflowX)) { + return elem; + } + var excludeStatic = elemStyle.position === 'absolute'; + var scrollParent = elem.parentElement || documentEl; + + if (scrollParent === documentEl || elemStyle.position === 'fixed') { + return documentEl; + } + + while (scrollParent.parentElement && scrollParent !== documentEl) { + var spStyle = $window.getComputedStyle(scrollParent); + if (excludeStatic && spStyle.position !== 'static') { + excludeStatic = false; + } + + if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) { + break; + } + scrollParent = scrollParent.parentElement; + } + + return scrollParent; + }, + + /** + * Provides read-only equivalent of jQuery's position function: + * http://api.jquery.com/position/ - distance to closest positioned + * ancestor. Does not account for margins by default like jQuery position. + * + * @param {element} elem - The element to caclulate the position on. + * @param {boolean=} [includeMargins=false] - Should margins be accounted + * for, default is false. + * + * @returns {object} An object with the following properties: + *
      + *
    • **width**: the width of the element
    • + *
    • **height**: the height of the element
    • + *
    • **top**: distance to top edge of offset parent
    • + *
    • **left**: distance to left edge of offset parent
    • + *
    + */ + position: function(elem, includeMagins) { + elem = this.getRawNode(elem); + + var elemOffset = this.offset(elem); + if (includeMagins) { + var elemStyle = $window.getComputedStyle(elem); + elemOffset.top -= this.parseStyle(elemStyle.marginTop); + elemOffset.left -= this.parseStyle(elemStyle.marginLeft); + } + var parent = this.offsetParent(elem); + var parentOffset = {top: 0, left: 0}; + + if (parent !== $document[0].documentElement) { + parentOffset = this.offset(parent); + parentOffset.top += parent.clientTop - parent.scrollTop; + parentOffset.left += parent.clientLeft - parent.scrollLeft; + } + + return { + width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth), + height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight), + top: Math.round(elemOffset.top - parentOffset.top), + left: Math.round(elemOffset.left - parentOffset.left) + }; + }, + + /** + * Provides read-only equivalent of jQuery's offset function: + * http://api.jquery.com/offset/ - distance to viewport. Does + * not account for borders, margins, or padding on the body + * element. + * + * @param {element} elem - The element to calculate the offset on. + * + * @returns {object} An object with the following properties: + *
      + *
    • **width**: the width of the element
    • + *
    • **height**: the height of the element
    • + *
    • **top**: distance to top edge of viewport
    • + *
    • **right**: distance to bottom edge of viewport
    • + *
    + */ + offset: function(elem) { + elem = this.getRawNode(elem); + + var elemBCR = elem.getBoundingClientRect(); + return { + width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth), + height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight), + top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)), + left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft)) + }; + }, + + /** + * Provides offset distance to the closest scrollable ancestor + * or viewport. Accounts for border and scrollbar width. + * + * Right and bottom dimensions represent the distance to the + * respective edge of the viewport element. If the element + * edge extends beyond the viewport, a negative value will be + * reported. + * + * @param {element} elem - The element to get the viewport offset for. + * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead + * of the first scrollable element, default is false. + * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element + * be accounted for, default is true. + * + * @returns {object} An object with the following properties: + *
      + *
    • **top**: distance to the top content edge of viewport element
    • + *
    • **bottom**: distance to the bottom content edge of viewport element
    • + *
    • **left**: distance to the left content edge of viewport element
    • + *
    • **right**: distance to the right content edge of viewport element
    • + *
    + */ + viewportOffset: function(elem, useDocument, includePadding) { + elem = this.getRawNode(elem); + includePadding = includePadding !== false ? true : false; + + var elemBCR = elem.getBoundingClientRect(); + var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0}; + + var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem); + var offsetParentBCR = offsetParent.getBoundingClientRect(); + + offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop; + offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft; + if (offsetParent === $document[0].documentElement) { + offsetBCR.top += $window.pageYOffset; + offsetBCR.left += $window.pageXOffset; + } + offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight; + offsetBCR.right = offsetBCR.left + offsetParent.clientWidth; + + if (includePadding) { + var offsetParentStyle = $window.getComputedStyle(offsetParent); + offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop); + offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom); + offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft); + offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight); + } + + return { + top: Math.round(elemBCR.top - offsetBCR.top), + bottom: Math.round(offsetBCR.bottom - elemBCR.bottom), + left: Math.round(elemBCR.left - offsetBCR.left), + right: Math.round(offsetBCR.right - elemBCR.right) + }; + }, + + /** + * Provides an array of placement values parsed from a placement string. + * Along with the 'auto' indicator, supported placement strings are: + *
      + *
    • top: element on top, horizontally centered on host element.
    • + *
    • top-left: element on top, left edge aligned with host element left edge.
    • + *
    • top-right: element on top, lerightft edge aligned with host element right edge.
    • + *
    • bottom: element on bottom, horizontally centered on host element.
    • + *
    • bottom-left: element on bottom, left edge aligned with host element left edge.
    • + *
    • bottom-right: element on bottom, right edge aligned with host element right edge.
    • + *
    • left: element on left, vertically centered on host element.
    • + *
    • left-top: element on left, top edge aligned with host element top edge.
    • + *
    • left-bottom: element on left, bottom edge aligned with host element bottom edge.
    • + *
    • right: element on right, vertically centered on host element.
    • + *
    • right-top: element on right, top edge aligned with host element top edge.
    • + *
    • right-bottom: element on right, bottom edge aligned with host element bottom edge.
    • + *
    + * A placement string with an 'auto' indicator is expected to be + * space separated from the placement, i.e: 'auto bottom-left' If + * the primary and secondary placement values do not match 'top, + * bottom, left, right' then 'top' will be the primary placement and + * 'center' will be the secondary placement. If 'auto' is passed, true + * will be returned as the 3rd value of the array. + * + * @param {string} placement - The placement string to parse. + * + * @returns {array} An array with the following values + *
      + *
    • **[0]**: The primary placement.
    • + *
    • **[1]**: The secondary placement.
    • + *
    • **[2]**: If auto is passed: true, else undefined.
    • + *
    + */ + parsePlacement: function(placement) { + var autoPlace = PLACEMENT_REGEX.auto.test(placement); + if (autoPlace) { + placement = placement.replace(PLACEMENT_REGEX.auto, ''); + } + + placement = placement.split('-'); + + placement[0] = placement[0] || 'top'; + if (!PLACEMENT_REGEX.primary.test(placement[0])) { + placement[0] = 'top'; + } + + placement[1] = placement[1] || 'center'; + if (!PLACEMENT_REGEX.secondary.test(placement[1])) { + placement[1] = 'center'; + } + + if (autoPlace) { + placement[2] = true; + } else { + placement[2] = false; + } + + return placement; + }, + + /** + * Provides coordinates for an element to be positioned relative to + * another element. Passing 'auto' as part of the placement parameter + * will enable smart placement - where the element fits. i.e: + * 'auto left-top' will check to see if there is enough space to the left + * of the hostElem to fit the targetElem, if not place right (same for secondary + * top placement). Available space is calculated using the viewportOffset + * function. + * + * @param {element} hostElem - The element to position against. + * @param {element} targetElem - The element to position. + * @param {string=} [placement=top] - The placement for the targetElem, + * default is 'top'. 'center' is assumed as secondary placement for + * 'top', 'left', 'right', and 'bottom' placements. Available placements are: + *
      + *
    • top
    • + *
    • top-right
    • + *
    • top-left
    • + *
    • bottom
    • + *
    • bottom-left
    • + *
    • bottom-right
    • + *
    • left
    • + *
    • left-top
    • + *
    • left-bottom
    • + *
    • right
    • + *
    • right-top
    • + *
    • right-bottom
    • + *
    + * @param {boolean=} [appendToBody=false] - Should the top and left values returned + * be calculated from the body element, default is false. + * + * @returns {object} An object with the following properties: + *
      + *
    • **top**: Value for targetElem top.
    • + *
    • **left**: Value for targetElem left.
    • + *
    • **placement**: The resolved placement.
    • + *
    + */ + positionElements: function(hostElem, targetElem, placement, appendToBody) { + hostElem = this.getRawNode(hostElem); + targetElem = this.getRawNode(targetElem); + + // need to read from prop to support tests. + var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth'); + var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight'); + + placement = this.parsePlacement(placement); + + var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem); + var targetElemPos = {top: 0, left: 0, placement: ''}; + + if (placement[2]) { + var viewportOffset = this.viewportOffset(hostElem, appendToBody); + + var targetElemStyle = $window.getComputedStyle(targetElem); + var adjustedSize = { + width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))), + height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom))) + }; + + placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' : + placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' : + placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' : + placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' : + placement[0]; + + placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' : + placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' : + placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' : + placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' : + placement[1]; + + if (placement[1] === 'center') { + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + var xOverflow = hostElemPos.width / 2 - targetWidth / 2; + if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) { + placement[1] = 'left'; + } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) { + placement[1] = 'right'; + } + } else { + var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2; + if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) { + placement[1] = 'top'; + } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) { + placement[1] = 'bottom'; + } + } + } + } + + switch (placement[0]) { + case 'top': + targetElemPos.top = hostElemPos.top - targetHeight; + break; + case 'bottom': + targetElemPos.top = hostElemPos.top + hostElemPos.height; + break; + case 'left': + targetElemPos.left = hostElemPos.left - targetWidth; + break; + case 'right': + targetElemPos.left = hostElemPos.left + hostElemPos.width; + break; + } + + switch (placement[1]) { + case 'top': + targetElemPos.top = hostElemPos.top; + break; + case 'bottom': + targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight; + break; + case 'left': + targetElemPos.left = hostElemPos.left; + break; + case 'right': + targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth; + break; + case 'center': + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2; + } else { + targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2; + } + break; + } + + targetElemPos.top = Math.round(targetElemPos.top); + targetElemPos.left = Math.round(targetElemPos.left); + targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1]; + + return targetElemPos; + }, + + /** + * Provides a way to adjust the top positioning after first + * render to correctly align element to top after content + * rendering causes resized element height + * + * @param {array} placementClasses - The array of strings of classes + * element should have. + * @param {object} containerPosition - The object with container + * position information + * @param {number} initialHeight - The initial height for the elem. + * @param {number} currentHeight - The current height for the elem. + */ + adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) { + if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) { + return { + top: containerPosition.top - currentHeight + 'px' + }; + } + }, + + /** + * Provides a way for positioning tooltip & dropdown + * arrows when using placement options beyond the standard + * left, right, top, or bottom. + * + * @param {element} elem - The tooltip/dropdown element. + * @param {string} placement - The placement for the elem. + */ + positionArrow: function(elem, placement) { + elem = this.getRawNode(elem); + + var innerElem = elem.querySelector('.tooltip-inner, .popover-inner'); + if (!innerElem) { + return; + } + + var isTooltip = angular.element(innerElem).hasClass('tooltip-inner'); + + var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); + if (!arrowElem) { + return; + } + + var arrowCss = { + top: '', + bottom: '', + left: '', + right: '' + }; + + placement = this.parsePlacement(placement); + if (placement[1] === 'center') { + // no adjustment necessary - just reset styles + angular.element(arrowElem).css(arrowCss); + return; + } + + var borderProp = 'border-' + placement[0] + '-width'; + var borderWidth = $window.getComputedStyle(arrowElem)[borderProp]; + + var borderRadiusProp = 'border-'; + if (PLACEMENT_REGEX.vertical.test(placement[0])) { + borderRadiusProp += placement[0] + '-' + placement[1]; + } else { + borderRadiusProp += placement[1] + '-' + placement[0]; + } + borderRadiusProp += '-radius'; + var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp]; + + switch (placement[0]) { + case 'top': + arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth; + break; + case 'bottom': + arrowCss.top = isTooltip ? '0' : '-' + borderWidth; + break; + case 'left': + arrowCss.right = isTooltip ? '0' : '-' + borderWidth; + break; + case 'right': + arrowCss.left = isTooltip ? '0' : '-' + borderWidth; + break; + } + + arrowCss[placement[1]] = borderRadius; + + angular.element(arrowElem).css(arrowCss); + } + }; + }]); + +angular.module('ui.bootstrap.datepickerPopup', ['ui.bootstrap.datepicker', 'ui.bootstrap.position']) + +.value('$datepickerPopupLiteralWarning', true) + +.constant('uibDatepickerPopupConfig', { + altInputFormats: [], + appendToBody: false, + clearText: 'Clear', + closeOnDateSelection: true, + closeText: 'Done', + currentText: 'Today', + datepickerPopup: 'yyyy-MM-dd', + datepickerPopupTemplateUrl: 'uib/template/datepickerPopup/popup.html', + datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html', + html5Types: { + date: 'yyyy-MM-dd', + 'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss', + 'month': 'yyyy-MM' + }, + onOpenFocus: true, + showButtonBar: true, + placement: 'auto bottom-left' +}) + +.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', '$datepickerPopupLiteralWarning', +function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, $datepickerPopupLiteralWarning) { + var cache = {}, + isHtml5DateInput = false; + var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, + datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl, + ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = []; + + this.init = function(_ngModel_) { + ngModel = _ngModel_; + ngModelOptions = extractOptions(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; + altInputFormats = angular.isDefined($attrs.altInputFormats) ? + $scope.$parent.$eval($attrs.altInputFormats) : + datepickerPopupConfig.altInputFormats; + + $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.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.uibDatepickerPopup) { + throw new Error('HTML5 date input types do not support custom formats.'); + } + + // popup element used to display calendar + popupEl = angular.element('
    '); + + 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 (!$scope.datepickerOptions) { + $scope.datepickerOptions = {}; + } + + if (isHtml5DateInput) { + if ($attrs.type === 'month') { + $scope.datepickerOptions.datepickerMode = 'month'; + $scope.datepickerOptions.minMode = 'month'; + } + } + + datepickerEl.attr('datepicker-options', 'datepickerOptions'); + + 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) { + if (ngModel.$isEmpty(value)) { + $scope.date = value; + return value; + } + + if (angular.isNumber(value)) { + value = new Date(value); + } + + $scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone')); + + return dateParser.filter($scope.date, dateFormat); + }); + } else { + ngModel.$formatters.push(function(value) { + $scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone')); + return value; + }); + } + + // Detect changes in the view from the text box + ngModel.$viewChangeListeners.push(function() { + $scope.date = parseDateString(ngModel.$viewValue); + }); + + $element.on('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.off('keydown', inputKeydownBind); + $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); + + //Clear all watch listeners on destroy + while (watchListeners.length) { + watchListeners.shift()(); + } + }); + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || datepickerPopupConfig[key + 'Text']; + }; + + $scope.isDisabled = function(date) { + if (date === 'today') { + date = dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone')); + } + + var dates = {}; + angular.forEach(['minDate', 'maxDate'], function(key) { + if (!$scope.datepickerOptions[key]) { + dates[key] = null; + } else if (angular.isDate($scope.datepickerOptions[key])) { + dates[key] = new Date($scope.datepickerOptions[key]); + } else { + if ($datepickerPopupLiteralWarning) { + $log.warn('Literal date support has been deprecated, please switch to date object usage'); + } + + dates[key] = new Date(dateFilter($scope.datepickerOptions[key], 'medium')); + } + }); + + return $scope.datepickerOptions && + dates.minDate && $scope.compare(date, dates.minDate) < 0 || + dates.maxDate && $scope.compare(date, dates.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) { + $scope.date = dt; + var date = $scope.date ? dateParser.filter($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) { + evt.stopPropagation(); + $scope.isOpen = false; + $element[0].focus(); + } + }; + + $scope.select = function(date, evt) { + evt.stopPropagation(); + + 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 = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone')); + date.setHours(0, 0, 0, 0); + } + } + $scope.dateSelection(date); + }; + + $scope.close = function(evt) { + evt.stopPropagation(); + + $scope.isOpen = false; + $element[0].focus(); + }; + + $scope.disabled = angular.isDefined($attrs.disabled) || false; + if ($attrs.ngDisabled) { + watchListeners.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(disabled) { + $scope.disabled = disabled; + })); + } + + $scope.$watch('isOpen', function(value) { + if (value) { + if (!$scope.disabled) { + $timeout(function() { + positionPopup(); + + if (onOpenFocus) { + $scope.$broadcast('uib:datepicker.focus'); + } + + $document.on('click', documentClickBind); + + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + if (appendToBody || $position.parsePlacement(placement)[2]) { + scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element)); + if (scrollParentEl) { + scrollParentEl.on('scroll', positionPopup); + } + } else { + scrollParentEl = null; + } + + angular.element($window).on('resize', positionPopup); + }, 0, false); + } else { + $scope.isOpen = false; + } + } else { + $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); + } + }); + + function cameltoDash(string) { + return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); }); + } + + function parseDateString(viewValue) { + var date = dateParser.parse(viewValue, dateFormat, $scope.date); + if (isNaN(date)) { + for (var i = 0; i < altInputFormats.length; i++) { + date = dateParser.parse(viewValue, altInputFormats[i], $scope.date); + if (!isNaN(date)) { + return date; + } + } + } + return date; + } + + function parseDate(viewValue) { + if (angular.isNumber(viewValue)) { + // presumably timestamp to date object + viewValue = new Date(viewValue); + } + + if (!viewValue) { + return null; + } + + if (angular.isDate(viewValue) && !isNaN(viewValue)) { + return viewValue; + } + + if (angular.isString(viewValue)) { + var date = parseDateString(viewValue); + if (!isNaN(date)) { + return dateParser.toTimezone(date, ngModelOptions.getOption('timezone')); + } + } + + return ngModelOptions.getOption('allowInvalid') ? viewValue : 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; + } + + if (angular.isDate(value) && !isNaN(value)) { + return true; + } + + if (angular.isString(value)) { + return !isNaN(parseDateString(value)); + } + + return false; + } + + function documentClickBind(event) { + if (!$scope.isOpen && $scope.disabled) { + return; + } + + 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; + }); + } + } + + function positionPopup() { + if ($scope.isOpen) { + var dpElement = angular.element($popup[0].querySelector('.uib-datepicker-popup')); + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + var position = $position.positionElements($element, dpElement, placement, appendToBody); + dpElement.css({top: position.top + 'px', left: position.left + 'px'}); + if (dpElement.hasClass('uib-position-measure')) { + dpElement.removeClass('uib-position-measure'); + } + } + } + + function extractOptions(ngModelCtrl) { + var ngModelOptions; + + if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing + // guarantee a value + ngModelOptions = angular.isObject(ngModelCtrl.$options) ? + ngModelCtrl.$options : + { + timezone: null + }; + + // mimic 1.6+ api + ngModelOptions.getOption = function (key) { + return ngModelOptions[key]; + }; + } else { // in angular >=1.6 $options is always present + ngModelOptions = ngModelCtrl.$options; + } + + return ngModelOptions; + } + + $scope.$on('uib:datepicker.mode', function() { + $timeout(positionPopup, 0, false); + }); +}]) + +.directive('uibDatepickerPopup', function() { + return { + require: ['ngModel', 'uibDatepickerPopup'], + controller: 'UibDatepickerPopupController', + scope: { + datepickerOptions: '=?', + isOpen: '=?', + currentText: '@', + clearText: '@', + closeText: '@' + }, + link: function(scope, element, attrs, ctrls) { + var ngModel = ctrls[0], + ctrl = ctrls[1]; + + ctrl.init(ngModel); + } + }; +}) + +.directive('uibDatepickerPopupWrap', function() { + return { + restrict: 'A', + transclude: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html'; + } + }; +}); + +angular.module('ui.bootstrap.debounce', []) +/** + * A helper, internal service that debounces a function + */ + .factory('$$debounce', ['$timeout', function($timeout) { + return function(callback, debounceTime) { + var timeoutPromise; + + return function() { + var self = this; + var args = Array.prototype.slice.call(arguments); + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + + timeoutPromise = $timeout(function() { + callback.apply(self, args); + }, debounceTime); + }; + }; + }]); + +angular.module('ui.bootstrap.multiMap', []) +/** + * 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]; + } + } + }; + } + }; + }); + +angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position']) + +.constant('uibDropdownConfig', { + appendToOpenClass: 'uib-dropdown-open', + openClass: 'open' +}) + +.service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) { + var openScope = null; + var openedContainers = $$multiMap.createNew(); + + this.isOnlyOpen = function(dropdownScope, appendTo) { + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) { + if (dropdown.scope === dropdownScope) { + return dropdown; + } + + return toClose; + }, {}); + if (openDropdown) { + return openedDropdowns.length === 1; + } + } + + return false; + }; + + this.open = function(dropdownScope, element, appendTo) { + if (!openScope) { + $document.on('click', closeDropdown); + } + + if (openScope && openScope !== dropdownScope) { + openScope.isOpen = false; + } + + openScope = dropdownScope; + + if (!appendTo) { + return; + } + + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var openedScopes = openedDropdowns.map(function(dropdown) { + return dropdown.scope; + }); + if (openedScopes.indexOf(dropdownScope) === -1) { + openedContainers.put(appendTo, { + scope: dropdownScope + }); + } + } else { + openedContainers.put(appendTo, { + scope: dropdownScope + }); + } + }; + + this.close = function(dropdownScope, element, appendTo) { + if (openScope === dropdownScope) { + $document.off('click', closeDropdown); + $document.off('keydown', this.keybindFilter); + openScope = null; + } + + if (!appendTo) { + return; + } + + var openedDropdowns = openedContainers.get(appendTo); + if (openedDropdowns) { + var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) { + if (dropdown.scope === dropdownScope) { + return dropdown; + } + + return toClose; + }, {}); + if (dropdownToClose) { + openedContainers.remove(appendTo, dropdownToClose); + } + } + }; + + 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 || !openScope.isOpen) { return; } + + if (evt && openScope.getAutoClose() === 'disabled') { return; } + + if (evt && evt.which === 3) { 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.focusToggleElement(); + openScope.isOpen = false; + + if (!$rootScope.$$phase) { + openScope.$apply(); + } + }; + + this.keybindFilter = function(evt) { + if (!openScope) { + // see this.close as ESC could have been pressed which kills the scope so we can not proceed + return; + } + + var dropdownElement = openScope.getDropdownElement(); + var toggleElement = openScope.getToggleElement(); + var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target); + var toggleElementTargeted = toggleElement && toggleElement[0].contains(evt.target); + if (evt.which === 27) { + evt.stopPropagation(); + openScope.focusToggleElement(); + closeDropdown(); + } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted || toggleElementTargeted)) { + 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, + appendToOpenClass = dropdownConfig.appendToOpenClass, + openClass = dropdownConfig.openClass, + getIsOpen, + setIsOpen = angular.noop, + toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop, + keynavEnabled = false, + selectedOption = null, + body = $document.find('body'); + + $element.addClass('dropdown'); + + this.init = function() { + if ($attrs.isOpen) { + getIsOpen = $parse($attrs.isOpen); + setIsOpen = getIsOpen.assign; + + $scope.$watch(getIsOpen, function(value) { + scope.isOpen = !!value; + }); + } + + keynavEnabled = angular.isDefined($attrs.keyboardNav); + }; + + this.toggle = function(open) { + scope.isOpen = arguments.length ? !!open : !scope.isOpen; + if (angular.isFunction(setIsOpen)) { + setIsOpen(scope, scope.isOpen); + } + + return 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') : + $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(); + } + }; + + function removeDropdownMenu() { + $element.append(self.dropdownMenu); + } + + scope.$watch('isOpen', function(isOpen, wasOpen) { + var appendTo = null, + appendToBody = false; + + if (angular.isDefined($attrs.dropdownAppendTo)) { + var appendToEl = $parse($attrs.dropdownAppendTo)(scope); + if (appendToEl) { + appendTo = angular.element(appendToEl); + } + } + + if (angular.isDefined($attrs.dropdownAppendToBody)) { + var appendToBodyValue = $parse($attrs.dropdownAppendToBody)(scope); + if (appendToBodyValue !== false) { + appendToBody = true; + } + } + + if (appendToBody && !appendTo) { + appendTo = body; + } + + if (appendTo && self.dropdownMenu) { + if (isOpen) { + appendTo.append(self.dropdownMenu); + $element.on('$destroy', removeDropdownMenu); + } else { + $element.off('$destroy', removeDropdownMenu); + removeDropdownMenu(); + } + } + + if (appendTo && self.dropdownMenu) { + var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true), + css, + rightalign, + scrollbarPadding, + scrollbarWidth = 0; + + css = { + top: pos.top + 'px', + display: isOpen ? 'block' : 'none' + }; + + rightalign = self.dropdownMenu.hasClass('dropdown-menu-right'); + if (!rightalign) { + css.left = pos.left + 'px'; + css.right = 'auto'; + } else { + css.left = 'auto'; + scrollbarPadding = $position.scrollbarPadding(appendTo); + + if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + scrollbarWidth = scrollbarPadding.scrollbarWidth; + } + + css.right = window.innerWidth - scrollbarWidth - + (pos.left + $element.prop('offsetWidth')) + 'px'; + } + + // Need to adjust our positioning to be relative to the appendTo container + // if it's not the body element + if (!appendToBody) { + var appendOffset = $position.offset(appendTo); + + css.top = pos.top - appendOffset.top + 'px'; + + if (!rightalign) { + css.left = pos.left - appendOffset.left + 'px'; + } else { + css.right = window.innerWidth - + (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px'; + } + } + + self.dropdownMenu.css(css); + } + + var openContainer = appendTo ? appendTo : $element; + var dropdownOpenClass = appendTo ? appendToOpenClass : openClass; + var hasOpenClass = openContainer.hasClass(dropdownOpenClass); + var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo); + + if (hasOpenClass === !isOpen) { + var toggleClass; + if (appendTo) { + toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass'; + } else { + toggleClass = isOpen ? 'addClass' : 'removeClass'; + } + $animate[toggleClass](openContainer, dropdownOpenClass).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; + $document.on('keydown', uibDropdownService.keybindFilter); + }); + }); + } else { + $document.on('keydown', uibDropdownService.keybindFilter); + } + + scope.focusToggleElement(); + uibDropdownService.open(scope, $element, appendTo); + } else { + uibDropdownService.close(scope, $element, appendTo); + if (self.dropdownMenuTemplateUrl) { + if (templateScope) { + templateScope.$destroy(); + } + var newEl = angular.element(''); + self.dropdownMenu.replaceWith(newEl); + self.dropdownMenu = newEl; + } + + self.selectedOption = null; + } + + if (angular.isFunction(setIsOpen)) { + setIsOpen($scope, isOpen); + } + }); +}]) + +.directive('uibDropdown', function() { + return { + controller: 'UibDropdownController', + link: function(scope, element, attrs, dropdownCtrl) { + dropdownCtrl.init(); + } + }; +}) + +.directive('uibDropdownMenu', function() { + return { + restrict: 'A', + 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('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.on('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.off('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.pop(); + }, + length: function() { + return stack.length; + } + }; + } + }; + }); +angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position']) +/** + * Pluggable resolve mechanism for the modal resolve resolution + * Supports UI Router's $resolve service + */ + .provider('$uibResolve', function() { + var resolve = this; + this.resolver = null; + + this.setResolver = function(resolver) { + this.resolver = resolver; + }; + + this.$get = ['$injector', '$q', function($injector, $q) { + var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null; + return { + resolve: function(invocables, locals, parent, self) { + if (resolver) { + return resolver.resolve(invocables, locals, parent, self); + } + + var promises = []; + + angular.forEach(invocables, function(value) { + if (angular.isFunction(value) || angular.isArray(value)) { + promises.push($q.resolve($injector.invoke(value))); + } else if (angular.isString(value)) { + promises.push($q.resolve($injector.get(value))); + } else { + promises.push($q.resolve(value)); + } + }); + + return $q.all(promises).then(function(resolves) { + var resolveObj = {}; + var resolveIter = 0; + angular.forEach(invocables, function(value, key) { + resolveObj[key] = resolves[resolveIter++]; + }); + + return resolveObj; + }); + } + }; + }]; + }) + +/** + * A helper directive for the $modal service. It creates a backdrop element. + */ + .directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack', + function($animate, $injector, $modalStack) { + return { + restrict: 'A', + compile: function(tElement, tAttrs) { + tElement.addClass(tAttrs.backdropClass); + return linkFn; + } + }; + + function linkFn(scope, element, attrs) { + if (attrs.modalInClass) { + $animate.addClass(element, attrs.modalInClass); + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + if (scope.modalOptions.animation) { + $animate.removeClass(element, attrs.modalInClass).then(done); + } else { + done(); + } + }); + } + } + }]) + + .directive('uibModalWindow', ['$uibModalStack', '$q', '$animateCss', '$document', + function($modalStack, $q, $animateCss, $document) { + return { + scope: { + index: '@' + }, + restrict: 'A', + transclude: true, + templateUrl: function(tElement, tAttrs) { + return tAttrs.templateUrl || 'uib/template/modal/window.html'; + }, + link: function(scope, element, attrs) { + 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 rendered. + var modalRenderDeferObj = $q.defer(); + // Resolve render promise post-digest + scope.$$postDigest(function() { + modalRenderDeferObj.resolve(); + }); + + modalRenderDeferObj.promise.then(function() { + var animationPromise = null; + + if (attrs.modalInClass) { + animationPromise = $animateCss(element, { + addClass: attrs.modalInClass + }).start(); + + scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) { + var done = setIsAsync(); + $animateCss(element, { + removeClass: attrs.modalInClass + }).start().then(done); + }); + } + + + $q.when(animationPromise).then(function() { + // Notify {@link $modalStack} that modal is rendered. + var modal = $modalStack.getTop(); + if (modal) { + $modalStack.modalRendered(modal.key); + } + + /** + * If something within the freshly-opened modal already has focus (perhaps via a + * directive that causes focus) then there's no need to try to focus anything. + */ + if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) { + 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(); + } + } + }); + }); + } + }; + }]) + + .directive('uibModalAnimationClass', function() { + return { + compile: function(tElement, tAttrs) { + if (tAttrs.modalAnimation) { + tElement.addClass(tAttrs.uibModalAnimationClass); + } + } + }; + }) + + .directive('uibModalTransclude', ['$animate', function($animate) { + return { + link: function(scope, element, attrs, controller, transclude) { + transclude(scope.$parent, function(clone) { + element.empty(); + $animate.enter(clone, element); + }); + } + }; + }]) + + .factory('$uibModalStack', ['$animate', '$animateCss', '$document', + '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition', + function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap, $uibPosition) { + 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' + }; + var topModalIndex = 0; + var previousTopOpenedModal = null; + var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count'; + + //Modal focus behavior + var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' + + 'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' + + 'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]'; + var scrollbarPadding; + var SNAKE_CASE_REGEXP = /[A-Z]/g; + + // TODO: extract into common dependency with tooltip + function snake_case(name) { + var separator = '-'; + return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { + return (pos ? separator : '') + letter.toLowerCase(); + }); + } + + function isVisible(element) { + return !!(element.offsetWidth || + element.offsetHeight || + element.getClientRects().length); + } + + 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; + } + } + + // If any backdrop exist, ensure that it's index is always + // right below the top modal + if (topBackdropIndex > -1 && topBackdropIndex < topModalIndex) { + topBackdropIndex = topModalIndex; + } + return topBackdropIndex; + } + + $rootScope.$watch(backdropIndex, function(newBackdropIndex) { + if (backdropScope) { + backdropScope.index = newBackdropIndex; + } + }); + + function removeModalWindow(modalInstance, elementToReceiveFocus) { + var modalWindow = openedWindows.get(modalInstance).value; + var appendToElement = modalWindow.appendTo; + + //clean up the stack + openedWindows.remove(modalInstance); + previousTopOpenedModal = openedWindows.top(); + if (previousTopOpenedModal) { + topModalIndex = parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10); + } + + removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() { + var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS; + openedClasses.remove(modalBodyClass, modalInstance); + var areAnyOpen = openedClasses.hasKey(modalBodyClass); + appendToElement.toggleClass(modalBodyClass, areAnyOpen); + if (!areAnyOpen && scrollbarPadding && scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + if (scrollbarPadding.originalRight) { + appendToElement.css({paddingRight: scrollbarPadding.originalRight + 'px'}); + } else { + appendToElement.css({paddingRight: ''}); + } + scrollbarPadding = null; + } + toggleTopWindowClass(true); + }, modalWindow.closedDeferred); + checkRemoveBackdrop(); + + //move focus to specified element if available, or else to body + if (elementToReceiveFocus && elementToReceiveFocus.focus) { + elementToReceiveFocus.focus(); + } else if (appendToElement.focus) { + appendToElement.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, closedDeferred) { + 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; + + $animate.leave(domEl).then(function() { + if (done) { + done(); + } + + domEl.remove(); + if (closedDeferred) { + closedDeferred.resolve(); + } + }); + + scope.$destroy(); + } + } + + $document.on('keydown', keydownListener); + + $rootScope.$on('$destroy', function() { + $document.off('keydown', keydownListener); + }); + + function keydownListener(evt) { + if (evt.isDefaultPrevented()) { + return evt; + } + + var modal = openedWindows.top(); + if (modal) { + switch (evt.which) { + case 27: { + if (modal.value.keyboard) { + evt.preventDefault(); + $rootScope.$apply(function() { + $modalStack.dismiss(modal.key, 'escape key press'); + }); + } + break; + } + case 9: { + var list = $modalStack.loadFocusElementList(modal); + var focusChanged = false; + if (evt.shiftKey) { + if ($modalStack.isFocusInFirstItem(evt, list) || $modalStack.isModalFocused(evt, modal)) { + focusChanged = $modalStack.focusLastFocusableElement(list); + } + } else { + if ($modalStack.isFocusInLastItem(evt, list)) { + focusChanged = $modalStack.focusFirstFocusableElement(list); + } + } + + 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); + + // Store the current top first, to determine what index we ought to use + // for the current top modal + previousTopOpenedModal = openedWindows.top(); + + openedWindows.add(modalInstance, { + deferred: modal.deferred, + renderDeferred: modal.renderDeferred, + closedDeferred: modal.closedDeferred, + modalScope: modal.scope, + backdrop: modal.backdrop, + keyboard: modal.keyboard, + openedClass: modal.openedClass, + windowTopClass: modal.windowTopClass, + animation: modal.animation, + appendTo: modal.appendTo + }); + + openedClasses.put(modalBodyClass, modalInstance); + + var appendToElement = modal.appendTo, + currBackdropIndex = backdropIndex(); + + if (currBackdropIndex >= 0 && !backdropDomEl) { + backdropScope = $rootScope.$new(true); + backdropScope.modalOptions = modal; + backdropScope.index = currBackdropIndex; + backdropDomEl = angular.element('
    '); + backdropDomEl.attr({ + 'class': 'modal-backdrop', + 'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}', + 'uib-modal-animation-class': 'fade', + 'modal-in-class': 'in' + }); + if (modal.backdropClass) { + backdropDomEl.addClass(modal.backdropClass); + } + + if (modal.animation) { + backdropDomEl.attr('modal-animation', 'true'); + } + $compile(backdropDomEl)(backdropScope); + $animate.enter(backdropDomEl, appendToElement); + if ($uibPosition.isScrollable(appendToElement)) { + scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement); + if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) { + appendToElement.css({paddingRight: scrollbarPadding.right + 'px'}); + } + } + } + + var content; + if (modal.component) { + content = document.createElement(snake_case(modal.component.name)); + content = angular.element(content); + content.attr({ + resolve: '$resolve', + 'modal-instance': '$uibModalInstance', + close: '$close($value)', + dismiss: '$dismiss($value)' + }); + } else { + content = modal.content; + } + + // Set the top modal index based on the index of the previous top modal + topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0; + var angularDomEl = angular.element('
    '); + angularDomEl.attr({ + 'class': 'modal', + 'template-url': modal.windowTemplateUrl, + 'window-top-class': modal.windowTopClass, + 'role': 'dialog', + 'aria-labelledby': modal.ariaLabelledBy, + 'aria-describedby': modal.ariaDescribedBy, + 'size': modal.size, + 'index': topModalIndex, + 'animate': 'animate', + 'ng-style': '{\'z-index\': 1050 + $$topModalIndex*10, display: \'block\'}', + 'tabindex': -1, + 'uib-modal-animation-class': 'fade', + 'modal-in-class': 'in' + }).append(content); + if (modal.windowClass) { + angularDomEl.addClass(modal.windowClass); + } + + if (modal.animation) { + angularDomEl.attr('modal-animation', 'true'); + } + + appendToElement.addClass(modalBodyClass); + if (modal.scope) { + // we need to explicitly add the modal index to the modal scope + // because it is needed by ngStyle to compute the zIndex property. + modal.scope.$$topModalIndex = topModalIndex; + } + $animate.enter($compile(angularDomEl)(modal.scope), appendToElement); + + openedWindows.top().value.modalDomEl = angularDomEl; + openedWindows.top().value.modalOpener = modalOpener; + + applyAriaHidden(angularDomEl); + + function applyAriaHidden(el) { + if (!el || el[0].tagName === 'BODY') { + return; + } + + getSiblings(el).forEach(function(sibling) { + var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true', + ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10); + + if (!ariaHiddenCount) { + ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0; + } + + sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1); + sibling.setAttribute('aria-hidden', 'true'); + }); + + return applyAriaHidden(el.parent()); + + function getSiblings(el) { + var children = el.parent() ? el.parent().children() : []; + + return Array.prototype.filter.call(children, function(child) { + return child !== el[0]; + }); + } + } + }; + + function broadcastClosing(modalWindow, resultOrReason, closing) { + return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented; + } + + function unhideBackgroundElements() { + Array.prototype.forEach.call( + document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'), + function(hiddenEl) { + var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10), + newHiddenCount = ariaHiddenCount - 1; + hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount); + + if (!newHiddenCount) { + hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME); + hiddenEl.removeAttribute('aria-hidden'); + } + } + ); + } + + $modalStack.close = function(modalInstance, result) { + var modalWindow = openedWindows.get(modalInstance); + unhideBackgroundElements(); + 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); + unhideBackgroundElements(); + 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(list) { + if (list.length > 0) { + list[0].focus(); + return true; + } + return false; + }; + + $modalStack.focusLastFocusableElement = function(list) { + if (list.length > 0) { + list[list.length - 1].focus(); + return true; + } + return false; + }; + + $modalStack.isModalFocused = function(evt, modalWindow) { + if (evt && modalWindow) { + var modalDomEl = modalWindow.value.modalDomEl; + if (modalDomEl && modalDomEl.length) { + return (evt.target || evt.srcElement) === modalDomEl[0]; + } + } + return false; + }; + + $modalStack.isFocusInFirstItem = function(evt, list) { + if (list.length > 0) { + return (evt.target || evt.srcElement) === list[0]; + } + return false; + }; + + $modalStack.isFocusInLastItem = function(evt, list) { + if (list.length > 0) { + return (evt.target || evt.srcElement) === list[list.length - 1]; + } + return false; + }; + + $modalStack.loadFocusElementList = function(modalWindow) { + if (modalWindow) { + var modalDomE1 = modalWindow.value.modalDomEl; + if (modalDomE1 && modalDomE1.length) { + var elements = modalDomE1[0].querySelectorAll(tabbableSelector); + return elements ? + Array.prototype.filter.call(elements, function(element) { + return isVisible(element); + }) : elements; + } + } + }; + + return $modalStack; + }]) + + .provider('$uibModal', function() { + var $modalProvider = { + options: { + animation: true, + backdrop: true, //can also be false or 'static' + keyboard: true + }, + $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack', + function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) { + var $modal = {}; + + function getTemplatePromise(options) { + return options.template ? $q.when(options.template) : + $templateRequest(angular.isFunction(options.templateUrl) ? + options.templateUrl() : options.templateUrl); + } + + var promiseChain = null; + $modal.getPromiseChain = function() { + return promiseChain; + }; + + $modal.open = function(modalOptions) { + var modalResultDeferred = $q.defer(); + var modalOpenedDeferred = $q.defer(); + var modalClosedDeferred = $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, + closed: modalClosedDeferred.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 || {}; + modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0); + + if (!modalOptions.appendTo.length) { + throw new Error('appendTo element not found. Make sure that the element passed is in DOM.'); + } + + //verify options + if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) { + throw new Error('One of component or template or templateUrl options is required.'); + } + + var templateAndResolvePromise; + if (modalOptions.component) { + templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null)); + } else { + templateAndResolvePromise = + $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]); + } + + 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 providedScope = modalOptions.scope || $rootScope; + + var modalScope = providedScope.$new(); + modalScope.$close = modalInstance.close; + modalScope.$dismiss = modalInstance.dismiss; + + modalScope.$on('$destroy', function() { + if (!modalScope.$$uibDestructionScheduled) { + modalScope.$dismiss('$uibUnscheduledDestruction'); + } + }); + + var modal = { + scope: modalScope, + deferred: modalResultDeferred, + renderDeferred: modalRenderDeferred, + closedDeferred: modalClosedDeferred, + animation: modalOptions.animation, + backdrop: modalOptions.backdrop, + keyboard: modalOptions.keyboard, + backdropClass: modalOptions.backdropClass, + windowTopClass: modalOptions.windowTopClass, + windowClass: modalOptions.windowClass, + windowTemplateUrl: modalOptions.windowTemplateUrl, + ariaLabelledBy: modalOptions.ariaLabelledBy, + ariaDescribedBy: modalOptions.ariaDescribedBy, + size: modalOptions.size, + openedClass: modalOptions.openedClass, + appendTo: modalOptions.appendTo + }; + + var component = {}; + var ctrlInstance, ctrlInstantiate, ctrlLocals = {}; + + if (modalOptions.component) { + constructLocals(component, false, true, false); + component.name = modalOptions.component; + modal.component = component; + } else if (modalOptions.controller) { + constructLocals(ctrlLocals, true, false, true); + + // the third param will make the controller instantiate later,private api + // @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126 + ctrlInstantiate = $controller(modalOptions.controller, ctrlLocals, true, modalOptions.controllerAs); + if (modalOptions.controllerAs && modalOptions.bindToController) { + ctrlInstance = ctrlInstantiate.instance; + ctrlInstance.$close = modalScope.$close; + ctrlInstance.$dismiss = modalScope.$dismiss; + angular.extend(ctrlInstance, { + $resolve: ctrlLocals.$scope.$resolve + }, providedScope); + } + + ctrlInstance = ctrlInstantiate(); + + if (angular.isFunction(ctrlInstance.$onInit)) { + ctrlInstance.$onInit(); + } + } + + if (!modalOptions.component) { + modal.content = tplAndVars[0]; + } + + $modalStack.open(modalInstance, modal); + modalOpenedDeferred.resolve(true); + + function constructLocals(obj, template, instanceOnScope, injectable) { + obj.$scope = modalScope; + obj.$scope.$resolve = {}; + if (instanceOnScope) { + obj.$scope.$uibModalInstance = modalInstance; + } else { + obj.$uibModalInstance = modalInstance; + } + + var resolves = template ? tplAndVars[1] : tplAndVars; + angular.forEach(resolves, function(value, key) { + if (injectable) { + obj[key] = value; + } + + obj.$scope.$resolve[key] = value; + }); + } + }, function resolveError(reason) { + modalOpenedDeferred.reject(reason); + modalResultDeferred.reject(reason); + })['finally'](function() { + if (promiseChain === samePromise) { + promiseChain = null; + } + }); + + return modalInstance; + }; + + return $modal; + } + ] + }; + + return $modalProvider; + }); + +angular.module('ui.bootstrap.paging', []) +/** + * Helper internal service for generating common controller code between the + * pager and pagination components + */ +.factory('uibPaging', ['$parse', function($parse) { + return { + create: function(ctrl, $scope, $attrs) { + ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop; + ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl + ctrl._watchers = []; + + ctrl.init = function(ngModelCtrl, config) { + ctrl.ngModelCtrl = ngModelCtrl; + ctrl.config = config; + + ngModelCtrl.$render = function() { + ctrl.render(); + }; + + if ($attrs.itemsPerPage) { + ctrl._watchers.push($scope.$parent.$watch($attrs.itemsPerPage, function(value) { + ctrl.itemsPerPage = parseInt(value, 10); + $scope.totalPages = ctrl.calculateTotalPages(); + ctrl.updatePage(); + })); + } else { + ctrl.itemsPerPage = config.itemsPerPage; + } + + $scope.$watch('totalItems', function(newTotal, oldTotal) { + if (angular.isDefined(newTotal) || newTotal !== oldTotal) { + $scope.totalPages = ctrl.calculateTotalPages(); + ctrl.updatePage(); + } + }); + }; + + ctrl.calculateTotalPages = function() { + var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage); + return Math.max(totalPages || 0, 1); + }; + + ctrl.render = function() { + $scope.page = parseInt(ctrl.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(); + } + ctrl.ngModelCtrl.$setViewValue(page); + ctrl.ngModelCtrl.$render(); + } + }; + + $scope.getText = function(key) { + return $scope[key + 'Text'] || ctrl.config[key + 'Text']; + }; + + $scope.noPrevious = function() { + return $scope.page === 1; + }; + + $scope.noNext = function() { + return $scope.page === $scope.totalPages; + }; + + ctrl.updatePage = function() { + ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable + + if ($scope.page > $scope.totalPages) { + $scope.selectPage($scope.totalPages); + } else { + ctrl.ngModelCtrl.$render(); + } + }; + + $scope.$on('$destroy', function() { + while (ctrl._watchers.length) { + ctrl._watchers.shift()(); + } + }); + } + }; +}]); + +angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex']) + +.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) { + $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align; + + uibPaging.create(this, $scope, $attrs); +}]) + +.constant('uibPagerConfig', { + itemsPerPage: 10, + previousText: '« Previous', + nextText: 'Next »', + align: true +}) + +.directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) { + return { + scope: { + totalItems: '=', + previousText: '@', + nextText: '@', + ngDisabled: '=' + }, + require: ['uibPager', '?ngModel'], + restrict: 'A', + controller: 'UibPagerController', + controllerAs: 'pager', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/pager/pager.html'; + }, + link: function(scope, element, attrs, ctrls) { + element.addClass('pager'); + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + paginationCtrl.init(ngModelCtrl, uibPagerConfig); + } + }; +}]); + +angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex']) +.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) { + var ctrl = this; + // Setup configuration parameters + var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize, + rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate, + forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses, + boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers, + pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity; + $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks; + $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks; + $attrs.$set('role', 'menu'); + + uibPaging.create(this, $scope, $attrs); + + if ($attrs.maxSize) { + ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) { + maxSize = parseInt(value, 10); + ctrl.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, pageLabel(number), number === currentPage); + pages.push(page); + } + + // Add links to move between page sets + if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) { + if (startPage > 1) { + if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning + var previousPageSet = makePage(startPage - 1, '...', false); + pages.unshift(previousPageSet); + } + if (boundaryLinkNumbers) { + if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential + var secondPageLink = makePage(2, '2', false); + pages.unshift(secondPageLink); + } + //add the first page + var firstPageLink = makePage(1, '1', false); + pages.unshift(firstPageLink); + } + } + + if (endPage < totalPages) { + if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end + var nextPageSet = makePage(endPage + 1, '...', false); + pages.push(nextPageSet); + } + if (boundaryLinkNumbers) { + if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential + var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false); + pages.push(secondToLastPageLink); + } + //add the last page + var lastPageLink = makePage(totalPages, totalPages, false); + pages.push(lastPageLink); + } + } + } + return pages; + } + + var originalRender = this.render; + this.render = function() { + originalRender(); + if ($scope.page > 0 && $scope.page <= $scope.totalPages) { + $scope.pages = getPages($scope.page, $scope.totalPages); + } + }; +}]) + +.constant('uibPaginationConfig', { + itemsPerPage: 10, + boundaryLinks: false, + boundaryLinkNumbers: false, + directionLinks: true, + firstText: 'First', + previousText: 'Previous', + nextText: 'Next', + lastText: 'Last', + rotate: true, + forceEllipses: false +}) + +.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) { + return { + scope: { + totalItems: '=', + firstText: '@', + previousText: '@', + nextText: '@', + lastText: '@', + ngDisabled:'=' + }, + require: ['uibPagination', '?ngModel'], + restrict: 'A', + controller: 'UibPaginationController', + controllerAs: 'pagination', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/pagination/pagination.html'; + }, + link: function(scope, element, attrs, ctrls) { + element.addClass('pagination'); + var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (!ngModelCtrl) { + return; // do nothing if no ng-model + } + + paginationCtrl.init(ngModelCtrl, uibPaginationConfig); + } + }; +}]); + +/** + * 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', + placementClassPrefix: '', + animation: true, + popupDelay: 0, + popupCloseDelay: 0, + useContentExp: false + }; + + // Default hide triggers for each show trigger + var triggerMap = { + 'mouseenter': 'mouseleave', + 'click': 'click', + 'outsideClick': 'outsideClick', + '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('keyup', keypressListener); + + $rootScope.$on('$destroy', function() { + $document.off('keyup', keypressListener); + }); + + function keypressListener(e) { + if (e.which === 27) { + var last = openedTooltips.top(); + if (last) { + last.value.close(); + 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 = + '
    ' + + '
    '; + + 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 adjustmentTimeout; + 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 lastPlacement; + + var positionTooltip = function() { + // check if tooltip exists and is not empty + if (!tooltip || !tooltip.html()) { return; } + + if (!positionTimeout) { + positionTimeout = $timeout(function() { + var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody); + var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); + var elementPos = appendToBody ? $position.offset(element) : $position.position(element); + tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' }); + var placementClasses = ttPosition.placement.split('-'); + + if (!tooltip.hasClass(placementClasses[0])) { + tooltip.removeClass(lastPlacement.split('-')[0]); + tooltip.addClass(placementClasses[0]); + } + + if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) { + tooltip.removeClass(options.placementClassPrefix + lastPlacement); + tooltip.addClass(options.placementClassPrefix + ttPosition.placement); + } + + adjustmentTimeout = $timeout(function() { + var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight'); + var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight); + if (adjustment) { + tooltip.css(adjustment); + } + adjustmentTimeout = null; + }, 0, false); + + // first time through tt element will have the + // uib-position-measure class or if the placement + // has changed we need to position the arrow. + if (tooltip.hasClass('uib-position-measure')) { + $position.positionArrow(tooltip, ttPosition.placement); + tooltip.removeClass('uib-position-measure'); + } else if (lastPlacement !== ttPosition.placement) { + $position.positionArrow(tooltip, ttPosition.placement); + } + lastPlacement = ttPosition.placement; + + 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; + + 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() { + if (!ttScope) { + return; + } + + // First things first: we don't show it anymore. + ttScope.$evalAsync(function() { + if (ttScope) { + 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); + } + }); + + openedTooltips.add(ttScope, { + close: hide + }); + + prepObservers(); + } + + function removeTooltip() { + cancelShow(); + cancelHide(); + unregisterObservers(); + + if (tooltip) { + tooltip.remove(); + + tooltip = null; + if (adjustmentTimeout) { + $timeout.cancel(adjustmentTimeout); + } + } + + openedTooltips.remove(ttScope); + + if (tooltipLinkedScope) { + tooltipLinkedScope.$destroy(); + tooltipLinkedScope = null; + } + } + + /** + * Set the initial scope values. Once + * the tooltip is created, the observers + * will be added to keep things in sync. + */ + 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 placement = $position.parsePlacement(ttScope.placement); + lastPlacement = placement[1] ? placement[0] + '-' + placement[1] : placement[0]; + + 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) { + if (ttScope && !val === ttScope.isOpen) { + toggleTooltipBind(); + } + }); + } + + 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; + } + } + + // hide tooltips/popovers for outsideClick trigger + function bodyHideTooltipBind(e) { + if (!ttScope || !ttScope.isOpen || !tooltip) { + return; + } + // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked + if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) { + hideTooltipBind(); + } + } + + // KeyboardEvent handler to hide the tooltip on Escape key press + function hideOnEscapeKey(e) { + if (e.which === 27) { + hideTooltipBind(); + } + } + + var unregisterTriggers = function() { + triggers.show.forEach(function(trigger) { + if (trigger === 'outsideClick') { + element.off('click', toggleTooltipBind); + } else { + element.off(trigger, showTooltipBind); + element.off(trigger, toggleTooltipBind); + } + element.off('keypress', hideOnEscapeKey); + }); + triggers.hide.forEach(function(trigger) { + if (trigger === 'outsideClick') { + $document.off('click', bodyHideTooltipBind); + } else { + element.off(trigger, hideTooltipBind); + } + }); + }; + + function prepTriggers() { + var showTriggers = [], hideTriggers = []; + var val = scope.$eval(attrs[prefix + 'Trigger']); + unregisterTriggers(); + + if (angular.isObject(val)) { + Object.keys(val).forEach(function(key) { + showTriggers.push(key); + hideTriggers.push(val[key]); + }); + triggers = { + show: showTriggers, + hide: hideTriggers + }; + } else { + triggers = getTriggers(val); + } + + if (triggers.show !== 'none') { + triggers.show.forEach(function(trigger, idx) { + if (trigger === 'outsideClick') { + element.on('click', toggleTooltipBind); + $document.on('click', bodyHideTooltipBind); + } else if (trigger === triggers.hide[idx]) { + element.on(trigger, toggleTooltipBind); + } else if (trigger) { + element.on(trigger, showTooltipBind); + element.on(triggers.hide[idx], hideTooltipBind); + } + element.on('keypress', hideOnEscapeKey); + }); + } + } + + prepTriggers(); + + var animation = scope.$eval(attrs[prefix + 'Animation']); + ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation; + + var appendToBodyVal; + var appendKey = prefix + 'AppendToBody'; + if (appendKey in attrs && attrs[appendKey] === undefined) { + appendToBodyVal = true; + } else { + appendToBodyVal = scope.$eval(attrs[appendKey]); + } + + appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody; + + // Make sure tooltip is destroyed and removed. + scope.$on('$destroy', function onDestroyTooltip() { + unregisterTriggers(); + removeTooltip(); + 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', ['$uibPosition', function($uibPosition) { + return { + restrict: 'A', + link: function(scope, element, attrs) { + // need to set the primary position so the + // arrow has space during position measure. + // tooltip.positionTooltip() + if (scope.placement) { + // // There are no top-left etc... classes + // // in TWBS, so we need the primary position. + var position = $uibPosition.parsePlacement(scope.placement); + element.addClass(position[0]); + } + + if (scope.popupClass) { + element.addClass(scope.popupClass); + } + + if (scope.animation) { + element.addClass(attrs.tooltipAnimationClass); + } + } + }; +}]) + +.directive('uibTooltipPopup', function() { + return { + restrict: 'A', + scope: { content: '@' }, + templateUrl: 'uib/template/tooltip/tooltip-popup.html' + }; +}) + +.directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter'); +}]) + +.directive('uibTooltipTemplatePopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&', originScope: '&' }, + templateUrl: 'uib/template/tooltip/tooltip-template-popup.html' + }; +}) + +.directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', { + useContentExp: true + }); +}]) + +.directive('uibTooltipHtmlPopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&' }, + templateUrl: 'uib/template/tooltip/tooltip-html-popup.html' + }; +}) + +.directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibTooltipHtml', '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 { + restrict: 'A', + scope: { uibTitle: '@', contentExp: '&', originScope: '&' }, + templateUrl: 'uib/template/popover/popover-template.html' + }; +}) + +.directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverTemplate', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverHtmlPopup', function() { + return { + restrict: 'A', + scope: { contentExp: '&', uibTitle: '@' }, + templateUrl: 'uib/template/popover/popover-html.html' + }; +}) + +.directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopoverHtml', 'popover', 'click', { + useContentExp: true + }); +}]) + +.directive('uibPopoverPopup', function() { + return { + restrict: 'A', + scope: { uibTitle: '@', content: '@' }, + templateUrl: 'uib/template/popover/popover.html' + }; +}) + +.directive('uibPopover', ['$uibTooltip', function($uibTooltip) { + return $uibTooltip('uibPopover', '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 = getMaxOrDefault(); + + this.addBar = function(bar, element, attrs) { + if (!animate) { + element.css({'transition': 'none'}); + } + + this.bars.push(bar); + + bar.max = getMaxOrDefault(); + 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(); + }); + }; + + //$attrs.$observe('maxParam', function(maxParam) { + $scope.$watch('maxParam', function(maxParam) { + self.bars.forEach(function(bar) { + bar.max = getMaxOrDefault(); + bar.recalculatePercentage(); + }); + }); + + function getMaxOrDefault () { + return angular.isDefined($scope.maxParam) ? $scope.maxParam : progressConfig.max; + } +}]) + +.directive('uibProgress', function() { + return { + replace: true, + transclude: true, + controller: 'UibProgressController', + require: 'uibProgress', + scope: { + maxParam: '=?max' + }, + templateUrl: 'uib/template/progressbar/progress.html' + }; +}) + +.directive('uibBar', function() { + return { + replace: true, + transclude: true, + require: '^uibProgress', + scope: { + value: '=', + type: '@' + }, + templateUrl: 'uib/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: '=', + maxParam: '=?max', + type: '@' + }, + templateUrl: 'uib/template/progressbar/progressbar.html', + link: function(scope, element, attrs, progressCtrl) { + progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title}); + } + }; +}); + +angular.module('ui.bootstrap.rating', []) + +.constant('uibRatingConfig', { + max: 5, + stateOn: null, + stateOff: null, + enableReset: true, + titles: ['one', 'two', 'three', 'four', 'five'] +}) + +.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) { + var ngModelCtrl = { $setViewValue: angular.noop }, + self = this; + + 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; + this.enableReset = angular.isDefined($attrs.enableReset) ? + $scope.$parent.$eval($attrs.enableReset) : ratingConfig.enableReset; + 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; + } + + return this.titles[index]; + }; + + $scope.rate = function(value) { + if (!$scope.readonly && value >= 0 && value <= $scope.range.length) { + var newViewValue = self.enableReset && ngModelCtrl.$viewValue === value ? 0 : value; + ngModelCtrl.$setViewValue(newViewValue); + 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; + $scope.title = self.getTitle($scope.value - 1); + }; +}]) + +.directive('uibRating', function() { + return { + require: ['uibRating', 'ngModel'], + restrict: 'A', + scope: { + readonly: '=?readOnly', + onHover: '&', + onLeave: '&' + }, + controller: 'UibRatingController', + templateUrl: 'uib/template/rating/rating.html', + link: function(scope, element, attrs, ctrls) { + var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + ratingCtrl.init(ngModelCtrl); + } + }; +}); + +angular.module('ui.bootstrap.tabs', []) + +.controller('UibTabsetController', ['$scope', function ($scope) { + var ctrl = this, + oldIndex; + ctrl.tabs = []; + + ctrl.select = function(index, evt) { + if (!destroyed) { + var previousIndex = findTabIndex(oldIndex); + var previousSelected = ctrl.tabs[previousIndex]; + if (previousSelected) { + previousSelected.tab.onDeselect({ + $event: evt, + $selectedIndex: index + }); + if (evt && evt.isDefaultPrevented()) { + return; + } + previousSelected.tab.active = false; + } + + var selected = ctrl.tabs[index]; + if (selected) { + selected.tab.onSelect({ + $event: evt + }); + selected.tab.active = true; + ctrl.active = selected.index; + oldIndex = selected.index; + } else if (!selected && angular.isDefined(oldIndex)) { + ctrl.active = null; + oldIndex = null; + } + } + }; + + ctrl.addTab = function addTab(tab) { + ctrl.tabs.push({ + tab: tab, + index: tab.index + }); + ctrl.tabs.sort(function(t1, t2) { + if (t1.index > t2.index) { + return 1; + } + + if (t1.index < t2.index) { + return -1; + } + + return 0; + }); + + if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) { + var newActiveIndex = findTabIndex(tab.index); + ctrl.select(newActiveIndex); + } + }; + + ctrl.removeTab = function removeTab(tab) { + var index; + for (var i = 0; i < ctrl.tabs.length; i++) { + if (ctrl.tabs[i].tab === tab) { + index = i; + break; + } + } + + if (ctrl.tabs[index].index === ctrl.active) { + var newActiveTabIndex = index === ctrl.tabs.length - 1 ? + index - 1 : index + 1 % ctrl.tabs.length; + ctrl.select(newActiveTabIndex); + } + + ctrl.tabs.splice(index, 1); + }; + + $scope.$watch('tabset.active', function(val) { + if (angular.isDefined(val) && val !== oldIndex) { + ctrl.select(findTabIndex(val)); + } + }); + + var destroyed; + $scope.$on('$destroy', function() { + destroyed = true; + }); + + function findTabIndex(index) { + for (var i = 0; i < ctrl.tabs.length; i++) { + if (ctrl.tabs[i].index === index) { + return i; + } + } + } +}]) + +.directive('uibTabset', function() { + return { + transclude: true, + replace: true, + scope: {}, + bindToController: { + active: '=?', + type: '@' + }, + controller: 'UibTabsetController', + controllerAs: 'tabset', + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/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; + } + }; +}) + +.directive('uibTab', ['$parse', function($parse) { + return { + require: '^uibTabset', + replace: true, + templateUrl: function(element, attrs) { + return attrs.templateUrl || 'uib/template/tabs/tab.html'; + }, + transclude: true, + scope: { + heading: '@', + index: '=?', + classes: '@?', + 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 + }, + controllerAs: 'tab', + link: function(scope, elm, attrs, tabsetCtrl, transclude) { + scope.disabled = false; + if (attrs.disable) { + scope.$parent.$watch($parse(attrs.disable), function(value) { + scope.disabled = !! value; + }); + } + + if (angular.isUndefined(attrs.index)) { + if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) { + scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1; + } else { + scope.index = 0; + } + } + + if (angular.isUndefined(attrs.classes)) { + scope.classes = ''; + } + + scope.select = function(evt) { + if (!scope.disabled) { + var index; + for (var i = 0; i < tabsetCtrl.tabs.length; i++) { + if (tabsetCtrl.tabs[i].tab === scope) { + index = i; + break; + } + } + + tabsetCtrl.select(index, evt); + } + }; + + 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', + 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', + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.uibTabContentTransclude).tab; + + //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('uib-tab-heading') || + node.hasAttribute('data-uib-tab-heading') || + node.hasAttribute('x-uib-tab-heading') || + node.tagName.toLowerCase() === 'uib-tab-heading' || + node.tagName.toLowerCase() === 'data-uib-tab-heading' || + node.tagName.toLowerCase() === 'x-uib-tab-heading' || + node.tagName.toLowerCase() === 'uib:tab-heading' + ); + } +}); + +angular.module('ui.bootstrap.timepicker', []) + +.constant('uibTimepickerConfig', { + hourStep: 1, + minuteStep: 1, + secondStep: 1, + showMeridian: true, + showSeconds: false, + meridians: null, + readonlyInput: false, + mousewheel: true, + arrowkeys: true, + showSpinners: true, + templateUrl: 'uib/template/timepicker/timepicker.html' +}) + +.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) { + var hoursModelCtrl, minutesModelCtrl, secondsModelCtrl; + var selected = new Date(), + watchers = [], + ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl + meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS, + padHours = angular.isDefined($attrs.padHours) ? $scope.$parent.$eval($attrs.padHours) : true; + + $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), + secondsInputEl = inputs.eq(2); + + hoursModelCtrl = hoursInputEl.controller('ngModel'); + minutesModelCtrl = minutesInputEl.controller('ngModel'); + secondsModelCtrl = secondsInputEl.controller('ngModel'); + + var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel; + + if (mousewheel) { + this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl); + } + + var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys; + if (arrowkeys) { + this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl); + } + + $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput; + this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl); + }; + + var hourStep = timepickerConfig.hourStep; + if ($attrs.hourStep) { + watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) { + hourStep = +value; + })); + } + + var minuteStep = timepickerConfig.minuteStep; + if ($attrs.minuteStep) { + watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) { + minuteStep = +value; + })); + } + + var min; + watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) { + var dt = new Date(value); + min = isNaN(dt) ? undefined : dt; + })); + + var max; + watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) { + var dt = new Date(value); + max = isNaN(dt) ? undefined : dt; + })); + + var disabled = false; + if ($attrs.ngDisabled) { + watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { + disabled = value; + })); + } + + $scope.noIncrementHours = function() { + var incrementedSelected = addMinutes(selected, hourStep * 60); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementHours = function() { + var decrementedSelected = addMinutes(selected, -hourStep * 60); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noIncrementMinutes = function() { + var incrementedSelected = addMinutes(selected, minuteStep); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementMinutes = function() { + var decrementedSelected = addMinutes(selected, -minuteStep); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noIncrementSeconds = function() { + var incrementedSelected = addSeconds(selected, secondStep); + return disabled || incrementedSelected > max || + incrementedSelected < selected && incrementedSelected < min; + }; + + $scope.noDecrementSeconds = function() { + var decrementedSelected = addSeconds(selected, -secondStep); + return disabled || decrementedSelected < min || + decrementedSelected > selected && decrementedSelected > max; + }; + + $scope.noToggleMeridian = function() { + if (selected.getHours() < 12) { + return disabled || addMinutes(selected, 12 * 60) > max; + } + + return disabled || addMinutes(selected, -12 * 60) < min; + }; + + var secondStep = timepickerConfig.secondStep; + if ($attrs.secondStep) { + watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) { + secondStep = +value; + })); + } + + $scope.showSeconds = timepickerConfig.showSeconds; + if ($attrs.showSeconds) { + watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) { + $scope.showSeconds = !!value; + })); + } + + // 12H / 24H mode + $scope.showMeridian = timepickerConfig.showMeridian; + if ($attrs.showMeridian) { + watchers.push($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 = +$scope.hours; + var valid = $scope.showMeridian ? hours > 0 && hours < 13 : + hours >= 0 && hours < 24; + if (!valid || $scope.hours === '') { + return undefined; + } + + if ($scope.showMeridian) { + if (hours === 12) { + hours = 0; + } + if ($scope.meridian === meridians[1]) { + hours = hours + 12; + } + } + return hours; + } + + function getMinutesFromTemplate() { + var minutes = +$scope.minutes; + var valid = minutes >= 0 && minutes < 60; + if (!valid || $scope.minutes === '') { + return undefined; + } + return minutes; + } + + function getSecondsFromTemplate() { + var seconds = +$scope.seconds; + return seconds >= 0 && seconds < 60 ? seconds : undefined; + } + + function pad(value, noPad) { + if (value === null) { + return ''; + } + + return angular.isDefined(value) && value.toString().length < 2 && !noPad ? + '0' + value : value.toString(); + } + + // Respond on mousewheel spin + this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + 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.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours()); + } + e.preventDefault(); + }); + + minutesInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes()); + } + e.preventDefault(); + }); + + secondsInputEl.on('mousewheel wheel', function(e) { + if (!disabled) { + $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds()); + } + e.preventDefault(); + }); + }; + + // Respond on up/down arrowkeys + this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + hoursInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementHours(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementHours(); + $scope.$apply(); + } + } + }); + + minutesInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementMinutes(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementMinutes(); + $scope.$apply(); + } + } + }); + + secondsInputEl.on('keydown', function(e) { + if (!disabled) { + if (e.which === 38) { // up + e.preventDefault(); + $scope.incrementSeconds(); + $scope.$apply(); + } else if (e.which === 40) { // down + e.preventDefault(); + $scope.decrementSeconds(); + $scope.$apply(); + } + } + }); + }; + + this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) { + if ($scope.readonlyInput) { + $scope.updateHours = angular.noop; + $scope.updateMinutes = angular.noop; + $scope.updateSeconds = angular.noop; + return; + } + + var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) { + ngModelCtrl.$setViewValue(null); + ngModelCtrl.$setValidity('time', false); + if (angular.isDefined(invalidHours)) { + $scope.invalidHours = invalidHours; + if (hoursModelCtrl) { + hoursModelCtrl.$setValidity('hours', false); + } + } + + if (angular.isDefined(invalidMinutes)) { + $scope.invalidMinutes = invalidMinutes; + if (minutesModelCtrl) { + minutesModelCtrl.$setValidity('minutes', false); + } + } + + if (angular.isDefined(invalidSeconds)) { + $scope.invalidSeconds = invalidSeconds; + if (secondsModelCtrl) { + secondsModelCtrl.$setValidity('seconds', false); + } + } + }; + + $scope.updateHours = function() { + var hours = getHoursFromTemplate(), + minutes = getMinutesFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(hours) && angular.isDefined(minutes)) { + selected.setHours(hours); + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(true); + } else { + refresh('h'); + } + } else { + invalidate(true); + } + }; + + hoursInputEl.on('blur', function(e) { + ngModelCtrl.$setTouched(); + if (modelIsEmpty()) { + makeValid(); + } else if ($scope.hours === null || $scope.hours === '') { + invalidate(true); + } else if (!$scope.invalidHours && $scope.hours < 10) { + $scope.$apply(function() { + $scope.hours = pad($scope.hours, !padHours); + }); + } + }); + + $scope.updateMinutes = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + selected.setHours(hours); + selected.setMinutes(minutes); + if (selected < min || selected > max) { + invalidate(undefined, true); + } else { + refresh('m'); + } + } else { + invalidate(undefined, true); + } + }; + + minutesInputEl.on('blur', function(e) { + ngModelCtrl.$setTouched(); + if (modelIsEmpty()) { + makeValid(); + } else if ($scope.minutes === null) { + invalidate(undefined, true); + } else if (!$scope.invalidMinutes && $scope.minutes < 10) { + $scope.$apply(function() { + $scope.minutes = pad($scope.minutes); + }); + } + }); + + $scope.updateSeconds = function() { + var seconds = getSecondsFromTemplate(); + + ngModelCtrl.$setDirty(); + + if (angular.isDefined(seconds)) { + selected.setSeconds(seconds); + refresh('s'); + } else { + invalidate(undefined, undefined, true); + } + }; + + secondsInputEl.on('blur', function(e) { + if (modelIsEmpty()) { + makeValid(); + } else if (!$scope.invalidSeconds && $scope.seconds < 10) { + $scope.$apply( function() { + $scope.seconds = pad($scope.seconds); + }); + } + }); + + }; + + 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() { + if (hoursModelCtrl) { + hoursModelCtrl.$setValidity('hours', true); + } + + if (minutesModelCtrl) { + minutesModelCtrl.$setValidity('minutes', true); + } + + if (secondsModelCtrl) { + secondsModelCtrl.$setValidity('seconds', true); + } + + ngModelCtrl.$setValidity('time', true); + $scope.invalidHours = false; + $scope.invalidMinutes = false; + $scope.invalidSeconds = false; + } + + function updateTemplate(keyboardChange) { + if (!ngModelCtrl.$modelValue) { + $scope.hours = null; + $scope.minutes = null; + $scope.seconds = null; + $scope.meridian = meridians[0]; + } else { + var hours = selected.getHours(), + minutes = selected.getMinutes(), + seconds = selected.getSeconds(); + + if ($scope.showMeridian) { + hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system + } + + $scope.hours = keyboardChange === 'h' ? hours : pad(hours, !padHours); + if (keyboardChange !== 'm') { + $scope.minutes = pad(minutes); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + + if (keyboardChange !== 's') { + $scope.seconds = pad(seconds); + } + $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1]; + } + } + + function addSecondsToSelected(seconds) { + selected = addSeconds(selected, seconds); + refresh(); + } + + function addMinutes(selected, minutes) { + return addSeconds(selected, minutes*60); + } + + function addSeconds(date, seconds) { + var dt = new Date(date.getTime() + seconds * 1000); + var newDate = new Date(date); + newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds()); + return newDate; + } + + function modelIsEmpty() { + return ($scope.hours === null || $scope.hours === '') && + ($scope.minutes === null || $scope.minutes === '') && + (!$scope.showSeconds || $scope.showSeconds && ($scope.seconds === null || $scope.seconds === '')); + } + + $scope.showSpinners = angular.isDefined($attrs.showSpinners) ? + $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners; + + $scope.incrementHours = function() { + if (!$scope.noIncrementHours()) { + addSecondsToSelected(hourStep * 60 * 60); + } + }; + + $scope.decrementHours = function() { + if (!$scope.noDecrementHours()) { + addSecondsToSelected(-hourStep * 60 * 60); + } + }; + + $scope.incrementMinutes = function() { + if (!$scope.noIncrementMinutes()) { + addSecondsToSelected(minuteStep * 60); + } + }; + + $scope.decrementMinutes = function() { + if (!$scope.noDecrementMinutes()) { + addSecondsToSelected(-minuteStep * 60); + } + }; + + $scope.incrementSeconds = function() { + if (!$scope.noIncrementSeconds()) { + addSecondsToSelected(secondStep); + } + }; + + $scope.decrementSeconds = function() { + if (!$scope.noDecrementSeconds()) { + addSecondsToSelected(-secondStep); + } + }; + + $scope.toggleMeridian = function() { + var minutes = getMinutesFromTemplate(), + hours = getHoursFromTemplate(); + + if (!$scope.noToggleMeridian()) { + if (angular.isDefined(minutes) && angular.isDefined(hours)) { + addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60)); + } else { + $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0]; + } + } + }; + + $scope.blur = function() { + ngModelCtrl.$setTouched(); + }; + + $scope.$on('$destroy', function() { + while (watchers.length) { + watchers.shift()(); + } + }); +}]) + +.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) { + return { + require: ['uibTimepicker', '?^ngModel'], + restrict: 'A', + controller: 'UibTimepickerController', + controllerAs: 'timepicker', + scope: {}, + templateUrl: function(element, attrs) { + return attrs.templateUrl || uibTimepickerConfig.templateUrl; + }, + link: function(scope, element, attrs, ctrls) { + var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1]; + + if (ngModelCtrl) { + timepickerCtrl.init(ngModelCtrl, element.find('input')); + } + } + }; +}]); + +angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', '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) { + // 000001111111100000000000002222222200000000000000003333333333333330000000000044444444000 + 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', '$$debounce', '$uibPosition', 'uibTypeaheadParser', + function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $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; + } + + originalScope.$watch(attrs.typeaheadMinLength, function (newVal) { + minLength = !newVal && newVal !== 0 ? 1 : newVal; + }); + + //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; + originalScope.$watch(attrs.typeaheadEditable, function (newVal) { + isEditable = newVal !== false; + }); + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a function to determine if an event should cause selection + var isSelectEvent = attrs.typeaheadShouldSelect ? $parse(attrs.typeaheadShouldSelect) : function(scope, vals) { + var evt = vals.$event; + return evt.which === 13 || evt.which === 9; + }; + + //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 appendTo = attrs.typeaheadAppendTo ? + originalScope.$eval(attrs.typeaheadAppendTo) : null; + + 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; + + //binding to a variable that indicates if dropdown is open + var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop; + + var showHint = originalScope.$eval(attrs.typeaheadShowHint) || 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.getOption('getterSetter')) { + return invokeModelSetter(scope, {$$$p: newValue}); + } + + 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 + }); + + var inputsContainer, hintInputElem; + //add read-only input to show hint + if (showHint) { + inputsContainer = angular.element('
    '); + inputsContainer.css('position', 'relative'); + element.after(inputsContainer); + hintInputElem = element.clone(); + hintInputElem.attr('placeholder', ''); + hintInputElem.attr('tabindex', '-1'); + hintInputElem.val(''); + hintInputElem.css({ + 'position': 'absolute', + 'top': '0px', + 'left': '0px', + 'border-color': 'transparent', + 'box-shadow': 'none', + 'opacity': 1, + 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)', + 'color': '#999' + }); + element.css({ + 'position': 'relative', + 'vertical-align': 'top', + 'background-color': 'transparent' + }); + + if (hintInputElem.attr('id')) { + hintInputElem.removeAttr('id'); // remove duplicate id if present. + } + inputsContainer.append(hintInputElem); + hintInputElem.after(element); + } + + //pop-up element used to display matches + var popUpEl = angular.element('
    '); + popUpEl.attr({ + id: popupId, + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx, evt)', + 'move-in-progress': 'moveInProgress', + query: 'query', + position: 'position', + 'assign-is-open': 'assignIsOpen(isOpen)', + debounce: 'debounceUpdate' + }); + //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 resetHint = function() { + if (showHint) { + hintInputElem.val(''); + } + }; + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + element.attr('aria-expanded', false); + resetHint(); + }; + + 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, evt) { + 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)) { + if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { + $$debounce(function() { + scope.select(0, evt); + }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + } else { + scope.select(0, evt); + } + } + + if (showHint) { + var firstLabel = scope.matches[0].label; + if (angular.isString(inputValue) && + inputValue.length > 0 && + firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) { + hintInputElem.val(inputValue + firstLabel.slice(inputValue.length)); + } else { + hintInputElem.val(''); + } + } + } 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).on('resize', fireRecalculating); + $document.find('body').on('scroll', fireRecalculating); + } + + // Declare the debounced function outside recalculating for + // proper debouncing + var debouncedRecalculate = $$debounce(function() { + // if popup is visible + if (scope.matches.length) { + recalculatePosition(); + } + + scope.moveInProgress = false; + }, eventDebounceTime); + + // Default progress type + scope.moveInProgress = false; + + function fireRecalculating() { + if (!scope.moveInProgress) { + scope.moveInProgress = true; + scope.$digest(); + } + + debouncedRecalculate(); + } + + // 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.assignIsOpen = function (isOpen) { + isOpenSetter(originalScope, isOpen); + }; + + scope.select = function(activeIdx, evt) { + //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), + $event: evt + }); + + 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.on('keydown', function(evt) { + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + var shouldSelect = isSelectEvent(originalScope, {$event: evt}); + + /** + * if there's nothing selected (i.e. focusFirst) and enter or tab is hit + * or + * shift + tab is pressed to bring focus to the previous element + * then clear the results + */ + if (scope.activeIdx === -1 && shouldSelect || evt.which === 9 && !!evt.shiftKey) { + resetMatches(); + scope.$digest(); + return; + } + + evt.preventDefault(); + var target; + switch (evt.which) { + case 27: // escape + evt.stopPropagation(); + + resetMatches(); + originalScope.$digest(); + break; + case 38: // up arrow + scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + case 40: // down arrow + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx]; + target.parentNode.scrollTop = target.offsetTop; + break; + default: + if (shouldSelect) { + scope.$apply(function() { + if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) { + $$debounce(function() { + scope.select(scope.activeIdx, evt); + }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']); + } else { + scope.select(scope.activeIdx, evt); + } + }); + } + } + }); + + element.on('focus', function (evt) { + hasFocus = true; + if (minLength === 0 && !modelCtrl.$viewValue) { + $timeout(function() { + getMatchesAsync(modelCtrl.$viewValue, evt); + }, 0); + } + }); + + element.on('blur', function(evt) { + if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) { + selected = true; + scope.$apply(function() { + if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) { + $$debounce(function() { + scope.select(scope.activeIdx, evt); + }, scope.debounceUpdate.blur); + } else { + scope.select(scope.activeIdx, evt); + } + }); + } + if (!isEditable && modelCtrl.$error.editable) { + modelCtrl.$setViewValue(); + scope.$apply(function() { + // Reset validity as we are clearing + modelCtrl.$setValidity('editable', true); + modelCtrl.$setValidity('parse', true); + }); + element.val(''); + } + 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) { + originalScope.$digest(); + } + } + }; + + $document.on('click', dismissClickHandler); + + originalScope.$on('$destroy', function() { + $document.off('click', dismissClickHandler); + if (appendToBody || appendTo) { + $popup.remove(); + } + + if (appendToBody) { + angular.element($window).off('resize', fireRecalculating); + $document.find('body').off('scroll', fireRecalculating); + } + // Prevent jQuery cache memory leak + popUpEl.remove(); + + if (showHint) { + inputsContainer.remove(); + } + }); + + var $popup = $compile(popUpEl)(scope); + + if (appendToBody) { + $document.find('body').append($popup); + } else if (appendTo) { + angular.element(appendTo).eq(0).append($popup); + } else { + element.after($popup); + } + + this.init = function(_modelCtrl) { + modelCtrl = _modelCtrl; + ngModelOptions = extractOptions(modelCtrl); + + scope.debounceUpdate = $parse(ngModelOptions.getOption('debounce'))(originalScope); + + //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; + } + + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return null; + } + + 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); + } + + //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; + }); + }; + + function extractOptions(ngModelCtrl) { + var ngModelOptions; + + if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing + // guarantee a value + ngModelOptions = ngModelCtrl.$options || {}; + + // mimic 1.6+ api + ngModelOptions.getOption = function (key) { + return ngModelOptions[key]; + }; + } else { // in angular >=1.6 $options is always present + ngModelOptions = ngModelCtrl.$options; + } + + return ngModelOptions; + } + }]) + + .directive('uibTypeahead', function() { + return { + controller: 'UibTypeaheadController', + require: ['ngModel', 'uibTypeahead'], + link: function(originalScope, element, attrs, ctrls) { + ctrls[1].init(ctrls[0]); + } + }; + }) + + .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) { + return { + scope: { + matches: '=', + query: '=', + active: '=', + position: '&', + moveInProgress: '=', + select: '&', + assignIsOpen: '&', + debounce: '&' + }, + replace: true, + templateUrl: function(element, attrs) { + return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html'; + }, + link: function(scope, element, attrs) { + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function() { + var isDropdownOpen = scope.matches.length > 0; + scope.assignIsOpen({ isOpen: isDropdownOpen }); + return isDropdownOpen; + }; + + scope.isActive = function(matchIdx) { + return scope.active === matchIdx; + }; + + scope.selectActive = function(matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function(activeIdx, evt) { + var debounce = scope.debounce(); + if (angular.isNumber(debounce) || angular.isObject(debounce)) { + $$debounce(function() { + scope.select({activeIdx: activeIdx, evt: evt}); + }, angular.isNumber(debounce) ? debounce : debounce['default']); + } else { + scope.select({activeIdx: activeIdx, evt: evt}); + } + }; + } + }; + }]) + + .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) || 'uib/template/typeahead/typeahead-match.html'; + $templateRequest(tplUrl).then(function(tplContent) { + var tplEl = angular.element(tplContent.trim()); + element.replaceWith(tplEl); + $compile(tplEl)(scope); + }); + } + }; + }]) + + .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'), '$&') : 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("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/accordion/accordion-group.html", + "
    \n" + + "

    \n" + + " {{heading}}\n" + + "

    \n" + + "
    \n" + + "
    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/accordion/accordion.html", + "
    "); +}]); + +angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/alert/alert.html", + "\n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/carousel/carousel.html", + "
    \n" + + " 1\">\n" + + " \n" + + " previous\n" + + "\n" + + " 1\">\n" + + " \n" + + " next\n" + + "\n" + + "
      1\">\n" + + "
    1. \n" + + " slide {{ $index + 1 }} of {{ slides.length }}, currently active\n" + + "
    2. \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/carousel/slide.html", + "
    \n" + + ""); +}]); + +angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/datepicker/datepicker.html", + "
    \n" + + "
    \n" + + "
    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/datepicker/day.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    {{::label.abbr}}
    {{ weekNumbers[$index] }}\n" + + " \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/datepicker/month.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    \n" + + " \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/datepicker/popup.html", + "
    \n" + + "
      \n" + + "
    • \n" + + "
    • \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    • \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/datepicker/year.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    \n" + + " \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/datepickerPopup/popup.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/datepickerPopup/popup.html", + "
      \n" + + "
    • \n" + + "
    • \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
    • \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("uib/template/modal/backdrop.html", + "
    \n" + + ""); +}]); + +angular.module("uib/template/modal/window.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/modal/window.html", + "
    \n" + + ""); +}]); + +angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/pager/pager.html", + "
  • {{::getText('previous')}}
  • \n" + + "
  • {{::getText('next')}}
  • \n" + + ""); +}]); + +angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/pagination/pagination.html", + "
  • {{::getText('first')}}
  • \n" + + "
  • {{::getText('previous')}}
  • \n" + + "
  • {{page.text}}
  • \n" + + "
  • {{::getText('next')}}
  • \n" + + "
  • {{::getText('last')}}
  • \n" + + ""); +}]); + +angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/tooltip/tooltip-html-popup.html", + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/tooltip/tooltip-popup.html", + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/tooltip/tooltip-template-popup.html", + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/popover/popover-html.html", + "
    \n" + + "\n" + + "
    \n" + + "

    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/popover/popover-template.html", + "
    \n" + + "\n" + + "
    \n" + + "

    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/popover/popover.html", + "
    \n" + + "\n" + + "
    \n" + + "

    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/progressbar/bar.html", + "
    \n" + + ""); +}]); + +angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/progressbar/progress.html", + "
    "); +}]); + +angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/progressbar/progressbar.html", + "
    \n" + + "
    \n" + + "
    \n" + + ""); +}]); + +angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/rating/rating.html", + "\n" + + " ({{ $index < value ? '*' : ' ' }})\n" + + " \n" + + "\n" + + ""); +}]); + +angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/tabs/tab.html", + "
  • \n" + + " {{heading}}\n" + + "
  • \n" + + ""); +}]); + +angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/tabs/tabset.html", + "
    \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + + ""); +}]); + +angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/timepicker/timepicker.html", + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "
        
      \n" + + " \n" + + " :\n" + + " \n" + + " :\n" + + " \n" + + "
        
      \n" + + ""); +}]); + +angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/typeahead/typeahead-match.html", + "\n" + + ""); +}]); + +angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function ($templateCache) { + $templateCache.put("uib/template/typeahead/typeahead-popup.html", + "
        \n" + + "
      • \n" + + "
        \n" + + "
      • \n" + + "
      \n" + + ""); +}]); +angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibCarouselCss && angular.element(document).find('head').prepend(''); angular.$$uibCarouselCss = true; }); +angular.module('ui.bootstrap.datepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerCss = true; }); +angular.module('ui.bootstrap.position').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibPositionCss && angular.element(document).find('head').prepend(''); angular.$$uibPositionCss = true; }); +angular.module('ui.bootstrap.datepickerPopup').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibDatepickerpopupCss && angular.element(document).find('head').prepend(''); angular.$$uibDatepickerpopupCss = true; }); +angular.module('ui.bootstrap.tooltip').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTooltipCss && angular.element(document).find('head').prepend(''); angular.$$uibTooltipCss = true; }); +angular.module('ui.bootstrap.timepicker').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTimepickerCss && angular.element(document).find('head').prepend(''); angular.$$uibTimepickerCss = true; }); +angular.module('ui.bootstrap.typeahead').run(function() {!angular.$$csp().noInlineStyle && !angular.$$uibTypeaheadCss && angular.element(document).find('head').prepend(''); angular.$$uibTypeaheadCss = true; }); +/** + * State-based routing for AngularJS + * @version v0.4.2 + * @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, + toJson = angular.toJson; + +function inherit(parent, extra) { + return extend(new (extend(function() {}, { prototype: parent }))(), extra); +} + +function merge(dst) { + forEach(arguments, function(obj) { + if (obj !== dst) { + forEach(obj, function(value, key) { + if (!dst.hasOwnProperty(key)) dst[key] = value; + }); + } + }); + return dst; +} + +/** + * Finds the common ancestor path between two states. + * + * @param {Object} first The first state. + * @param {Object} second The second state. + * @return {Array} Returns an array of state names in descending order, not including the root. + */ +function ancestors(first, second) { + var path = []; + + for (var n in first.path) { + if (first.path[n] !== second.path[n]) break; + path.push(first.path[n]); + } + return path; +} + +/** + * IE8-safe wrapper for `Object.keys()`. + * + * @param {Object} object A JavaScript object. + * @return {Array} Returns the keys of the object as an array. + */ +function objectKeys(object) { + if (Object.keys) { + return Object.keys(object); + } + var result = []; + + forEach(object, function(val, key) { + result.push(key); + }); + return result; +} + +/** + * IE8-safe wrapper for `Array.prototype.indexOf()`. + * + * @param {Array} array A JavaScript array. + * @param {*} value A value to search the array for. + * @return {Number} Returns the array index value of `value`, or `-1` if not present. + */ +function indexOf(array, value) { + if (Array.prototype.indexOf) { + return array.indexOf(value, Number(arguments[2]) || 0); + } + var len = array.length >>> 0, from = Number(arguments[2]) || 0; + from = (from < 0) ? Math.ceil(from) : Math.floor(from); + + if (from < 0) from += len; + + for (; from < len; from++) { + if (from in array && array[from] === value) return from; + } + return -1; +} + +/** + * Merges a set of parameters with all parameters inherited between the common parents of the + * current state and a given destination state. + * + * @param {Object} currentParams The value of the current state parameters ($stateParams). + * @param {Object} newParams The set of parameters which will be composited with inherited params. + * @param {Object} $current Internal definition of object representing the current state. + * @param {Object} $to Internal definition of object representing state to transition to. + */ +function inheritParams(currentParams, newParams, $current, $to) { + var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = []; + + for (var i in parents) { + if (!parents[i] || !parents[i].params) continue; + parentParams = objectKeys(parents[i].params); + if (!parentParams.length) continue; + + for (var j in parentParams) { + if (indexOf(inheritList, parentParams[j]) >= 0) continue; + inheritList.push(parentParams[j]); + inherited[parentParams[j]] = currentParams[parentParams[j]]; + } + } + return extend({}, inherited, newParams); +} + +/** + * Performs a non-strict comparison of the subset of two objects, defined by a list of keys. + * + * @param {Object} a The first object. + * @param {Object} b The second object. + * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified, + * it defaults to the list of keys in `a`. + * @return {Boolean} Returns `true` if the keys match, otherwise `false`. + */ +function equalForKeys(a, b, keys) { + if (!keys) { + keys = []; + for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility + } + + for (var i=0; i + * + * + * + * + * + * + * + * + * + * + * + * + */ +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. + *
      +   * $resolve.study(invocables)(locals, parent, self)
      +   * 
      + * is equivalent to + *
      +   * $resolve.resolve(invocables, locals, parent, self)
      +   * 
      + * 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 = silenceUncaughtInPromise(resolution.promise), + promises = result.$$promises = {}, + values = extend({}, locals), + wait = 1 + plan.length/3, + merged = false; + + silenceUncaughtInPromise(result); + + 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 + * Impact on loading templates for more details about this mechanism. + * + * @param {boolean} value + */ + this.shouldUnsafelyUseHttp = function(value) { + shouldUnsafelyUseHttp = !!value; + }; + + /** + * @ngdoc object + * @name ui.router.util.$templateFactory + * + * @requires $http + * @requires $templateCache + * @requires $injector + * + * @description + * Service. Manages loading of templates. + */ + this.$get = ['$http', '$templateCache', '$injector', function($http, $templateCache, $injector){ + return new TemplateFactory($http, $templateCache, $injector, shouldUnsafelyUseHttp);}]; +} + + +/** + * @ngdoc object + * @name ui.router.util.$templateFactory + * + * @requires $http + * @requires $templateCache + * @requires $injector + * + * @description + * Service. Manages loading of templates. + */ +function TemplateFactory($http, $templateCache, $injector, shouldUnsafelyUseHttp) { + + /** + * @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.} 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 { + if(!shouldUnsafelyUseHttp) { + return $injector.get('$templateRequest')(url); + } 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.} 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').provider('$templateFactory', TemplateFactoryProvider); + +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\[\]]+)(?:\:\s*( ... ))?\} - 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\[\]]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g, + searchPlaceholder = /([:]?)([\w\[\].-]+)|\{([\w\[\].-]+)(?:\:\s*((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/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, optional) { + var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&"); + if (!pattern) return result; + switch(squash) { + case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break; + case true: + result = result.replace(/\/$/, ''); + 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); + + if (regexp) { + type = $$UMFP.type(regexp) || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) }); + } + + 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, param.isOptional); + 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: + *
      + * new UrlMatcher('/user/{id}?q').concat('/details?date');
      + * new UrlMatcher('/user/{id}/details?q&date');
      + * 
      + * + * @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 + *
      + * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
      + *   x: '1', q: 'hello'
      + * });
      + * // returns { id: 'bob', q: 'hello', r: null }
      + * 
      + * + * @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(/\\-/g, "-"); } + + var split = reverseString(string).split(/-(?!\\)/); + var allReversed = map(split, reverseString); + return map(allReversed, unquoteDashes).reverse(); + } + + var param, paramVal; + for (i = 0; i < nPath; i++) { + paramName = paramNames[i]; + param = this.params[paramName]; + paramVal = m[i+1]; + // if the param value matches a pre-replace pair, replace the value before decoding. + for (j = 0; j < param.replace.length; j++) { + if (param.replace[j].from === paramVal) paramVal = param.replace[j].to; + } + if (paramVal && param.array === true) paramVal = decodePathArray(paramVal); + if (isDefined(paramVal)) paramVal = param.type.decode(paramVal); + values[paramName] = param.value(paramVal); + } + for (/**/; i < nTotal; i++) { + paramName = paramNames[i]; + values[paramName] = this.params[paramName].value(searchParams[paramName]); + param = this.params[paramName]; + paramVal = searchParams[paramName]; + for (j = 0; j < param.replace.length; j++) { + if (param.replace[j].from === paramVal) paramVal = param.replace[j].to; + } + if (isDefined(paramVal)) paramVal = param.type.decode(paramVal); + values[paramName] = param.value(paramVal); + } + + 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.} 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#validates + * @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 + *
      + * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
      + * // returns '/user/bob?q=yes'
      + * 
      + * + * @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]; + var isFinalPathParam = i + 1 === nPath; + + 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; + } + + if (isFinalPathParam && param.squash === true && result.slice(-1) === '/') result = result.slice(0, -1); + } else { + if (encoded == null || (isDefaultValue && squash !== false)) continue; + if (!isArray(encoded)) encoded = [ encoded ]; + if (encoded.length === 0) continue; + 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 + *
      + * {
      + *   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+/
      + * }
      + * 
      + * + * @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 + "}"; }; + +/** Given an encoded string, or a decoded object, returns a decoded object */ +Type.prototype.$normalize = function(val) { + return this.is(val) ? val : this.decode(val); +}; + +/* + * 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"); + + 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) { + if (isArray(val) && val.length === 0) return 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.$normalize = arrayHandler(bindTo(type, '$normalize')); + this.name = type.name; + this.$arrayMode = mode; + } + + return new ArrayType(this, 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; + + // Use tildes to pre-encode slashes. + // If the slashes are simply URLEncoded, the browser can choose to pre-decode them, + // and bidirectional encoding/decoding fails. + // Tilde was chosen because it's not a RFC 3986 section 2.2 Reserved Character + function valToString(val) { return val != null ? val.toString().replace(/(~|\/)/g, function (m) { return {'~':'~~', '/':'~2F'}[m]; }) : val; } + function valFromString(val) { return val != null ? val.toString().replace(/(~~|~2F)/g, function (m) { return {'~~':'~', '~2F':'/'}[m]; }) : val; } + + var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = { + "string": { + encode: valToString, + decode: valFromString, + // TODO: in 1.0, make string .is() return false if value is undefined/null by default. + // In 0.2.x, string params are optional by default for backwards compat + is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; }, + pattern: /[^/]*/ + }, + "int": { + encode: valToString, + decode: function(val) { return parseInt(val, 10); }, + is: function(val) { return val !== undefined && val !== null && 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, + 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: + * + *
      +   * 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" });
      +   * 
      + * + * 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: + * + *
      +   * // 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
      +   *   },
      +   *   // ...
      +   * });
      +   * 
      + */ + 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); + + if (angular.isString(config.type)) + return $types[config.type]; + if (config.type instanceof Type) + return config.type; + return 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"); + var defaultValue = injector.invoke(config.$$fn); + if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue)) + throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")"); + return defaultValue; + } + + /** + * [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) ? $$getDefaultValue() : self.type.$normalize(value); + } + + 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 keys = this.$$keys(), i, param, rawVal, normalized, encoded; + for (i = 0; i < keys.length; i++) { + param = this[keys[i]]; + rawVal = paramValues[keys[i]]; + if ((rawVal === undefined || rawVal === null) && param.isOptional) + break; // There was no parameter value, but the param is optional + normalized = param.type.$normalize(rawVal); + if (!param.type.is(normalized)) + return false; // The value was not of the correct Type, and could not be decoded to the correct Type + encoded = param.type.encode(normalized); + if (angular.isString(encoded) && !param.type.pattern.exec(encoded)) + return false; // The value was of the correct type, but when encoded, did not match the Type's regexp + } + return true; + }, + $$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 + *
      +   * 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;
      +   *     }
      +   *   });
      +   * });
      +   * 
      + * + * @param {function} rule Handler function that takes `$injector` and `$location` + * services as arguments. You can use them to return a valid path as a string. + * + * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance + */ + this.rule = function (rule) { + if (!isFunction(rule)) throw new Error("'rule' must be a function"); + rules.push(rule); + return this; + }; + + /** + * @ngdoc object + * @name ui.router.router.$urlRouterProvider#otherwise + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Defines a path that is used when an invalid route is requested. + * + * @example + *
      +   * 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';
      +   *   });
      +   * });
      +   * 
      + * + * @param {string|function} rule The url path you want to redirect to or a function + * rule that returns the url path. The function version is passed two params: + * `$injector` and `$location` services, and must return a url string. + * + * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance + */ + this.otherwise = function (rule) { + if (isString(rule)) { + var redirect = rule; + rule = function () { return redirect; }; + } + else if (!isFunction(rule)) throw new Error("'rule' must be a function"); + otherwise = rule; + return this; + }; + + + function handleIfMatch($injector, handler, match) { + if (!match) return false; + var result = $injector.invoke(handler, handler, { $match: match }); + return isDefined(result) ? result : true; + } + + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#when + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Registers a handler for a given url matching. + * + * If the handler is a string, it is + * treated as a redirect, and is interpolated according to the syntax of match + * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise). + * + * If the handler is a function, it is injectable. It gets invoked if `$location` + * matches. You have the option of inject the match object as `$match`. + * + * The handler can return + * + * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter` + * will continue trying to find another one that matches. + * - **string** which is treated as a redirect and passed to `$location.url()` + * - **void** or any **truthy** value tells `$urlRouter` that the url was handled. + * + * @example + *
      +   * 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);
      +   *     }
      +   *   });
      +   * });
      +   * 
      + * + * @param {string|object} what The incoming path that you want to redirect. + * @param {string|function} handler The path you want to redirect your user to. + */ + this.when = function (what, handler) { + var redirect, handlerIsString = isString(handler); + if (isString(what)) what = $urlMatcherFactory.compile(what); + + if (!handlerIsString && !isFunction(handler) && !isArray(handler)) + throw new Error("invalid 'handler' in when()"); + + var strategies = { + matcher: function (what, handler) { + if (handlerIsString) { + redirect = $urlMatcherFactory.compile(handler); + handler = ['$match', function ($match) { return redirect.format($match); }]; + } + return extend(function ($injector, $location) { + return handleIfMatch($injector, handler, what.exec($location.path(), $location.search())); + }, { + prefix: isString(what.prefix) ? what.prefix : '' + }); + }, + regex: function (what, handler) { + if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky"); + + if (handlerIsString) { + redirect = handler; + handler = ['$match', function ($match) { return interpolate(redirect, $match); }]; + } + return extend(function ($injector, $location) { + return handleIfMatch($injector, handler, what.exec($location.path())); + }, { + prefix: regExpPrefix(what) + }); + } + }; + + var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp }; + + for (var n in check) { + if (check[n]) return this.rule(strategies[n](what, handler)); + } + + throw new Error("invalid 'what' in when()"); + }; + + /** + * @ngdoc function + * @name ui.router.router.$urlRouterProvider#deferIntercept + * @methodOf ui.router.router.$urlRouterProvider + * + * @description + * Disables (or enables) deferring location change interception. + * + * If you wish to customize the behavior of syncing the URL (for example, if you wish to + * defer a transition but maintain the current URL), call this method at configuration time. + * Then, at run time, call `$urlRouter.listen()` after you have configured your own + * `$locationChangeSuccess` event handler. + * + * @example + *
      +   * 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();
      +   * });
      +   * 
      + * + * @param {boolean} defer Indicates whether to defer location change interception. Passing + no parameter is equivalent to `true`. + */ + this.deferIntercept = function (defer) { + if (defer === undefined) defer = true; + interceptDeferred = defer; + }; + + /** + * @ngdoc object + * @name ui.router.router.$urlRouter + * + * @requires $location + * @requires $rootScope + * @requires $injector + * @requires $browser + * + * @description + * + */ + this.$get = $get; + $get.$inject = ['$location', '$rootScope', '$injector', '$browser', '$sniffer']; + function $get( $location, $rootScope, $injector, $browser, $sniffer) { + + var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl; + + function appendBasePath(url, isHtml5, absolute) { + if (baseHref === '/') return url; + if (isHtml5) return baseHref.slice(0, -1) + url; + if (absolute) return baseHref.slice(1) + url; + return url; + } + + // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree + function update(evt) { + if (evt && evt.defaultPrevented) return; + var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl; + lastPushedUrl = undefined; + // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573 + //if (ignoreUpdate) return true; + + function check(rule) { + var handled = rule($injector, $location); + + if (!handled) return false; + if (isString(handled)) $location.replace().url(handled); + return true; + } + var n = rules.length, i; + + for (i = 0; i < n; i++) { + if (check(rules[i])) return; + } + // always check otherwise last to allow dynamic updates to the set of rules + if (otherwise) check(otherwise); + } + + function listen() { + listener = listener || $rootScope.$on('$locationChangeSuccess', update); + return listener; + } + + if (!interceptDeferred) listen(); + + return { + /** + * @ngdoc function + * @name ui.router.router.$urlRouter#sync + * @methodOf ui.router.router.$urlRouter + * + * @description + * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`. + * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event, + * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed + * with the transition by calling `$urlRouter.sync()`. + * + * @example + *
      +       * 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();
      +       *     });
      +       * });
      +       * 
      + */ + sync: function() { + update(); + }, + + listen: function() { + return listen(); + }, + + update: function(read) { + if (read) { + location = $location.url(); + return; + } + if ($location.url() === location) return; + + $location.url(location); + $location.replace(); + }, + + push: function(urlMatcher, params, options) { + var url = urlMatcher.format(params || {}); + + // Handle the special hash param, if needed + if (url !== null && params && params['#']) { + url += '#' + params['#']; + } + + $location.url(url); + lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined; + if (options && options.replace) $location.replace(); + }, + + /** + * @ngdoc function + * @name ui.router.router.$urlRouter#href + * @methodOf ui.router.router.$urlRouter + * + * @description + * A URL generation method that returns the compiled URL for a given + * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters. + * + * @example + *
      +       * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
      +       *   person: "bob"
      +       * });
      +       * // $bob == "/about/bob";
      +       * 
      + * + * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate. + * @param {object=} params An object of parameter values to fill the matcher's required parameters. + * @param {object=} options Options object. The options are: + * + * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl". + * + * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher` + */ + href: function(urlMatcher, params, options) { + if (!urlMatcher.validates(params)) return null; + + var isHtml5 = $locationProvider.html5Mode(); + if (angular.isObject(isHtml5)) { + isHtml5 = isHtml5.enabled; + } + + isHtml5 = isHtml5 && $sniffer.history; + + var url = urlMatcher.format(params); + options = options || {}; + + if (!isHtml5 && url !== null) { + url = "#" + $locationProvider.hashPrefix() + url; + } + + // Handle special hash param, if needed + if (url !== null && params && params['#']) { + url += '#' + params['#']; + } + + url = appendBasePath(url, isHtml5, options.absolute); + + if (!options.absolute || !url) { + return url; + } + + var slash = (!isHtml5 && url ? '/' : ''), port = $location.port(); + port = (port === 80 || port === 443 ? '' : ':' + port); + + return [$location.protocol(), '://', $location.host(), port, slash, url].join(''); + } + }; + } +} + +angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider); + +/** + * @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 = inherit(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) { + var ownParams = pick(state.ownParams, state.ownParams.$$keys()); + return state.parent && state.parent.params ? extend(state.parent.params.$$new(), 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; + view.resolveAs = view.resolveAs || state.resolveAs || '$resolve'; + 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 single stars + for (var i = 0, l = globSegments.length; i < l; i++) { + if (globSegments[i] === '*') { + segments[i] = '*'; + } + } + + //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; + } + + 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 + *
      +   * // 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.
      +   * 
      + * + * @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 + * + * 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 + * + *
      template:
      +   *   "

      inline template definition

      " + + * "
      "
      + *
      template: function(params) {
      +   *       return "

      generated template

      "; }
      + *
      + * + * @param {string|function=} stateConfig.templateUrl + * + * + * 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 + * + *
      templateUrl: "home.html"
      + *
      templateUrl: function(params) {
      +   *     return myTemplates[params.pageId]; }
      + * + * @param {function=} stateConfig.templateProvider + * + * Provider function that returns HTML content string. + *
       templateProvider:
      +   *       function(MyTemplateService, params) {
      +   *         return MyTemplateService.getTemplate(params.pageId);
      +   *       }
      + * + * @param {string|function=} stateConfig.controller + * + * + * 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. + *
      controller: "MyRegisteredController"
      + *
      controller:
      +   *     "MyRegisteredController as fooCtrl"}
      + *
      controller: function($scope, MyService) {
      +   *     $scope.data = MyService.getData(); }
      + * + * @param {function=} stateConfig.controllerProvider + * + * + * Injectable provider function that returns the actual controller or string. + *
      controllerProvider:
      +   *   function(MyResolveData) {
      +   *     if (MyResolveData.foo)
      +   *       return "FooCtrl"
      +   *     else if (MyResolveData.bar)
      +   *       return "BarCtrl";
      +   *     else return function($scope) {
      +   *       $scope.baz = "Qux";
      +   *     }
      +   *   }
      + * + * @param {string=} stateConfig.controllerAs + * + * + * A controller alias name. If present the controller will be + * published to scope under the controllerAs name. + *
      controllerAs: "myCtrl"
      + * + * @param {string|object=} stateConfig.parent + * + * Optionally specifies the parent state of this state. + * + *
      parent: 'parentState'
      + *
      parent: parentState // JS variable
      + * + * @param {object=} stateConfig.resolve + * + * + * 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. + * + *
      resolve: {
      +   *     myResolve1:
      +   *       function($http, $stateParams) {
      +   *         return $http.get("/api/foos/"+stateParams.fooID);
      +   *       }
      +   *     }
      + * + * @param {string=} stateConfig.url + * + * + * 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. + * + * (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for + * more details on acceptable patterns ) + * + * examples: + *
      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}"
      +   * url: "/messages/:mailboxid?{before:date}&{after:date}"
      +   * 
      + * + * @param {object=} stateConfig.views + * + * 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 + *
      views: {
      +   *     header: {
      +   *       controller: "headerCtrl",
      +   *       templateUrl: "header.html"
      +   *     }, body: {
      +   *       controller: "bodyCtrl",
      +   *       templateUrl: "body.html"
      +   *     }, footer: {
      +   *       controller: "footCtrl",
      +   *       templateUrl: "footer.html"
      +   *     }
      +   *   }
      + * + * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template. + *
      views: {
      +   *     'header@top': {
      +   *       controller: "msgHeaderCtrl",
      +   *       templateUrl: "msgHeader.html"
      +   *     }, 'body': {
      +   *       controller: "messagesCtrl",
      +   *       templateUrl: "messages.html"
      +   *     }
      +   *   }
      + * + * @param {boolean=} [stateConfig.abstract=false] + * + * An abstract state will never be directly activated, + * but can provide inherited properties to its common children states. + *
      abstract: true
      + * + * @param {function=} stateConfig.onEnter + * + * + * 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 explicitly annotate this function, + * because it won't be automatically annotated by your build tools. + * + *
      onEnter: function(MyService, $stateParams) {
      +   *     MyService.foo($stateParams.myParam);
      +   * }
      + * + * @param {function=} stateConfig.onExit + * + * + * 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 explicitly annotate this function, + * because it won't be automatically annotated by your build tools. + * + *
      onExit: function(MyService, $stateParams) {
      +   *     MyService.cleanup($stateParams.myParam);
      +   * }
      + * + * @param {boolean=} [stateConfig.reloadOnSearch=true] + * + * + * 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. + *
      reloadOnSearch: false
      + * + * @param {object=} stateConfig.data + * + * + * 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. + * + *
      data: {
      +   *     requiredRole: 'foo'
      +   * } 
      + * + * @param {object=} stateConfig.params + * + * + * 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.: + * + *
      // define a parameter's default value
      +   * params: {
      +   *     param1: { value: "defaultValue" }
      +   * }
      +   * // shorthand default values
      +   * params: {
      +   *     param1: "defaultValue",
      +   *     param2: "param2Default"
      +   * }
      + * + * - ** 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' }`). + * + *
      params: {
      +   *     param1: { array: true }
      +   * }
      + * + * - ** 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. + * - `""`: The parameter's default value is replaced with an arbitrary placeholder of your choice. + * + *
      params: {
      +   *     param1: {
      +   *       value: "defaultId",
      +   *       squash: true
      +   * } }
      +   * // squash "defaultValue" to "~"
      +   * params: {
      +   *     param1: {
      +   *       value: "defaultValue",
      +   *       squash: "~"
      +   * } }
      +   * 
      + * + * + * @example + *
      +   * // 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", {});
      +   * 
      + * + */ + 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 TransitionSupersededError = new Error('transition superseded'); + + var TransitionSuperseded = silenceUncaughtInPromise($q.reject(TransitionSupersededError)); + var TransitionPrevented = silenceUncaughtInPromise($q.reject(new Error('transition prevented'))); + var TransitionAborted = silenceUncaughtInPromise($q.reject(new Error('transition aborted'))); + var TransitionFailed = silenceUncaughtInPromise($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 + * + *
      +       * // 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
      +       * })
      +       * 
      + */ + 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) { + $rootScope.$broadcast('$stateChangeCancel', redirect.to, redirect.toParams, state, params); + 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, + * controllers reinstantiated, and events re-fired. + * + * @example + *
      +     * var app angular.module('app', ['ui.router']);
      +     *
      +     * app.controller('ctrl', function ($scope, $state) {
      +     *   $scope.reload = function(){
      +     *     $state.reload();
      +     *   }
      +     * });
      +     * 
      + * + * `reload()` is just an alias for: + *
      +     * $state.transitionTo($state.current, $stateParams, { 
      +     *   reload: true, inherit: false, notify: true
      +     * });
      +     * 
      + * + * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved. + * @example + *
      +     * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
      +     * //and current state is 'contacts.detail.item'
      +     * var app angular.module('app', ['ui.router']);
      +     *
      +     * app.controller('ctrl', function ($scope, $state) {
      +     *   $scope.reload = function(){
      +     *     //will reload 'contact.detail' and 'contact.detail.item' states
      +     *     $state.reload('contact.detail');
      +     *   }
      +     * });
      +     * 
      + * + * `reload()` is just an alias for: + *
      +     * $state.transitionTo($state.current, $stateParams, { 
      +     *   reload: true, inherit: false, notify: true
      +     * });
      +     * 
      + + * @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(state) { + return $state.transitionTo($state.current, $stateParams, { reload: state || 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 + *
      +     * var app = angular.module('app', ['ui.router']);
      +     *
      +     * app.controller('ctrl', function ($scope, $state) {
      +     *   $scope.changeState = function () {
      +     *     $state.go('contact.detail');
      +     *   };
      +     * });
      +     * 
      + * + * + * @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. Only parameters specified in the state definition can be overridden, new + * parameters will be ignored. 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|string|object}, If `true` will force transition even if no state or params + * have changed. It will reload the resolves and views of the current state and parent states. + * If `reload` is a string (or state object), the state object is fetched (by name, or object reference); and \ + * the transition reloads the resolves and views for that matched state, and all its children states. + * + * @returns {promise} A promise representing the state of the new transition. + * + * Possible success values: + * + * - $state.current + * + *
      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 + *
      +     * var app = angular.module('app', ['ui.router']);
      +     *
      +     * app.controller('ctrl', function ($scope, $state) {
      +     *   $scope.changeState = function () {
      +     *     $state.transitionTo('contact.detail');
      +     *   };
      +     * });
      +     * 
      + * + * @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|string=|object=}, 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. + * if String, then will reload the state with the name given in reload, and any children. + * if Object, then a stateObj is expected, will reload the state found in stateObj, and any children. + * + * @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); + + // Store the hash param for later (since it will be stripped out by various methods) + var hash = toParams['#']; + + 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]; + } + } else if (isString(options.reload) || isObject(options.reload)) { + if (isObject(options.reload) && !options.reload.name) { + throw new Error('Invalid reload state object'); + } + + var reloadState = options.reload === true ? fromPath[0] : findState(options.reload); + if (options.reload && !reloadState) { + throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'"); + } + + while (state && state === fromPath[keep] && state !== reloadState) { + 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 (shouldSkipReload(to, toParams, from, fromParams, locals, options)) { + if (hash) toParams['#'] = hash; + $state.params = toParams; + copy($state.params, $stateParams); + copy(filterByKeys(to.params.$$keys(), $stateParams), to.locals.globals.$stateParams); + if (options.location && to.navigable && to.navigable.url) { + $urlRouter.push(to.navigable.url, toParams, { + $$avoidResync: true, replace: options.location === 'replace' + }); + $urlRouter.update(true); + } + $state.transition = null; + return $q.when($state.current); + } + + // Filter parameters before we pass them to event handlers etc. + toParams = filterByKeys(to.params.$$keys(), toParams || {}); + + // Re-add the saved hash before we start returning things or broadcasting $stateChangeStart + if (hash) toParams['#'] = hash; + + // 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 + * + *
      +         * $rootScope.$on('$stateChangeStart',
      +         * function(event, toState, toParams, fromState, fromParams){
      +         *     event.preventDefault();
      +         *     // transitionTo() promise will be rejected with
      +         *     // a 'transition prevented' error
      +         * })
      +         * 
      + */ + if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams, options).defaultPrevented) { + $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams); + //Don't update and resync url if there's been a new transition started. see issue #2238, #600 + if ($state.transition == null) $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) { + $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams); + 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) { + $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams); + 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; + }).then(null, function (error) { + // propagate TransitionSuperseded error without emitting $stateChangeCancel + // as it was already emitted in the success handler above + if (error === TransitionSupersededError) return TransitionSuperseded; + + if ($state.transition !== transition) { + $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams); + 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); + }); + + silenceUncaughtInPromise(transition); + 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 + *
      +     * $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
      +     * 
      Item
      + *
      + * + * @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 || objectKeys(params).reduce(function(acc, key) { + var paramDef = state.params[key]; + return acc && !paramDef || paramDef.type.equals($stateParams[key], params[key]); + }, 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 + *
      +     * $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
      +     * 
      Item
      + *
      + * + * Basic globbing patterns + *
      +     * $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
      +     * 
      + * + * @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; } + if (!params) { return true; } + + var keys = objectKeys(params); + for (var i = 0; i < keys.length; i++) { + var key = keys[i], paramDef = state.params[key]; + if (paramDef && !paramDef.type.equals($stateParams[key], params[key])) { + return false; + } + } + + return objectKeys(params).reduce(function(acc, key) { + var paramDef = state.params[key]; + return acc && !paramDef || paramDef.type.equals($stateParams[key], params[key]); + }, 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 + *
      +     * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
      +     * 
      + * + * @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().concat('#'), 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); + + function resolveViews() { + var viewsPromises = []; + + // 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: dst.globals, params: $stateParams, notify: options.notify }) || ''; + }]; + + viewsPromises.push($resolve.resolve(injectables, dst.globals, 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, dst.globals); + 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; + result.$$resolveAs = view.resolveAs; + dst[name] = result; + })); + }); + + return $q.all(viewsPromises).then(function(){ + return dst.globals; + }); + } + + // Wait for all the promises and then return the activation object + return $q.all(promises).then(resolveViews).then(function (values) { + return dst; + }); + } + + return $state; + } + + function shouldSkipReload(to, toParams, from, fromParams, locals, options) { + // Return true if there are no differences in non-search (path/object) params, false if there are differences + function nonSearchParamsEqual(fromAndToState, fromParams, toParams) { + // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params. + function notSearchParam(key) { + return fromAndToState.params[key].location != "search"; + } + var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam); + var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys)); + var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams); + return nonQueryParamSet.$$equals(fromParams, toParams); + } + + // If reload was not explicitly requested + // and we're transitioning to the same state we're already in + // and the locals didn't change + // or they changed in a way that doesn't merit reloading + // (reloadOnParams:false, or reloadOnSearch.false and only search params changed) + // Then return true. + if (!options.reload && to === from && + (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) { + return true; + } + } +} + +angular.module('ui.router.state') + .factory('$stateParams', function () { return {}; }) + .constant("$state.runtime", { autoinject: true }) + .provider('$state', $StateProvider) + // Inject $state to initialize when entering runtime. #2574 + .run(['$injector', function ($injector) { + // Allow tests (stateSpec.js) to turn this off by defining this constant + if ($injector.get("$state.runtime").autoinject) { + $injector.get('$state'); + } + }]); + + +$ViewProvider.$inject = []; +function $ViewProvider() { + + this.$get = $get; + /** + * @ngdoc object + * @name ui.router.state.$view + * + * @requires ui.router.util.$templateFactory + * @requires $rootScope + * + * @description + * + */ + $get.$inject = ['$rootScope', '$templateFactory']; + function $get( $rootScope, $templateFactory) { + return { + // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... }) + /** + * @ngdoc function + * @name ui.router.state.$view#load + * @methodOf ui.router.state.$view + * + * @description + * + * @param {string} name name + * @param {object} options option object. + */ + load: function load(name, options) { + var result, defaults = { + template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {} + }; + options = extend(defaults, options); + + if (options.view) { + result = $templateFactory.fromConfig(options.view, options.params, options.locals); + } + return result; + } + }; + } +} + +angular.module('ui.router.state').provider('$view', $ViewProvider); + +/** + * @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) { + return $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. + *
      + * 
      + * 
      + * + * + *
      + *
      + * + * 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: + *
      + * 
      + * $stateProvider.state("home", { + * template: "

      HELLO!

      " + * }) + *
      + * + * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#methods_state `views`} + * config property, by name, in this case an empty name: + *
      + * $stateProvider.state("home", {
      + *   views: {
      + *     "": {
      + *       template: "

      HELLO!

      " + * } + * } + * }) + *
      + * + * 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: + *
      + * 
      + *
      + *
      + * $stateProvider.state("home", {
      + *   views: {
      + *     "main": {
      + *       template: "

      HELLO!

      " + * } + * } + * }) + *
      + * + * Really though, you'll use views to set up multiple views: + *
      + * 
      + *
      + *
      + *
      + * + *
      + * $stateProvider.state("home", {
      + *   views: {
      + *     "": {
      + *       template: "

      HELLO!

      " + * }, + * "chart": { + * template: "" + * }, + * "data": { + * template: "" + * } + * } + * }) + *
      + * + * Examples for `autoscroll`: + * + *
      + * 
      + * 
      + *
      + * 
      + * 
      + * 
      + * 
      + * 
      + * + * Resolve data: + * + * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this + * can be customized using [[ViewDeclaration.resolveAs]]). This can be then accessed from the template. + * + * Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the + * controller is instantiated. The `$onInit()` hook can be used to perform initialization code which + * depends on `$resolve` data. + * + * Example usage of $resolve in a view template + *
      + * $stateProvider.state('home', {
      + *   template: '',
      + *   resolve: {
      + *     user: function(UserService) { return UserService.fetchUser(); }
      + *   }
      + * });
      + * 
      + */ +$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q']; +function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) { + + function getService() { + return ($injector.has) ? function(service) { + return $injector.has(service) ? $injector.get(service) : null; + } : function(service) { + try { + return $injector.get(service); + } catch (e) { + return null; + } + }; + } + + var service = getService(), + $animator = service('$animator'), + $animate = service('$animate'); + + // Returns a set of DOM manipulation functions based on which Angular version + // it should use + function getRenderer(attrs, scope) { + var statics = function() { + return { + enter: function (element, target, cb) { target.after(element); cb(); }, + leave: function (element, cb) { element.remove(); cb(); } + }; + }; + + if ($animate) { + return { + enter: function(element, target, cb) { + if (angular.version.minor > 2) { + $animate.enter(element, null, target).then(cb); + } else { + $animate.enter(element, null, target, cb); + } + }, + leave: function(element, cb) { + if (angular.version.minor > 2) { + $animate.leave(element).then(cb); + } else { + $animate.leave(element, cb); + } + } + }; + } + + if ($animator) { + var animate = $animator && $animator(scope, attrs); + + return { + enter: function(element, target, cb) {animate.enter(element, null, target); cb(); }, + leave: function(element, cb) { animate.leave(element); cb(); } + }; + } + + return statics(); + } + + var directive = { + restrict: 'ECA', + terminal: true, + priority: 400, + transclude: 'element', + compile: function (tElement, tAttrs, $transclude) { + return function (scope, $element, attrs) { + var previousEl, currentEl, currentScope, latestLocals, + onloadExp = attrs.onload || '', + autoScrollExp = attrs.autoscroll, + renderer = getRenderer(attrs, scope), + inherited = $element.inheritedData('$uiView'); + + scope.$on('$stateChangeSuccess', function() { + updateView(false); + }); + + updateView(true); + + function cleanupLastView() { + if (previousEl) { + previousEl.remove(); + previousEl = null; + } + + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + + if (currentEl) { + var $uiViewData = currentEl.data('$uiViewAnim'); + renderer.leave(currentEl, function() { + $uiViewData.$$animLeave.resolve(); + previousEl = null; + }); + + previousEl = currentEl; + currentEl = null; + } + } + + function updateView(firstTime) { + var newScope, + name = getUiViewName(scope, attrs, $element, $interpolate), + previousLocals = name && $state.$current && $state.$current.locals[name]; + + if (!firstTime && previousLocals === latestLocals) return; // nothing to do + newScope = scope.$new(); + latestLocals = $state.$current.locals[name]; + + /** + * @ngdoc event + * @name ui.router.state.directive:ui-view#$viewContentLoading + * @eventOf ui.router.state.directive:ui-view + * @eventType emits on ui-view directive scope + * @description + * + * Fired once the view **begins loading**, *before* the DOM is rendered. + * + * @param {Object} event Event object. + * @param {string} viewName Name of the view. + */ + newScope.$emit('$viewContentLoading', name); + + var clone = $transclude(newScope, function(clone) { + var animEnter = $q.defer(), animLeave = $q.defer(); + var viewAnimData = { + $animEnter: animEnter.promise, + $animLeave: animLeave.promise, + $$animLeave: animLeave + }; + + clone.data('$uiViewAnim', viewAnimData); + renderer.enter(clone, $element, function onUiViewEnter() { + animEnter.resolve(); + if(currentScope) { + currentScope.$emit('$viewContentAnimationEnded'); + } + + if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) { + $uiViewScroll(clone); + } + }); + cleanupLastView(); + }); + + currentEl = clone; + currentScope = newScope; + /** + * @ngdoc event + * @name ui.router.state.directive:ui-view#$viewContentLoaded + * @eventOf ui.router.state.directive:ui-view + * @eventType emits on ui-view directive scope + * @description + * Fired once the view is **loaded**, *after* the DOM is rendered. + * + * @param {Object} event Event object. + * @param {string} viewName Name of the view. + */ + currentScope.$emit('$viewContentLoaded', name); + currentScope.$eval(onloadExp); + } + }; + } + }; + + return directive; +} + +$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate']; +function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) { + return { + restrict: 'ECA', + priority: -400, + compile: function (tElement) { + var initial = tElement.html(); + if (tElement.empty) { + tElement.empty(); + } else { + // ng 1.0.0 doesn't have empty(), which cleans up data and handlers + tElement[0].innerHTML = null; + } + + return function (scope, $element, attrs) { + var current = $state.$current, + name = getUiViewName(scope, attrs, $element, $interpolate), + locals = current && current.locals[name]; + + if (! locals) { + $element.html(initial); + $compile($element.contents())(scope); + return; + } + + $element.data('$uiView', { name: name, state: locals.$$state }); + $element.html(locals.$template ? locals.$template : initial); + + var resolveData = angular.extend({}, locals); + scope[locals.$$resolveAs] = resolveData; + + var link = $compile($element.contents()); + + if (locals.$$controller) { + locals.$scope = scope; + locals.$element = $element; + var controller = $controller(locals.$$controller, locals); + if (locals.$$controllerAs) { + scope[locals.$$controllerAs] = controller; + scope[locals.$$controllerAs][locals.$$resolveAs] = resolveData; + } + if (isFunction(controller.$onInit)) controller.$onInit(); + $element.data('$ngControllerController', controller); + $element.children().data('$ngControllerController', controller); + } + + link(scope); + }; + } + }; +} + +/** + * Shared ui-view code for both directives: + * Given scope, element, and its attributes, return the view's name + */ +function getUiViewName(scope, attrs, element, $interpolate) { + var name = $interpolate(attrs.uiView || attrs.name || '')(scope); + var uiViewCreatedBy = element.inheritedData('$uiView'); + return name.indexOf('@') >= 0 ? name : (name + '@' + (uiViewCreatedBy ? uiViewCreatedBy.state.name : '')); +} + +angular.module('ui.router.state').directive('uiView', $ViewDirective); +angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); + +function parseStateRef(ref, current) { + var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed; + if (preparsed) ref = current + '(' + preparsed[1] + ')'; + parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/); + if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'"); + return { state: parsed[1], paramExpr: parsed[3] || null }; +} + +function stateContext(el) { + var stateData = el.parent().inheritedData('$uiView'); + + if (stateData && stateData.state && stateData.state.name) { + return stateData.state; + } +} + +function getTypeInfo(el) { + // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. + var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]'; + var isForm = el[0].nodeName === "FORM"; + + return { + attr: isForm ? "action" : (isSvg ? 'xlink:href' : 'href'), + isAnchor: el.prop("tagName").toUpperCase() === "A", + clickable: !isForm + }; +} + +function clickHook(el, $state, $timeout, type, current) { + return function(e) { + var button = e.which || e.button, target = current(); + + if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || el.attr('target'))) { + // HACK: This is to allow ng-clicks to be processed before the transition is initiated: + var transition = $timeout(function() { + $state.go(target.state, target.params, target.options); + }); + e.preventDefault(); + + // if the state has no URL, ignore one preventDefault from the directive. + var ignorePreventDefaultCount = type.isAnchor && !target.href ? 1: 0; + + e.preventDefault = function() { + if (ignorePreventDefaultCount-- <= 0) $timeout.cancel(transition); + }; + } + }; +} + +function defaultOpts(el, $state) { + return { relative: stateContext(el) || $state.$current, inherit: true }; +} + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref + * + * @requires ui.router.state.$state + * @requires $timeout + * + * @restrict A + * + * @description + * A directive that binds a link (`` tag) to a state. If the state has an associated + * URL, the directive will automatically generate & update the `href` attribute via + * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking + * the link will trigger a state transition with optional parameters. + * + * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be + * handled natively by the browser. + * + * You can also use relative state paths within ui-sref, just like the relative + * paths passed to `$state.go()`. You just need to be aware that the path is relative + * to the state that the link lives in, in other words the state that loaded the + * template containing the link. + * + * You can specify options to pass to {@link ui.router.state.$state#methods_go $state.go()} + * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`, + * and `reload`. + * + * @example + * Here's an example of how you'd use ui-sref and how it would compile. If you have the + * following template: + *
      + * Home | About | Next page
      + *
      + * 
      + * 
      + * + * Then the compiled html would be (assuming Html5Mode is off and current state is contacts): + *
      + * Home | About | Next page
      + *
      + * 
        + *
      • + * Joe + *
      • + *
      • + * Alice + *
      • + *
      • + * Bob + *
      • + *
      + * + * Home + *
      + * + * @param {string} ui-sref 'stateName' can be any valid absolute or relative state + * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#methods_go $state.go()} + */ +$StateRefDirective.$inject = ['$state', '$timeout']; +function $StateRefDirective($state, $timeout) { + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function(scope, element, attrs, uiSrefActive) { + var ref = parseStateRef(attrs.uiSref, $state.current.name); + var def = { state: ref.state, href: null, params: null }; + var type = getTypeInfo(element); + var active = uiSrefActive[1] || uiSrefActive[0]; + var unlinkInfoFn = null; + var hookFn; + + def.options = extend(defaultOpts(element, $state), attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {}); + + var update = function(val) { + if (val) def.params = angular.copy(val); + def.href = $state.href(ref.state, def.params, def.options); + + if (unlinkInfoFn) unlinkInfoFn(); + if (active) unlinkInfoFn = active.$$addStateInfo(ref.state, def.params); + if (def.href !== null) attrs.$set(type.attr, def.href); + }; + + if (ref.paramExpr) { + scope.$watch(ref.paramExpr, function(val) { if (val !== def.params) update(val); }, true); + def.params = angular.copy(scope.$eval(ref.paramExpr)); + } + update(); + + if (!type.clickable) return; + hookFn = clickHook(element, $state, $timeout, type, function() { return def; }); + element[element.on ? 'on' : 'bind']("click", hookFn); + scope.$on('$destroy', function() { + element[element.off ? 'off' : 'unbind']("click", hookFn); + }); + } + }; +} + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-state + * + * @requires ui.router.state.uiSref + * + * @restrict A + * + * @description + * Much like ui-sref, but will accept named $scope properties to evaluate for a state definition, + * params and override options. + * + * @param {string} ui-state 'stateName' can be any valid absolute or relative state + * @param {Object} ui-state-params params to pass to {@link ui.router.state.$state#methods_href $state.href()} + * @param {Object} ui-state-opts options to pass to {@link ui.router.state.$state#methods_go $state.go()} + */ +$StateRefDynamicDirective.$inject = ['$state', '$timeout']; +function $StateRefDynamicDirective($state, $timeout) { + return { + restrict: 'A', + require: ['?^uiSrefActive', '?^uiSrefActiveEq'], + link: function(scope, element, attrs, uiSrefActive) { + var type = getTypeInfo(element); + var active = uiSrefActive[1] || uiSrefActive[0]; + var group = [attrs.uiState, attrs.uiStateParams || null, attrs.uiStateOpts || null]; + var watch = '[' + group.map(function(val) { return val || 'null'; }).join(', ') + ']'; + var def = { state: null, params: null, options: null, href: null }; + var unlinkInfoFn = null; + var hookFn; + + function runStateRefLink (group) { + def.state = group[0]; def.params = group[1]; def.options = group[2]; + def.href = $state.href(def.state, def.params, def.options); + + if (unlinkInfoFn) unlinkInfoFn(); + if (active) unlinkInfoFn = active.$$addStateInfo(def.state, def.params); + if (def.href) attrs.$set(type.attr, def.href); + } + + scope.$watch(watch, runStateRefLink, true); + runStateRefLink(scope.$eval(watch)); + + if (!type.clickable) return; + hookFn = clickHook(element, $state, $timeout, type, function() { return def; }); + element[element.on ? 'on' : 'bind']("click", hookFn); + scope.$on('$destroy', function() { + element[element.off ? 'off' : 'unbind']("click", hookFn); + }); + } + }; +} + + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref-active + * + * @requires ui.router.state.$state + * @requires ui.router.state.$stateParams + * @requires $interpolate + * + * @restrict A + * + * @description + * A directive working alongside ui-sref to add classes to an element when the + * related ui-sref directive's state is active, and removing them when it is inactive. + * The primary use-case is to simplify the special appearance of navigation menus + * relying on `ui-sref`, by having the "active" state's menu button appear different, + * distinguishing it from the inactive menu items. + * + * ui-sref-active can live on the same element as ui-sref or on a parent element. The first + * ui-sref-active found at the same level or above the ui-sref will be used. + * + * Will activate when the ui-sref's target state or any child state is active. If you + * need to activate only when the ui-sref target state is active and *not* any of + * it's children, then you will use + * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq} + * + * @example + * Given the following template: + *
      + * 
      + * 
      + * + * + * 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): + *
      + * 
      + * 
      + * + * 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: + *
      + * 
        + *
      • + * link + *
      • + *
      + *
      + * + * It is also possible to pass ui-sref-active an expression that evaluates + * to an object hash, whose keys represent active class names and whose + * values represent the respective state names/globs. + * ui-sref-active will match if the current active state **includes** any of + * the specified state names/globs, even the abstract ones. + * + * @Example + * Given the following template, with "admin" being an abstract state: + *
      + * 
      + * Roles + *
      + *
      + * + * When the current state is "admin.roles" the "active" class will be applied + * to both the
      and elements. It is important to note that the state + * names/globs passed to ui-sref-active shadow the state provided by ui-sref. + */ + +/** + * @ngdoc directive + * @name ui.router.state.directive:ui-sref-active-eq + * + * @requires ui.router.state.$state + * @requires ui.router.state.$stateParams + * @requires $interpolate + * + * @restrict A + * + * @description + * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate + * when the exact target state used in the `ui-sref` is active; no child states. + * + */ +$StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate']; +function $StateRefActiveDirective($state, $stateParams, $interpolate) { + return { + restrict: "A", + controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) { + var states = [], activeClasses = {}, activeEqClass, uiSrefActive; + + // There probably isn't much point in $observing this + // uiSrefActive and uiSrefActiveEq share the same directive object with some + // slight difference in logic routing + activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope); + + try { + uiSrefActive = $scope.$eval($attrs.uiSrefActive); + } catch (e) { + // Do nothing. uiSrefActive is not a valid expression. + // Fall back to using $interpolate below + } + uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope); + if (isObject(uiSrefActive)) { + forEach(uiSrefActive, function(stateOrName, activeClass) { + if (isString(stateOrName)) { + var ref = parseStateRef(stateOrName, $state.current.name); + addState(ref.state, $scope.$eval(ref.paramExpr), activeClass); + } + }); + } + + // Allow uiSref to communicate with uiSrefActive[Equals] + this.$$addStateInfo = function (newState, newParams) { + // we already got an explicit state provided by ui-sref-active, so we + // shadow the one that comes from ui-sref + if (isObject(uiSrefActive) && states.length > 0) { + return; + } + var deregister = addState(newState, newParams, uiSrefActive); + update(); + return deregister; + }; + + $scope.$on('$stateChangeSuccess', update); + + function addState(stateName, stateParams, activeClass) { + var state = $state.get(stateName, stateContext($element)); + var stateHash = createStateHash(stateName, stateParams); + + var stateInfo = { + state: state || { name: stateName }, + params: stateParams, + hash: stateHash + }; + + states.push(stateInfo); + activeClasses[stateHash] = activeClass; + + return function removeState() { + var idx = states.indexOf(stateInfo); + if (idx !== -1) states.splice(idx, 1); + }; + } + + /** + * @param {string} state + * @param {Object|string} [params] + * @return {string} + */ + function createStateHash(state, params) { + if (!isString(state)) { + throw new Error('state should be a string'); + } + if (isObject(params)) { + return state + toJson(params); + } + params = $scope.$eval(params); + if (isObject(params)) { + return state + toJson(params); + } + return state; + } + + // Update route state + function update() { + for (var i = 0; i < states.length; i++) { + if (anyMatch(states[i].state, states[i].params)) { + addClass($element, activeClasses[states[i].hash]); + } else { + removeClass($element, activeClasses[states[i].hash]); + } + + if (exactMatch(states[i].state, states[i].params)) { + addClass($element, activeEqClass); + } else { + removeClass($element, activeEqClass); + } + } + } + + function addClass(el, className) { $timeout(function () { el.addClass(className); }); } + function removeClass(el, className) { el.removeClass(className); } + function anyMatch(state, params) { return $state.includes(state.name, params); } + function exactMatch(state, params) { return $state.is(state.name, params); } + + update(); + }] + }; +} + +angular.module('ui.router.state') + .directive('uiSref', $StateRefDirective) + .directive('uiSrefActive', $StateRefActiveDirective) + .directive('uiSrefActiveEq', $StateRefActiveDirective) + .directive('uiState', $StateRefDynamicDirective); + +/** + * @ngdoc filter + * @name ui.router.state.filter:isState + * + * @requires ui.router.state.$state + * + * @description + * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}. + */ +$IsStateFilter.$inject = ['$state']; +function $IsStateFilter($state) { + var isFilter = function (state, params) { + return $state.is(state, params); + }; + isFilter.$stateful = true; + return isFilter; +} + +/** + * @ngdoc filter + * @name ui.router.state.filter:includedByState + * + * @requires ui.router.state.$state + * + * @description + * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}. + */ +$IncludedByStateFilter.$inject = ['$state']; +function $IncludedByStateFilter($state) { + var includesFilter = function (state, params, options) { + return $state.includes(state, params, options); + }; + includesFilter.$stateful = true; + return includesFilter; +} + +angular.module('ui.router.state') + .filter('isState', $IsStateFilter) + .filter('includedByState', $IncludedByStateFilter); +})(window, window.angular); +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.angularStripe = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * + * Copyright (c) 2014 Jon Schlinkert, contributors. + * Licensed under the MIT license. + */ + +var isNumber = _dereq_('is-number'); +var slice = _dereq_('array-slice'); + +module.exports = function last(arr, num) { + if (!Array.isArray(arr)) { + throw new Error('array-last expects an array as the first argument.'); + } + + if (arr.length === 0) { + return null; + } + + var res = slice(arr, arr.length - (isNumber(num) ? +num : 1)); + if (+num === 1 || num == null) { + return res[0]; + } + return res; +}; + +},{"array-slice":8,"is-number":20}],8:[function(_dereq_,module,exports){ +/*! + * array-slice + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +module.exports = function slice(arr, start, end) { + var len = arr.length >>> 0; + var range = []; + + start = idx(arr, start); + end = idx(arr, end, len); + + while (start < end) { + range.push(arr[start++]); + } + return range; +}; + + +function idx(arr, pos, end) { + var len = arr.length >>> 0; + + if (pos == null) { + pos = end || 0; + } else if (pos < 0) { + pos = Math.max(len + pos, 0); + } else { + pos = Math.min(pos, len); + } + + return pos; +} +},{}],9:[function(_dereq_,module,exports){ +"use strict"; + +// rawAsap provides everything we need except exception management. +var rawAsap = _dereq_("./raw"); +// RawTasks are recycled to reduce GC churn. +var freeTasks = []; +// We queue errors to ensure they are thrown in right order (FIFO). +// Array-as-queue is good enough here, since we are just dealing with exceptions. +var pendingErrors = []; +var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError); + +function throwFirstError() { + if (pendingErrors.length) { + throw pendingErrors.shift(); + } +} + +/** + * Calls a task as soon as possible after returning, in its own event, with priority + * over other events like animation, reflow, and repaint. An error thrown from an + * event will not interrupt, nor even substantially slow down the processing of + * other events, but will be rather postponed to a lower priority event. + * @param {{call}} task A callable object, typically a function that takes no + * arguments. + */ +module.exports = asap; +function asap(task) { + var rawTask; + if (freeTasks.length) { + rawTask = freeTasks.pop(); + } else { + rawTask = new RawTask(); + } + rawTask.task = task; + rawAsap(rawTask); +} + +// We wrap tasks with recyclable task objects. A task object implements +// `call`, just like a function. +function RawTask() { + this.task = null; +} + +// The sole purpose of wrapping the task is to catch the exception and recycle +// the task object after its single use. +RawTask.prototype.call = function () { + try { + this.task.call(); + } catch (error) { + if (asap.onerror) { + // This hook exists purely for testing purposes. + // Its name will be periodically randomized to break any code that + // depends on its existence. + asap.onerror(error); + } else { + // In a web browser, exceptions are not fatal. However, to avoid + // slowing down the queue of pending tasks, we rethrow the error in a + // lower priority turn. + pendingErrors.push(error); + requestErrorThrow(); + } + } finally { + this.task = null; + freeTasks[freeTasks.length] = this; + } +}; + +},{"./raw":10}],10:[function(_dereq_,module,exports){ +(function (global){ +"use strict"; + +// Use the fastest means possible to execute a task in its own turn, with +// priority over other events including IO, animation, reflow, and redraw +// events in browsers. +// +// An exception thrown by a task will permanently interrupt the processing of +// subsequent tasks. The higher level `asap` function ensures that if an +// exception is thrown by a task, that the task queue will continue flushing as +// soon as possible, but if you use `rawAsap` directly, you are responsible to +// either ensure that no exceptions are thrown from your task, or to manually +// call `rawAsap.requestFlush` if an exception is thrown. +module.exports = rawAsap; +function rawAsap(task) { + if (!queue.length) { + requestFlush(); + flushing = true; + } + // Equivalent to push, but avoids a function call. + queue[queue.length] = task; +} + +var queue = []; +// Once a flush has been requested, no further calls to `requestFlush` are +// necessary until the next `flush` completes. +var flushing = false; +// `requestFlush` is an implementation-specific method that attempts to kick +// off a `flush` event as quickly as possible. `flush` will attempt to exhaust +// the event queue before yielding to the browser's own event loop. +var requestFlush; +// The position of the next task to execute in the task queue. This is +// preserved between calls to `flush` so that it can be resumed if +// a task throws an exception. +var index = 0; +// If a task schedules additional tasks recursively, the task queue can grow +// unbounded. To prevent memory exhaustion, the task queue will periodically +// truncate already-completed tasks. +var capacity = 1024; + +// The flush function processes all tasks that have been scheduled with +// `rawAsap` unless and until one of those tasks throws an exception. +// If a task throws an exception, `flush` ensures that its state will remain +// consistent and will resume where it left off when called again. +// However, `flush` does not make any arrangements to be called again if an +// exception is thrown. +function flush() { + while (index < queue.length) { + var currentIndex = index; + // Advance the index before calling the task. This ensures that we will + // begin flushing on the next task the task throws an error. + index = index + 1; + queue[currentIndex].call(); + // Prevent leaking memory for long chains of recursive calls to `asap`. + // If we call `asap` within tasks scheduled by `asap`, the queue will + // grow, but to avoid an O(n) walk for every task we execute, we don't + // shift tasks off the queue after they have been executed. + // Instead, we periodically shift 1024 tasks off the queue. + if (index > capacity) { + // Manually shift all values starting at the index back to the + // beginning of the queue. + for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) { + queue[scan] = queue[scan + index]; + } + queue.length -= index; + index = 0; + } + } + queue.length = 0; + index = 0; + flushing = false; +} + +// `requestFlush` is implemented using a strategy based on data collected from +// every available SauceLabs Selenium web driver worker at time of writing. +// https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593 + +// Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that +// have WebKitMutationObserver but not un-prefixed MutationObserver. +// Must use `global` or `self` instead of `window` to work in both frames and web +// workers. `global` is a provision of Browserify, Mr, Mrs, or Mop. + +/* globals self */ +var scope = typeof global !== "undefined" ? global : self; +var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver; + +// MutationObservers are desirable because they have high priority and work +// reliably everywhere they are implemented. +// They are implemented in all modern browsers. +// +// - Android 4-4.3 +// - Chrome 26-34 +// - Firefox 14-29 +// - Internet Explorer 11 +// - iPad Safari 6-7.1 +// - iPhone Safari 7-7.1 +// - Safari 6-7 +if (typeof BrowserMutationObserver === "function") { + requestFlush = makeRequestCallFromMutationObserver(flush); + +// MessageChannels are desirable because they give direct access to the HTML +// task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera +// 11-12, and in web workers in many engines. +// Although message channels yield to any queued rendering and IO tasks, they +// would be better than imposing the 4ms delay of timers. +// However, they do not work reliably in Internet Explorer or Safari. + +// Internet Explorer 10 is the only browser that has setImmediate but does +// not have MutationObservers. +// Although setImmediate yields to the browser's renderer, it would be +// preferrable to falling back to setTimeout since it does not have +// the minimum 4ms penalty. +// Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and +// Desktop to a lesser extent) that renders both setImmediate and +// MessageChannel useless for the purposes of ASAP. +// https://github.com/kriskowal/q/issues/396 + +// Timers are implemented universally. +// We fall back to timers in workers in most engines, and in foreground +// contexts in the following browsers. +// However, note that even this simple case requires nuances to operate in a +// broad spectrum of browsers. +// +// - Firefox 3-13 +// - Internet Explorer 6-9 +// - iPad Safari 4.3 +// - Lynx 2.8.7 +} else { + requestFlush = makeRequestCallFromTimer(flush); +} + +// `requestFlush` requests that the high priority event queue be flushed as +// soon as possible. +// This is useful to prevent an error thrown in a task from stalling the event +// queue if the exception handled by Node.js’s +// `process.on("uncaughtException")` or by a domain. +rawAsap.requestFlush = requestFlush; + +// To request a high priority event, we induce a mutation observer by toggling +// the text of a text node between "1" and "-1". +function makeRequestCallFromMutationObserver(callback) { + var toggle = 1; + var observer = new BrowserMutationObserver(callback); + var node = document.createTextNode(""); + observer.observe(node, {characterData: true}); + return function requestCall() { + toggle = -toggle; + node.data = toggle; + }; +} + +// The message channel technique was discovered by Malte Ubl and was the +// original foundation for this library. +// http://www.nonblocking.io/2011/06/windownexttick.html + +// Safari 6.0.5 (at least) intermittently fails to create message ports on a +// page's first load. Thankfully, this version of Safari supports +// MutationObservers, so we don't need to fall back in that case. + +// function makeRequestCallFromMessageChannel(callback) { +// var channel = new MessageChannel(); +// channel.port1.onmessage = callback; +// return function requestCall() { +// channel.port2.postMessage(0); +// }; +// } + +// For reasons explained above, we are also unable to use `setImmediate` +// under any circumstances. +// Even if we were, there is another bug in Internet Explorer 10. +// It is not sufficient to assign `setImmediate` to `requestFlush` because +// `setImmediate` must be called *by name* and therefore must be wrapped in a +// closure. +// Never forget. + +// function makeRequestCallFromSetImmediate(callback) { +// return function requestCall() { +// setImmediate(callback); +// }; +// } + +// Safari 6.0 has a problem where timers will get lost while the user is +// scrolling. This problem does not impact ASAP because Safari 6.0 supports +// mutation observers, so that implementation is used instead. +// However, if we ever elect to use timers in Safari, the prevalent work-around +// is to add a scroll event listener that calls for a flush. + +// `setTimeout` does not call the passed callback if the delay is less than +// approximately 7 in web workers in Firefox 8 through 18, and sometimes not +// even then. + +function makeRequestCallFromTimer(callback) { + return function requestCall() { + // We dispatch a timeout with a specified delay of 0 for engines that + // can reliably accommodate that request. This will usually be snapped + // to a 4 milisecond delay, but once we're flushing, there's no delay + // between events. + var timeoutHandle = setTimeout(handleTimer, 0); + // However, since this timer gets frequently dropped in Firefox + // workers, we enlist an interval handle that will try to fire + // an event 20 times per second until it succeeds. + var intervalHandle = setInterval(handleTimer, 50); + + function handleTimer() { + // Whichever timer succeeds will cancel both timers and + // execute the callback. + clearTimeout(timeoutHandle); + clearInterval(intervalHandle); + callback(); + } + }; +} + +// This is for `asap.js` only. +// Its name will be periodically randomized to break any code that depends on +// its existence. +rawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer; + +// ASAP was originally a nextTick shim included in Q. This was factored out +// into this ASAP package. It was later adapted to RSVP which made further +// amendments. These decisions, particularly to marginalize MessageChannel and +// to capture the MutationObserver implementation in a closure, were integrated +// back into ASAP proper. +// https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],11:[function(_dereq_,module,exports){ +'use strict' + +var assert = _dereq_('assert-ok') +var format = _dereq_('simple-format') +var print = _dereq_('print-value') + +module.exports = function assertEqual (a, b) { + assert(a === b, format('expected `%s` to equal `%s`', print(a), print(b))) +} + +},{"assert-ok":13,"print-value":30,"simple-format":32}],12:[function(_dereq_,module,exports){ +'use strict' + +module.exports = function assertFunction (value) { + if (typeof value !== 'function') { + throw new TypeError('Expected function, got: ' + value) + } +} + +},{}],13:[function(_dereq_,module,exports){ +'use strict' + +module.exports = function assertOk (value, message) { + if (!value) { + throw new Error(message || 'Expected true, got ' + value) + } +} + +},{}],14:[function(_dereq_,module,exports){ +'use strict' + +module.exports = CallAll + +function CallAll (fns) { + fns = Array.isArray(fns) ? fns : arguments + return function callAll () { + var args = arguments + var ret = new Array(fns.length) + for (var i = 0, ii = fns.length; i < ii; i++) { + ret[i] = fns[i].apply(null, args) + } + return ret + } +} + +},{}],15:[function(_dereq_,module,exports){ +/** + * cuid.js + * Collision-resistant UID generator for browsers and node. + * Sequential for fast db lookups and recency sorting. + * Safe for element IDs and server-side lookups. + * + * Extracted from CLCTR + * + * Copyright (c) Eric Elliott 2012 + * MIT License + */ + +/*global window, navigator, document, require, process, module */ +(function (app) { + 'use strict'; + var namespace = 'cuid', + c = 0, + blockSize = 4, + base = 36, + discreteValues = Math.pow(base, blockSize), + + pad = function pad(num, size) { + var s = "000000000" + num; + return s.substr(s.length-size); + }, + + randomBlock = function randomBlock() { + return pad((Math.random() * + discreteValues << 0) + .toString(base), blockSize); + }, + + safeCounter = function () { + c = (c < discreteValues) ? c : 0; + c++; // this is not subliminal + return c - 1; + }, + + api = function cuid() { + // Starting with a lowercase letter makes + // it HTML element ID friendly. + var letter = 'c', // hard-coded allows for sequential access + + // timestamp + // warning: this exposes the exact date and time + // that the uid was created. + timestamp = (new Date().getTime()).toString(base), + + // Prevent same-machine collisions. + counter, + + // A few chars to generate distinct ids for different + // clients (so different computers are far less + // likely to generate the same id) + fingerprint = api.fingerprint(), + + // Grab some more chars from Math.random() + random = randomBlock() + randomBlock(); + + counter = pad(safeCounter().toString(base), blockSize); + + return (letter + timestamp + counter + fingerprint + random); + }; + + api.slug = function slug() { + var date = new Date().getTime().toString(36), + counter, + print = api.fingerprint().slice(0,1) + + api.fingerprint().slice(-1), + random = randomBlock().slice(-2); + + counter = safeCounter().toString(36).slice(-4); + + return date.slice(-2) + + counter + print + random; + }; + + api.globalCount = function globalCount() { + // We want to cache the results of this + var cache = (function calc() { + var i, + count = 0; + + for (i in window) { + count++; + } + + return count; + }()); + + api.globalCount = function () { return cache; }; + return cache; + }; + + api.fingerprint = function browserPrint() { + return pad((navigator.mimeTypes.length + + navigator.userAgent.length).toString(36) + + api.globalCount().toString(36), 4); + }; + + // don't change anything from here down. + if (app.register) { + app.register(namespace, api); + } else if (typeof module !== 'undefined') { + module.exports = api; + } else { + app[namespace] = api; + } + +}(this.applitude || this)); + +},{}],16:[function(_dereq_,module,exports){ +var wrappy = _dereq_('wrappy') +module.exports = wrappy(dezalgo) + +var asap = _dereq_('asap') + +function dezalgo (cb) { + var sync = true + asap(function () { + sync = false + }) + + return function zalgoSafe() { + var args = arguments + var me = this + if (sync) + asap(function() { + cb.apply(me, args) + }) + else + cb.apply(me, args) + } +} + +},{"asap":9,"wrappy":35}],17:[function(_dereq_,module,exports){ +'use strict'; +var isObj = _dereq_('is-obj'); + +module.exports.get = function (obj, path) { + if (!isObj(obj) || typeof path !== 'string') { + return obj; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + var descriptor = Object.getOwnPropertyDescriptor(obj, pathArr[i]) || Object.getOwnPropertyDescriptor(Object.prototype, pathArr[i]); + if (descriptor && !descriptor.enumerable) { + return; + } + + obj = obj[pathArr[i]]; + + if (obj === undefined || obj === null) { + // `obj` is either `undefined` or `null` so we want to stop the loop, and + // if this is not the last bit of the path, and + // if it did't return `undefined` + // it would return `null` if `obj` is `null` + // but we want `get({foo: null}, 'foo.bar')` to equal `undefined` not `null` + if (i !== pathArr.length - 1) { + return undefined; + } + + break; + } + } + + return obj; +}; + +module.exports.set = function (obj, path, value) { + if (!isObj(obj) || typeof path !== 'string') { + return; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + var p = pathArr[i]; + + if (!isObj(obj[p])) { + obj[p] = {}; + } + + if (i === pathArr.length - 1) { + obj[p] = value; + } + + obj = obj[p]; + } +}; + +module.exports.delete = function (obj, path) { + if (!isObj(obj) || typeof path !== 'string') { + return; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + var p = pathArr[i]; + + if (i === pathArr.length - 1) { + delete obj[p]; + return; + } + + obj = obj[p]; + } +}; + +module.exports.has = function (obj, path) { + if (!isObj(obj) || typeof path !== 'string') { + return false; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + obj = obj[pathArr[i]]; + + if (obj === undefined) { + return false; + } + } + + return true; +}; + +function getPathSegments(path) { + var pathArr = path.split('.'); + var parts = []; + + for (var i = 0; i < pathArr.length; i++) { + var p = pathArr[i]; + + while (p[p.length - 1] === '\\' && pathArr[i + 1] !== undefined) { + p = p.slice(0, -1) + '.'; + p += pathArr[++i]; + } + + parts.push(p); + } + + return parts; +} + +},{"is-obj":21}],18:[function(_dereq_,module,exports){ +'use strict' + +var assertFn = _dereq_('assert-function') + +module.exports = Ear + +function Ear () { + var callbacks = [] + + function listeners () { + var args = arguments + var i = 0 + var length = callbacks.length + for (; i < length; i++) { + var callback = callbacks[i] + callback.apply(null, args) + } + } + + listeners.add = function (listener) { + assertFn(listener) + callbacks.push(listener) + return function remove () { + var i = 0 + var length = callbacks.length + for (; i < length; i++) { + if (callbacks[i] === listener) { + callbacks.splice(i, 1) + return + } + } + } + } + + return listeners +} + +},{"assert-function":12}],19:[function(_dereq_,module,exports){ +(function (global){ +var win; + +if (typeof window !== "undefined") { + win = window; +} else if (typeof global !== "undefined") { + win = global; +} else if (typeof self !== "undefined"){ + win = self; +} else { + win = {}; +} + +module.exports = win; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}],20:[function(_dereq_,module,exports){ +/*! + * is-number + * + * Copyright (c) 2014 Jon Schlinkert, contributors. + * Licensed under the MIT License + */ + +'use strict'; + +module.exports = function isNumber(n) { + return !!(+n) || n === 0 || n === '0'; +}; + +},{}],21:[function(_dereq_,module,exports){ +'use strict'; +module.exports = function (x) { + var type = typeof x; + return x !== null && (type === 'object' || type === 'function'); +}; + +},{}],22:[function(_dereq_,module,exports){ +module.exports = Array.isArray || function (arr) { + return Object.prototype.toString.call(arr) == '[object Array]'; +}; + +},{}],23:[function(_dereq_,module,exports){ +/*! + * isobject + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +'use strict'; + +var isArray = _dereq_('isarray'); + +module.exports = function isObject(o) { + return o != null && typeof o === 'object' && !isArray(o); +}; + +},{"isarray":22}],24:[function(_dereq_,module,exports){ +exports = module.exports = stringify +exports.getSerialize = serializer + +function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) +} + +function serializer(replacer, cycleReplacer) { + var stack = [], keys = [] + + if (cycleReplacer == null) cycleReplacer = function(key, value) { + if (stack[0] === value) return "[Circular ~]" + return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" + } + + return function(key, value) { + if (stack.length > 0) { + var thisPos = stack.indexOf(this) + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) + if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) + } + else stack.push(value) + + return replacer == null ? value : replacer.call(this, key, value) + } +} + +},{}],25:[function(_dereq_,module,exports){ +'use strict' + +var assert = _dereq_('assert-ok') +var assertEqual = _dereq_('assert-equal') +var dot = _dereq_('dot-prop') +var toArray = _dereq_('to-array') +var last = _dereq_('array-last') +var dezalgo = _dereq_('dezalgo') +var all = _dereq_('call-all-fns') + +module.exports = Lazy + +function Lazy (methods, load) { + assert(Array.isArray(methods), 'methods are required') + assertEqual(typeof load, 'function', 'load fn is required') + + var api = null + var error = null + var queue = [] + + load(function (err, lib) { + error = err + api = lib + all(queue)(err, lib) + queue = null + }) + + return methods.reduce(function (lazy, method) { + dot.set(lazy, method, Deferred(method)) + return lazy + }, {}) + + function Deferred (method) { + return function deferred () { + var args = arguments + onReady(function (err, api) { + if (!err) return dot.get(api, method).apply(null, args) + var callback = last(toArray(args)) + if (typeof callback === 'function') { + return callback(err) + } + }) + } + } + + function onReady (callback) { + callback = dezalgo(callback) + + if (api || error) return callback(error, api) + queue.push(callback) + } +} + +},{"array-last":7,"assert-equal":11,"assert-ok":13,"call-all-fns":14,"dezalgo":16,"dot-prop":26,"to-array":34}],26:[function(_dereq_,module,exports){ +'use strict'; +var isObj = _dereq_('is-obj'); + +module.exports.get = function (obj, path) { + if (!isObj(obj) || typeof path !== 'string') { + return obj; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + obj = obj[pathArr[i]]; + + if (obj === undefined) { + break; + } + } + + return obj; +}; + +module.exports.set = function (obj, path, value) { + if (!isObj(obj) || typeof path !== 'string') { + return; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + var p = pathArr[i]; + + if (!isObj(obj[p])) { + obj[p] = {}; + } + + if (i === pathArr.length - 1) { + obj[p] = value; + } + + obj = obj[p]; + } +}; + +module.exports.delete = function (obj, path) { + if (!isObj(obj) || typeof path !== 'string') { + return; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + var p = pathArr[i]; + + if (i === pathArr.length - 1) { + delete obj[p]; + return; + } + + obj = obj[p]; + } +}; + +module.exports.has = function (obj, path) { + if (!isObj(obj) || typeof path !== 'string') { + return false; + } + + var pathArr = getPathSegments(path); + + for (var i = 0; i < pathArr.length; i++) { + obj = obj[pathArr[i]]; + + if (obj === undefined) { + return false; + } + } + + return true; +}; + +function getPathSegments(path) { + var pathArr = path.split('.'); + var parts = []; + + for (var i = 0; i < pathArr.length; i++) { + var p = pathArr[i]; + + while (p[p.length - 1] === '\\') { + p = p.slice(0, -1) + '.'; + p += pathArr[++i]; + } + + parts.push(p); + } + + return parts; +} + +},{"is-obj":21}],27:[function(_dereq_,module,exports){ +'use strict' + +var load = _dereq_('load-script') +var window = _dereq_('global/window') +var extend = _dereq_('xtend') +var assert = _dereq_('assert-ok') +var dezalgo = _dereq_('dezalgo') +var Listeners = _dereq_('ear') +var extendQuery = _dereq_('query-extend') +var cuid = _dereq_('cuid') + +module.exports = loadGlobal + +var listeners = {} + +function loadGlobal (options, callback) { + assert(options, 'options required') + assert(options.url, 'url required') + assert(options.global, 'global required') + assert(callback, 'callback required') + + options = extend(options) + callback = dezalgo(callback) + + if (getGlobal(options)) { + return callback(null, getGlobal(options)) + } + + callback = cache(options, callback) + if (!callback) return + + if (options.jsonp) { + var id = jsonpCallback(options, callback) + options.url = extendQuery(options.url, {callback: id}) + } + + load(options.url, options, function (err) { + if (err) return callback(err) + if (!options.jsonp) { + var library = getGlobal(options) + if (!library) return callback(new Error('expected: `window.' + options.global + '`, actual: `' + library + '`')) + callback(null, library) + } + }) +} + +function cache (options, callback) { + if (!get()) { + set(Listeners()) + get().add(callback) + return function onComplete (err, lib) { + get()(err, lib) + set(Listeners()) + } + } + + get().add(callback) + return undefined + + function get () { + return listeners[options.global] + } + + function set (value) { + listeners[options.global] = value + } +} + +function getGlobal (options) { + return window[options.global] +} + +function jsonpCallback (options, callback) { + var id = cuid() + window[id] = function jsonpCallback () { + callback(null, getGlobal(options)) + delete window[id] + } + return id +} + +},{"assert-ok":13,"cuid":15,"dezalgo":16,"ear":18,"global/window":19,"load-script":28,"query-extend":31,"xtend":36}],28:[function(_dereq_,module,exports){ + +module.exports = function load (src, opts, cb) { + var head = document.head || document.getElementsByTagName('head')[0] + var script = document.createElement('script') + + if (typeof opts === 'function') { + cb = opts + opts = {} + } + + opts = opts || {} + cb = cb || function() {} + + script.type = opts.type || 'text/javascript' + script.charset = opts.charset || 'utf8'; + script.async = 'async' in opts ? !!opts.async : true + script.src = src + + if (opts.attrs) { + setAttributes(script, opts.attrs) + } + + if (opts.text) { + script.text = '' + opts.text + } + + var onend = 'onload' in script ? stdOnEnd : ieOnEnd + onend(script, cb) + + // some good legacy browsers (firefox) fail the 'in' detection above + // so as a fallback we always set onload + // old IE will ignore this and new IE will set onload + if (!script.onload) { + stdOnEnd(script, cb); + } + + head.appendChild(script) +} + +function setAttributes(script, attrs) { + for (var attr in attrs) { + script.setAttribute(attr, attrs[attr]); + } +} + +function stdOnEnd (script, cb) { + script.onload = function () { + this.onerror = this.onload = null + cb(null, script) + } + script.onerror = function () { + // this.onload = null here is necessary + // because even IE9 works not like others + this.onerror = this.onload = null + cb(new Error('Failed to load ' + this.src), script) + } +} + +function ieOnEnd (script, cb) { + script.onreadystatechange = function () { + if (this.readyState != 'complete' && this.readyState != 'loaded') return + this.onreadystatechange = null + cb(null, script) // there is no way to catch loading errors in IE8 + } +} + +},{}],29:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = function split(str) { + var a = 1, + res = ''; + + var parts = str.split('%'), + len = parts.length; + + if (len > 0) { res += parts[0]; } + + for (var i = 1; i < len; i++) { + if (parts[i][0] === 's' || parts[i][0] === 'd') { + var value = arguments[a++]; + res += parts[i][0] === 'd' ? Math.floor(value) : value; + } else if (parts[i][0]) { + res += '%' + parts[i][0]; + } else { + i++; + res += '%' + parts[i][0]; + } + + res += parts[i].substring(1); + } + + return res; +}; + +},{}],30:[function(_dereq_,module,exports){ +'use strict' + +var isObject = _dereq_('isobject') +var safeStringify = _dereq_('json-stringify-safe') + +module.exports = function print (value) { + var toString = isJson(value) ? stringify : String + return toString(value) +} + +function isJson (value) { + return isObject(value) || Array.isArray(value) +} + +function stringify (value) { + return safeStringify(value, null, '') +} + +},{"isobject":23,"json-stringify-safe":24}],31:[function(_dereq_,module,exports){ +!function(glob) { + + var queryToObject = function(query) { + var obj = {}; + if (!query) return obj; + each(query.split('&'), function(val) { + var pieces = val.split('='); + var key = parseKey(pieces[0]); + var keyDecoded = decodeURIComponent(key.val); + var valDecoded = pieces[1] && decodeURIComponent(pieces[1]); + + if (key.type === 'array') { + if (!obj[keyDecoded]) obj[keyDecoded] = []; + obj[keyDecoded].push(valDecoded); + } else if (key.type === 'string') { + obj[keyDecoded] = valDecoded; + } + }); + return obj; + }; + + var objectToQuery = function(obj) { + var pieces = [], encodedKey; + for (var k in obj) { + if (!obj.hasOwnProperty(k)) continue; + if (typeof obj[k] === 'undefined') { + pieces.push(encodeURIComponent(k)); + continue; + } + encodedKey = encodeURIComponent(k); + if (isArray(obj[k])) { + each(obj[k], function(val) { + pieces.push(encodedKey + '[]=' + encodeURIComponent(val)); + }); + continue; + } + pieces.push(encodedKey + '=' + encodeURIComponent(obj[k])); + } + return pieces.length ? ('?' + pieces.join('&')) : ''; + }; + + // for now we will only support string and arrays + var parseKey = function(key) { + var pos = key.indexOf('['); + if (pos === -1) return { type: 'string', val: key }; + return { type: 'array', val: key.substr(0, pos) }; + }; + + var isArray = function(val) { + return Object.prototype.toString.call(val) === '[object Array]'; + }; + + var extract = function(url) { + var pos = url.lastIndexOf('?'); + var hasQuery = pos !== -1; + var base = void 0; + + if (hasQuery && pos > 0) { + base = url.substring(0, pos); + } else if (!hasQuery && (url && url.length > 0)) { + base = url; + } + + return { + base: base, + query: hasQuery ? url.substring(pos+1) : void 0 + }; + }; + + // thanks raynos! + // https://github.com/Raynos/xtend + var extend = function() { + var target = {}; + for (var i = 0; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (source.hasOwnProperty(key)) { + target[key] = source[key]; + } + } + } + return target; + }; + + var queryExtend = function() { + var args = Array.prototype.slice.call(arguments, 0); + var asObject = args[args.length-1] === true; + var base = ''; + + if (!args.length) { + return base; + } + + if (asObject) { + args.pop(); + } + + var normalized = map(args, function(param) { + if (typeof param === 'string') { + var extracted = extract(param); + if (extracted.base) base = extracted.base; + return queryToObject(extracted.query); + } + return param; + }); + + if (asObject) { + return extend.apply({}, normalized); + } else { + return base + objectToQuery(extend.apply({}, normalized)); + } + + }; + + var each = function(arr, fn) { + for (var i = 0, l = arr.length; i < l; i++) { + fn(arr[i], i); + } + }; + + var map = function(arr, fn) { + var res = []; + for (var i = 0, l = arr.length; i < l; i++) { + res.push( fn(arr[i], i) ); + } + return res; + }; + + if (typeof module !== 'undefined' && module.exports) { + // Node.js / browserify + module.exports = queryExtend; + } else if (typeof define === 'function' && define.amd) { + // require.js / AMD + define(function() { + return queryExtend; + }); + } else { + // +